mirror of
https://github.com/mRemoteNG/mRemoteNG.git
synced 2026-02-17 22:11:48 +08:00
Compare commits
4746 Commits
1.72
...
2025.02.05
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
59ac3020fe | ||
|
|
f0e4cee613 | ||
|
|
0f395dad9e | ||
|
|
7e59362b5c | ||
|
|
e69492e94f | ||
|
|
2b31b173c0 | ||
|
|
8d3a1ab3a7 | ||
|
|
0349c5b402 | ||
|
|
0b7be8b0c6 | ||
|
|
3a8ca5fb4a | ||
|
|
c88b50ad89 | ||
|
|
3746ba1819 | ||
|
|
945bff3c17 | ||
|
|
d7e318b1f8 | ||
|
|
eb3db198a1 | ||
|
|
b380d32ce1 | ||
|
|
7a6056c95f | ||
|
|
a908a783be | ||
|
|
1ae02885cf | ||
|
|
8ea210e14f | ||
|
|
1ae9960e82 | ||
|
|
bed946988a | ||
|
|
e42632b82d | ||
|
|
5aa8425209 | ||
|
|
b794aa8089 | ||
|
|
e8bb0bffaa | ||
|
|
e1e0661f25 | ||
|
|
7848c83c72 | ||
|
|
2de37ed9c7 | ||
|
|
425b433cd2 | ||
|
|
44e07e3cdb | ||
|
|
689f882104 | ||
|
|
207beaee12 | ||
|
|
bd21b85de7 | ||
|
|
2e7579cac5 | ||
|
|
ec63812af0 | ||
|
|
8dfe1a22c9 | ||
|
|
c0e3f547ec | ||
|
|
dafb1b364c | ||
|
|
7d1b9d8635 | ||
|
|
8041f58ffd | ||
|
|
d018fdbace | ||
|
|
489602ffb3 | ||
|
|
600ba73f5f | ||
|
|
88600ae6df | ||
|
|
5e3bce9a92 | ||
|
|
9b28b7057d | ||
|
|
0a17220446 | ||
|
|
7684b19d13 | ||
|
|
47baf3b662 | ||
|
|
bc5e2b9437 | ||
|
|
cf0ef6d9d0 | ||
|
|
b6f3047f82 | ||
|
|
0a418561d7 | ||
|
|
6da79a41df | ||
|
|
3ddeac6c74 | ||
|
|
98f97c442d | ||
|
|
472d33ba12 | ||
|
|
d906cfcc1e | ||
|
|
0f0d8bdcdb | ||
|
|
005b2485f6 | ||
|
|
ffd90bed70 | ||
|
|
70264a24ab | ||
|
|
ad3a37fde3 | ||
|
|
9dac0eeaac | ||
|
|
8dac393bb4 | ||
|
|
287d1a5575 | ||
|
|
20278e0e53 | ||
|
|
a3c3ec8c5c | ||
|
|
3039b25a66 | ||
|
|
0b7ce92af8 | ||
|
|
e3ec521a1d | ||
|
|
88999263a7 | ||
|
|
9687ccd01d | ||
|
|
ca62502499 | ||
|
|
8b3a68f58e | ||
|
|
08eb35a360 | ||
|
|
dec82b41f2 | ||
|
|
b9ab5c76f7 | ||
|
|
6de0c536e8 | ||
|
|
3f0aca97bf | ||
|
|
ccb51a2603 | ||
|
|
99d9e70921 | ||
|
|
b8b54271dd | ||
|
|
49a1549e5a | ||
|
|
b3f936c194 | ||
|
|
f3b10d4c20 | ||
|
|
94b17037d0 | ||
|
|
83f3846ce6 | ||
|
|
711a751117 | ||
|
|
e013e32792 | ||
|
|
6e64b09256 | ||
|
|
e32d268587 | ||
|
|
05635dec93 | ||
|
|
e694923d33 | ||
|
|
7322683465 | ||
|
|
64688f3512 | ||
|
|
762760af2f | ||
|
|
48e6881401 | ||
|
|
95c958136f | ||
|
|
53a6be0f82 | ||
|
|
0b4d95be1c | ||
|
|
aad3948475 | ||
|
|
093370081e | ||
|
|
33426ceee9 | ||
|
|
5f5700b948 | ||
|
|
802e9abfef | ||
|
|
45149d6547 | ||
|
|
595681a982 | ||
|
|
e1cac723d6 | ||
|
|
683f84f053 | ||
|
|
b6d882a06f | ||
|
|
9d4bfbcb1c | ||
|
|
b563747046 | ||
|
|
1ebd0a430f | ||
|
|
ef4366be4d | ||
|
|
484283fef8 | ||
|
|
b546a48a5f | ||
|
|
51bd64d005 | ||
|
|
2c65d12b92 | ||
|
|
c43a3b5115 | ||
|
|
515778b13d | ||
|
|
bb4b9ce9e7 | ||
|
|
1d6707fecd | ||
|
|
5b2a46ed91 | ||
|
|
24fa97da4c | ||
|
|
b0109c2581 | ||
|
|
20f237ec4c | ||
|
|
6ea535ae29 | ||
|
|
57ca8a3eee | ||
|
|
307ea42a0f | ||
|
|
ac41c04f67 | ||
|
|
b3496e79de | ||
|
|
4f7aa755e3 | ||
|
|
d3af50f009 | ||
|
|
ba77a7b7c0 | ||
|
|
666a0e1537 | ||
|
|
96f4307910 | ||
|
|
81e997eb60 | ||
|
|
7132c6e7c2 | ||
|
|
cb01e92fe2 | ||
|
|
2c9c512d74 | ||
|
|
8d05c9040b | ||
|
|
4e994fb202 | ||
|
|
ab2a778694 | ||
|
|
115375820d | ||
|
|
8cb753916b | ||
|
|
ec3a0cee9b | ||
|
|
13f8f82537 | ||
|
|
54544dd2aa | ||
|
|
ac99115424 | ||
|
|
d6c6038193 | ||
|
|
c97b84e4a2 | ||
|
|
6698ec0b06 | ||
|
|
290b1c4de5 | ||
|
|
d497a50727 | ||
|
|
2837c91f5e | ||
|
|
9a717f28c6 | ||
|
|
e416c2c9c9 | ||
|
|
331cdaa6ce | ||
|
|
aa329b1a9a | ||
|
|
16c080942d | ||
|
|
4acc111b3d | ||
|
|
afe453d371 | ||
|
|
e93c120dac | ||
|
|
2f701458f4 | ||
|
|
6a35961dfb | ||
|
|
1a90095ba3 | ||
|
|
5dc87213b5 | ||
|
|
ff8044b09b | ||
|
|
f1fa40f6fd | ||
|
|
9bc5b62828 | ||
|
|
5aae586812 | ||
|
|
bc5e5be42e | ||
|
|
be68573378 | ||
|
|
cf0bc3d270 | ||
|
|
d5773eb2e9 | ||
|
|
50523e5b67 | ||
|
|
feeca86c6a | ||
|
|
a2e2671483 | ||
|
|
0b4d6ef8ef | ||
|
|
bdf0e532a5 | ||
|
|
d8cf72d09b | ||
|
|
6c3ec8f058 | ||
|
|
fbd9b76a09 | ||
|
|
bee216220a | ||
|
|
4ca7f34989 | ||
|
|
51015e6b51 | ||
|
|
46100d6d94 | ||
|
|
c94eac3863 | ||
|
|
965b0e5b6d | ||
|
|
000cea1dda | ||
|
|
6d4d60e3b1 | ||
|
|
9957282bfa | ||
|
|
9b603dd956 | ||
|
|
759ee55780 | ||
|
|
93b2edb5a5 | ||
|
|
4d49c14bb9 | ||
|
|
6398808a0a | ||
|
|
64e20f3665 | ||
|
|
3b542d8868 | ||
|
|
d5af0e43a5 | ||
|
|
7a8ef87dc7 | ||
|
|
7a5293cde3 | ||
|
|
26400631a0 | ||
|
|
97a5c584be | ||
|
|
0700b6573c | ||
|
|
d8f66a4188 | ||
|
|
c22971cf54 | ||
|
|
9d2dd5dd93 | ||
|
|
c19d512631 | ||
|
|
9efda5fef3 | ||
|
|
a27cdcfdb5 | ||
|
|
5667a07b25 | ||
|
|
7d0bd85466 | ||
|
|
4f269442a6 | ||
|
|
16d182fca2 | ||
|
|
6d1a206cc9 | ||
|
|
d8a7fdbf4f | ||
|
|
516b365ebc | ||
|
|
6da6a5ede9 | ||
|
|
67244f1957 | ||
|
|
0a304e9a03 | ||
|
|
05154af606 | ||
|
|
7454b15ec9 | ||
|
|
38f435f639 | ||
|
|
5f38ec7210 | ||
|
|
ca143bf969 | ||
|
|
402e5c3665 | ||
|
|
290dbcc3ed | ||
|
|
a7749c70e7 | ||
|
|
9f0cfd5f22 | ||
|
|
f47434c09e | ||
|
|
795a4b571e | ||
|
|
f71727e05e | ||
|
|
d2fa8e86b1 | ||
|
|
23dcd9d30f | ||
|
|
d6890c6ecd | ||
|
|
b1a6ba78d4 | ||
|
|
e6abe3f3a1 | ||
|
|
6c32540ecb | ||
|
|
951ad5cd0a | ||
|
|
5f56477123 | ||
|
|
3180f38a1d | ||
|
|
07f15993e5 | ||
|
|
fc8e9c7689 | ||
|
|
7cb20b9e28 | ||
|
|
ffc2351d64 | ||
|
|
f10fec61ac | ||
|
|
e41200067c | ||
|
|
30f6d3dde7 | ||
|
|
7f9dcb11f0 | ||
|
|
d5fa2f62c0 | ||
|
|
fa7967190d | ||
|
|
20e04c0d75 | ||
|
|
d5eea9db20 | ||
|
|
8cb598ce25 | ||
|
|
f3a57d7f23 | ||
|
|
14e47def9d | ||
|
|
7573081d7a | ||
|
|
aaca581324 | ||
|
|
43c2797a51 | ||
|
|
da6bbf65f0 | ||
|
|
7d0cbf423e | ||
|
|
d94eab71da | ||
|
|
b0a8ccc5b6 | ||
|
|
5cbf1f1568 | ||
|
|
22cde74db2 | ||
|
|
cdc3759c04 | ||
|
|
38abc8a167 | ||
|
|
7c7c7086ce | ||
|
|
50daf64025 | ||
|
|
dc38df5389 | ||
|
|
a46b2c9d98 | ||
|
|
200756361c | ||
|
|
877a1e4cd0 | ||
|
|
a382206f5a | ||
|
|
1af7a622a6 | ||
|
|
3f95b2aa7e | ||
|
|
b7871e2e04 | ||
|
|
6b6ceda497 | ||
|
|
b01b8e4bc8 | ||
|
|
20f7cb1cd0 | ||
|
|
3e1ee0056d | ||
|
|
a3a851e57f | ||
|
|
cf650d4318 | ||
|
|
58e7420f04 | ||
|
|
aedaf084d9 | ||
|
|
c18f638989 | ||
|
|
fb50cdc058 | ||
|
|
68e702c344 | ||
|
|
651b18a8b3 | ||
|
|
2174e56407 | ||
|
|
ea27a4da02 | ||
|
|
321fd1162b | ||
|
|
c51f38ddc7 | ||
|
|
b34cd1acc5 | ||
|
|
43f20bb83a | ||
|
|
79b8ddef15 | ||
|
|
2642ea0601 | ||
|
|
7a2f934f6a | ||
|
|
f299fefcdc | ||
|
|
209319f460 | ||
|
|
5ec4f4dcea | ||
|
|
16f67d58d1 | ||
|
|
542d794ab4 | ||
|
|
ede1573b56 | ||
|
|
bc8eec8887 | ||
|
|
59b0654b95 | ||
|
|
cae3477591 | ||
|
|
6a3115b80a | ||
|
|
1232d7c288 | ||
|
|
8e1b4c9271 | ||
|
|
0f8810e22a | ||
|
|
7bbff38b6b | ||
|
|
4a7b8fa250 | ||
|
|
53acc94976 | ||
|
|
ab8d83b12d | ||
|
|
713a0a6174 | ||
|
|
a3b9695b81 | ||
|
|
85dd381bd9 | ||
|
|
05b418e9b6 | ||
|
|
40af584afe | ||
|
|
93fe32491b | ||
|
|
4b67fcb0d4 | ||
|
|
0b0c92717d | ||
|
|
4564cafa85 | ||
|
|
2e21f8d03a | ||
|
|
4336254d56 | ||
|
|
bc5e9279a5 | ||
|
|
1bfbb956c2 | ||
|
|
b336db773c | ||
|
|
30e7d2424c | ||
|
|
cbb8c0234d | ||
|
|
95469107fd | ||
|
|
ba0058c0b9 | ||
|
|
8f35afe353 | ||
|
|
aaf219eb90 | ||
|
|
1167794d58 | ||
|
|
929babd69a | ||
|
|
870b7c1ffd | ||
|
|
9ed9d835b9 | ||
|
|
985feb4b91 | ||
|
|
c43c85bb21 | ||
|
|
038074131c | ||
|
|
ab9156a7d3 | ||
|
|
f4b76818e8 | ||
|
|
12a399a354 | ||
|
|
dfdbbea85c | ||
|
|
5fad5ec4c7 | ||
|
|
fe94da4727 | ||
|
|
361ae3af50 | ||
|
|
a2e302ebfa | ||
|
|
186ab9c00e | ||
|
|
9a2453524a | ||
|
|
3a2e128e98 | ||
|
|
1bd2dbf9f0 | ||
|
|
b7f880e7f8 | ||
|
|
13cd926b4f | ||
|
|
6fb7b1c6e4 | ||
|
|
8f35015a6c | ||
|
|
a926bccb97 | ||
|
|
b9f2abf5eb | ||
|
|
8dc5bda171 | ||
|
|
d24bdb543b | ||
|
|
dc921161e4 | ||
|
|
8e248cb545 | ||
|
|
ae93be4cfb | ||
|
|
42949fbe91 | ||
|
|
21dd175456 | ||
|
|
a3df0da26b | ||
|
|
6b9d4fd107 | ||
|
|
fac2ae6399 | ||
|
|
46732803c3 | ||
|
|
39d205bd4d | ||
|
|
8e2bd7997e | ||
|
|
76db9a6c62 | ||
|
|
c03d5e891d | ||
|
|
b21f6e8ce7 | ||
|
|
4b916f1888 | ||
|
|
1f80e5339b | ||
|
|
dc616f8ea1 | ||
|
|
dc6588243f | ||
|
|
690d3b0d4e | ||
|
|
4bc5dffabb | ||
|
|
55a1e4a544 | ||
|
|
5cb32dd75a | ||
|
|
f858c9fe48 | ||
|
|
0ac39af404 | ||
|
|
fc757b236f | ||
|
|
a921e6e3d4 | ||
|
|
8cf0f50565 | ||
|
|
575ad7664f | ||
|
|
be17488070 | ||
|
|
f504107928 | ||
|
|
0113f549c5 | ||
|
|
9ff831faab | ||
|
|
a9ca243c0b | ||
|
|
553bbef45f | ||
|
|
8f713f861b | ||
|
|
324054b9d7 | ||
|
|
d3fc9404ee | ||
|
|
5bb780439b | ||
|
|
f0e6008441 | ||
|
|
39dd69f15e | ||
|
|
7b2e89df26 | ||
|
|
aa853f8481 | ||
|
|
83bba75af6 | ||
|
|
23889aa5b1 | ||
|
|
5e6094fc42 | ||
|
|
a4181cb6d6 | ||
|
|
88c49f0722 | ||
|
|
513d9e199c | ||
|
|
e80975c56e | ||
|
|
9051ac102c | ||
|
|
39a9b2e619 | ||
|
|
dbc55d248f | ||
|
|
2b46180bfb | ||
|
|
5abe6c7e27 | ||
|
|
4595ebeb9a | ||
|
|
815c08e6d4 | ||
|
|
a72ad218a0 | ||
|
|
944ad1f769 | ||
|
|
e17a68f61c | ||
|
|
0b8196be68 | ||
|
|
d9c01148b7 | ||
|
|
2b3cfd992f | ||
|
|
7e4bd7a6f3 | ||
|
|
161e0ed637 | ||
|
|
1ee03e863c | ||
|
|
2411481d8b | ||
|
|
0314a627ed | ||
|
|
4d339a0b09 | ||
|
|
c171e7f94b | ||
|
|
eac4d966d9 | ||
|
|
c20868c20c | ||
|
|
bb74d46f1f | ||
|
|
152d48c583 | ||
|
|
b7a0155ba4 | ||
|
|
0414724b21 | ||
|
|
469f21db8d | ||
|
|
b6c9d30195 | ||
|
|
7b89430317 | ||
|
|
5bd854116a | ||
|
|
3fb7575529 | ||
|
|
a3a868c2ce | ||
|
|
1eb6fc2235 | ||
|
|
5ff4502f0a | ||
|
|
3126b8d4b0 | ||
|
|
dfb1d34b8a | ||
|
|
ace62c07be | ||
|
|
ec2f3024b6 | ||
|
|
25543f6561 | ||
|
|
3278657da7 | ||
|
|
90c4d12688 | ||
|
|
4ceaea99f7 | ||
|
|
585de34ef1 | ||
|
|
60e1a3ce93 | ||
|
|
413d77ff1a | ||
|
|
d8bb561063 | ||
|
|
5dff2c20b2 | ||
|
|
27803e7787 | ||
|
|
58f9c1575f | ||
|
|
7b909665b9 | ||
|
|
11eee991a1 | ||
|
|
7056a859ef | ||
|
|
2455f0d73e | ||
|
|
943d36e23e | ||
|
|
b563ac6e0c | ||
|
|
ed37a8ca2a | ||
|
|
55bee8b0d8 | ||
|
|
6bd18c29e3 | ||
|
|
37fc611767 | ||
|
|
f95fe351e2 | ||
|
|
2fb0ab1d91 | ||
|
|
85f7be1d79 | ||
|
|
b3e5c1abc2 | ||
|
|
9e216c0020 | ||
|
|
a9f00b6a8c | ||
|
|
46e7b02da2 | ||
|
|
8db0bc6e6f | ||
|
|
1702b5867a | ||
|
|
c03a6452ef | ||
|
|
69b840fb12 | ||
|
|
9385ab287f | ||
|
|
d2b2c39b97 | ||
|
|
f8ff978738 | ||
|
|
d50b32236c | ||
|
|
88d1db2840 | ||
|
|
b1dbe562d6 | ||
|
|
e95b6f1b5d | ||
|
|
1cbb7bb5dd | ||
|
|
34f3ffa129 | ||
|
|
ecd25a673e | ||
|
|
58d0778c3d | ||
|
|
81a5422974 | ||
|
|
eb859e5ede | ||
|
|
c8035b5071 | ||
|
|
a98a336e52 | ||
|
|
d7f6535b85 | ||
|
|
f9d4e3152b | ||
|
|
69bd816aeb | ||
|
|
783810749c | ||
|
|
92c3c967ba | ||
|
|
c632ba4306 | ||
|
|
e29d2c25ba | ||
|
|
5cfdc96cd2 | ||
|
|
a20e10b342 | ||
|
|
39b7414553 | ||
|
|
0eef20caf6 | ||
|
|
859d12b450 | ||
|
|
cd7c594b76 | ||
|
|
39862e15f6 | ||
|
|
87b0cf5c3f | ||
|
|
a14219e1e2 | ||
|
|
ee4660707c | ||
|
|
ccdf15c79c | ||
|
|
b31362afab | ||
|
|
74d6c88565 | ||
|
|
638f64b888 | ||
|
|
ffff9c1529 | ||
|
|
39968c7c6e | ||
|
|
2c3edf0ff2 | ||
|
|
2c4445a2d9 | ||
|
|
09114a5ed3 | ||
|
|
22e7825d65 | ||
|
|
62862141a8 | ||
|
|
920461920c | ||
|
|
f5d0e93ecd | ||
|
|
952a2f536b | ||
|
|
fbb9d849b4 | ||
|
|
65a13dee68 | ||
|
|
09d26b37c7 | ||
|
|
47de6905df | ||
|
|
c0cf316c16 | ||
|
|
c055f8069d | ||
|
|
575356214f | ||
|
|
4b5dc4152a | ||
|
|
a023409fe9 | ||
|
|
d36c59658d | ||
|
|
4f832ee70b | ||
|
|
7b97097515 | ||
|
|
86b3cb8d5d | ||
|
|
33e007ad48 | ||
|
|
efea9f0857 | ||
|
|
aa755a0093 | ||
|
|
807e80acbe | ||
|
|
4096247ee8 | ||
|
|
782d09ddbc | ||
|
|
4c304e11f3 | ||
|
|
aee497de03 | ||
|
|
823f8614e5 | ||
|
|
b54afb8823 | ||
|
|
22e611c63b | ||
|
|
3deb01a65d | ||
|
|
6f24b0bb71 | ||
|
|
9d7921f538 | ||
|
|
a4e66b3d11 | ||
|
|
3866ff76f9 | ||
|
|
82f947abdd | ||
|
|
47c6f53b43 | ||
|
|
606657524e | ||
|
|
168285944c | ||
|
|
1746af927b | ||
|
|
2eca8ffba2 | ||
|
|
6ef0c06a19 | ||
|
|
af68f1a16e | ||
|
|
f429839a17 | ||
|
|
66770e602a | ||
|
|
f760131cf5 | ||
|
|
cdc1d5064f | ||
|
|
3b707d29b9 | ||
|
|
df632bb1f0 | ||
|
|
74cb453995 | ||
|
|
f8eec94b04 | ||
|
|
697efacc58 | ||
|
|
ff9cff8f44 | ||
|
|
d78388646d | ||
|
|
05ffcdeef7 | ||
|
|
3b8e347a9e | ||
|
|
37f160f698 | ||
|
|
6f1446339c | ||
|
|
fed44dc366 | ||
|
|
c71ec34998 | ||
|
|
157bda8f51 | ||
|
|
d80e5bc792 | ||
|
|
f9669e8158 | ||
|
|
04c83c608b | ||
|
|
9f50037e11 | ||
|
|
33fd741ba7 | ||
|
|
035c3f9319 | ||
|
|
3df922340d | ||
|
|
72b9190e2f | ||
|
|
8fe1bdf39a | ||
|
|
f78bf2bc8a | ||
|
|
a1f0a69874 | ||
|
|
8a26b2f433 | ||
|
|
a18784d2c7 | ||
|
|
3c25a987fd | ||
|
|
7cebc4ef42 | ||
|
|
a8ab46d5c8 | ||
|
|
8bac5a8c54 | ||
|
|
801a41ae10 | ||
|
|
47e37afe12 | ||
|
|
942bf7d8cb | ||
|
|
0e8d573819 | ||
|
|
2bdfd7401e | ||
|
|
68dd6b1e23 | ||
|
|
2fe85ae0e5 | ||
|
|
e9fa1c3b5e | ||
|
|
f543b83cfc | ||
|
|
2d917ca11a | ||
|
|
2c830627ef | ||
|
|
46003a3509 | ||
|
|
6b0412586e | ||
|
|
36ca7ffbdb | ||
|
|
e5244f1fec | ||
|
|
359a7272e8 | ||
|
|
82cd3a7e56 | ||
|
|
a2e78fd08a | ||
|
|
a48b5e9d65 | ||
|
|
f4c4d9ea8c | ||
|
|
02ed45fa61 | ||
|
|
ad227ea2ee | ||
|
|
d7e2358d3e | ||
|
|
196e98e4a7 | ||
|
|
00bc45fba1 | ||
|
|
62c0d3f07f | ||
|
|
d42574a16c | ||
|
|
a854b04293 | ||
|
|
96632da4d7 | ||
|
|
cf18d6b1e9 | ||
|
|
262e2e1b85 | ||
|
|
743c46de70 | ||
|
|
9624bc96c9 | ||
|
|
1643bb7d59 | ||
|
|
edad5a838f | ||
|
|
fb35226e20 | ||
|
|
40605737b1 | ||
|
|
01a62e099e | ||
|
|
80748bb66b | ||
|
|
c6c77e9bc4 | ||
|
|
1f462648c4 | ||
|
|
099256e5d4 | ||
|
|
8735d24122 | ||
|
|
b2fc3967d3 | ||
|
|
0ec02cfa56 | ||
|
|
ade36d6876 | ||
|
|
73313d7dae | ||
|
|
7db9865ffa | ||
|
|
40656cb70b | ||
|
|
b5a07f3e79 | ||
|
|
66877ce9b1 | ||
|
|
57b067b4e2 | ||
|
|
74999e9394 | ||
|
|
f4d6f1e5ee | ||
|
|
096f670eb3 | ||
|
|
bceaca49bf | ||
|
|
5e94c84516 | ||
|
|
28dc14e739 | ||
|
|
d5d28e80cb | ||
|
|
f1eea76249 | ||
|
|
0976404eda | ||
|
|
8074c9a7c6 | ||
|
|
513af43fca | ||
|
|
afd5d76992 | ||
|
|
a31da5f880 | ||
|
|
be470f50ec | ||
|
|
a04494346e | ||
|
|
797533459e | ||
|
|
22e879ef18 | ||
|
|
dc6bc5b4d6 | ||
|
|
a6c7457c59 | ||
|
|
b609cfba2a | ||
|
|
345980f457 | ||
|
|
5a86a0a8cb | ||
|
|
24c1ce3f4a | ||
|
|
fc65c2583e | ||
|
|
1df3c33f69 | ||
|
|
98a9cc7436 | ||
|
|
ff88d4649c | ||
|
|
8c61306a8d | ||
|
|
6496e34868 | ||
|
|
7d6c9d517c | ||
|
|
d918d8ffd1 | ||
|
|
ca61ea09d0 | ||
|
|
faff230d42 | ||
|
|
c288c3d071 | ||
|
|
500a22b0b6 | ||
|
|
ba1317d245 | ||
|
|
5cfbfc5071 | ||
|
|
bf4b9d5355 | ||
|
|
904f0bc202 | ||
|
|
94c546bd0f | ||
|
|
64f1e10a5e | ||
|
|
8b20da78ff | ||
|
|
1be4ef437d | ||
|
|
76da9c3247 | ||
|
|
1e4dcc23d7 | ||
|
|
9c95e7fd96 | ||
|
|
cea5f57c60 | ||
|
|
19e6d37b62 | ||
|
|
018979d17f | ||
|
|
8bd398cbd6 | ||
|
|
59872b14cf | ||
|
|
c5240d72e1 | ||
|
|
77b54f9a10 | ||
|
|
b5bcb5b3bb | ||
|
|
87c500138b | ||
|
|
f3f384ebcc | ||
|
|
08de2eacd6 | ||
|
|
b763d968fe | ||
|
|
3b05eba2e2 | ||
|
|
b6d2c72a40 | ||
|
|
e6d2c9791d | ||
|
|
91a0eeb29a | ||
|
|
8c9c4865f2 | ||
|
|
6fbd4a7c88 | ||
|
|
239adaa129 | ||
|
|
9f8c82b649 | ||
|
|
7875bb6ebd | ||
|
|
a33abf3f56 | ||
|
|
1fadadcdb5 | ||
|
|
a294dec0dc | ||
|
|
5de75e6a8a | ||
|
|
4b44be2e54 | ||
|
|
b5f1d63175 | ||
|
|
1c5695df7e | ||
|
|
1808ad9ad7 | ||
|
|
8dc5e81374 | ||
|
|
b899056a8e | ||
|
|
f884c47ad8 | ||
|
|
07eae639b6 | ||
|
|
d2349bd713 | ||
|
|
28ee3c6e8f | ||
|
|
4430f269f7 | ||
|
|
a72f7d63f6 | ||
|
|
859b6f8ac5 | ||
|
|
a7f238aba3 | ||
|
|
e0cff27aae | ||
|
|
d9c4a6ec76 | ||
|
|
4d95bc4255 | ||
|
|
fdea0144b0 | ||
|
|
7398a373c2 | ||
|
|
c88e45cde8 | ||
|
|
f77848e3d9 | ||
|
|
5ed78cc3b6 | ||
|
|
1b4593bb62 | ||
|
|
c620e26644 | ||
|
|
74af24c3db | ||
|
|
ac1f32f773 | ||
|
|
99c7dbb332 | ||
|
|
b0632b8910 | ||
|
|
d26a9615c0 | ||
|
|
f609d6e0b2 | ||
|
|
e5042712b6 | ||
|
|
bf93d3af04 | ||
|
|
ea43abe392 | ||
|
|
0e74314ddc | ||
|
|
54eabd6a74 | ||
|
|
2f52473566 | ||
|
|
98e7250b3c | ||
|
|
882438f5e0 | ||
|
|
bb93dddd39 | ||
|
|
59f6a8a933 | ||
|
|
8ce83f3cd0 | ||
|
|
745e402da9 | ||
|
|
6cbe1c92d5 | ||
|
|
54fc2fb4c6 | ||
|
|
adc2815ab5 | ||
|
|
75f9f647ee | ||
|
|
f14f7dbb72 | ||
|
|
4978618e9a | ||
|
|
59b9f4f15b | ||
|
|
7ff0ce5369 | ||
|
|
93bd278819 | ||
|
|
b97b1f3690 | ||
|
|
307f374be1 | ||
|
|
17f701824e | ||
|
|
c7b89dcf71 | ||
|
|
fc527a947f | ||
|
|
d0520690a2 | ||
|
|
315d020b6f | ||
|
|
8a4bcdef52 | ||
|
|
727ef34c6d | ||
|
|
d8bc06d05d | ||
|
|
def214dde2 | ||
|
|
219f948c4a | ||
|
|
72193eccf0 | ||
|
|
7eb85c6a3d | ||
|
|
954c667173 | ||
|
|
33c738df5f | ||
|
|
2141fe298f | ||
|
|
8e73f512b4 | ||
|
|
92dddb8fd8 | ||
|
|
e9869f4c88 | ||
|
|
52597d4dcb | ||
|
|
dc7ce27b81 | ||
|
|
4242595a66 | ||
|
|
50b9a11503 | ||
|
|
621a723602 | ||
|
|
6260cc3655 | ||
|
|
300e668327 | ||
|
|
3cf274c37a | ||
|
|
2ca356ee5c | ||
|
|
ca8751c40c | ||
|
|
3c7e97d2d9 | ||
|
|
dbe2d690da | ||
|
|
949410e2cc | ||
|
|
100f856b5f | ||
|
|
d7bee01454 | ||
|
|
5566081986 | ||
|
|
29f7dd93f3 | ||
|
|
3dd8db5728 | ||
|
|
17e70d11f5 | ||
|
|
f8afd439b2 | ||
|
|
0dc61b1c26 | ||
|
|
de6c4fcb17 | ||
|
|
57f5c854ff | ||
|
|
4f4523ab77 | ||
|
|
e34e632519 | ||
|
|
f810b902a6 | ||
|
|
fd5bdc1484 | ||
|
|
e3a12ae6c5 | ||
|
|
c9c5664ec6 | ||
|
|
75cff549ce | ||
|
|
885cb6915d | ||
|
|
f9396a4ecf | ||
|
|
f1a03329e5 | ||
|
|
f65be671a3 | ||
|
|
fa9e8f6cba | ||
|
|
67a3e76cfe | ||
|
|
e4569c0bb8 | ||
|
|
6f1a62e917 | ||
|
|
3f6e21f15a | ||
|
|
46c4287c67 | ||
|
|
1ea1826a27 | ||
|
|
151457daf0 | ||
|
|
38cc21fa3a | ||
|
|
77f759f258 | ||
|
|
07c04061c2 | ||
|
|
d4bf6ed0c8 | ||
|
|
ebd46efe81 | ||
|
|
24d193efb0 | ||
|
|
3b2a63178f | ||
|
|
78f60afb85 | ||
|
|
52a7957789 | ||
|
|
6870583b1c | ||
|
|
9ffe514350 | ||
|
|
fc6e04497a | ||
|
|
cfb3f9a3ca | ||
|
|
d47fac791e | ||
|
|
490b70c2d3 | ||
|
|
3eecdd9ada | ||
|
|
7bdbc5417c | ||
|
|
2cd07dcb72 | ||
|
|
adb83ade67 | ||
|
|
a90f5da8b0 | ||
|
|
38937a4f83 | ||
|
|
31f35a23ee | ||
|
|
74de010d3c | ||
|
|
a9e4c880df | ||
|
|
b7fe265604 | ||
|
|
25c4af5a9f | ||
|
|
b3ecf702e1 | ||
|
|
c34a3cc7e7 | ||
|
|
158783f2d1 | ||
|
|
624192d301 | ||
|
|
e9be139ed0 | ||
|
|
2b9195ed9c | ||
|
|
dfdfecba57 | ||
|
|
2d6fec13fb | ||
|
|
216f340468 | ||
|
|
a4211a7e55 | ||
|
|
d6a2488fde | ||
|
|
d4d60439b6 | ||
|
|
75edd0d8ef | ||
|
|
b4e6e21094 | ||
|
|
8c48bc926e | ||
|
|
031b6fb30d | ||
|
|
647542e462 | ||
|
|
dbf28d83f3 | ||
|
|
142acdd42f | ||
|
|
ce103d30d3 | ||
|
|
556d65e8b4 | ||
|
|
e0cf070bd0 | ||
|
|
70c5a336c2 | ||
|
|
f82d5fbd2d | ||
|
|
31418ba6f7 | ||
|
|
3c10bb2669 | ||
|
|
0e009a2762 | ||
|
|
cfd718872b | ||
|
|
f57b278ef8 | ||
|
|
730833feff | ||
|
|
4168dda535 | ||
|
|
639261a5a4 | ||
|
|
c4db6fac13 | ||
|
|
264ac7662d | ||
|
|
8237ed1a1d | ||
|
|
f82cb73100 | ||
|
|
15fc27681f | ||
|
|
81beb3285e | ||
|
|
39ff3798ca | ||
|
|
8826b0dba0 | ||
|
|
e56884c1d8 | ||
|
|
95b81193e7 | ||
|
|
508e93483e | ||
|
|
c55d9b4042 | ||
|
|
aef47609a3 | ||
|
|
5bb3c077b0 | ||
|
|
501304c51f | ||
|
|
d9c1a385e0 | ||
|
|
d6c51427a0 | ||
|
|
2cf5a4317d | ||
|
|
b344e3a749 | ||
|
|
6751ff1af3 | ||
|
|
7006796f6b | ||
|
|
1d11d4bca8 | ||
|
|
43af32585a | ||
|
|
7aa5d399e9 | ||
|
|
004e161895 | ||
|
|
ba1c6d44c6 | ||
|
|
87a08077ed | ||
|
|
33b6a4e6cd | ||
|
|
29b0af1d62 | ||
|
|
7e3b0f7aed | ||
|
|
135c5988ba | ||
|
|
67dfd6b036 | ||
|
|
162eb0c958 | ||
|
|
a2dcc42f68 | ||
|
|
6b7f6dcb19 | ||
|
|
fbcf9ccbd6 | ||
|
|
02e4e71b5d | ||
|
|
a71ba82521 | ||
|
|
cda0e3cee3 | ||
|
|
1218638794 | ||
|
|
761694cdcc | ||
|
|
bb63e3a1f1 | ||
|
|
a9b1d84b6e | ||
|
|
a465614506 | ||
|
|
3055ef208e | ||
|
|
e6cf9dce1c | ||
|
|
9232535763 | ||
|
|
091c44bb45 | ||
|
|
f088249260 | ||
|
|
dc108576e1 | ||
|
|
5c4da38a52 | ||
|
|
1e0a19f865 | ||
|
|
390e84a96a | ||
|
|
4a91a8e4c3 | ||
|
|
0b1d6d5e04 | ||
|
|
a5eed483e5 | ||
|
|
4e9622fde7 | ||
|
|
f6447c3343 | ||
|
|
605d4a89ac | ||
|
|
b5d4b8514a | ||
|
|
5470a3d808 | ||
|
|
d845e6b520 | ||
|
|
ee4d8a1939 | ||
|
|
acbd233345 | ||
|
|
77d1e51ce4 | ||
|
|
af7e75c2df | ||
|
|
9b4519978b | ||
|
|
8244b3d731 | ||
|
|
0d721900f9 | ||
|
|
585de5dbc6 | ||
|
|
7b85394a8b | ||
|
|
3b842798b8 | ||
|
|
1aaf3c1cb5 | ||
|
|
84206f701a | ||
|
|
9e62c3df7e | ||
|
|
430a2529db | ||
|
|
6911a928af | ||
|
|
cf9b7f1cb5 | ||
|
|
f695bbdc8b | ||
|
|
40a4291b07 | ||
|
|
97bd0a11f3 | ||
|
|
9b77d390c0 | ||
|
|
c0a6240764 | ||
|
|
86e0b058e5 | ||
|
|
c0a107cbfd | ||
|
|
7912623810 | ||
|
|
b72b740d7a | ||
|
|
395ab7ff9e | ||
|
|
bb75b3ea75 | ||
|
|
6bc22fe060 | ||
|
|
7e634e75cd | ||
|
|
3770bf44ed | ||
|
|
30914437b2 | ||
|
|
892a9b98fa | ||
|
|
a791befd49 | ||
|
|
76812b3233 | ||
|
|
3b000189fd | ||
|
|
b066feeb7e | ||
|
|
f7872d0d84 | ||
|
|
46e5d8e669 | ||
|
|
57018bfba7 | ||
|
|
3f7e7c50b3 | ||
|
|
90065605a9 | ||
|
|
956128683a | ||
|
|
a0dd270578 | ||
|
|
d71067d246 | ||
|
|
f8567bca79 | ||
|
|
455e417a70 | ||
|
|
99b525d42f | ||
|
|
41ccb34bd4 | ||
|
|
277d6fdd45 | ||
|
|
f40b8e9a75 | ||
|
|
eb580ab82b | ||
|
|
7eaa7f6f29 | ||
|
|
358f92e720 | ||
|
|
fde3699ebc | ||
|
|
705525cc1d | ||
|
|
38fed7b55e | ||
|
|
ec494c2e0f | ||
|
|
fd80dcee61 | ||
|
|
366d87e6ec | ||
|
|
f7e2685213 | ||
|
|
c2ef8fc188 | ||
|
|
8ea8c411d6 | ||
|
|
09f82a6079 | ||
|
|
91106be934 | ||
|
|
772a1527f0 | ||
|
|
e39db26c13 | ||
|
|
56bb513bdc | ||
|
|
0d511c58ed | ||
|
|
7ddd6fa22a | ||
|
|
256ea606ed | ||
|
|
e767dcc5cf | ||
|
|
381850ec4b | ||
|
|
382af99ade | ||
|
|
d719a0d52a | ||
|
|
68d2bd7ea9 | ||
|
|
d30c10f9dc | ||
|
|
9da9616cc4 | ||
|
|
87ca31fc45 | ||
|
|
193cbdb7aa | ||
|
|
55e7df18f5 | ||
|
|
f850635260 | ||
|
|
aecaf3fae9 | ||
|
|
fe4c687bdf | ||
|
|
c5f185462a | ||
|
|
0b80ffa1d0 | ||
|
|
5cafdcd4ee | ||
|
|
57d72d2cf6 | ||
|
|
5ccc2b1d2a | ||
|
|
ea654cf9e6 | ||
|
|
cb926c4178 | ||
|
|
d7b47ab8b1 | ||
|
|
5fe0ef3646 | ||
|
|
1a10acbb90 | ||
|
|
b565a0375c | ||
|
|
87f842203c | ||
|
|
b03b5830b1 | ||
|
|
9577bb77da | ||
|
|
431172515a | ||
|
|
2c64625eac | ||
|
|
9687847630 | ||
|
|
c9239e2d87 | ||
|
|
3931174890 | ||
|
|
a9b062a9d5 | ||
|
|
8578df57c6 | ||
|
|
7b621afa98 | ||
|
|
ef83160308 | ||
|
|
fad42f373e | ||
|
|
d3f66dd7bb | ||
|
|
85fefdfabd | ||
|
|
e36fb09ce5 | ||
|
|
bdcb8da2db | ||
|
|
4be7385628 | ||
|
|
7ed4bf46d7 | ||
|
|
98dd451502 | ||
|
|
c5ded4ceac | ||
|
|
8ba2bf39ff | ||
|
|
0bafacbee3 | ||
|
|
ff9292bc42 | ||
|
|
4df334ae86 | ||
|
|
7e10a613b6 | ||
|
|
eece2a13c5 | ||
|
|
1165e639fd | ||
|
|
ab9ffb45fd | ||
|
|
e2cf3095e4 | ||
|
|
0caa0ee004 | ||
|
|
464fcc54ce | ||
|
|
049374b8ad | ||
|
|
6b392f55c6 | ||
|
|
421076062c | ||
|
|
9b86b92a65 | ||
|
|
ec0e733fc6 | ||
|
|
a62b8103a0 | ||
|
|
1eedc99114 | ||
|
|
c8774abbab | ||
|
|
8d2217aa88 | ||
|
|
85d0a88420 | ||
|
|
a66b6d7392 | ||
|
|
0ebcc3c06d | ||
|
|
f534883d62 | ||
|
|
4726671035 | ||
|
|
bdd54afc8a | ||
|
|
d511976ba6 | ||
|
|
f83ed6dfb2 | ||
|
|
4137e7f25e | ||
|
|
aca46b76b1 | ||
|
|
4c773738d7 | ||
|
|
eb8ab94e01 | ||
|
|
11f5fb4aa2 | ||
|
|
f025733926 | ||
|
|
9e2b8e2003 | ||
|
|
ef081848b0 | ||
|
|
6aa1eea2fd | ||
|
|
ab5aa4d83b | ||
|
|
a8005293af | ||
|
|
81891e0976 | ||
|
|
fcf146fccc | ||
|
|
8729e3f040 | ||
|
|
10673362a6 | ||
|
|
a8a70e43cb | ||
|
|
369b3402be | ||
|
|
bc70548627 | ||
|
|
39245b93d9 | ||
|
|
1f8582b8fa | ||
|
|
acb1bdc8e1 | ||
|
|
cd7a102dc9 | ||
|
|
58ed22a326 | ||
|
|
5771c7f798 | ||
|
|
9598927dec | ||
|
|
abffbb8598 | ||
|
|
36b9d01917 | ||
|
|
417eec1d45 | ||
|
|
0eb823ef89 | ||
|
|
56c9435512 | ||
|
|
98bd901545 | ||
|
|
e9607c30fb | ||
|
|
281e3f0c79 | ||
|
|
4f98b59e4f | ||
|
|
1d40791557 | ||
|
|
212cd8378c | ||
|
|
ae108f1e14 | ||
|
|
e7ed902f24 | ||
|
|
3d93f793d5 | ||
|
|
9257b7ac52 | ||
|
|
28d131f74d | ||
|
|
76e1ae3596 | ||
|
|
8357cfc9b5 | ||
|
|
1250f389af | ||
|
|
d0ae6ee21a | ||
|
|
40f5bd9444 | ||
|
|
c3ea05b862 | ||
|
|
488c6f5f64 | ||
|
|
a1940e3ab9 | ||
|
|
eda873911a | ||
|
|
048f890dcb | ||
|
|
28c31fc101 | ||
|
|
852c6576cf | ||
|
|
00a5fc0142 | ||
|
|
48cca34055 | ||
|
|
b2c5298400 | ||
|
|
cf5d5e6974 | ||
|
|
3266485307 | ||
|
|
d27ae45512 | ||
|
|
c9f0ea47b7 | ||
|
|
d5100660fb | ||
|
|
5c07327467 | ||
|
|
1e374d3a25 | ||
|
|
d68c419bab | ||
|
|
a9c4d0dee5 | ||
|
|
75a453e598 | ||
|
|
404f23e7e6 | ||
|
|
104be16061 | ||
|
|
742301edf8 | ||
|
|
c3734f2e09 | ||
|
|
e0cd4f2e0a | ||
|
|
db6042a30e | ||
|
|
0e233e7027 | ||
|
|
8e5d41bfea | ||
|
|
8bb03195b5 | ||
|
|
de2bafd5a0 | ||
|
|
4c08d06e93 | ||
|
|
ab5b58f3ae | ||
|
|
31f637bc51 | ||
|
|
fbcfdb3fea | ||
|
|
b1c650bb72 | ||
|
|
209a3fb381 | ||
|
|
772a11cb1d | ||
|
|
758f7770a1 | ||
|
|
b319eb188a | ||
|
|
8ad7547640 | ||
|
|
8b060745ef | ||
|
|
baf2037ca1 | ||
|
|
7bf00e30e6 | ||
|
|
55c983c565 | ||
|
|
6a4cd70811 | ||
|
|
851f8dedec | ||
|
|
652a580ec6 | ||
|
|
0d2a2134b9 | ||
|
|
a330d3e943 | ||
|
|
0034f78edc | ||
|
|
27e71ebfc7 | ||
|
|
f0f3ddef87 | ||
|
|
f65478ee36 | ||
|
|
4bd7d63d9c | ||
|
|
bf31b23878 | ||
|
|
c2ba6d87b2 | ||
|
|
978a53852a | ||
|
|
d20a186e5e | ||
|
|
c384ec373a | ||
|
|
c5e0a18dfa | ||
|
|
4d1ab9ee9f | ||
|
|
02384dc26f | ||
|
|
e46a53f188 | ||
|
|
0343254cad | ||
|
|
9aaafa5015 | ||
|
|
04bd78e91a | ||
|
|
7ce17c7fb3 | ||
|
|
2a796d3bf5 | ||
|
|
944aa6b13e | ||
|
|
58add600d6 | ||
|
|
b1089de1ad | ||
|
|
69c1e2ccc2 | ||
|
|
6ec23d9d69 | ||
|
|
de3072c67e | ||
|
|
35f6d3eb90 | ||
|
|
f0ffcc6944 | ||
|
|
5ab3f71551 | ||
|
|
cef3b9294d | ||
|
|
11e3075347 | ||
|
|
0008631771 | ||
|
|
8e5caaca55 | ||
|
|
8c962e8b60 | ||
|
|
7ebcba3c21 | ||
|
|
7ac45dbd32 | ||
|
|
b83488d08a | ||
|
|
5528b701fb | ||
|
|
8767d103f0 | ||
|
|
ca66656a8e | ||
|
|
41e299aaaf | ||
|
|
c3f44ef70a | ||
|
|
fa44e66bd9 | ||
|
|
0b944c4222 | ||
|
|
11abc789a0 | ||
|
|
15e2cdaad0 | ||
|
|
10b6973f0d | ||
|
|
24cd9f257c | ||
|
|
8ecb49709e | ||
|
|
e12d20ab85 | ||
|
|
8b7f7b16a8 | ||
|
|
117e57d7b0 | ||
|
|
56b3f9355a | ||
|
|
70af08616f | ||
|
|
6066cb708e | ||
|
|
c1b6bd4679 | ||
|
|
88617759f6 | ||
|
|
3df98602ee | ||
|
|
3da989e37f | ||
|
|
f735237b3a | ||
|
|
125330b695 | ||
|
|
11199eabf3 | ||
|
|
12b4eb64ba | ||
|
|
d62fd25db5 | ||
|
|
58851aadb0 | ||
|
|
322049196c | ||
|
|
ce85801dcf | ||
|
|
04ad29d7c9 | ||
|
|
92f532c694 | ||
|
|
9188979367 | ||
|
|
b21379952b | ||
|
|
900c5c8351 | ||
|
|
5cab342c8c | ||
|
|
69e4bc2650 | ||
|
|
cabd712b75 | ||
|
|
fe4266a41e | ||
|
|
07fa8232e4 | ||
|
|
d02ec9bec6 | ||
|
|
47a508b9ea | ||
|
|
6c620b09c9 | ||
|
|
cb822c44ce | ||
|
|
aa9d5ce04e | ||
|
|
8876cb91c9 | ||
|
|
91b1007083 | ||
|
|
17e227236f | ||
|
|
22ba4c86bb | ||
|
|
017d9c8290 | ||
|
|
2f7c930859 | ||
|
|
983637973b | ||
|
|
bb2e0f0b7d | ||
|
|
0d8a3b84fe | ||
|
|
3503652df3 | ||
|
|
1a82e48b3d | ||
|
|
f5a86814a9 | ||
|
|
bde3b691ff | ||
|
|
ed53db1bdc | ||
|
|
ecbd62676b | ||
|
|
b343ebec15 | ||
|
|
99e1de2503 | ||
|
|
94f8c495d2 | ||
|
|
1fc11c65a5 | ||
|
|
f7700b9279 | ||
|
|
4007568d93 | ||
|
|
208134e970 | ||
|
|
bda5d481b2 | ||
|
|
00609f5a5d | ||
|
|
066e783a37 | ||
|
|
3e1bf001c7 | ||
|
|
09f52a32a6 | ||
|
|
73c14a9ad0 | ||
|
|
8cc826a57b | ||
|
|
02e17f9845 | ||
|
|
3e18a25ed3 | ||
|
|
4c31106839 | ||
|
|
2b8ff1d090 | ||
|
|
9e7690054d | ||
|
|
2e6fdce592 | ||
|
|
eee40c2130 | ||
|
|
818fe3adfc | ||
|
|
f6bfaf415f | ||
|
|
35a3190b28 | ||
|
|
65edebca2e | ||
|
|
aef9be8d9b | ||
|
|
3acb9f1842 | ||
|
|
9f7271178f | ||
|
|
5038deaf97 | ||
|
|
d6e04156f1 | ||
|
|
07d7f10622 | ||
|
|
78a1058aa1 | ||
|
|
f16894869a | ||
|
|
31240fa50d | ||
|
|
0f7c2c83b3 | ||
|
|
22e43c84fc | ||
|
|
f9160677c3 | ||
|
|
0b1f04abe3 | ||
|
|
c28b4d39ad | ||
|
|
519efe633f | ||
|
|
cf680eba4a | ||
|
|
63c78f986d | ||
|
|
57d8b0c803 | ||
|
|
722e18c07d | ||
|
|
a66ec00587 | ||
|
|
2b5100c019 | ||
|
|
cae906d226 | ||
|
|
1ebe773b6e | ||
|
|
594f005476 | ||
|
|
027e37545b | ||
|
|
4fc82ab7c1 | ||
|
|
565e163b3e | ||
|
|
e6c26dde7b | ||
|
|
6e8acc0f4c | ||
|
|
b7fff50d31 | ||
|
|
a2d240ee4a | ||
|
|
a69f6925e9 | ||
|
|
c4544832d2 | ||
|
|
74da05e6e8 | ||
|
|
af24e3b284 | ||
|
|
fb68d0bff3 | ||
|
|
d3a2ba26ba | ||
|
|
2f70ec5cb6 | ||
|
|
e9dcb986f9 | ||
|
|
93b6fe7aab | ||
|
|
d6aadef2b8 | ||
|
|
4e0fb6bf65 | ||
|
|
93c4f99619 | ||
|
|
7c489cb9aa | ||
|
|
1d74cc8225 | ||
|
|
8021529a41 | ||
|
|
2791f60a58 | ||
|
|
88e88be756 | ||
|
|
13d42ae288 | ||
|
|
5408cc6015 | ||
|
|
c97fbba173 | ||
|
|
79d9e806d5 | ||
|
|
12589ffbe0 | ||
|
|
f21b62241a | ||
|
|
b6cd10b51e | ||
|
|
e7344cdc24 | ||
|
|
4e118493d2 | ||
|
|
590b526079 | ||
|
|
b051e8cc46 | ||
|
|
952c2e570e | ||
|
|
d9834eae6d | ||
|
|
1901686a2a | ||
|
|
9efe4f41bf | ||
|
|
3fad296e3f | ||
|
|
100b101e60 | ||
|
|
b66022fb64 | ||
|
|
3e354868ea | ||
|
|
a6fda6c76a | ||
|
|
7ea08a0f6b | ||
|
|
dda6726a43 | ||
|
|
70deb278e5 | ||
|
|
017934ab8e | ||
|
|
c8c98ebc02 | ||
|
|
939d49db57 | ||
|
|
9994a214b5 | ||
|
|
5d44ff8ca5 | ||
|
|
60cef9fa49 | ||
|
|
dda98b2902 | ||
|
|
19ddc9de97 | ||
|
|
7985344042 | ||
|
|
9173e9e56a | ||
|
|
259768f92f | ||
|
|
e61716e25c | ||
|
|
99d747aabe | ||
|
|
d789e0d5a4 | ||
|
|
6524bbed43 | ||
|
|
11ad8a8398 | ||
|
|
9b9dfbe46a | ||
|
|
12c67f4244 | ||
|
|
cd66d335e8 | ||
|
|
20a33eb69f | ||
|
|
ee0fabca8e | ||
|
|
527d0b85ad | ||
|
|
4a72a0535b | ||
|
|
361d2246ed | ||
|
|
ffd8a9e87b | ||
|
|
0d518e44c7 | ||
|
|
be347da9f2 | ||
|
|
c01b2ca4be | ||
|
|
12ad4d2a22 | ||
|
|
18d992b96d | ||
|
|
59c74c6101 | ||
|
|
cf4f552595 | ||
|
|
c6a16823b0 | ||
|
|
0eb6185b6c | ||
|
|
d11e99b918 | ||
|
|
ff721ae820 | ||
|
|
dab605f50d | ||
|
|
cd40f04443 | ||
|
|
160e5696fe | ||
|
|
e98cd4ef55 | ||
|
|
79fddf1395 | ||
|
|
753e8bc049 | ||
|
|
2ae59b049c | ||
|
|
140429c931 | ||
|
|
ad756617dd | ||
|
|
8812cd18ad | ||
|
|
ce71fe2b06 | ||
|
|
360366b9f5 | ||
|
|
29f4123ab8 | ||
|
|
9fdcffda2f | ||
|
|
52d1b9a270 | ||
|
|
068f5942e7 | ||
|
|
703cefaf19 | ||
|
|
b9d326e9de | ||
|
|
06917327d8 | ||
|
|
df92aaae56 | ||
|
|
cafb03393c | ||
|
|
3d32557177 | ||
|
|
ea1f72b8b7 | ||
|
|
993b6759cc | ||
|
|
b89ff616f0 | ||
|
|
9e37959a50 | ||
|
|
40f740de59 | ||
|
|
500608b383 | ||
|
|
02c05a6c63 | ||
|
|
4b4444e8d7 | ||
|
|
bbeaf76a68 | ||
|
|
82c289b2f5 | ||
|
|
fd461b8e9e | ||
|
|
7d8a5a2bdf | ||
|
|
edda670b4f | ||
|
|
ccfac74cc3 | ||
|
|
0359e75de7 | ||
|
|
f709ea2a95 | ||
|
|
1cd7e27911 | ||
|
|
e17d0e990d | ||
|
|
43169bc512 | ||
|
|
0015300b19 | ||
|
|
a52c59a36b | ||
|
|
6ce89a9f57 | ||
|
|
78d2c42bdd | ||
|
|
fce7fdbee5 | ||
|
|
a717ec0ae7 | ||
|
|
0ce8eafe6c | ||
|
|
c0b07307f5 | ||
|
|
8751bd75da | ||
|
|
15f23769d6 | ||
|
|
2d175fd575 | ||
|
|
01bfdf52ff | ||
|
|
bdeb4b4dcc | ||
|
|
bdfbb57504 | ||
|
|
c0eebdf74b | ||
|
|
851b460a32 | ||
|
|
aca775a96e | ||
|
|
41750808f0 | ||
|
|
b0458ddda5 | ||
|
|
5a317c5d3e | ||
|
|
af7c14b2f2 | ||
|
|
d151524ed9 | ||
|
|
175acc86e2 | ||
|
|
bb07acf45e | ||
|
|
38d1c756f3 | ||
|
|
6c89913bc6 | ||
|
|
efa72cb697 | ||
|
|
a31fec5fb9 | ||
|
|
2349d2320f | ||
|
|
166e5b73fa | ||
|
|
a39b87f9fd | ||
|
|
162f945a9e | ||
|
|
5be5f54351 | ||
|
|
dfe9f5b05c | ||
|
|
4c1e28fe39 | ||
|
|
bf67168c7a | ||
|
|
b30ed702d3 | ||
|
|
9c33caf74e | ||
|
|
65e1ab616b | ||
|
|
13c5dd7b72 | ||
|
|
abeb546116 | ||
|
|
b37dc9900c | ||
|
|
b27cd0c914 | ||
|
|
446b40d8a6 | ||
|
|
6ba1d28f13 | ||
|
|
5a226ff95a | ||
|
|
01f4af6876 | ||
|
|
0ee173475e | ||
|
|
1e36112c06 | ||
|
|
c149701f3f | ||
|
|
752a36d88a | ||
|
|
ed81030976 | ||
|
|
54322ca949 | ||
|
|
738b159e95 | ||
|
|
e43302b821 | ||
|
|
4f5b9233f4 | ||
|
|
149778000a | ||
|
|
3641da1faa | ||
|
|
7c4df9b1c4 | ||
|
|
7260678620 | ||
|
|
292c20d317 | ||
|
|
8fe8739897 | ||
|
|
75bd285f5c | ||
|
|
822766c831 | ||
|
|
8440bde9d7 | ||
|
|
6c792b208c | ||
|
|
a4b869a381 | ||
|
|
db0b48f1ad | ||
|
|
e8f2e4f50c | ||
|
|
5cd201440e | ||
|
|
5c6c76b898 | ||
|
|
5b1486cb6f | ||
|
|
4847ce054b | ||
|
|
ec42fe7d7d | ||
|
|
116e405ae3 | ||
|
|
202a92998a | ||
|
|
f06618fdaf | ||
|
|
a8bd031935 | ||
|
|
d0dd92578b | ||
|
|
07a20ed5ad | ||
|
|
95f60c78b3 | ||
|
|
b5228e547c | ||
|
|
29be020e32 | ||
|
|
8b443ed10b | ||
|
|
ae5abbd039 | ||
|
|
9d24c6091b | ||
|
|
6d2b6259a9 | ||
|
|
2c7425bc51 | ||
|
|
6c75fc3506 | ||
|
|
46ab5829b7 | ||
|
|
5c5daf053b | ||
|
|
3cb9bf984b | ||
|
|
4b59d79977 | ||
|
|
7fc987f1a2 | ||
|
|
f14fd6bf5a | ||
|
|
d3f3a14b1f | ||
|
|
b812a1b06f | ||
|
|
4f95722915 | ||
|
|
2282c19f46 | ||
|
|
ccc28ba54c | ||
|
|
a512465d07 | ||
|
|
1bb18abf5a | ||
|
|
1fec4b33b2 | ||
|
|
6266aab466 | ||
|
|
31bff169a2 | ||
|
|
2f956ed400 | ||
|
|
b16831fc86 | ||
|
|
ee74a25eae | ||
|
|
80eb314d03 | ||
|
|
fd6478c7f5 | ||
|
|
56ba332dc5 | ||
|
|
14deac00f6 | ||
|
|
481ca5194a | ||
|
|
0cb4a2cfcc | ||
|
|
c6d6c2323c | ||
|
|
d65c52f6ea | ||
|
|
fb7095f2d3 | ||
|
|
c16689d936 | ||
|
|
9f3bf545bf | ||
|
|
09c8c97b4e | ||
|
|
4cb6c9e2e8 | ||
|
|
aef9f98095 | ||
|
|
e143c6b5b4 | ||
|
|
c570c1c0eb | ||
|
|
4bc3fe0c1b | ||
|
|
1f46339900 | ||
|
|
a4f354161d | ||
|
|
8a7bc4583b | ||
|
|
8468a3de7c | ||
|
|
e4ff7b43f2 | ||
|
|
220396d016 | ||
|
|
91f01abbae | ||
|
|
7eda617af9 | ||
|
|
595a22efc3 | ||
|
|
d2578e5be5 | ||
|
|
0cd1485f85 | ||
|
|
ab1dcd0e45 | ||
|
|
8281f488f3 | ||
|
|
c4a1879ae9 | ||
|
|
fcc51b591e | ||
|
|
1b44334361 | ||
|
|
7ec677f021 | ||
|
|
08201b0f00 | ||
|
|
89055d2ae9 | ||
|
|
7393159f26 | ||
|
|
dda2b4af11 | ||
|
|
7e1bc19a13 | ||
|
|
650f1df0ee | ||
|
|
f7bfa82517 | ||
|
|
b0bf31b29c | ||
|
|
54bb9a8f5a | ||
|
|
7a38c1055a | ||
|
|
cda557b6fb | ||
|
|
e1fc272e1c | ||
|
|
aa9b32a383 | ||
|
|
c836e29d2f | ||
|
|
39b919c38a | ||
|
|
4b8d06dfe8 | ||
|
|
b3cfcc1a5e | ||
|
|
41f1a65ce6 | ||
|
|
f8800384e0 | ||
|
|
765bb3886d | ||
|
|
1768104d47 | ||
|
|
ab2f4b339a | ||
|
|
531b1dfa33 | ||
|
|
935719fd43 | ||
|
|
120d928402 | ||
|
|
6ad29aaf3c | ||
|
|
ea48c6d68c | ||
|
|
34c8f21cc6 | ||
|
|
18913eedae | ||
|
|
9b159268bd | ||
|
|
a6cd5656f8 | ||
|
|
c93deb7696 | ||
|
|
0699e895fd | ||
|
|
1f700f7842 | ||
|
|
3db50096c5 | ||
|
|
36bb614a27 | ||
|
|
968471ec4a | ||
|
|
401a518f0f | ||
|
|
96af6b252a | ||
|
|
6bac9c2432 | ||
|
|
ad44a6ccee | ||
|
|
d7f6f28413 | ||
|
|
6161307cbb | ||
|
|
96952817c1 | ||
|
|
c8d4a9a671 | ||
|
|
f059a7732f | ||
|
|
adefd95fb6 | ||
|
|
a0935e8613 | ||
|
|
bebc9c9dd4 | ||
|
|
82507aabcb | ||
|
|
4e8d336527 | ||
|
|
9977f33461 | ||
|
|
4e11baa579 | ||
|
|
ef82df5e72 | ||
|
|
506ae1cbc6 | ||
|
|
6156ce48ac | ||
|
|
cd32b69d86 | ||
|
|
1ca38386b7 | ||
|
|
ef48f51834 | ||
|
|
8bcd6b9e53 | ||
|
|
7c22ea6b01 | ||
|
|
b5034fd925 | ||
|
|
a765a71474 | ||
|
|
f3ad6e66e8 | ||
|
|
3713f98f7b | ||
|
|
0c6778b5a3 | ||
|
|
f56f1160ba | ||
|
|
cc184b7c58 | ||
|
|
53fdf2bbbf | ||
|
|
033635fb43 | ||
|
|
40682bc842 | ||
|
|
9da26358ba | ||
|
|
2f0550de8e | ||
|
|
f6a1d603a9 | ||
|
|
764521414f | ||
|
|
ffe44706c2 | ||
|
|
41cab2f652 | ||
|
|
35fada7fcd | ||
|
|
5b8fb68f65 | ||
|
|
f5e668b716 | ||
|
|
acbc03dc82 | ||
|
|
294281e0ac | ||
|
|
2d450d4dbd | ||
|
|
e2865fafde | ||
|
|
aa546d87ff | ||
|
|
72e637d389 | ||
|
|
842f7045f8 | ||
|
|
4f4f3a0370 | ||
|
|
6a354f8757 | ||
|
|
abd9aaa567 | ||
|
|
32c4f984e9 | ||
|
|
86e806d021 | ||
|
|
32cff8af6d | ||
|
|
40ac2124d8 | ||
|
|
a107d0586f | ||
|
|
8a94000d98 | ||
|
|
bb90261521 | ||
|
|
fd246bc83b | ||
|
|
018a97fb8e | ||
|
|
9d10ee2da5 | ||
|
|
8a62942894 | ||
|
|
71cabea03e | ||
|
|
a37cd62d7e | ||
|
|
f180a5cd70 | ||
|
|
4405a1fbf7 | ||
|
|
7603587ce3 | ||
|
|
27a06f6cfa | ||
|
|
190204a47f | ||
|
|
7e4003968d | ||
|
|
174dfbe95e | ||
|
|
9fef6dd2d3 | ||
|
|
4ac51a7e58 | ||
|
|
8487dde026 | ||
|
|
52bdf64cdb | ||
|
|
e254d6978e | ||
|
|
9eb15a25a5 | ||
|
|
8a63200ab9 | ||
|
|
b18ffc350f | ||
|
|
1340bc9902 | ||
|
|
df36523d6c | ||
|
|
e626b65f5d | ||
|
|
3ea8a75b3f | ||
|
|
6fed9375d3 | ||
|
|
7764c60797 | ||
|
|
141567f303 | ||
|
|
ecb2b05c5a | ||
|
|
eda60a7142 | ||
|
|
e5fc8bfb2c | ||
|
|
d8cdba262b | ||
|
|
20f3b19f29 | ||
|
|
52ef0c8bcb | ||
|
|
85aeb059fc | ||
|
|
edfbad2432 | ||
|
|
ea5b0362df | ||
|
|
1c7d5a5803 | ||
|
|
adb7035f3e | ||
|
|
31f9611478 | ||
|
|
543da46457 | ||
|
|
6409cb276f | ||
|
|
c07e2e48d0 | ||
|
|
df8613f6be | ||
|
|
3b35e5a44d | ||
|
|
69216c8902 | ||
|
|
3fcafbd76b | ||
|
|
a45772bceb | ||
|
|
aa0be76c6b | ||
|
|
3581f2c818 | ||
|
|
469e6b301c | ||
|
|
854af92f3d | ||
|
|
bacf832158 | ||
|
|
31744be691 | ||
|
|
59155b9f6c | ||
|
|
9692e67aab | ||
|
|
3ab3b79bc7 | ||
|
|
132713f274 | ||
|
|
62f7347178 | ||
|
|
3dcb0b2c69 | ||
|
|
9e962b468e | ||
|
|
d87283b7d7 | ||
|
|
e442cacf5f | ||
|
|
f8ed5b37ac | ||
|
|
c36d873636 | ||
|
|
ca2f45fc41 | ||
|
|
3a3f8546d1 | ||
|
|
42befd9ce7 | ||
|
|
0f315f414d | ||
|
|
44c35c7fb3 | ||
|
|
b1a4d839ac | ||
|
|
1ad46b484f | ||
|
|
67420e5416 | ||
|
|
166cec0483 | ||
|
|
2c3aace7a9 | ||
|
|
d3e40d7c3d | ||
|
|
efad824fe3 | ||
|
|
3bb6962ccc | ||
|
|
6edb94758e | ||
|
|
e8e70bc81b | ||
|
|
269bbb71f3 | ||
|
|
5cfee0974e | ||
|
|
a08454a09a | ||
|
|
ce371e45ae | ||
|
|
82808be0c7 | ||
|
|
f124ec7131 | ||
|
|
3ea94fc5e0 | ||
|
|
afac6a9a89 | ||
|
|
80864c7ca4 | ||
|
|
19765848d5 | ||
|
|
21b3ca0b2f | ||
|
|
710fdef0fb | ||
|
|
4f0237209a | ||
|
|
8f5c40487c | ||
|
|
5cecd2be81 | ||
|
|
c24b79cb9d | ||
|
|
0e804d0bba | ||
|
|
c749f9ead4 | ||
|
|
87f8317b97 | ||
|
|
e6e4ecacaf | ||
|
|
d458bb9a45 | ||
|
|
362bf7b141 | ||
|
|
9c81766b78 | ||
|
|
28580103f3 | ||
|
|
1f93ca1cf8 | ||
|
|
2778175752 | ||
|
|
3e0ed5b0af | ||
|
|
325bd510ff | ||
|
|
2b5a327e53 | ||
|
|
c322946c32 | ||
|
|
aefee9c92b | ||
|
|
172f7f7085 | ||
|
|
bb9d2a0a69 | ||
|
|
2d9cc286f0 | ||
|
|
6629faa4d5 | ||
|
|
d3d397cf70 | ||
|
|
8c91b37dc8 | ||
|
|
fc4b33f7f0 | ||
|
|
918da6e0ee | ||
|
|
963f487648 | ||
|
|
ba1c49e23a | ||
|
|
95a27fe2c9 | ||
|
|
2951f96daf | ||
|
|
9bf2e07006 | ||
|
|
332274f2a8 | ||
|
|
35c052e605 | ||
|
|
91de1dbd27 | ||
|
|
3bc392fdc4 | ||
|
|
79c14ad801 | ||
|
|
0f7503875c | ||
|
|
75e1eac785 | ||
|
|
f77b0a51f4 | ||
|
|
8cef1dd51a | ||
|
|
8bdc1590dd | ||
|
|
ea0cf96a4a | ||
|
|
1bd96f6a88 | ||
|
|
5f4bd19dcc | ||
|
|
843f59b492 | ||
|
|
f9ccf9e6aa | ||
|
|
3275c03a21 | ||
|
|
a1a9229852 | ||
|
|
fc09245cdb | ||
|
|
6ac6646ac2 | ||
|
|
db8ebcc3d5 | ||
|
|
e0aa053e95 | ||
|
|
c8a24374fb | ||
|
|
cde9ac63aa | ||
|
|
b2d3aa8c11 | ||
|
|
ed786dfb11 | ||
|
|
cc93f008a4 | ||
|
|
2284432057 | ||
|
|
c623748d27 | ||
|
|
954f905c8c | ||
|
|
f23d1faa33 | ||
|
|
1f8bfdac5f | ||
|
|
b580da9e9f | ||
|
|
46a188093a | ||
|
|
1c7f6438ff | ||
|
|
f9da7afd75 | ||
|
|
9d49dd2f40 | ||
|
|
fb8e19285b | ||
|
|
505abb3029 | ||
|
|
2014598eaf | ||
|
|
1b39c345af | ||
|
|
bdb1812705 | ||
|
|
0b8bcafb72 | ||
|
|
c0c699eab6 | ||
|
|
5a99c2dcef | ||
|
|
cbce5729fd | ||
|
|
7290245729 | ||
|
|
5c1bb564f2 | ||
|
|
94ee854196 | ||
|
|
91602c0be5 | ||
|
|
a31f287b2b | ||
|
|
f3a0d82304 | ||
|
|
f2669bb875 | ||
|
|
4801559edc | ||
|
|
9496b71bb7 | ||
|
|
ba1e8cc448 | ||
|
|
d307adbbef | ||
|
|
933bc0a9e3 | ||
|
|
ee7319c702 | ||
|
|
aa63d11816 | ||
|
|
b1b402b011 | ||
|
|
5931e8f813 | ||
|
|
52a5a54bb3 | ||
|
|
7ca987fb1c | ||
|
|
66dbf21df0 | ||
|
|
cc0d42f34f | ||
|
|
38a79d8d3e | ||
|
|
9420420622 | ||
|
|
ce1139f114 | ||
|
|
d8ce0416fa | ||
|
|
a0e696eca4 | ||
|
|
6b20ffeeba | ||
|
|
ac1c0c90e7 | ||
|
|
3663384ccf | ||
|
|
ecf8d437c7 | ||
|
|
3c07fc0387 | ||
|
|
8bd2b9d7a7 | ||
|
|
b2b78c67f4 | ||
|
|
94147c23b1 | ||
|
|
ba588c9edf | ||
|
|
8810f453df | ||
|
|
c7d42305a3 | ||
|
|
40025db415 | ||
|
|
c0b8a8b8eb | ||
|
|
92416b4661 | ||
|
|
aaa93b1bcc | ||
|
|
b328331d7c | ||
|
|
f51079e065 | ||
|
|
534c622980 | ||
|
|
6cc1cb5afa | ||
|
|
2372d5ca92 | ||
|
|
331af8cd24 | ||
|
|
d58b8fffdc | ||
|
|
e5d05b88bc | ||
|
|
61f823b6be | ||
|
|
06f23d5754 | ||
|
|
e567f1386a | ||
|
|
15a734ace6 | ||
|
|
6f0309b6a7 | ||
|
|
08191278d7 | ||
|
|
6a4b31ffe6 | ||
|
|
a150757a97 | ||
|
|
8eded14f85 | ||
|
|
b311f85ca0 | ||
|
|
9423c8135f | ||
|
|
5221907ef8 | ||
|
|
4cc1847ea6 | ||
|
|
2a0032a199 | ||
|
|
9b722eacdb | ||
|
|
c6f15411e3 | ||
|
|
90a16b7149 | ||
|
|
2df9741cd6 | ||
|
|
497b8bfce5 | ||
|
|
0fdbb8d8a1 | ||
|
|
7a8a2fbf77 | ||
|
|
1e48cb831a | ||
|
|
9e96732b73 | ||
|
|
e863906eaa | ||
|
|
13ec6e2dca | ||
|
|
968e7222fd | ||
|
|
a84159ee70 | ||
|
|
703f9deb9a | ||
|
|
07147f13a8 | ||
|
|
a57d9d41c3 | ||
|
|
a40f66fc94 | ||
|
|
097c97ca32 | ||
|
|
3e1d418269 | ||
|
|
5ee7b3fcbf | ||
|
|
15adf97174 | ||
|
|
4bd5befaaa | ||
|
|
5fe6272915 | ||
|
|
7a01a1f33f | ||
|
|
92759aa9dc | ||
|
|
65e21190b9 | ||
|
|
f6877631d9 | ||
|
|
1e4fa8432b | ||
|
|
ed5423368e | ||
|
|
673acad72c | ||
|
|
efc04cc82d | ||
|
|
b880f7c946 | ||
|
|
7304de0102 | ||
|
|
a64601afcc | ||
|
|
79ce3c0d74 | ||
|
|
83c31ad2b4 | ||
|
|
3bcb769965 | ||
|
|
75e0b8f4c2 | ||
|
|
78ef0fdaf4 | ||
|
|
8d29ff2d61 | ||
|
|
eea79da1ae | ||
|
|
cb5447f86e | ||
|
|
8cb31ad524 | ||
|
|
6ca52a0db1 | ||
|
|
25e30672c8 | ||
|
|
abcab2aadf | ||
|
|
19bb8f7595 | ||
|
|
0427956e8b | ||
|
|
78bf40282a | ||
|
|
6c6a82f8e6 | ||
|
|
4b9cc7be08 | ||
|
|
dc2d6b8160 | ||
|
|
083456e33a | ||
|
|
80e43e8634 | ||
|
|
e99b8ed453 | ||
|
|
4aba36b756 | ||
|
|
2ebf654973 | ||
|
|
f560fb86d8 | ||
|
|
981689aa26 | ||
|
|
b3fd266c31 | ||
|
|
062b7a5986 | ||
|
|
088b1ee1c2 | ||
|
|
1d572bb4b6 | ||
|
|
cf8e033831 | ||
|
|
43977a74cc | ||
|
|
d67e96539b | ||
|
|
09d1aff6a8 | ||
|
|
41c5a49abc | ||
|
|
eacff9025e | ||
|
|
f56b8f8e42 | ||
|
|
8ab221e5a8 | ||
|
|
21e51c8455 | ||
|
|
3234896caf | ||
|
|
b00dd1c5f5 | ||
|
|
992a3f9d1c | ||
|
|
6b280b5aa4 | ||
|
|
e6713520c7 | ||
|
|
4f75b0343e | ||
|
|
097ebccdcd | ||
|
|
954e1de4da | ||
|
|
63dc79699d | ||
|
|
63f342bbdb | ||
|
|
7c9b90ed7d | ||
|
|
00e45b60ad | ||
|
|
d1a7a37909 | ||
|
|
1c12b52ada | ||
|
|
722fe40899 | ||
|
|
b2e7ebf43d | ||
|
|
3ed8e768aa | ||
|
|
d362691389 | ||
|
|
4b7c54d5b5 | ||
|
|
fbd0407863 | ||
|
|
7bab1b4297 | ||
|
|
cfce9e9887 | ||
|
|
9df2a96027 | ||
|
|
d967c719f5 | ||
|
|
ec80a5aa70 | ||
|
|
0c95f178ca | ||
|
|
e0405b15df | ||
|
|
e029f30acf | ||
|
|
44ed836b7c | ||
|
|
00401201d1 | ||
|
|
726491feee | ||
|
|
36a94e1399 | ||
|
|
855c83a325 | ||
|
|
d9e65719d3 | ||
|
|
c774070feb | ||
|
|
5aeff8c9c0 | ||
|
|
4d5f5cf04b | ||
|
|
1d6b87ffa7 | ||
|
|
3b82435740 | ||
|
|
17499abfd0 | ||
|
|
9a57deb9ab | ||
|
|
089c58310c | ||
|
|
647de5d473 | ||
|
|
b223b121d1 | ||
|
|
49fbb37542 | ||
|
|
1496192756 | ||
|
|
f34cd50b9c | ||
|
|
20f46bea61 | ||
|
|
f4ff8d37d5 | ||
|
|
15108645d4 | ||
|
|
b5e8f32f1f | ||
|
|
4027c41c73 | ||
|
|
7037139ab3 | ||
|
|
b976fe4bad | ||
|
|
d674a201ee | ||
|
|
d4a3e292ed | ||
|
|
50084e47f7 | ||
|
|
6e0a16a4d1 | ||
|
|
5e87d8de18 | ||
|
|
9a6c2fa7c8 | ||
|
|
f440f91233 | ||
|
|
ccf364f2dd | ||
|
|
3c5baae568 | ||
|
|
7710ff32a6 | ||
|
|
e081d2b73a | ||
|
|
0c77fa43ff | ||
|
|
6ee4353bbd | ||
|
|
bddab24d48 | ||
|
|
45f9a32e59 | ||
|
|
39b297fd00 | ||
|
|
fd2167d679 | ||
|
|
d07249e1b2 | ||
|
|
31dc59c9d5 | ||
|
|
f3e82dac19 | ||
|
|
41db80dba8 | ||
|
|
8efe922b50 | ||
|
|
a5d22d287c | ||
|
|
793095900b | ||
|
|
ee63292e55 | ||
|
|
9fbcde3ca0 | ||
|
|
9e48c8e359 | ||
|
|
da07f50e49 | ||
|
|
cc872cd2b4 | ||
|
|
a3fa1e541c | ||
|
|
b579e823bd | ||
|
|
fc5b1ec85e | ||
|
|
e0c2037831 | ||
|
|
9f44d6b75b | ||
|
|
b6f2fff42b | ||
|
|
f10e54e47b | ||
|
|
704e0c1dc1 | ||
|
|
067ac8fb56 | ||
|
|
4428089146 | ||
|
|
44ce674166 | ||
|
|
0c79a9acde | ||
|
|
ebfc2715e7 | ||
|
|
b0dbc9dc18 | ||
|
|
507cdf75a5 | ||
|
|
8f8492b0be | ||
|
|
457e715188 | ||
|
|
3fad827b9f | ||
|
|
8bd571c78d | ||
|
|
1724521ebf | ||
|
|
b0fb3596aa | ||
|
|
38ff8340e4 | ||
|
|
f759ea4bc2 | ||
|
|
fb228d72b1 | ||
|
|
916361a3be | ||
|
|
011d0cad8c | ||
|
|
408c40f699 | ||
|
|
4173f6d775 | ||
|
|
e6f3c22064 | ||
|
|
a013518eac | ||
|
|
9c88cacb3d | ||
|
|
d49bf04b15 | ||
|
|
1dbd5dc5bc | ||
|
|
c103706a54 | ||
|
|
b0a027df52 | ||
|
|
868641378a | ||
|
|
11baae3107 | ||
|
|
7526bb430f | ||
|
|
7dbef77687 | ||
|
|
ed8125042e | ||
|
|
25b2655d0f | ||
|
|
145a264154 | ||
|
|
3e33170ae0 | ||
|
|
d18bf68f0e | ||
|
|
368917e108 | ||
|
|
cdea4c3911 | ||
|
|
b4b9b55bbf | ||
|
|
6092c63df4 | ||
|
|
fda5132184 | ||
|
|
6a9fb25a18 | ||
|
|
18d7344690 | ||
|
|
ba50cf20a0 | ||
|
|
7bd6e126e2 | ||
|
|
f7521c81d5 | ||
|
|
f483a2dc2f | ||
|
|
9769d5af06 | ||
|
|
0a2dc3563e | ||
|
|
e9f0157b2b | ||
|
|
e8e566fcdd | ||
|
|
72b7d22cef | ||
|
|
f79da476fd | ||
|
|
ef31b7844c | ||
|
|
f1ed1bf115 | ||
|
|
23ea028965 | ||
|
|
42046a614f | ||
|
|
36055f56e6 | ||
|
|
b693cb30fc | ||
|
|
20377d4ff5 | ||
|
|
570d732b0e | ||
|
|
d9d2b1de70 | ||
|
|
0a7eaaf36f | ||
|
|
36dd3e496d | ||
|
|
83fd914d7b | ||
|
|
281c6b13fa | ||
|
|
00b7b1221c | ||
|
|
56cbf0ff3f | ||
|
|
f852a4d341 | ||
|
|
7c8c7d482a | ||
|
|
9e95ae2cb0 | ||
|
|
0d2d935f17 | ||
|
|
eeb320a825 | ||
|
|
9452d4dbe3 | ||
|
|
03d2387cdd | ||
|
|
61b325ccb9 | ||
|
|
e57de9a4de | ||
|
|
a259ab9541 | ||
|
|
96946f3a1e | ||
|
|
08569276eb | ||
|
|
edc4be2d44 | ||
|
|
8cd6fb7ae2 | ||
|
|
a5afa90790 | ||
|
|
f634eb8d37 | ||
|
|
694b61a67b | ||
|
|
88f0d00a15 | ||
|
|
e31088fd2e | ||
|
|
1d7bb63710 | ||
|
|
64422c60bb | ||
|
|
a8b082ed4b | ||
|
|
5a5ade0d60 | ||
|
|
7e24e2dcfb | ||
|
|
930579d983 | ||
|
|
af1ed5349f | ||
|
|
9dcf71dc31 | ||
|
|
8bf9af0ed8 | ||
|
|
aecfad5871 | ||
|
|
3edd155898 | ||
|
|
2c419c0b2e | ||
|
|
2fb67e7042 | ||
|
|
82847f07b2 | ||
|
|
29483b2625 | ||
|
|
7fc59e79f3 | ||
|
|
79b3e21148 | ||
|
|
ee91117010 | ||
|
|
0548037ff3 | ||
|
|
46002632bb | ||
|
|
9659ac1611 | ||
|
|
bbc497e68d | ||
|
|
d7ec7574ad | ||
|
|
2f476d9e61 | ||
|
|
c74f37f0de | ||
|
|
d27a62cbfc | ||
|
|
8029e491a3 | ||
|
|
fe56268421 | ||
|
|
2db6fabbe9 | ||
|
|
035e89801a | ||
|
|
a7280da30c | ||
|
|
ac2920820d | ||
|
|
fcecc4b31e | ||
|
|
4abef50ca0 | ||
|
|
5e16445b08 | ||
|
|
be593b8185 | ||
|
|
ca27cb9981 | ||
|
|
5b64e629c9 | ||
|
|
18640826b6 | ||
|
|
e4d3239831 | ||
|
|
e834eadbe1 | ||
|
|
0ec8f66972 | ||
|
|
28b49aab70 | ||
|
|
458c462f49 | ||
|
|
6f6e2a1254 | ||
|
|
f46a3d69e1 | ||
|
|
fa787ed082 | ||
|
|
b5b748f993 | ||
|
|
3bdcf655fd | ||
|
|
284755f298 | ||
|
|
ad5eed96e7 | ||
|
|
431c830ea0 | ||
|
|
da047427a5 | ||
|
|
69da1dca5f | ||
|
|
cc61501f63 | ||
|
|
4ced2d3392 | ||
|
|
7c72bfdf6b | ||
|
|
b8878e1458 | ||
|
|
abd40cec1b | ||
|
|
cc18da4f28 | ||
|
|
1a39039162 | ||
|
|
cf9dd72ea5 | ||
|
|
b7cdf81665 | ||
|
|
8497a05fd1 | ||
|
|
7db4eec45c | ||
|
|
cd822b545a | ||
|
|
e872581d3c | ||
|
|
defe9e094c | ||
|
|
5662735cb8 | ||
|
|
05c96da98f | ||
|
|
8646dce21b | ||
|
|
ea53fc190b | ||
|
|
99a3eabbaf | ||
|
|
c57bd386f2 | ||
|
|
f2a52b03df | ||
|
|
860e1ccfaa | ||
|
|
49967b38d4 | ||
|
|
36038fff6d | ||
|
|
c5958954b0 | ||
|
|
d0d63016ca | ||
|
|
35f2484adf | ||
|
|
662b5bde31 | ||
|
|
388a4ed75b | ||
|
|
c2cf496ded | ||
|
|
8a172f02a9 | ||
|
|
f27935ea61 | ||
|
|
5311b522b7 | ||
|
|
ea682e218d | ||
|
|
aa5f7ef2a8 | ||
|
|
043df0aec3 | ||
|
|
63a2e18760 | ||
|
|
c3ced7ed03 | ||
|
|
f597e14b3d | ||
|
|
aff4ba9115 | ||
|
|
554e0805e3 | ||
|
|
f4efa74a23 | ||
|
|
ddc19587fa | ||
|
|
5f1232727e | ||
|
|
9c373e8f0a | ||
|
|
83942d788f | ||
|
|
73d6fec6f3 | ||
|
|
a37b5deaa1 | ||
|
|
3b9de847e7 | ||
|
|
924f1f1e48 | ||
|
|
7bdebbe25b | ||
|
|
8f46c25dc9 | ||
|
|
8bb4a03639 | ||
|
|
7b5bc5e057 | ||
|
|
2c62218fd6 | ||
|
|
35582a5e6a | ||
|
|
20340fd31f | ||
|
|
d6d7664b48 | ||
|
|
ce97e63876 | ||
|
|
378b98ff89 | ||
|
|
ce31199e57 | ||
|
|
227f3b2924 | ||
|
|
5076f1354c | ||
|
|
e5a34388ae | ||
|
|
6a5f65c018 | ||
|
|
6d5f41b3d8 | ||
|
|
64f10ead63 | ||
|
|
575dae446f | ||
|
|
9b438576f2 | ||
|
|
cbd32f1a07 | ||
|
|
516182ec40 | ||
|
|
4ad3a68d80 | ||
|
|
2f9ba32c07 | ||
|
|
dfc45a2904 | ||
|
|
563fdffb67 | ||
|
|
f2e9c5e2c0 | ||
|
|
86a591364c | ||
|
|
2a82485f81 | ||
|
|
e13549d361 | ||
|
|
a85c1bd7d3 | ||
|
|
946679f490 | ||
|
|
b6f27eac18 | ||
|
|
2cc82145a3 | ||
|
|
2dae0f2d8e | ||
|
|
412f6edc36 | ||
|
|
764791b8e5 | ||
|
|
bd20d6ae7d | ||
|
|
8db0bf7bea | ||
|
|
cff6aa72fc | ||
|
|
63ddf06057 | ||
|
|
3d9d57b7fa | ||
|
|
2c1734aea6 | ||
|
|
301c39aad0 | ||
|
|
5be346c89d | ||
|
|
e4eaf0037e | ||
|
|
8f97be82cb | ||
|
|
4ab7f92b82 | ||
|
|
0ec95a7729 | ||
|
|
98c38716cd | ||
|
|
6a46df780c | ||
|
|
7788198f26 | ||
|
|
3010963283 | ||
|
|
6522524c0f | ||
|
|
160434c114 | ||
|
|
36acb9ac12 | ||
|
|
1a06783dab | ||
|
|
ec38ee9abc | ||
|
|
2e82551b7c | ||
|
|
49121fb945 | ||
|
|
6e436f55c3 | ||
|
|
afb0131a28 | ||
|
|
92588282a6 | ||
|
|
765e997976 | ||
|
|
ddbf6a2e7a | ||
|
|
8567e912a3 | ||
|
|
42e4f168d1 | ||
|
|
7c98f2809c | ||
|
|
e68b529a34 | ||
|
|
1ed4987277 | ||
|
|
a5d1f0995c | ||
|
|
b6951df72e | ||
|
|
b1c31048a9 | ||
|
|
e13faa1b66 | ||
|
|
9349aca76e | ||
|
|
4946726d1e | ||
|
|
72c7800c02 | ||
|
|
e4c35b2ba2 | ||
|
|
bccb885508 | ||
|
|
7b7e0e0522 | ||
|
|
991d1d82b8 | ||
|
|
5832205624 | ||
|
|
8aeea4d212 | ||
|
|
e1934cd1b0 | ||
|
|
056cce2f97 | ||
|
|
61f6463e59 | ||
|
|
a929552c3d | ||
|
|
10cd02d2e7 | ||
|
|
afac50c18f | ||
|
|
24ade35df8 | ||
|
|
d50341ff8e | ||
|
|
54bd6d5336 | ||
|
|
7bc26787db | ||
|
|
e923f816a4 | ||
|
|
c6e4439ab9 | ||
|
|
94f66da84e | ||
|
|
384399c1c8 | ||
|
|
7bac63310f | ||
|
|
f85de2c960 | ||
|
|
173b516270 | ||
|
|
3af25610b8 | ||
|
|
0898ed8c00 | ||
|
|
0d0b056f6b | ||
|
|
3f6b572f51 | ||
|
|
1a2b906e0a | ||
|
|
d48331b706 | ||
|
|
d95cc62c8e | ||
|
|
f04aa78fd7 | ||
|
|
b03d355d69 | ||
|
|
834e7c1abb | ||
|
|
c37caa95a4 | ||
|
|
3ffcc5d5ba | ||
|
|
52fa87a7c6 | ||
|
|
0ddcfbeed7 | ||
|
|
a2f56682e6 | ||
|
|
edc4af6da5 | ||
|
|
283354d4d9 | ||
|
|
c3fbc573e2 | ||
|
|
e7f0091b48 | ||
|
|
446327dffd | ||
|
|
03a0449662 | ||
|
|
3fb72dfc24 | ||
|
|
7c4ccde69b | ||
|
|
167a50816f | ||
|
|
a753b868e6 | ||
|
|
0958c9da44 | ||
|
|
bdee98feb0 | ||
|
|
6f9c76c9ba | ||
|
|
c4fdf075b3 | ||
|
|
0a19874ef3 | ||
|
|
39af07cc22 | ||
|
|
7ac3a264da | ||
|
|
8951b45a39 | ||
|
|
a4e4315002 | ||
|
|
3a9d108b76 | ||
|
|
651a4ae2bf | ||
|
|
75007db630 | ||
|
|
d0adfebf7a | ||
|
|
f9e8496d3f | ||
|
|
f583b741d4 | ||
|
|
1d80b166b1 | ||
|
|
42e59ee564 | ||
|
|
e2c82086be | ||
|
|
e9d47f046d | ||
|
|
4acc73ac19 | ||
|
|
96c27efa68 | ||
|
|
52ad4435ac | ||
|
|
b6426dd202 | ||
|
|
abbdbdbc18 | ||
|
|
9ad6c20d42 | ||
|
|
d96c854756 | ||
|
|
ac4c578396 | ||
|
|
5d527c8e72 | ||
|
|
15ef6c77dc | ||
|
|
8b990ac273 | ||
|
|
99ffe9eac2 | ||
|
|
3d8dda23ce | ||
|
|
4fe5df51fa | ||
|
|
d7d6aa78f5 | ||
|
|
3646cb0ce6 | ||
|
|
e2bd1b8ba3 | ||
|
|
b061f7e405 | ||
|
|
78f38f1a48 | ||
|
|
eeec81bf3b | ||
|
|
4682096d98 | ||
|
|
ca369e2df5 | ||
|
|
1a6efebea4 | ||
|
|
bba3a45f0f | ||
|
|
de83a273a9 | ||
|
|
ba2954baf4 | ||
|
|
9e4f2ee3e7 | ||
|
|
0c6ad58bca | ||
|
|
8319ec988f | ||
|
|
fc65476918 | ||
|
|
42b45408ed | ||
|
|
792f0b5c7a | ||
|
|
b68e0600bd | ||
|
|
a667502836 | ||
|
|
254d545e58 | ||
|
|
cdde4b0cc9 | ||
|
|
0346497f45 | ||
|
|
af3527d6cf | ||
|
|
08186d98f5 | ||
|
|
9b8b01515c | ||
|
|
49e5b71235 | ||
|
|
f63980f122 | ||
|
|
9cee827f6b | ||
|
|
9c57976906 | ||
|
|
ef5b09b6fa | ||
|
|
469b4224dc | ||
|
|
afc410cfe6 | ||
|
|
b686bc1112 | ||
|
|
25bee76fff | ||
|
|
98a8b9944e | ||
|
|
66745d2ddf | ||
|
|
cf9e1fe54c | ||
|
|
9bf59bed2f | ||
|
|
48a9968d20 | ||
|
|
d23355b147 | ||
|
|
83ec5658c4 | ||
|
|
aed509155b | ||
|
|
0120762dbe | ||
|
|
85b67ecd0b | ||
|
|
7451383c24 | ||
|
|
88d735ed56 | ||
|
|
7a002e4b89 | ||
|
|
01ad0b4875 | ||
|
|
4defa5fa9c | ||
|
|
32a1dd64ab | ||
|
|
5ce8171f12 | ||
|
|
e3121cb043 | ||
|
|
99e52ab0b5 | ||
|
|
2b672dc4fc | ||
|
|
6db7adf900 | ||
|
|
65782285a3 | ||
|
|
64bd5a93ad | ||
|
|
93d7ef48eb | ||
|
|
a90b7abff7 | ||
|
|
a479b4b109 | ||
|
|
326e1118da | ||
|
|
90f1e324ce | ||
|
|
41e0228c03 | ||
|
|
e72691a164 | ||
|
|
7b1d2a0b38 | ||
|
|
470d76faab | ||
|
|
aec9a75bba | ||
|
|
fcbac22577 | ||
|
|
1153a5dd3c | ||
|
|
04fccad19c | ||
|
|
1bce756bd6 | ||
|
|
68f052efcb | ||
|
|
4a8baf79fb | ||
|
|
499ac0295e | ||
|
|
29c422501a | ||
|
|
8efe74f614 | ||
|
|
4e0f12f4b9 | ||
|
|
cbd3a61259 | ||
|
|
2406a7be49 | ||
|
|
87322a36ca | ||
|
|
bbe4d28a40 | ||
|
|
dd40b56047 | ||
|
|
f14a0d5ee3 | ||
|
|
2994419381 | ||
|
|
81f06026d0 | ||
|
|
2df9441c20 | ||
|
|
47190d9c02 | ||
|
|
e0383d6c59 | ||
|
|
a436d9c070 | ||
|
|
eeabcd94ed | ||
|
|
8a3e37041a | ||
|
|
a1e9aefeb2 | ||
|
|
670cb51352 | ||
|
|
a43dff88c5 | ||
|
|
fb3c87359f | ||
|
|
a49dec7e6d | ||
|
|
a7156235c3 | ||
|
|
3cb52ba3f1 | ||
|
|
024f1a7047 | ||
|
|
b77c7e922f | ||
|
|
9335c4706b | ||
|
|
e09a9dabd6 | ||
|
|
e4e2f50015 | ||
|
|
61865050c7 | ||
|
|
05d90c26ff | ||
|
|
43cdb29eb6 | ||
|
|
87ff1cb2b2 | ||
|
|
f73c9c9d02 | ||
|
|
d15e444cb7 | ||
|
|
2eff6c3dc4 | ||
|
|
41fe211aec | ||
|
|
2acfa6d88d | ||
|
|
348b0c728c | ||
|
|
ece2bda680 | ||
|
|
875e1573a4 | ||
|
|
926dc868ef | ||
|
|
4a69ff6428 | ||
|
|
75cf17e2ce | ||
|
|
af3f66a5fa | ||
|
|
61adf1f784 | ||
|
|
1d0311a194 | ||
|
|
bda9974eb8 | ||
|
|
0306296ae9 | ||
|
|
86ecb38ae2 | ||
|
|
f3ca58111c | ||
|
|
3124d3ca1c | ||
|
|
9012506209 | ||
|
|
adb874ca74 | ||
|
|
f006a8aab6 | ||
|
|
6aaf2f031b | ||
|
|
1669377938 | ||
|
|
da45abaafb | ||
|
|
2b942f03b2 | ||
|
|
985b5e6724 | ||
|
|
198c235765 | ||
|
|
e82ee97666 | ||
|
|
8be7dac209 | ||
|
|
d2e35d8b2c | ||
|
|
5938bc72b4 | ||
|
|
23860e5897 | ||
|
|
11adbed079 | ||
|
|
07eb45ad76 | ||
|
|
726e908b5a | ||
|
|
526c6d8852 | ||
|
|
6d2be2e0a1 | ||
|
|
a8a9423ab0 | ||
|
|
78a82be4a0 | ||
|
|
1acdb27996 | ||
|
|
66f2c55343 | ||
|
|
0f3fa86bed | ||
|
|
e5c2cfd243 | ||
|
|
1a7ce13ec3 | ||
|
|
b4c535c76a | ||
|
|
1c7592c707 | ||
|
|
e4f8f96b83 | ||
|
|
105baa0557 | ||
|
|
ec554dae2e | ||
|
|
da86f113b8 | ||
|
|
ef1c397f11 | ||
|
|
12e0cc3d7b | ||
|
|
ff6bb6ebb0 | ||
|
|
6b78215b2e | ||
|
|
29a25b708c | ||
|
|
376f22ce08 | ||
|
|
6f25a72fb7 | ||
|
|
ec50078f18 | ||
|
|
c4900f5a66 | ||
|
|
edb5dff064 | ||
|
|
3961e1844c | ||
|
|
d09ecac35d | ||
|
|
5d9a02657f | ||
|
|
792edd9146 | ||
|
|
fd045fcc39 | ||
|
|
7a2ab59346 | ||
|
|
dc7970ac80 | ||
|
|
d08d4fa488 | ||
|
|
0807e7fec1 | ||
|
|
1c4b5d1ca5 | ||
|
|
a3d9b2b9cb | ||
|
|
d0df08de2c | ||
|
|
14d670a9a2 | ||
|
|
67801506e0 | ||
|
|
e98291498b | ||
|
|
d82a86e01b | ||
|
|
760d053cd7 | ||
|
|
48eb6dbbe0 | ||
|
|
d520c6a816 | ||
|
|
a3894323db | ||
|
|
a4b902d5af | ||
|
|
5f9f0769eb | ||
|
|
49390574bf | ||
|
|
71fab09581 | ||
|
|
9225df85da | ||
|
|
81160ac1ea | ||
|
|
5dd02602b7 | ||
|
|
a92dfa3920 | ||
|
|
0c19a1aafe | ||
|
|
5429bb7d2b | ||
|
|
664799c01b | ||
|
|
d88c5b9db2 | ||
|
|
509acd1192 | ||
|
|
8db74a4514 | ||
|
|
0d6f98f50a | ||
|
|
0fe7a693f8 | ||
|
|
d368fbbf5b | ||
|
|
1e7c123145 | ||
|
|
9a2efd9686 | ||
|
|
069b0b0153 | ||
|
|
c12c64380d | ||
|
|
88961fbdb5 | ||
|
|
a11ceced09 | ||
|
|
9da94b38b3 | ||
|
|
ca35662ca2 | ||
|
|
e7c4bbe1e8 | ||
|
|
1bc56b5c9e | ||
|
|
25a0630bcb | ||
|
|
1f7d122bee | ||
|
|
d9cb32ca53 | ||
|
|
9cdfc61a30 | ||
|
|
760587ee2e | ||
|
|
d2fe8a2ddb | ||
|
|
d2dc76baf1 | ||
|
|
232de66683 | ||
|
|
3d61d1bca0 | ||
|
|
c2c9531c1b | ||
|
|
461df14cf7 | ||
|
|
81b0be0489 | ||
|
|
3074a211f8 | ||
|
|
0659b140f5 | ||
|
|
0b8160ae34 | ||
|
|
dbf2b7b4b6 | ||
|
|
1e8afc8ea4 | ||
|
|
70bd2e8a78 | ||
|
|
b1dfe6e714 | ||
|
|
46bc0fe8b4 | ||
|
|
8045544051 | ||
|
|
0cef4dc9b3 | ||
|
|
f1cfa4330a | ||
|
|
a50b370262 | ||
|
|
7cc3639758 | ||
|
|
24b5dd0c8c | ||
|
|
32afdfc257 | ||
|
|
10e61e28c6 | ||
|
|
d88be9aca4 | ||
|
|
064fd217ba | ||
|
|
dd2e2734ce | ||
|
|
390c230de9 | ||
|
|
b43f868eba | ||
|
|
119a18aa15 | ||
|
|
b79c35042a | ||
|
|
12f8e0fe71 | ||
|
|
0b065b16b1 | ||
|
|
b4d5451b11 | ||
|
|
2fb4c8d227 | ||
|
|
9d8f3db511 | ||
|
|
76813e9df4 | ||
|
|
35da9cfb54 | ||
|
|
3f28594376 | ||
|
|
64c142d21e | ||
|
|
36304e8356 | ||
|
|
4090930142 | ||
|
|
cd4d5df4db | ||
|
|
b921964c88 | ||
|
|
6dcef71ebc | ||
|
|
c69d4107d8 | ||
|
|
d6c34f2312 | ||
|
|
8666c491ce | ||
|
|
7f4cfc267e | ||
|
|
9d48eb6d74 | ||
|
|
7f80cad356 | ||
|
|
36b3278698 | ||
|
|
2633b4c876 | ||
|
|
fd26e9755d | ||
|
|
c5623a10c1 | ||
|
|
4049695425 | ||
|
|
25b0d12576 | ||
|
|
cb1c490a1c | ||
|
|
565a20a83e | ||
|
|
077965f083 | ||
|
|
eae4f569c2 | ||
|
|
3f999e525a | ||
|
|
509606dbda | ||
|
|
1b12a689a3 | ||
|
|
f91d09b2ae | ||
|
|
89c02483eb | ||
|
|
9725082b06 | ||
|
|
4c9471c415 | ||
|
|
2bee87916d | ||
|
|
7838b6dc0e | ||
|
|
6ae279c292 | ||
|
|
28aea45f95 | ||
|
|
38d0be992c | ||
|
|
73b0c8c68c | ||
|
|
b8002ce577 | ||
|
|
5eebcd01ef | ||
|
|
5a455e1558 | ||
|
|
c2d0eec9d2 | ||
|
|
97104d820e | ||
|
|
f334f804b6 | ||
|
|
38bcafbfe5 | ||
|
|
72196c705e | ||
|
|
eb4d800e40 | ||
|
|
ad508a30ea | ||
|
|
7068309b04 | ||
|
|
06ab054118 | ||
|
|
4f5c929a9b | ||
|
|
268a146775 | ||
|
|
7151c347a0 | ||
|
|
98d813c172 | ||
|
|
7b5a805eda | ||
|
|
d2b6429c8b | ||
|
|
450c9bb755 | ||
|
|
a45cd01e26 | ||
|
|
28f7a7315e | ||
|
|
d9e7e359f9 | ||
|
|
bee41e0e75 | ||
|
|
5e826a3392 | ||
|
|
635ff88a53 | ||
|
|
83156bf28e | ||
|
|
9ba081f241 | ||
|
|
179c3725ca | ||
|
|
645fedb1ed | ||
|
|
104ee3829f | ||
|
|
9f80eb2f74 | ||
|
|
44b3a643dd | ||
|
|
cf61a6bbe7 | ||
|
|
93b45fba84 | ||
|
|
0d1851a73b | ||
|
|
5d6377f71c | ||
|
|
28872f39c1 | ||
|
|
c0b8eab4b8 | ||
|
|
75ba00735d | ||
|
|
fdd7951d13 | ||
|
|
e0427ae200 | ||
|
|
ec269f7177 | ||
|
|
2226082b6b | ||
|
|
c69c188de9 | ||
|
|
8499655c94 | ||
|
|
4c0c13e792 | ||
|
|
9d2d693a63 | ||
|
|
e8d4db3f80 | ||
|
|
e7ebea535e | ||
|
|
c3a7e91a1d | ||
|
|
44be066b26 | ||
|
|
47f4efe60c | ||
|
|
a4d7aff651 | ||
|
|
d55b41963b | ||
|
|
6bfad8e2d0 | ||
|
|
e06ba65e6c | ||
|
|
3acd77a63d | ||
|
|
25b32a6444 | ||
|
|
62ae4fb265 | ||
|
|
57fe2a6cc2 | ||
|
|
a6ef8e0b40 | ||
|
|
467ceb89b1 | ||
|
|
873bc3f582 | ||
|
|
9ebb37c524 | ||
|
|
ccf4ba2269 | ||
|
|
e377a72f75 | ||
|
|
4ed806ea97 | ||
|
|
57ba991673 | ||
|
|
21a48ccab5 | ||
|
|
0f0c27ad09 | ||
|
|
40ed6450ff | ||
|
|
a0c0ea4c4c | ||
|
|
2dc413ca3a | ||
|
|
692b26622c | ||
|
|
72a56d33d0 | ||
|
|
ce4bfc55c1 | ||
|
|
faba53fa5f | ||
|
|
f0faec9def | ||
|
|
e6ef28050c | ||
|
|
547ec3cb3a | ||
|
|
5bfc67af4f | ||
|
|
c72d9b544e | ||
|
|
cd34619acd | ||
|
|
f091817d66 | ||
|
|
f2c65314b4 | ||
|
|
a9fcdd3341 | ||
|
|
36a5cc7446 | ||
|
|
fc0f278962 | ||
|
|
1f3db15892 | ||
|
|
e3e4c49427 | ||
|
|
1520b8bf73 | ||
|
|
c9e7f82905 | ||
|
|
14a1a665ef | ||
|
|
d2357fa779 | ||
|
|
703178ddf1 | ||
|
|
ec836e2e79 | ||
|
|
4e9c5de16c | ||
|
|
2a677aaf02 | ||
|
|
2a141cd9b3 | ||
|
|
3d1fcc35df | ||
|
|
e20d7afb72 | ||
|
|
5ade8b208e | ||
|
|
9c5e4f7a7c | ||
|
|
07e2bc3858 | ||
|
|
5c785ca1c6 | ||
|
|
b389b20ca3 | ||
|
|
6f1d496a9e | ||
|
|
fe12cb345e | ||
|
|
2607994c4c | ||
|
|
47a02afea6 | ||
|
|
a2e0a0c6f7 | ||
|
|
f3b11d6f72 | ||
|
|
edac8f764b | ||
|
|
4fa68e3bb0 | ||
|
|
69f310c02c | ||
|
|
09789e163c | ||
|
|
84c8851ff8 | ||
|
|
95dacdd6da | ||
|
|
3308f1146c | ||
|
|
37b9e46e96 | ||
|
|
8e8cf3df8d | ||
|
|
f4b188a9ac | ||
|
|
35c886a59c | ||
|
|
26fdd924ef | ||
|
|
e87a49796f | ||
|
|
7e0b1e479c | ||
|
|
1af2f7848e | ||
|
|
ea3494f6e7 | ||
|
|
0e7b93771e | ||
|
|
ea5a21e487 | ||
|
|
dc72b87479 | ||
|
|
2f20acfa34 | ||
|
|
4869058f46 | ||
|
|
fa006afd0d | ||
|
|
8e06255758 | ||
|
|
ba57b25cd4 | ||
|
|
4fa9d98171 | ||
|
|
6d19832d01 | ||
|
|
bdaa059fa3 | ||
|
|
f7bfc51735 | ||
|
|
998e156b21 | ||
|
|
491bf850e8 | ||
|
|
9bf53c6b25 | ||
|
|
e2fb49037a | ||
|
|
3291c24c8c | ||
|
|
f0bc375421 | ||
|
|
e3c223dc0a | ||
|
|
53c8b3b66d | ||
|
|
af5000b0f8 | ||
|
|
d5d4fdefc8 | ||
|
|
1caa0c6210 | ||
|
|
5b646617d1 | ||
|
|
9f028d9104 | ||
|
|
f40be696c7 | ||
|
|
95a503a390 | ||
|
|
88c51f4933 | ||
|
|
d63177f3c7 | ||
|
|
e9d41fd01b | ||
|
|
224d2987db | ||
|
|
8439d6d3aa | ||
|
|
13b38955e6 | ||
|
|
ae3fb9a2dd | ||
|
|
9d78d769c4 | ||
|
|
e436a31a18 | ||
|
|
929ade554c | ||
|
|
d6c7066bfa | ||
|
|
2387f183e9 | ||
|
|
e8d645467f | ||
|
|
a8e022cab4 | ||
|
|
579e8abda1 | ||
|
|
4d44440e90 | ||
|
|
709f914561 | ||
|
|
0afa8e092e | ||
|
|
e886619b22 | ||
|
|
fa5f423bbd | ||
|
|
bfbf5c70d5 | ||
|
|
2c19a442a7 | ||
|
|
984ca69e8c | ||
|
|
544d7a15d4 | ||
|
|
858f49d265 | ||
|
|
1b66d446f9 | ||
|
|
6e92fc0505 | ||
|
|
ea84403811 | ||
|
|
65a02795d6 | ||
|
|
c5549367eb | ||
|
|
6aa4a4d205 | ||
|
|
9e20f1dd33 | ||
|
|
16be19b5d3 | ||
|
|
80ac0259b8 | ||
|
|
9f7911923c | ||
|
|
686005071e | ||
|
|
e16d31d605 | ||
|
|
8b201d22cb | ||
|
|
0f6f8d43bd | ||
|
|
8f171cddd9 | ||
|
|
c77c323f73 | ||
|
|
9e358309e4 | ||
|
|
485438f38d | ||
|
|
fe26a60337 | ||
|
|
d2c2de4dd7 | ||
|
|
d49d58f7f8 | ||
|
|
c0c5579027 | ||
|
|
164d4fff8b | ||
|
|
6e7e4a129b | ||
|
|
7726406674 | ||
|
|
ed38b39fec | ||
|
|
9188d4316e | ||
|
|
cf374c6b8b | ||
|
|
82388dcbc3 | ||
|
|
7a6a99e2b6 | ||
|
|
c383736834 | ||
|
|
6e5e78df3b | ||
|
|
311bf1b641 | ||
|
|
5c9933791c | ||
|
|
039d4d11aa | ||
|
|
c0f2d2aa84 | ||
|
|
45099bfa07 | ||
|
|
695ae9d970 | ||
|
|
21eb0064b1 | ||
|
|
513fe402af | ||
|
|
b27e5754e8 | ||
|
|
fa7231d77b | ||
|
|
f5a30ecb33 | ||
|
|
7f22289889 | ||
|
|
f80c39077a | ||
|
|
75c60a1cc4 | ||
|
|
2cf38d6b7c | ||
|
|
482c9c1574 | ||
|
|
882e59a260 | ||
|
|
7a036956b7 | ||
|
|
7520b20cf9 | ||
|
|
d47ccd75d5 | ||
|
|
56ff81a0ed | ||
|
|
8db76f5324 | ||
|
|
a9442ea06e | ||
|
|
92bd0d3ea0 | ||
|
|
512d7322b2 | ||
|
|
45645c439f | ||
|
|
84ebb82cae | ||
|
|
576f6a3bd6 | ||
|
|
96df821eca | ||
|
|
8589778e92 | ||
|
|
a8a7de9ee6 | ||
|
|
1d99340425 | ||
|
|
a8cdfb56f3 | ||
|
|
0958cdaa2d | ||
|
|
ebfb3dd31e | ||
|
|
2293a14a90 | ||
|
|
3e664a7c2c | ||
|
|
30b37951b2 | ||
|
|
0d177d12fe | ||
|
|
f7c8a570f8 | ||
|
|
1814aa86b7 | ||
|
|
d0a5e658d1 | ||
|
|
2cbc1da286 | ||
|
|
75d21231ae | ||
|
|
f2d340a012 | ||
|
|
1b28111c6d | ||
|
|
4f4efcbc23 | ||
|
|
7006d18e93 | ||
|
|
049959b567 | ||
|
|
7cd8c0475e | ||
|
|
207accf21f | ||
|
|
bd1e62abc1 | ||
|
|
d2e33ee423 | ||
|
|
f2934f8453 | ||
|
|
20fb269828 | ||
|
|
cc13707fea | ||
|
|
a9db6a0473 | ||
|
|
282ddb85c7 | ||
|
|
3943b8753f | ||
|
|
9e26ea1866 | ||
|
|
8152a87514 | ||
|
|
b28775c2c6 | ||
|
|
9b27200793 | ||
|
|
0b7d7dac38 | ||
|
|
3eb96ef765 | ||
|
|
8805584cbe | ||
|
|
c2edf314c2 | ||
|
|
2da2d54013 | ||
|
|
f433b911a0 | ||
|
|
8b44815b70 | ||
|
|
788f597a3f | ||
|
|
4ee70acf12 | ||
|
|
be5c66bd93 | ||
|
|
5347e5a3aa | ||
|
|
9b22254b6c | ||
|
|
e5ccd27fd9 | ||
|
|
d34ad62c8a | ||
|
|
0d0bee674d | ||
|
|
279747d3a4 | ||
|
|
1e4bf38402 | ||
|
|
93d1163c5f | ||
|
|
a3e436e42d | ||
|
|
e824886e19 | ||
|
|
9c724b31de | ||
|
|
6a67e0ea8b | ||
|
|
7b118995ea | ||
|
|
35b4564644 | ||
|
|
30df947365 | ||
|
|
b1ec975612 | ||
|
|
a4851c8d81 | ||
|
|
469f48f473 | ||
|
|
c915ac4a74 | ||
|
|
9b74c470da | ||
|
|
1034e434c4 | ||
|
|
0a4935d193 | ||
|
|
5995f6fbef | ||
|
|
0ca15a6c65 | ||
|
|
72193833a9 | ||
|
|
e40ffabe4a | ||
|
|
f419bff545 | ||
|
|
69eec0135e | ||
|
|
a267e32e0e | ||
|
|
92a5249e45 | ||
|
|
fbfe664438 | ||
|
|
b52b5b4287 | ||
|
|
ba3513341a | ||
|
|
de06ce909e | ||
|
|
fa29b746e7 | ||
|
|
5527ebf085 | ||
|
|
cca2052b15 | ||
|
|
ec0338bb30 | ||
|
|
f40a1b6de8 | ||
|
|
7110337dcd | ||
|
|
cbfe850351 | ||
|
|
d6d768029b | ||
|
|
d003e086bb | ||
|
|
a3b66ec456 | ||
|
|
1c3d41c03b | ||
|
|
56b1751c3f | ||
|
|
93724063e2 | ||
|
|
72b8cd2ac8 | ||
|
|
e5b22255e9 | ||
|
|
a4639e295d | ||
|
|
8dc4246c98 | ||
|
|
a187832cdc | ||
|
|
d08a46a573 | ||
|
|
e86fcc9636 | ||
|
|
1f60405d43 | ||
|
|
8f568cc6ac | ||
|
|
4d61820a78 | ||
|
|
b6d49233fb | ||
|
|
beac6be7db | ||
|
|
b99931df95 | ||
|
|
00ba661456 | ||
|
|
2ffde30340 | ||
|
|
f97a84d2df | ||
|
|
c5bb10db4c | ||
|
|
acf4d2f740 | ||
|
|
1c2f6913bb | ||
|
|
31be8bee95 | ||
|
|
2a113efe5c | ||
|
|
801791b4df | ||
|
|
643a2ee739 | ||
|
|
cd9c97ffc0 | ||
|
|
c105319b08 | ||
|
|
d98fa381e8 | ||
|
|
1f39b43869 | ||
|
|
3aeb6a662a | ||
|
|
d7c176d4fd | ||
|
|
cac1df7a66 | ||
|
|
aafa383f56 | ||
|
|
af6e2abdb2 | ||
|
|
05430f7e47 | ||
|
|
2daf4cf46a | ||
|
|
52333beabe | ||
|
|
49d1e0622b | ||
|
|
eebd87c6b7 | ||
|
|
c1b6b30144 | ||
|
|
7703986c9e | ||
|
|
6c272d9336 | ||
|
|
73b0416dbe | ||
|
|
c7760b0ed9 | ||
|
|
c0db476f80 | ||
|
|
7999c29b51 | ||
|
|
1d111a252e | ||
|
|
93d085e23e | ||
|
|
67e0e34f40 | ||
|
|
098e61295c | ||
|
|
1d2666a343 | ||
|
|
b8c2c86c59 | ||
|
|
edf0342404 | ||
|
|
dc6815045c | ||
|
|
23ed1cf5e5 | ||
|
|
97715f260f | ||
|
|
87411ee952 | ||
|
|
d0bbf1e4ad | ||
|
|
3bbd088733 | ||
|
|
d96dd5fe82 | ||
|
|
6028e3f299 | ||
|
|
37bfce1905 | ||
|
|
5993b30ccf | ||
|
|
2fc6c4c0bc | ||
|
|
0195a6bab5 | ||
|
|
ccb35bdc5a | ||
|
|
bfadfaf261 | ||
|
|
d0180a6633 | ||
|
|
afbc580ae6 | ||
|
|
2b2e01f3d1 | ||
|
|
529f1ca835 | ||
|
|
ee7993b918 | ||
|
|
8239a40c72 | ||
|
|
2c9f9a36c5 | ||
|
|
307169fcc3 | ||
|
|
5ee69d24f7 | ||
|
|
077106b71b | ||
|
|
141e660d91 | ||
|
|
be73699013 | ||
|
|
65e3649999 | ||
|
|
994f1ae7ed | ||
|
|
548e633b72 | ||
|
|
87b98e7949 | ||
|
|
5692baf1f3 | ||
|
|
8a10a2aa18 | ||
|
|
797bbf8870 | ||
|
|
edb3f9e885 | ||
|
|
a0008e165b | ||
|
|
3d55b033d6 | ||
|
|
93fb0efc67 | ||
|
|
b7585ef02c | ||
|
|
fce07288e9 | ||
|
|
3ccfdb2e97 | ||
|
|
5840e2ba07 | ||
|
|
4daba88970 | ||
|
|
3180feb874 | ||
|
|
17a430f31f | ||
|
|
20d330f0f1 | ||
|
|
27d0467e08 | ||
|
|
5437967218 | ||
|
|
1228f0572d | ||
|
|
9cf44c3050 | ||
|
|
aa9b641787 | ||
|
|
2e3c4f33a2 | ||
|
|
39ef6ebdbf | ||
|
|
4d08c22204 | ||
|
|
4041dd3ad5 | ||
|
|
a38de8198f | ||
|
|
3369374e43 | ||
|
|
d74631bbdb | ||
|
|
be8c392562 | ||
|
|
f564efb4d6 | ||
|
|
dee9e3c326 | ||
|
|
4ff8d7d68e | ||
|
|
789e467573 | ||
|
|
745b85a3ac | ||
|
|
4f84c2cb01 | ||
|
|
83414acb1d | ||
|
|
356effc6a9 | ||
|
|
88ec186b98 | ||
|
|
530615f049 | ||
|
|
afec12822f | ||
|
|
f117620ead | ||
|
|
1c9f34ba1b | ||
|
|
c26cd9876a | ||
|
|
32f7d37768 | ||
|
|
3873e680e4 | ||
|
|
ddc8a9a286 | ||
|
|
399bde7588 | ||
|
|
5e51febee8 | ||
|
|
6e8200a380 | ||
|
|
fc9961b2de | ||
|
|
d750e3e996 | ||
|
|
1cd15c6f7c | ||
|
|
5c47acbcc3 | ||
|
|
1363c2893b | ||
|
|
cbc426014b | ||
|
|
17178e3ae3 | ||
|
|
b9299d90df | ||
|
|
aff5b46c5e | ||
|
|
2e83f81672 | ||
|
|
e352cc76cf | ||
|
|
cab2ee4fa4 | ||
|
|
bdfc839080 | ||
|
|
6beb21349b | ||
|
|
b2240e8f1a | ||
|
|
32f43d46b5 | ||
|
|
803d689bd3 | ||
|
|
66255bf443 | ||
|
|
338a486513 | ||
|
|
b543343045 | ||
|
|
28c60332e8 | ||
|
|
9ddc45721f | ||
|
|
2849baf857 | ||
|
|
768fdcd0e4 | ||
|
|
5280ef32cd | ||
|
|
59a7e4179b | ||
|
|
bd2054e5a4 | ||
|
|
69be19c02b | ||
|
|
fcffeb5734 | ||
|
|
11b67208f4 | ||
|
|
03e8342c90 | ||
|
|
fcd070066c | ||
|
|
9b38308ad1 | ||
|
|
5d79ad527e | ||
|
|
afcdf96e5c | ||
|
|
ffc8b5619e | ||
|
|
3e40b08525 | ||
|
|
9be49b3738 | ||
|
|
724488911e | ||
|
|
8ac0bf5865 | ||
|
|
57c1c38d52 | ||
|
|
237cf037be | ||
|
|
8b98181752 | ||
|
|
21993a4862 | ||
|
|
6e8e1ad74b | ||
|
|
03da5b3cf1 | ||
|
|
f348ef1f96 | ||
|
|
193cebed81 | ||
|
|
f2f6156bd0 | ||
|
|
0d7c3838f6 | ||
|
|
105e3360be | ||
|
|
23dd3b4f41 | ||
|
|
bc225f9656 | ||
|
|
dd9f76f73b | ||
|
|
47ae8b903e | ||
|
|
3dcfd3738d | ||
|
|
8993794a88 | ||
|
|
a530e7e70a | ||
|
|
4791b8e53c | ||
|
|
6ddd19ac54 | ||
|
|
6a871e73e0 | ||
|
|
7ba106d97b | ||
|
|
69c021cc54 | ||
|
|
7a63bb395f | ||
|
|
1931695b03 | ||
|
|
b953240966 | ||
|
|
e1c06af3b2 | ||
|
|
1cc7bfcb2d | ||
|
|
2445c74638 | ||
|
|
6a69ed48a9 | ||
|
|
714c7eb8d1 | ||
|
|
6790287470 | ||
|
|
217693937f | ||
|
|
94a8a5c753 | ||
|
|
22300577cf | ||
|
|
6a07f4e731 | ||
|
|
63ace60dcc | ||
|
|
601951582d | ||
|
|
ca4d22602f | ||
|
|
42774a6227 | ||
|
|
7f65bfeebb | ||
|
|
88178a6b6b | ||
|
|
3f9ca1454e | ||
|
|
9a3f1bd5cf | ||
|
|
0186254f74 | ||
|
|
6dc432e6a9 | ||
|
|
01db774e05 | ||
|
|
aaef3dc19c | ||
|
|
bb8009363d | ||
|
|
5fd403cf08 | ||
|
|
168c77eac5 | ||
|
|
bf865cbe5d | ||
|
|
3e3029461b | ||
|
|
c750e0a64e | ||
|
|
fc2662c402 | ||
|
|
29dad51ff7 | ||
|
|
9250dde5b0 | ||
|
|
29ca72d8ca | ||
|
|
87c89a0de5 | ||
|
|
73f72e39d3 | ||
|
|
e00ba62606 | ||
|
|
48d53cecce | ||
|
|
da44bba3fe | ||
|
|
b55e42ba78 | ||
|
|
233a9843d7 | ||
|
|
6184087456 | ||
|
|
237ad2fa3c | ||
|
|
b8037c04d0 | ||
|
|
cdb9c2eddb | ||
|
|
8a284f0767 | ||
|
|
83a57b08ab | ||
|
|
d1a251ff6c | ||
|
|
12f0c826c8 | ||
|
|
4aa5da8c0d | ||
|
|
e0fd81e938 | ||
|
|
58509bcf41 | ||
|
|
8aa0eb1d64 | ||
|
|
ece974c1be | ||
|
|
56dd3c32cb | ||
|
|
118e3a3bd3 | ||
|
|
8cd3c2a89f | ||
|
|
9c4672fd68 | ||
|
|
04f6f4f9cf | ||
|
|
7b851e7ac8 | ||
|
|
6c0b33c0dc | ||
|
|
4d951c9827 | ||
|
|
172232be48 | ||
|
|
d749a13611 | ||
|
|
3998be3f93 | ||
|
|
a381827890 | ||
|
|
eecfe45443 | ||
|
|
ad5eb7b594 | ||
|
|
9ce09eb406 | ||
|
|
9c23f3d1d9 | ||
|
|
3f779b7ceb | ||
|
|
3bc53bd7f8 | ||
|
|
27b61fe9a4 | ||
|
|
4d94c7ac7a | ||
|
|
4eb1125b7b | ||
|
|
496b481c31 | ||
|
|
77120cf156 | ||
|
|
80f11c0f70 | ||
|
|
bee8059cbe | ||
|
|
0f2e72172b | ||
|
|
25d1e0a74d | ||
|
|
cbd9d16e4c | ||
|
|
cb708b3217 | ||
|
|
4e5a22a7e8 | ||
|
|
3722db93c6 | ||
|
|
b43331dc27 | ||
|
|
80c6ce81e1 | ||
|
|
1a776359c4 | ||
|
|
113e4035e5 | ||
|
|
a03095ab8e | ||
|
|
d03f830622 | ||
|
|
9b42dc9f10 | ||
|
|
fd7adf3c64 | ||
|
|
d0b7e72f15 | ||
|
|
5a61c6b7df | ||
|
|
c897eae04e | ||
|
|
c33095cd0e | ||
|
|
05d8b7983a | ||
|
|
166d9f7133 | ||
|
|
2ce31f35cc | ||
|
|
ce5d0cefe3 | ||
|
|
6cc668fe83 | ||
|
|
ac0aa8daf4 | ||
|
|
16b094be44 | ||
|
|
b3e6fd7b96 | ||
|
|
893d5d92f3 | ||
|
|
77add0b39d | ||
|
|
b4f8ab0a49 | ||
|
|
1d46c44c21 | ||
|
|
12da35f875 | ||
|
|
8970a15d4e | ||
|
|
82217478d9 | ||
|
|
4c792308bb | ||
|
|
978d94a2cd | ||
|
|
b195d89b76 | ||
|
|
875888a341 | ||
|
|
6631a13d20 | ||
|
|
28728fe29f | ||
|
|
aa954cbea9 | ||
|
|
dae339f494 | ||
|
|
5bcb59f876 | ||
|
|
9132592fd7 | ||
|
|
ca592fac77 | ||
|
|
ea657824c2 | ||
|
|
99e7072e27 | ||
|
|
ad32f4a0a3 | ||
|
|
b9cdd0996d | ||
|
|
e92a76f95b | ||
|
|
10d152342f | ||
|
|
7e4b0635b2 | ||
|
|
eec17f10d5 | ||
|
|
ac9d5dfa95 | ||
|
|
4c2b3738c2 | ||
|
|
6f29183b45 | ||
|
|
dd65c9c588 | ||
|
|
cb8bb05627 | ||
|
|
256f6d8fdd | ||
|
|
a88fdddecb | ||
|
|
fd5e3e24df | ||
|
|
244d27427d | ||
|
|
55c42a3cca | ||
|
|
3c9cfbdf92 | ||
|
|
46dd58d26e | ||
|
|
051dd2e567 | ||
|
|
17ccf40082 | ||
|
|
0f2b61694d | ||
|
|
0aed66df47 | ||
|
|
94a6e9a886 | ||
|
|
355f48b6ba | ||
|
|
d1a6526c22 | ||
|
|
975a308647 | ||
|
|
5138eaeb68 | ||
|
|
45dfaa1b21 | ||
|
|
715f92f3d5 | ||
|
|
8d5a765040 | ||
|
|
34fa6b158b | ||
|
|
78c1f6b8e3 | ||
|
|
adccb91106 | ||
|
|
1e213dfff7 | ||
|
|
d469fa1d40 | ||
|
|
737db469ba | ||
|
|
a44a33d508 | ||
|
|
a518d41c78 | ||
|
|
d5360c6568 | ||
|
|
d468107311 | ||
|
|
3f65e1bfa0 | ||
|
|
a22758f5cd | ||
|
|
88b5b9d1e7 | ||
|
|
e38f7c880e | ||
|
|
6367b58994 | ||
|
|
5a54b5ee64 | ||
|
|
002e41f418 | ||
|
|
f7787c748a | ||
|
|
f9b18b45a4 | ||
|
|
8966f44272 | ||
|
|
3dbcbae61f | ||
|
|
24508abe7a | ||
|
|
27f8f2542e | ||
|
|
44ee3ceec8 | ||
|
|
b08112aec0 | ||
|
|
9d2bbda2b9 | ||
|
|
6eff685b18 | ||
|
|
2d08146de8 | ||
|
|
f131420a44 | ||
|
|
245ea5463e | ||
|
|
71bb90a769 | ||
|
|
e71a22fbb3 | ||
|
|
802c4c28f5 | ||
|
|
2cda6fc057 | ||
|
|
a0303c70f9 | ||
|
|
f5b9f58747 | ||
|
|
0d8dee0ed7 | ||
|
|
ace02263dd | ||
|
|
a2398bfb3a | ||
|
|
cd7ddb5ba1 | ||
|
|
ae37a4c85d | ||
|
|
32e1716061 | ||
|
|
d92616f39f | ||
|
|
0c13db46fd | ||
|
|
b6fc9a8ec1 | ||
|
|
a672141352 | ||
|
|
df354e483a | ||
|
|
89874545b1 | ||
|
|
b0fdd09f39 | ||
|
|
fbf620ae36 | ||
|
|
509f7da12e | ||
|
|
64a40e4ed0 | ||
|
|
ab253dbf28 | ||
|
|
feb19df20f | ||
|
|
d5d2241f1b | ||
|
|
5af1a0aa87 | ||
|
|
816621651d | ||
|
|
8f2aa08dbe | ||
|
|
d20e1a6e9c | ||
|
|
2a5a4c9ace | ||
|
|
2e7fd3fb60 | ||
|
|
56579100f1 | ||
|
|
1e5cd24acd | ||
|
|
7f152cef20 | ||
|
|
3958d2d597 | ||
|
|
84a3b31e8d | ||
|
|
cc15c12d19 | ||
|
|
50e7b89b15 | ||
|
|
0b507cb66a | ||
|
|
b1c697ea09 | ||
|
|
dbcf15e45b | ||
|
|
21d864ce7a | ||
|
|
dcf1f1af10 | ||
|
|
9c25a39b7b | ||
|
|
d79687db53 | ||
|
|
77428e0fe5 | ||
|
|
53df3b5cfa | ||
|
|
f2e1c9e959 | ||
|
|
a004b2f3e0 | ||
|
|
65b4398b0e | ||
|
|
0b83d2869d | ||
|
|
1a408b810c | ||
|
|
bab5879c3d | ||
|
|
6f79954989 | ||
|
|
8d4622dc31 | ||
|
|
fecdc48b09 | ||
|
|
0edb51a32a | ||
|
|
750bd2f61b | ||
|
|
f6fa9821f0 | ||
|
|
3ed06dafb0 | ||
|
|
4be6a5b5a9 | ||
|
|
a4c77ce33e | ||
|
|
f6babd01ca | ||
|
|
17ce9a0903 | ||
|
|
824b4681e4 | ||
|
|
7ba7e0b2d8 | ||
|
|
0391eabc79 | ||
|
|
e2ebf6b8fe | ||
|
|
58d3d678ca | ||
|
|
b52b68595b | ||
|
|
0a8d606686 | ||
|
|
d1ef9f3e99 | ||
|
|
5a830c17db | ||
|
|
10c4a4d665 | ||
|
|
e93e7f05c6 | ||
|
|
e2c6240798 | ||
|
|
24ad483214 | ||
|
|
bfa53224a6 | ||
|
|
00ec25d38c | ||
|
|
348fc156de | ||
|
|
1a9cb5335a | ||
|
|
0abfab96a4 | ||
|
|
bbfbbbf91e | ||
|
|
8db0150c4a | ||
|
|
3d7db6136f | ||
|
|
ace6c1167b | ||
|
|
4644dfbd7b | ||
|
|
c7b205dbde | ||
|
|
ffff2636ae | ||
|
|
c69e1bb091 | ||
|
|
49531285c9 | ||
|
|
b9824d0095 | ||
|
|
3dc04f9246 | ||
|
|
1bf6dc37d9 | ||
|
|
97a0e2413e | ||
|
|
c5f199bdf8 | ||
|
|
656e3ff022 | ||
|
|
94f1a80323 | ||
|
|
11256e6988 | ||
|
|
db6c32891a | ||
|
|
6d819206ea | ||
|
|
32240ee475 | ||
|
|
77a1c2cfb8 | ||
|
|
0f6d7ce2a4 | ||
|
|
e8738adc40 | ||
|
|
bca02edead | ||
|
|
abf81ac26b | ||
|
|
011d2f83f6 | ||
|
|
1a80c5273c | ||
|
|
cf8f31168e | ||
|
|
e25d7ca995 | ||
|
|
51fa8994c3 | ||
|
|
78852b7119 | ||
|
|
193bb0bf26 | ||
|
|
d862462bbf | ||
|
|
dcecd30e7c | ||
|
|
a01ef3bb08 | ||
|
|
60cd2c2b12 | ||
|
|
daad1cac5e | ||
|
|
c6b89425b1 | ||
|
|
70408ca8ee | ||
|
|
b3bf86d404 | ||
|
|
296c337eb6 | ||
|
|
a6f71911fe | ||
|
|
b91a781d27 | ||
|
|
6aa363830b | ||
|
|
fffc0f9eae | ||
|
|
1d2889f5b8 | ||
|
|
6ed7568cd9 | ||
|
|
22ca5b76b4 | ||
|
|
a03ce5bf7b | ||
|
|
2994806a35 | ||
|
|
be17960ce6 | ||
|
|
eeb2f0ad60 | ||
|
|
314e5bc364 | ||
|
|
a294bfde11 | ||
|
|
baa496fc07 | ||
|
|
4ca29d5d2e | ||
|
|
5e7074a297 | ||
|
|
fd6268ce8a | ||
|
|
806da552ce | ||
|
|
47561340e3 | ||
|
|
b374952141 | ||
|
|
e098970d82 | ||
|
|
6ca5f5e877 | ||
|
|
6b023c5fb6 | ||
|
|
bd30103758 | ||
|
|
e9ead72e79 | ||
|
|
513edc58ab | ||
|
|
fd7e7bd439 | ||
|
|
f510f20f57 | ||
|
|
a0a92b9b40 | ||
|
|
ba878c7587 | ||
|
|
f1f76a4910 | ||
|
|
3ea4bc29ba | ||
|
|
82848aa4cd | ||
|
|
5131604ba7 | ||
|
|
1be821b07a | ||
|
|
1ed475d469 | ||
|
|
e11413eab8 | ||
|
|
98db923ac7 | ||
|
|
7376ff1c9b | ||
|
|
d427e3e0ae | ||
|
|
550119a53a | ||
|
|
2df182c4c9 | ||
|
|
2b9b872ad4 | ||
|
|
94853714c7 | ||
|
|
01529a8028 | ||
|
|
71d348227e | ||
|
|
76f585a051 | ||
|
|
f1085716d9 | ||
|
|
93bb620099 | ||
|
|
ded0bd7d12 | ||
|
|
088f646d6f | ||
|
|
e6a00f7935 | ||
|
|
e5d625f294 | ||
|
|
6e3bfcdf04 | ||
|
|
10bd19e390 | ||
|
|
9a1cb822d1 | ||
|
|
8ad5f3426c | ||
|
|
2070a98a72 | ||
|
|
b97410dab7 | ||
|
|
d47bc021a5 | ||
|
|
0b2dfecf27 | ||
|
|
46ea97333e | ||
|
|
598a9f6a0f | ||
|
|
ef7d92d6bd | ||
|
|
da4fe41f34 | ||
|
|
91fe4050b2 | ||
|
|
23da6cf69e | ||
|
|
51883b450f | ||
|
|
cbdf02db55 | ||
|
|
196096cd65 | ||
|
|
476ac38883 | ||
|
|
90fea916fd | ||
|
|
6fccd203a4 | ||
|
|
f2f0ee0179 | ||
|
|
1f9218bd5a | ||
|
|
966f7b2d61 | ||
|
|
58c4b541b0 | ||
|
|
f6c490fc62 | ||
|
|
be59392814 | ||
|
|
d0e21d7d8a | ||
|
|
737373940e | ||
|
|
c639b89511 | ||
|
|
c530b3f8b4 | ||
|
|
1592a448f3 | ||
|
|
c5ab2b1373 | ||
|
|
f14f2cb5b7 | ||
|
|
9c2945d270 | ||
|
|
05357c24f6 | ||
|
|
75839f330c | ||
|
|
ba0a9bbe58 | ||
|
|
fb082471fe | ||
|
|
f8968f2eb1 | ||
|
|
990605948b | ||
|
|
21c96281a8 | ||
|
|
1ad7fce7a5 | ||
|
|
76162953e6 | ||
|
|
b934e29dc0 | ||
|
|
93c128438c | ||
|
|
c283ddbb6a | ||
|
|
e3085a7054 | ||
|
|
4aa9a6e7a3 | ||
|
|
76b0057deb | ||
|
|
5d964f7638 | ||
|
|
81610dee0e | ||
|
|
f64c0ae266 | ||
|
|
324992f6ff | ||
|
|
c35ce9ffe1 | ||
|
|
a13a6d3919 | ||
|
|
ac01c6fff2 | ||
|
|
53074c7b20 | ||
|
|
1d4ef9474f | ||
|
|
59e948fa06 | ||
|
|
55fe30b02c | ||
|
|
e2929f43a3 | ||
|
|
9d67f58490 | ||
|
|
a862240ed7 | ||
|
|
0f604f453d | ||
|
|
113e9eba79 | ||
|
|
9af610a45b | ||
|
|
e8db5d267e | ||
|
|
201fbb71c1 | ||
|
|
2f87e55ae6 | ||
|
|
34af797ad2 | ||
|
|
4ca640eee5 | ||
|
|
29d1bf7ce5 | ||
|
|
c2f9c687ce | ||
|
|
a8da64cc6a | ||
|
|
d522258e0a | ||
|
|
5f738f97ea | ||
|
|
db319825a1 | ||
|
|
cbb9ceb709 | ||
|
|
333ee232ae | ||
|
|
963b483697 | ||
|
|
49425a37ac | ||
|
|
0da975a9d3 | ||
|
|
1ea0522fcb | ||
|
|
aaaa0e6aea | ||
|
|
42368f05f8 | ||
|
|
4e51a5db73 | ||
|
|
4bd5a3675f | ||
|
|
a4d3850f13 | ||
|
|
864d370994 | ||
|
|
47f0550cea | ||
|
|
1bbc2b0165 | ||
|
|
fc8a1ec4fc | ||
|
|
4baabd7d07 | ||
|
|
92ba170cdf | ||
|
|
e842850695 | ||
|
|
41fcbb2989 | ||
|
|
d6a6cf6a59 | ||
|
|
113c8f6d4a | ||
|
|
8218acddca | ||
|
|
7cb65df19e | ||
|
|
91ba993279 | ||
|
|
505f3dd947 | ||
|
|
7eca66e2c4 | ||
|
|
3111c5437b | ||
|
|
d32a1adead | ||
|
|
9c5a2c0196 | ||
|
|
19a76008d7 | ||
|
|
2ffcccdc6f | ||
|
|
67c01046be | ||
|
|
c755d651cc | ||
|
|
0529729626 | ||
|
|
984d989f14 | ||
|
|
a204e34d53 | ||
|
|
3e47041820 | ||
|
|
c471f621b2 | ||
|
|
e2e010c418 | ||
|
|
913e0dd3e4 | ||
|
|
192a718d08 | ||
|
|
a881232eb1 | ||
|
|
cf73e827e0 | ||
|
|
bf97905fe9 | ||
|
|
f962b8a573 | ||
|
|
9e9786ecc7 | ||
|
|
59d22b536a | ||
|
|
e8d17fd733 | ||
|
|
42f4cb618b | ||
|
|
faa240ab97 | ||
|
|
a02e72d895 | ||
|
|
852b43b121 | ||
|
|
020823510d | ||
|
|
5c92128b17 | ||
|
|
7cee3c9050 | ||
|
|
f31cf9f839 | ||
|
|
960de69c20 | ||
|
|
6d8e4d14e6 | ||
|
|
fd7929134b | ||
|
|
40016f0d52 | ||
|
|
05c46653a1 | ||
|
|
a932129d52 | ||
|
|
426c07ce75 | ||
|
|
140723108e | ||
|
|
418f023780 | ||
|
|
85b6e09ea6 | ||
|
|
d6bd7e0886 | ||
|
|
2df93bea24 | ||
|
|
b474a67ef4 | ||
|
|
ddd94da47b | ||
|
|
48bd1854e4 | ||
|
|
6582ebcf57 | ||
|
|
41cd6f4290 | ||
|
|
5f40bab9c5 | ||
|
|
4b90b259a8 | ||
|
|
279682df11 | ||
|
|
64b6c5967d | ||
|
|
5826f055e3 | ||
|
|
e4ec71928d | ||
|
|
cb2ff4002f | ||
|
|
36d5c888a5 | ||
|
|
de7493bc00 | ||
|
|
6819c1d9ff | ||
|
|
021a3fe3bc | ||
|
|
0d70033e4c | ||
|
|
812cf21f98 | ||
|
|
f0da088836 | ||
|
|
d7501751e3 | ||
|
|
94648f072e | ||
|
|
a7d34b2c1b | ||
|
|
26403096e8 | ||
|
|
62190bde17 | ||
|
|
293b3d90b9 | ||
|
|
1928b904e5 | ||
|
|
a71cf6080f | ||
|
|
14d56c3e28 | ||
|
|
707e3bc08f | ||
|
|
0614aef362 | ||
|
|
2ff9a7ff22 | ||
|
|
3e4cfc16df | ||
|
|
fcd846bd6d | ||
|
|
05028c958e | ||
|
|
40455e199b | ||
|
|
e5c9ccbdcc | ||
|
|
be22ebd789 | ||
|
|
050d12a8ec | ||
|
|
5746d0264a | ||
|
|
0a4010d9a9 | ||
|
|
ab30dd2294 | ||
|
|
f53e77ecc9 | ||
|
|
91da8f1b5f | ||
|
|
1bf616579b | ||
|
|
da2d48b49a | ||
|
|
48b209155b | ||
|
|
7fe8749807 | ||
|
|
b73547ced9 | ||
|
|
9c8c22979f | ||
|
|
60b387ac56 | ||
|
|
20339c1452 | ||
|
|
7a15a8151c | ||
|
|
967d9b1036 | ||
|
|
faec28ac78 | ||
|
|
0832c8610b | ||
|
|
fa7ca994e9 | ||
|
|
288221918e | ||
|
|
5462e96090 | ||
|
|
a1b5d8bdc5 | ||
|
|
bd280ad2b6 | ||
|
|
0c460f7aab | ||
|
|
634c420799 | ||
|
|
0bdb57c44e | ||
|
|
2540777e18 | ||
|
|
eaced33ed5 | ||
|
|
0b97c47f4e | ||
|
|
584075a9b1 | ||
|
|
3febb2be5e | ||
|
|
c4a9841ba6 | ||
|
|
c613c3b4e1 | ||
|
|
814f07212d | ||
|
|
a63120b78f | ||
|
|
598a39fcc1 | ||
|
|
425fdb502c | ||
|
|
3016ef8545 | ||
|
|
87b4dd09e4 | ||
|
|
95f54b9330 | ||
|
|
1a8d4ea7fd | ||
|
|
e27424102b | ||
|
|
d35540e691 | ||
|
|
bda536cc62 | ||
|
|
cf3443ace3 | ||
|
|
b8e05fc7da | ||
|
|
ddfbb1d42e | ||
|
|
c4c255d560 | ||
|
|
b1fd47ff9d | ||
|
|
636848ed26 | ||
|
|
6e713ebd83 | ||
|
|
80a4d2af59 | ||
|
|
d0153da3d1 | ||
|
|
aa6f467942 | ||
|
|
34f8a123a5 | ||
|
|
63ae7d845c | ||
|
|
12d8097774 | ||
|
|
ae4ce70c48 | ||
|
|
cc43eab6ee | ||
|
|
ea5ec58ca3 | ||
|
|
775499a1ff | ||
|
|
46d04541f1 | ||
|
|
d80497011b | ||
|
|
02fea4ad5d | ||
|
|
effc6fc9dd | ||
|
|
300fb55ec5 | ||
|
|
8a1f2cbca8 | ||
|
|
dec411e866 | ||
|
|
88f40841d4 | ||
|
|
c73aaa5455 | ||
|
|
6bf493f9a1 | ||
|
|
b0be42815d | ||
|
|
79a423fc18 | ||
|
|
7047f2580f | ||
|
|
e8080aff58 | ||
|
|
d8ffed6e75 | ||
|
|
0a3f201cb7 | ||
|
|
00adac42b8 | ||
|
|
9a07ac075f | ||
|
|
44053f92c6 | ||
|
|
21475e0596 | ||
|
|
3ac09748db | ||
|
|
91ff0786cc | ||
|
|
45b207f399 | ||
|
|
d7d15a0ab3 | ||
|
|
bce8d2e4b8 | ||
|
|
d4e48e8b63 | ||
|
|
6de57ee0d4 | ||
|
|
c3c9bb33a2 | ||
|
|
ff4e6ab3ca | ||
|
|
b646e7ef70 | ||
|
|
981919335d | ||
|
|
42fcdd2267 | ||
|
|
2b6367a8a9 | ||
|
|
cbe0c1a550 | ||
|
|
759c072c11 | ||
|
|
d39e2a506f | ||
|
|
f035872592 | ||
|
|
0324225c38 | ||
|
|
a352516176 | ||
|
|
07fab69221 | ||
|
|
82804c1390 | ||
|
|
536d6f7c29 | ||
|
|
a4ec3291ca | ||
|
|
10420d9e7f | ||
|
|
522d139044 | ||
|
|
81a944a95c | ||
|
|
75b4ea175b | ||
|
|
62b1402b43 | ||
|
|
688dbd1ce9 | ||
|
|
941e3d5904 | ||
|
|
a938e098e5 | ||
|
|
1bda39bc7e | ||
|
|
6c5fedb631 | ||
|
|
55e48742a9 | ||
|
|
9a13273726 | ||
|
|
45e641067b | ||
|
|
6a7dedfe67 | ||
|
|
28fa043b32 | ||
|
|
d7c339ccea | ||
|
|
cdc6567628 | ||
|
|
fb315fd782 | ||
|
|
0907a19821 | ||
|
|
00b0552e5e | ||
|
|
9949907adc | ||
|
|
94ab1ddfbc | ||
|
|
c13ff71f9a | ||
|
|
e64ebf06f1 | ||
|
|
20f527ae94 | ||
|
|
1b0f9fcd88 | ||
|
|
05799767a0 | ||
|
|
86cb233cf6 | ||
|
|
5838ff045a | ||
|
|
95ddca0d7c | ||
|
|
a03e2783d4 | ||
|
|
f0638b0dd8 | ||
|
|
ab3e00f42c | ||
|
|
c67971035d | ||
|
|
3c55446e86 | ||
|
|
fd559b61b1 | ||
|
|
77ca139363 | ||
|
|
f0828d3870 | ||
|
|
0e4ffde6ca | ||
|
|
a50f96606b | ||
|
|
ecb77238a3 | ||
|
|
3fe5801366 | ||
|
|
9807231b72 | ||
|
|
eed85b3641 | ||
|
|
7d7d4e62a3 | ||
|
|
f0f9d2a6aa | ||
|
|
7b2a30dcbf | ||
|
|
b214efc029 | ||
|
|
e74884bcea | ||
|
|
4accd71744 | ||
|
|
fa7c170540 | ||
|
|
92360eb2ce | ||
|
|
b13382a6ee | ||
|
|
b9718edfd3 | ||
|
|
d3e2976ff9 | ||
|
|
5e84b7cffe | ||
|
|
764eb764ce | ||
|
|
a992b41e26 | ||
|
|
7d882c3f74 | ||
|
|
668e8de7df | ||
|
|
29ad420916 | ||
|
|
f743942e65 | ||
|
|
0a120e402e | ||
|
|
6bc0c79624 | ||
|
|
6860ddced3 | ||
|
|
ddf5e38396 | ||
|
|
2ff2e448be | ||
|
|
9882dda905 | ||
|
|
88d005caf6 | ||
|
|
eba5fc9caf | ||
|
|
3a2f35f697 | ||
|
|
2ba3e95379 | ||
|
|
023570298b | ||
|
|
cf7e4d1f52 | ||
|
|
a09ce702c1 | ||
|
|
4c6f5dd72d | ||
|
|
dabed1568c | ||
|
|
79dee21f9b | ||
|
|
6ccbe69c02 | ||
|
|
3ad8a14786 | ||
|
|
c79ee46551 | ||
|
|
b391940037 | ||
|
|
d5a54dd63d | ||
|
|
6799ac2b30 | ||
|
|
2f10e60dbf | ||
|
|
95c818d92e | ||
|
|
e6f846107a | ||
|
|
b757ab7424 | ||
|
|
d439a0df00 | ||
|
|
c3e14afe0a | ||
|
|
22f43d63ec | ||
|
|
07c364cf0b | ||
|
|
96485d9b9f | ||
|
|
4cdfeeecf2 | ||
|
|
00abd53b19 | ||
|
|
6666552b8a | ||
|
|
b65fdbdd78 | ||
|
|
7c7d791f31 | ||
|
|
d66f333bd5 | ||
|
|
5afb9c572e | ||
|
|
866aeab76b | ||
|
|
ae13b5f920 | ||
|
|
cfbcb502d8 | ||
|
|
5f8d274b94 | ||
|
|
092171982b | ||
|
|
30ccc0c76c | ||
|
|
bf23d78e41 | ||
|
|
08466f5179 | ||
|
|
4ca07d534b | ||
|
|
3237875aee | ||
|
|
c8db438cca | ||
|
|
5ea2304d5e | ||
|
|
197652f8eb | ||
|
|
80b16d8b57 | ||
|
|
b8d78c1733 | ||
|
|
8bf5004147 | ||
|
|
89538e7289 | ||
|
|
ff46d39a6a | ||
|
|
b843a55a25 | ||
|
|
54008af8bf | ||
|
|
d73f0d2401 | ||
|
|
86228e4270 | ||
|
|
5ab5688844 | ||
|
|
53cb4149da | ||
|
|
1056e20ec2 | ||
|
|
a589f337b7 | ||
|
|
73ee88ab08 | ||
|
|
1e68483deb | ||
|
|
9c168087af | ||
|
|
d988644b6a | ||
|
|
22c21222d3 | ||
|
|
73627a680c | ||
|
|
e70e8262e6 | ||
|
|
8a74809ea4 | ||
|
|
9cf3c23328 | ||
|
|
40347c09ba | ||
|
|
64ed15ffae | ||
|
|
617ec4cc3e | ||
|
|
bec154e538 | ||
|
|
6e565c0b31 | ||
|
|
c2b32de533 | ||
|
|
454f007e42 | ||
|
|
44a81cb7d8 | ||
|
|
4623853399 | ||
|
|
15c894845c | ||
|
|
10597aed2f | ||
|
|
2810fa7f80 | ||
|
|
a6dbc59ad1 | ||
|
|
1afe93be66 | ||
|
|
1bd6839b21 | ||
|
|
b5e3564724 | ||
|
|
8b143a2713 | ||
|
|
543f5e126f | ||
|
|
a5ac70cd3f | ||
|
|
07ed1b3f74 | ||
|
|
7218208355 | ||
|
|
d483c6a726 | ||
|
|
cb3fe686f3 | ||
|
|
69477cf3b4 | ||
|
|
c114c68f15 | ||
|
|
1858c0f000 | ||
|
|
5c203c8e30 | ||
|
|
005425efb1 | ||
|
|
0c87954755 | ||
|
|
3c11092af1 | ||
|
|
4b71540dc7 | ||
|
|
e22e3967f5 | ||
|
|
50809f45a3 | ||
|
|
82bbf9d39f | ||
|
|
4692b20a74 | ||
|
|
9a32aa3350 | ||
|
|
6d1fa4764d | ||
|
|
267af5d6db | ||
|
|
4bcb06c35b | ||
|
|
ff14c91fe1 | ||
|
|
a0f7241f85 | ||
|
|
66e7dfe6d5 | ||
|
|
1538628e16 | ||
|
|
1b9d18adc1 | ||
|
|
124ebaa970 | ||
|
|
b44e657279 | ||
|
|
de24dbbb7a | ||
|
|
60f79b4400 | ||
|
|
03c8f96a46 | ||
|
|
13d833ac5c | ||
|
|
71683992a0 | ||
|
|
2c785ade51 | ||
|
|
530819e788 | ||
|
|
c4c175e107 | ||
|
|
6280fdf973 | ||
|
|
269366264d | ||
|
|
8137b0831c | ||
|
|
396f63a3c9 | ||
|
|
cf87f2ee4d | ||
|
|
c98e672a73 | ||
|
|
21fe39542d | ||
|
|
8747da47a5 | ||
|
|
b3ff4de2ae | ||
|
|
bc52362123 | ||
|
|
dcd63f321c | ||
|
|
093f8c2714 | ||
|
|
94ee9a39b4 | ||
|
|
1c5ea9e3ff | ||
|
|
3c69644f21 | ||
|
|
a89a593f51 | ||
|
|
8f685faf9b | ||
|
|
020433d7ad | ||
|
|
80aa4fb852 | ||
|
|
a1a8bd9656 | ||
|
|
72623281c4 | ||
|
|
ccaa70c1a4 | ||
|
|
aa1ce22ada | ||
|
|
003793b082 | ||
|
|
15611cea43 | ||
|
|
7a366de0e3 | ||
|
|
3baaf8bda4 | ||
|
|
23dcb3e823 | ||
|
|
66e7790ee0 | ||
|
|
3065126ed1 | ||
|
|
98ae737fb8 | ||
|
|
52cfda1d8e | ||
|
|
f727c13e20 | ||
|
|
0c01479866 | ||
|
|
34c9ae7518 | ||
|
|
960334b638 | ||
|
|
32e7ee1917 | ||
|
|
6e221e753b | ||
|
|
501dffe5b2 | ||
|
|
90dc8274c5 | ||
|
|
0a5cb40f55 | ||
|
|
6d7d587a33 | ||
|
|
35b6e2bf35 | ||
|
|
d2ebb207ab | ||
|
|
c3a7f3c950 | ||
|
|
b44efbd998 | ||
|
|
8bdba217a6 | ||
|
|
81dd315dfe | ||
|
|
d0be7f16ba | ||
|
|
818ac03e84 | ||
|
|
41127e0e38 | ||
|
|
b4af59a873 | ||
|
|
08972ff234 | ||
|
|
6653f9b110 | ||
|
|
c6f2ebd92a | ||
|
|
b50c061c0a | ||
|
|
bb0e92c519 | ||
|
|
682dc36d34 | ||
|
|
3a173821d7 | ||
|
|
0df478d2aa | ||
|
|
8d3ba881e5 | ||
|
|
4555db8e97 | ||
|
|
bc2beb2c56 | ||
|
|
29bd6af2a2 | ||
|
|
1bbd20de3c | ||
|
|
d1f4164498 | ||
|
|
d1c238a812 | ||
|
|
675bbd602f | ||
|
|
ab1be61fda | ||
|
|
754caca613 | ||
|
|
8097ab146c | ||
|
|
c0c4b69c64 | ||
|
|
315d90391c | ||
|
|
869a5fd773 | ||
|
|
9940cfcf12 | ||
|
|
463b6cd791 | ||
|
|
b981516913 | ||
|
|
9ffdbee400 | ||
|
|
def55c9dfe | ||
|
|
69668c5e55 | ||
|
|
f41adec096 | ||
|
|
3cc56f9886 | ||
|
|
be8b302e20 | ||
|
|
6da05af387 | ||
|
|
2eb74afc20 | ||
|
|
c44bb1e27d | ||
|
|
12cd38a047 | ||
|
|
e5c896cb36 | ||
|
|
d89a2487e7 | ||
|
|
e59f91a976 | ||
|
|
9a80777fe9 | ||
|
|
69b1da50b8 | ||
|
|
04ada28ffc | ||
|
|
369a6c2f04 | ||
|
|
f438fb65f8 | ||
|
|
6590011ef1 | ||
|
|
b6eb3d2361 | ||
|
|
13b9b77f0d | ||
|
|
20419856c1 | ||
|
|
43100930cd | ||
|
|
aac7e4cc53 | ||
|
|
1c8f3e2328 | ||
|
|
a54f0a2e89 | ||
|
|
fdb3cecf8e | ||
|
|
8762c39dd5 | ||
|
|
fc56c56883 | ||
|
|
ae950ac075 | ||
|
|
8bbb9a70c6 | ||
|
|
e6532dc155 | ||
|
|
ee6af8c187 | ||
|
|
bc1a0725df | ||
|
|
37905f8249 | ||
|
|
8163e7273e | ||
|
|
58fdba1d89 | ||
|
|
203dcf714f | ||
|
|
649f864236 | ||
|
|
292f3b03cc | ||
|
|
ec8fe3c343 | ||
|
|
823b0518ae | ||
|
|
c48caf5bdc | ||
|
|
20bc6d079f | ||
|
|
dccd318f60 | ||
|
|
2b0f670fb7 | ||
|
|
b5c431dca8 | ||
|
|
57dd93f136 | ||
|
|
976e02effb | ||
|
|
396387340a | ||
|
|
d9bf021597 | ||
|
|
b4e26f513b | ||
|
|
2eef31f74d | ||
|
|
cc96e2b3eb | ||
|
|
4526a0b875 | ||
|
|
4568bcc0ae | ||
|
|
cf3af14c6a | ||
|
|
d320c84bc8 | ||
|
|
8eff262e8f | ||
|
|
8c7d90f0c4 | ||
|
|
0e189551dd | ||
|
|
981acd6510 | ||
|
|
404aae6bf3 | ||
|
|
57cc9b8de4 | ||
|
|
d2def0a34d | ||
|
|
f1a5e1c029 | ||
|
|
19ae192887 | ||
|
|
cc87227559 | ||
|
|
9ad0c74a56 | ||
|
|
55a8b743c8 | ||
|
|
7adb150d1c | ||
|
|
afdb392289 | ||
|
|
1c8e24a117 | ||
|
|
266e79a2c3 | ||
|
|
6c5d629a88 | ||
|
|
84ed778b67 | ||
|
|
c1d41847a2 | ||
|
|
73c50837d8 | ||
|
|
89075aa3e9 | ||
|
|
bffe4454ba | ||
|
|
69a8f50b58 | ||
|
|
8c6f6534b6 | ||
|
|
791a78154d | ||
|
|
f08ce0acd5 | ||
|
|
757b195d5c | ||
|
|
c6a39a9220 | ||
|
|
a2054b458b | ||
|
|
6ee12d447f | ||
|
|
f95e3fb719 | ||
|
|
aa9141505f | ||
|
|
b4f0d2f2ae | ||
|
|
5bc67ab796 | ||
|
|
4f86e0da62 | ||
|
|
12165ac9a6 | ||
|
|
dd783c2dc0 | ||
|
|
4b65086622 | ||
|
|
de2542c172 | ||
|
|
973de3507e | ||
|
|
1945d5ac6b | ||
|
|
cb3614d54c | ||
|
|
4375f2d5d1 | ||
|
|
92b6cf201e | ||
|
|
b2b53561ba | ||
|
|
73a1923db6 | ||
|
|
76e290f6b7 | ||
|
|
be02814021 | ||
|
|
d60c007ac6 | ||
|
|
e92c1eaa9d | ||
|
|
8d638602b9 | ||
|
|
48161aca47 | ||
|
|
11314972fa | ||
|
|
83ac202a6b | ||
|
|
02c601fd3a | ||
|
|
86f6f85142 | ||
|
|
ca4cb433a9 | ||
|
|
e5bed0afcb | ||
|
|
11cf85f771 | ||
|
|
18d7a9cfe9 | ||
|
|
b3bac32441 | ||
|
|
0b9fb06c08 | ||
|
|
c6219ff315 | ||
|
|
2b7668aa68 | ||
|
|
c8a684e10d | ||
|
|
5a5d675f3e | ||
|
|
a4acc60f1d | ||
|
|
b3bc04251b | ||
|
|
203111eb5a | ||
|
|
472168b9e4 | ||
|
|
328ebbe3f7 | ||
|
|
999cd19d81 | ||
|
|
d23f6a69e5 | ||
|
|
38c7fe9e0a | ||
|
|
dc02f51a15 | ||
|
|
dd7ea02c9f | ||
|
|
5e0bf0da6e | ||
|
|
f2f4b14f5e | ||
|
|
e27b4fa2f9 | ||
|
|
c71a57bd69 | ||
|
|
8c57b271d8 | ||
|
|
e167602a32 | ||
|
|
590f4ba2c6 | ||
|
|
b2c0dc235c | ||
|
|
f357a53e0e | ||
|
|
5f7d1f9f3f | ||
|
|
5ebd255a22 | ||
|
|
010c0a5061 | ||
|
|
6ec4bdd926 | ||
|
|
fe023b798d | ||
|
|
141c916b7b | ||
|
|
f160e456ae | ||
|
|
49c6219a84 | ||
|
|
0db444e78c | ||
|
|
57c2dc0a88 | ||
|
|
15ba2d3156 | ||
|
|
48e2ba11bf | ||
|
|
2a3fc01937 | ||
|
|
aa3e8344fb | ||
|
|
df5e619349 | ||
|
|
0c06983411 | ||
|
|
dda9c1f1af | ||
|
|
6779a02cf9 | ||
|
|
7671328245 | ||
|
|
ba7608602b | ||
|
|
92dcd8981d | ||
|
|
cbe18b70ca | ||
|
|
ab26b94808 | ||
|
|
0e176cee74 | ||
|
|
23eaba0d47 | ||
|
|
c6b079c9cb | ||
|
|
9d544fa7fe | ||
|
|
8aba1be3e6 | ||
|
|
72d28c5219 | ||
|
|
85dc45a479 | ||
|
|
283714f88c | ||
|
|
eb696b49fe | ||
|
|
063be0dbe2 | ||
|
|
1e4656769e | ||
|
|
3079cad8d6 | ||
|
|
d5c86b91c3 | ||
|
|
d5de0b8fa3 | ||
|
|
167c02f433 | ||
|
|
327e03c3b1 | ||
|
|
5304ecb71d | ||
|
|
64dfa2fa33 | ||
|
|
8f6431f657 | ||
|
|
5a59c8c179 | ||
|
|
2a7fa03df1 | ||
|
|
be009d55f4 | ||
|
|
d8789926a7 | ||
|
|
e7de6bd42b | ||
|
|
3f6b70e783 | ||
|
|
d1ab5cc40f | ||
|
|
a8376ebb96 | ||
|
|
f0456cce0e | ||
|
|
bc46f8c432 | ||
|
|
4de8a8e915 | ||
|
|
4f0d425711 | ||
|
|
7679c255f3 | ||
|
|
3027ba6dc7 | ||
|
|
9b556436bb | ||
|
|
1c84205a5e | ||
|
|
d39373a4f5 | ||
|
|
3dbfdf3b5b | ||
|
|
286cfc961e | ||
|
|
3148879bf7 | ||
|
|
70924b6823 | ||
|
|
2a63fe6a09 | ||
|
|
91c09ebd7e | ||
|
|
4957e62765 | ||
|
|
80ef32b8b5 | ||
|
|
39bf3e754d | ||
|
|
3d8c0ce181 | ||
|
|
07042d16b0 | ||
|
|
fce583a346 | ||
|
|
7ba5a3b8cd | ||
|
|
ae16c021da | ||
|
|
70ff34438f | ||
|
|
b105585f5a | ||
|
|
29c7859ea1 | ||
|
|
678194f4f7 | ||
|
|
60353be2c0 | ||
|
|
6410acbb48 | ||
|
|
731c2e22b8 | ||
|
|
cd950f39b4 | ||
|
|
8cbf56f7a5 | ||
|
|
17f986ea26 | ||
|
|
e5f9fce01e | ||
|
|
4c34a88a10 | ||
|
|
324d34aa3c | ||
|
|
3f984fb65e | ||
|
|
c108c60c30 | ||
|
|
cf1a84f4b2 | ||
|
|
350afcdb70 | ||
|
|
e5b319e518 | ||
|
|
a43785d1f0 | ||
|
|
b90981a17c | ||
|
|
47bbe20001 | ||
|
|
668b9dd903 | ||
|
|
6ca98149a7 | ||
|
|
af2de33809 | ||
|
|
737054873e | ||
|
|
1dd0961a48 | ||
|
|
3fcb6bcbd0 | ||
|
|
53660cc7dc | ||
|
|
996af9b268 | ||
|
|
512044617c | ||
|
|
a91f712735 | ||
|
|
fb7a0fcb50 | ||
|
|
749ed5432b | ||
|
|
53be4c259e | ||
|
|
e32630511d | ||
|
|
c9a6d69d6f | ||
|
|
5a78a71316 | ||
|
|
da9680d210 | ||
|
|
1ec37d657d | ||
|
|
99df3df42c | ||
|
|
1c83609971 | ||
|
|
32da8433bb | ||
|
|
485536dc90 | ||
|
|
0b605ddb70 | ||
|
|
755d7b0742 | ||
|
|
15030ab7cb | ||
|
|
a438d6c3c3 | ||
|
|
865a926047 | ||
|
|
a36ec166cf | ||
|
|
77e702da6c | ||
|
|
1560339377 | ||
|
|
f0aaab1299 | ||
|
|
4eced42981 | ||
|
|
1da2839cc3 | ||
|
|
0200281732 | ||
|
|
3d9f9ff4c2 | ||
|
|
60048b4450 | ||
|
|
4bd67c15c0 | ||
|
|
3237e18d2d | ||
|
|
fe17123707 | ||
|
|
a51df79925 | ||
|
|
efbe78a77e | ||
|
|
9f860a3f3e | ||
|
|
9dc5442743 | ||
|
|
e2d66235b5 | ||
|
|
cafad5599d | ||
|
|
6d8f375ae1 | ||
|
|
a055d7f4a8 | ||
|
|
09102325eb | ||
|
|
33c9774b21 | ||
|
|
d7f64fec4f | ||
|
|
eab5a42d93 | ||
|
|
5c7f120a7e | ||
|
|
d7d78d8053 | ||
|
|
cde7f114f9 | ||
|
|
69f4904b5a | ||
|
|
93d54a915d | ||
|
|
fe9aa59690 | ||
|
|
381a87ab50 | ||
|
|
ffea221568 | ||
|
|
aaf3316e65 | ||
|
|
41664d2ef4 | ||
|
|
717d4f0b9a | ||
|
|
24873ffa9b | ||
|
|
7f6e90758a | ||
|
|
5f4d565483 | ||
|
|
727e23ada9 | ||
|
|
497363bc3a | ||
|
|
6acf8b84ad | ||
|
|
daff0de0bd | ||
|
|
f6daa3b5dc | ||
|
|
2c87d36cbb | ||
|
|
3af3074ab9 | ||
|
|
c00342df6f | ||
|
|
9bbd95a6f2 | ||
|
|
f1fe5d215c | ||
|
|
36e1522a32 | ||
|
|
7c5f795bf4 | ||
|
|
90258636ed | ||
|
|
06ce840a8f | ||
|
|
268c5a92d8 | ||
|
|
7f11783d46 | ||
|
|
ae2d18e79c | ||
|
|
58b9755d0c | ||
|
|
11a79c2f1f | ||
|
|
32742a6f4f | ||
|
|
083e71370f | ||
|
|
0278390279 | ||
|
|
f344ef054f | ||
|
|
68e3953bcc | ||
|
|
7755a5cd9a | ||
|
|
05a7fec4c4 | ||
|
|
8f0e15db85 | ||
|
|
b7dc56fef5 | ||
|
|
da14fb56fa | ||
|
|
b15e892c60 | ||
|
|
eeeb13d094 | ||
|
|
03cb3cd161 | ||
|
|
f1b3558565 | ||
|
|
6a69725ca9 | ||
|
|
63fdabd902 | ||
|
|
d624f04cde | ||
|
|
ba08c7d589 | ||
|
|
35d072f550 | ||
|
|
4c28eebbdf | ||
|
|
3ca0e2fc58 | ||
|
|
7450b22130 | ||
|
|
e39faaa550 | ||
|
|
ccde45acc9 | ||
|
|
6b1c5b5b92 | ||
|
|
e6af5aaea3 | ||
|
|
a792c98630 | ||
|
|
46985ab39d | ||
|
|
28d882af7f | ||
|
|
9d6423e7d7 | ||
|
|
0a88ae6a52 | ||
|
|
395741a502 | ||
|
|
00dbbdf453 | ||
|
|
77cf6202fe | ||
|
|
7ed00bb42d | ||
|
|
b97fe50958 | ||
|
|
0501d73e7b | ||
|
|
8abe2bffb8 | ||
|
|
8477625472 | ||
|
|
08851a2b7a | ||
|
|
d4b9ccf1c2 | ||
|
|
7b177f1fd8 | ||
|
|
7d6e814745 | ||
|
|
3cfb6ecf6f | ||
|
|
11085a0037 | ||
|
|
a679bcf26b | ||
|
|
8ed1764fb3 | ||
|
|
b2696003f0 | ||
|
|
bc818704df | ||
|
|
d21f3c1bcf | ||
|
|
e6ae513474 | ||
|
|
4360f45ab8 | ||
|
|
74ef73ebf4 | ||
|
|
35b512de19 | ||
|
|
a5966592e1 | ||
|
|
8e2e1769c3 | ||
|
|
19da883e14 | ||
|
|
600d5f4475 | ||
|
|
0e0e3b28de | ||
|
|
2439ae0508 | ||
|
|
f9633cb860 | ||
|
|
2c43f35b0b | ||
|
|
a1beff754c | ||
|
|
647fa12cdc | ||
|
|
725235c250 | ||
|
|
25d438d691 | ||
|
|
37232b5302 | ||
|
|
2d250282ee | ||
|
|
18e1c112e0 | ||
|
|
32891e5155 | ||
|
|
ef7cbf097b | ||
|
|
41ab8033e7 | ||
|
|
4f392690eb | ||
|
|
d591e4ebc2 | ||
|
|
a315c19ebb | ||
|
|
764b956221 | ||
|
|
f0b0086ca5 | ||
|
|
8c43494c03 | ||
|
|
634df366bd | ||
|
|
86585feb56 | ||
|
|
f4335ff339 | ||
|
|
61100d167f | ||
|
|
77c7ba3194 | ||
|
|
669e3a762b | ||
|
|
71254ba6e2 | ||
|
|
8e7fe19585 | ||
|
|
95f605ea9d | ||
|
|
19d0e5d42f | ||
|
|
5433a69775 | ||
|
|
f2ac6dace6 | ||
|
|
028889f9ec | ||
|
|
f0c3395c00 | ||
|
|
ee180440e0 | ||
|
|
4eac00c0d9 | ||
|
|
8e15b38e03 | ||
|
|
61e44af487 | ||
|
|
605fecedea | ||
|
|
ac1a5e257e | ||
|
|
401ac0225f | ||
|
|
dbda032a4c | ||
|
|
b60b55dca2 | ||
|
|
e3229f4400 | ||
|
|
34435b3046 | ||
|
|
1077d8ef4b | ||
|
|
9527780d99 | ||
|
|
9878b39571 | ||
|
|
34f0b54284 | ||
|
|
49d8aa7352 | ||
|
|
3ad6246e68 | ||
|
|
66eccf0032 | ||
|
|
995daecaff | ||
|
|
1757c141fe | ||
|
|
45b3f3d56f | ||
|
|
b758b053d5 | ||
|
|
7a3088d553 | ||
|
|
15e297eef3 | ||
|
|
9476ba6578 | ||
|
|
a532120662 | ||
|
|
b33f1a988c | ||
|
|
24663a1944 | ||
|
|
983ce80703 | ||
|
|
4f3dbaa7ff | ||
|
|
0c93614423 | ||
|
|
2060b19e92 | ||
|
|
cc3fc47cc3 | ||
|
|
dd81a6ea91 | ||
|
|
95f59ffb1b | ||
|
|
eaf1eaaee3 | ||
|
|
2ebccbe372 | ||
|
|
adc49e61e7 | ||
|
|
58142e70af | ||
|
|
bae61f58af | ||
|
|
f7ac538f1d | ||
|
|
8196e61a2c | ||
|
|
403983ac6b | ||
|
|
f3e2f495d3 | ||
|
|
78f7ded7a8 | ||
|
|
053d59153e | ||
|
|
84d849da4c | ||
|
|
90eba686c7 | ||
|
|
5a4d53908d | ||
|
|
e76dd18962 | ||
|
|
071c4537ee | ||
|
|
e4e92c8b21 | ||
|
|
43a54cdc76 | ||
|
|
c88ad088c0 | ||
|
|
f462340ac8 | ||
|
|
b0cef15fa2 | ||
|
|
160b0101a9 | ||
|
|
63e0cd198c | ||
|
|
8a601b4d00 | ||
|
|
ff81202d8a | ||
|
|
eed8df7391 | ||
|
|
12df1caead | ||
|
|
15a7565cfb | ||
|
|
38f17b05c2 | ||
|
|
d4e81daee3 | ||
|
|
143724f146 | ||
|
|
a3223d2f01 | ||
|
|
9520fbe4a1 | ||
|
|
91bce23a5a | ||
|
|
b39b367ef2 | ||
|
|
97736b19cd | ||
|
|
a5c6fc9bed | ||
|
|
a68bbc7950 | ||
|
|
40941a3b22 | ||
|
|
7ea6902b87 | ||
|
|
7c74d447bc | ||
|
|
a937d5c4f7 | ||
|
|
2f90496a6e | ||
|
|
36fa99cef0 | ||
|
|
eadbb1672f | ||
|
|
eb7fad1054 | ||
|
|
b4b7dc667f | ||
|
|
8fe56722a5 | ||
|
|
7dfc0643d5 | ||
|
|
b3c2fa1770 | ||
|
|
4303c452e1 | ||
|
|
6b4f2d3e18 | ||
|
|
f8dfc3c652 | ||
|
|
21014f2e0e | ||
|
|
5835e5f72c | ||
|
|
7350e94662 | ||
|
|
d5d2d5bf82 | ||
|
|
14d1f6cde2 | ||
|
|
9f17b917f7 | ||
|
|
c046b48ee9 | ||
|
|
cb6cf7c01f | ||
|
|
cd9b07ca0a | ||
|
|
eb435bc6b0 | ||
|
|
c9ca613348 | ||
|
|
771855fd07 | ||
|
|
e6967bbc07 | ||
|
|
095d8ca719 | ||
|
|
9c089bed37 | ||
|
|
7e94c6f1c9 | ||
|
|
df44296ee0 | ||
|
|
da42b6f3b2 | ||
|
|
264756d986 | ||
|
|
49d9887d03 | ||
|
|
39f22dec4c | ||
|
|
69befbc97b | ||
|
|
820ed1c457 | ||
|
|
c0c6a6dc4b | ||
|
|
a410709f92 | ||
|
|
3b20e85ff0 | ||
|
|
5d8e7a9fa8 | ||
|
|
63ad0760cd | ||
|
|
2ea29ef8cb | ||
|
|
f34baa389c | ||
|
|
41db2f7fe7 | ||
|
|
2db04225a7 | ||
|
|
b6004117e6 | ||
|
|
fd73b28061 | ||
|
|
ba721c9db6 | ||
|
|
9b707cf93d | ||
|
|
a73b42409f | ||
|
|
e743b2c699 | ||
|
|
107c44df88 | ||
|
|
5e19c420e7 | ||
|
|
2f55324708 | ||
|
|
6cc6c34c4b | ||
|
|
539a0198f3 | ||
|
|
9beebac7fc | ||
|
|
50567fd967 | ||
|
|
3ee004ce4e | ||
|
|
a58fd7559c | ||
|
|
73d738887a | ||
|
|
e96199dc37 | ||
|
|
f5cde10d62 | ||
|
|
1d5df08e78 | ||
|
|
1c33494547 | ||
|
|
4acaefe9c3 | ||
|
|
8c3f4c06df | ||
|
|
ed589f4b51 | ||
|
|
8c61e95a5b | ||
|
|
a6fa6e19d0 | ||
|
|
8273bdf70f | ||
|
|
5267de8b87 | ||
|
|
6036a792e5 | ||
|
|
1341db0812 | ||
|
|
c16a4134c7 | ||
|
|
771047ddd7 | ||
|
|
718d67f20c | ||
|
|
d18a510310 | ||
|
|
3a3da1c6f1 | ||
|
|
00647e4963 |
130
.github/workflows/build-x86_64.yml
vendored
Normal file
130
.github/workflows/build-x86_64.yml
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
name: Build x86_64
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- v1.77.3-dev
|
||||
|
||||
jobs:
|
||||
Build-Debug-MSI:
|
||||
runs-on: windows-2022
|
||||
steps:
|
||||
- name: 01. Copy repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: 02. Setup MSBuild.exe
|
||||
uses: microsoft/setup-msbuild@v1.3.1 # Add MSBuild to the PATH: https://github.com/microsoft/setup-msbuild
|
||||
with:
|
||||
vs-version: '17.12.4'
|
||||
|
||||
- name: 03. Restore nuget packages for solution
|
||||
shell: pwsh
|
||||
run: |
|
||||
dotnet restore
|
||||
|
||||
- name: 04. Compile mRemoteNG
|
||||
shell: pwsh
|
||||
run: |
|
||||
msbuild "$Env:GITHUB_WORKSPACE\mRemoteNG.sln" -p:Configuration="Debug Installer" -p:Platform=x64 /verbosity:normal
|
||||
|
||||
- name: 05. Publish MSI as Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: debug-msi-x86_64
|
||||
path: ${{ github.workspace }}\mRemoteNGInstaller\Installer\bin\x64\Debug\en-US\
|
||||
if-no-files-found: error
|
||||
|
||||
Build-Debug-Portable:
|
||||
runs-on: windows-2022
|
||||
steps:
|
||||
- name: 01. Copy repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: 02. Setup MSBuild.exe
|
||||
uses: microsoft/setup-msbuild@v1.3.1 # Add MSBuild to the PATH: https://github.com/microsoft/setup-msbuild
|
||||
with:
|
||||
vs-version: '17.8.3'
|
||||
|
||||
- name: 03. Restore nuget packages for solution
|
||||
shell: pwsh
|
||||
run: |
|
||||
dotnet restore
|
||||
|
||||
- name: 04. Compile mRemoteNG
|
||||
shell: pwsh
|
||||
run: |
|
||||
msbuild "$Env:GITHUB_WORKSPACE\mRemoteNG.sln" -p:Configuration="Debug Portable" -p:Platform=x64 /verbosity:normal
|
||||
|
||||
- name: 05. Publish Portable Binary as Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: debug-portable-x86_64
|
||||
path: ${{ github.workspace }}\mRemoteNG\bin\x64\Debug Portable\
|
||||
if-no-files-found: error
|
||||
|
||||
Build-Release:
|
||||
runs-on: windows-2022
|
||||
steps:
|
||||
- name: 01. Copy repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: 02. Setup MSBuild.exe
|
||||
uses: microsoft/setup-msbuild@v1.3.1 # Add MSBuild to the PATH: https://github.com/microsoft/setup-msbuild
|
||||
with:
|
||||
vs-version: '17.12.4'
|
||||
|
||||
- name: 03. Restore nuget packages for solution
|
||||
shell: pwsh
|
||||
run: |
|
||||
dotnet restore
|
||||
|
||||
- name: 04. Compile mRemoteNG
|
||||
shell: pwsh
|
||||
run: |
|
||||
msbuild "$Env:GITHUB_WORKSPACE\mRemoteNG.sln" -p:Configuration="Release Installer and Portable" -p:Platform=x64 /verbosity:normal
|
||||
|
||||
- name: 05. Publish MSI Binary as Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: release-msi-x86_64
|
||||
path: ${{ github.workspace }}\mRemoteNGInstaller\Installer\bin\x64\Release\en-US\
|
||||
if-no-files-found: error
|
||||
|
||||
- name: 06. Publish Portable Binary as Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: release-portable-x86_64
|
||||
path: ${{ github.workspace }}\mRemoteNG\bin\x64\Release
|
||||
if-no-files-found: error
|
||||
|
||||
Create-Release:
|
||||
needs: [Build-Debug-MSI, Build-Debug-Portable, Build-Release]
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: 01. Copy repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: 02. Download Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
- name: 03. Create compressed archives # Needs to be done because "actions/download-artifact@v4" is extracting the zipped Artifacts
|
||||
shell: bash
|
||||
run: |
|
||||
zip -r debug-msi-x86_64.zip debug-msi-x86_64/
|
||||
zip -r debug-portable-x86_64.zip debug-portable-x86_64/
|
||||
zip -r release-msi-x86_64.zip release-msi-x86_64/
|
||||
zip -r release-portable-x86_64.zip release-portable-x86_64/
|
||||
|
||||
- name: 04. Create Release
|
||||
shell: bash
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
gh release create "v1.77.3-dev-${GITHUB_RUN_NUMBER}" \
|
||||
--title "v1.77.3-dev-${GITHUB_RUN_NUMBER}" \
|
||||
--prerelease \
|
||||
--generate-notes \
|
||||
$GITHUB_WORKSPACE/debug-msi-x86_64.zip \
|
||||
$GITHUB_WORKSPACE/debug-portable-x86_64.zip \
|
||||
$GITHUB_WORKSPACE/release-msi-x86_64.zip \
|
||||
$GITHUB_WORKSPACE/release-portable-x86_64.zip
|
||||
278
.gitignore
vendored
278
.gitignore
vendored
@@ -1,16 +1,13 @@
|
||||
Release/
|
||||
mRemoteV1/bin/
|
||||
mRemoteV1/obj/
|
||||
mRemoteV1/publish/
|
||||
**/bin/
|
||||
**/obj/
|
||||
**/[Rr]elease [Pp]ortable/
|
||||
**/[Dd]ebug [Pp]ortable/
|
||||
|
||||
*.pfx
|
||||
*.suo
|
||||
*.vbproj.user
|
||||
Thumbs.db
|
||||
[Dd]ebug/
|
||||
_Re[Ss]harper.*
|
||||
*.resharper
|
||||
*.resharper.user
|
||||
*.suo
|
||||
*.cache
|
||||
*~
|
||||
*.swp
|
||||
@@ -24,3 +21,268 @@ UpgradeLog.XML
|
||||
*.sdf
|
||||
*.opensdf
|
||||
*.ipch
|
||||
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
|
||||
# Visual Studio 2015 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUNIT
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# DNX
|
||||
project.lock.json
|
||||
artifacts/
|
||||
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_i.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# JustCode is a .NET coding add-in
|
||||
.JustCode
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# TODO: Comment the next line if you want to checkin your web deploy settings
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/packages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/packages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/packages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignoreable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
node_modules/
|
||||
orleans.codegen.cs
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# JetBrains Rider
|
||||
.idea/
|
||||
*.sln.iml
|
||||
*.msi
|
||||
Installer/Resources/License.rtf
|
||||
Installer/Fragments/FilesFragment.wxs
|
||||
Installer Projects/Installer/Resources/License.rtf
|
||||
Installer Projects/Installer/Fragments/FilesFragment.wxs
|
||||
Installer Projects/Installer/Fragments/HelpFilesFragment.wxs
|
||||
InstallerProjects/Installer/Fragments/FilesFragment.wxs
|
||||
InstallerProjects/Installer/Resources/License.rtf
|
||||
|
||||
# mRemoteNG
|
||||
**/mRemoteNG/Properties/AssemblyInfo.tt
|
||||
**/mRemoteNG/Properties/AssemblyInfo.cs
|
||||
14
.readthedocs.yaml
Normal file
14
.readthedocs.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
# .readthedocs.yaml
|
||||
# Read the Docs configuration file
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
|
||||
# Required
|
||||
version: 2
|
||||
|
||||
# Build documentation in the docs/ directory with Sphinx
|
||||
sphinx:
|
||||
configuration: mRemoteNGDocumentation/conf.py
|
||||
|
||||
# Optionally build your docs in additional formats such as PDF
|
||||
formats:
|
||||
- pdf
|
||||
11
Add-ons/README.md
Normal file
11
Add-ons/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
## Add-ons library by 3rd party
|
||||
This is a list of add-ons, plugins and extentions what could be used with mRemoteNG, if you wish to add yours to this list - just drop me a line: <a href="mailto:support@mremoteng.org">support@mremoteng.org</a>
|
||||
<br>
|
||||
|
||||
| <b>Date added</b> | <b>Author</b> | <b>Type</b> | <b>Name</b> | <b>Description</b> | <b>Repository</b> |
|
||||
| :---------------------------------------------------------:|:-------------------------------------------------:| :---------: |-------------|--------------------|:-----------------:|
|
||||
| 02-08-2022 | <a href="https://github.com/JustBeta"><img align="left" src="https://avatars.githubusercontent.com/u/25150896?v=4" alt="JustBeta" width="30px"/>JustBeta</a> | script | Export-MobaXterm2mRemoteNG | Conversion of MobaXterm's ini file to mRemoteNG format. | [GITHUB Repository](https://github.com/JustBeta/Export-MobaXtern2mRemoteNG/tree/main) |
|
||||
|
||||
<br>
|
||||
|
||||
For a detailed usage examples and documentation please reach out authors.
|
||||
149
BUILD.CMD
149
BUILD.CMD
@@ -1,149 +0,0 @@
|
||||
@echo off
|
||||
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
set VERSIONTAG=
|
||||
|
||||
set VCVARSALL="%ProgramFiles(x86)%\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"
|
||||
set DEVENV="devenv.exe"
|
||||
set MAKENSIS="%ProgramFiles(x86)%\NSIS\Unicode\makensis.exe"
|
||||
set RAR="%ProgramFiles%\WinRAR\WinRAR.exe"
|
||||
set SIGNCMD=signtool.exe sign /n "Astrospark Technologies, LLC" /sha1 "1cbd910dbd6e77f26506e7f600736972f700673f" /tr http://timestamp.comodoca.com/rfc3161
|
||||
|
||||
rem Windows Sysinternals Sigcheck from http://technet.microsoft.com/en-us/sysinternals/bb897441
|
||||
set SIGCHECK="%ProgramFiles(x86)%\Sigcheck\sigcheck.exe"
|
||||
|
||||
call %VCVARSALL% x86
|
||||
|
||||
rmdir /s /q "%~dp0\mRemoteV1\bin" > nul 2>&1
|
||||
rmdir /s /q "%~dp0\mRemoteV1\obj" > nul 2>&1
|
||||
|
||||
if exist "%~dp0\mRemoteV1\bin" goto ERROR_RMDIR
|
||||
if exist "%~dp0\mRemoteV1\obj" goto ERROR_RMDIR
|
||||
goto NOERROR_RMDIR
|
||||
|
||||
:ERROR_RMDIR
|
||||
echo.
|
||||
echo Could not clean output directories.
|
||||
echo.
|
||||
echo Build process failed.
|
||||
echo.
|
||||
goto END
|
||||
|
||||
:NOERROR_RMDIR
|
||||
|
||||
echo Building release version...
|
||||
%DEVENV% "%~dp0\mRemoteV1.sln" /build "Release"
|
||||
|
||||
echo Building portable version...
|
||||
%DEVENV% "%~dp0\mRemoteV1.sln" /build "Release Portable"
|
||||
|
||||
echo Signing binaries...
|
||||
%SIGNCMD% ^
|
||||
"%~dp0\mRemoteV1\bin\Release\de\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release\el\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release\en-US\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release\es\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release\es-AR\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release\fr\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release\hu\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release\it\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release\nb-NO\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release\nl\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release\pl\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release\pt\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release\pt-BR\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release\ru\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release\uk\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release\zh-CN\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release\zh-TW\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release\AxInterop.MSTSCLib.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release\AxInterop.WFICALib.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release\Interop.EOLWTSCOM.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release\Interop.MSTSCLib.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release\Interop.ShDocVw.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release\Interop.WFICALib.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release\mRemoteNG.exe" ^
|
||||
"%~dp0\mRemoteV1\bin\Release Portable\de\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release Portable\el\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release Portable\en-US\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release Portable\es\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release Portable\es-AR\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release Portable\fr\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release Portable\hu\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release Portable\it\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release Portable\nb-NO\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release Portable\nl\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release Portable\pl\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release Portable\pt\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release Portable\pt-BR\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release Portable\ru\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release Portable\uk\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release Portable\zh-CN\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release Portable\zh-TW\mRemoteNG.resources.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release Portable\AxInterop.MSTSCLib.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release Portable\AxInterop.WFICALib.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release Portable\Interop.EOLWTSCOM.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release Portable\Interop.MSTSCLib.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release Portable\Interop.ShDocVw.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release Portable\Interop.WFICALib.dll" ^
|
||||
"%~dp0\mRemoteV1\bin\Release Portable\mRemoteNG.exe" ^
|
||||
|
||||
|
||||
rem Do not remove the two blank lines above this line.
|
||||
|
||||
mkdir "%~dp0\Release" > nul 2>&1
|
||||
|
||||
echo Getting product version...
|
||||
set VERSIONNSH="%~dp0\Release\Version.nsh"
|
||||
set SIGCHECK=!SIGCHECK:"=^"!
|
||||
set SIGCHECK=!SIGCHECK: =^^ !
|
||||
set SIGCHECK=!SIGCHECK:(=^^(!
|
||||
set SIGCHECK=!SIGCHECK:)=^^)!
|
||||
for /F "usebackq delims=. tokens=1-4" %%i in (`!SIGCHECK! /accepteula -q -n "%~dp0\mRemoteV1\bin\Release\mRemoteNG.exe"`) do (
|
||||
set PRODUCT_VERSION_SHORT=%%i.%%j
|
||||
echo ^^!define PRODUCT_VERSION "%%i.%%j.%%k.%%l" > %VERSIONNSH%
|
||||
echo ^^!define PRODUCT_VERSION_SHORT "%%i.%%j" >> %VERSIONNSH%
|
||||
echo ^^!define PRODUCT_VERSION_MAJOR "%%i" >> %VERSIONNSH%
|
||||
echo ^^!define PRODUCT_VERSION_MINOR "%%j" >> %VERSIONNSH%
|
||||
)
|
||||
echo Version is %PRODUCT_VERSION_SHORT%
|
||||
|
||||
echo Creating installer package...
|
||||
if defined VERSIONTAG (
|
||||
%MAKENSIS% /DPRODUCT_VERSION_TAG=%VERSIONTAG% "%~dp0\Installer\mRemote.nsi"
|
||||
|
||||
set INSTALLEREXE="%~dp0\Release\mRemoteNG-Installer-%PRODUCT_VERSION_SHORT%-%VERSIONTAG%.exe"
|
||||
set BINARYZIP="%~dp0\Release\mRemoteNG-%PRODUCT_VERSION_SHORT%-%VERSIONTAG%.zip"
|
||||
set PORTABLEZIP="%~dp0\Release\mRemoteNG-Portable-%PRODUCT_VERSION_SHORT%-%VERSIONTAG%.zip"
|
||||
) else (
|
||||
%MAKENSIS% "%~dp0\Installer\mRemote.nsi"
|
||||
|
||||
set INSTALLEREXE="%~dp0\Release\mRemoteNG-Installer-%PRODUCT_VERSION_SHORT%.exe"
|
||||
set BINARYZIP="%~dp0\Release\mRemoteNG-%PRODUCT_VERSION_SHORT%.zip"
|
||||
set PORTABLEZIP="%~dp0\Release\mRemoteNG-Portable-%PRODUCT_VERSION_SHORT%.zip"
|
||||
)
|
||||
|
||||
del %VERSIONNSH%
|
||||
|
||||
echo Signing installer package...
|
||||
%SIGNCMD% %INSTALLEREXE%
|
||||
|
||||
echo Creating release ZIP file...
|
||||
del /f /q %BINARYZIP% > nul 2>&1
|
||||
%RAR% a -m5 -r -ep1 -afzip -inul %BINARYZIP% "%~dp0\mRemoteV1\bin\Release\*.*"
|
||||
%RAR% a -m5 -r -ep1 -afzip -inul %BINARYZIP% "%~dp0\Installer\Dependencies\*.*"
|
||||
%RAR% a -m5 -ep -afzip -inul %BINARYZIP% "%~dp0\*.TXT"
|
||||
|
||||
echo Creating portable ZIP file...
|
||||
del /f /q %PORTABLEZIP% > nul 2>&1
|
||||
%RAR% a -m5 -r -ep1 -afzip -inul %PORTABLEZIP% "%~dp0\mRemoteV1\bin\Release Portable\*.*"
|
||||
%RAR% a -m5 -r -ep1 -afzip -inul %PORTABLEZIP% "%~dp0\Installer\Dependencies\*.*"
|
||||
%RAR% a -m5 -ep -afzip -inul %PORTABLEZIP% "%~dp0\*.TXT"
|
||||
|
||||
echo.
|
||||
echo Build process complete.
|
||||
echo.
|
||||
|
||||
:END
|
||||
pause
|
||||
815
CHANGELOG.TXT
815
CHANGELOG.TXT
@@ -1,815 +0,0 @@
|
||||
1.72 (2013-11-13):
|
||||
Fixed issue MR-592 - Unable to run VBS script as an external tool
|
||||
Fixed issue MR-596 - Incorrect escaping of quotation marks in external tool arguments
|
||||
|
||||
1.71 (2013-10-29):
|
||||
Fixed issue MR-574 - Crash when retrieving RDP session list if eolwtscom.dll is not registered
|
||||
Fixed issue MR-578 - Connections file is reset
|
||||
Fixed log file not showing operating system version on Windows XP and Windows Server 2003.
|
||||
Fixed the wrong connections file opening on startup under certain conditions.
|
||||
Fixed checking for updates even when disabled.
|
||||
Improved error reporting when loading connections files.
|
||||
Removed warning message when mRemoteNG is started for the first time about new connections file being created.
|
||||
|
||||
1.71 Release Candidate 2 (2013-10-16):
|
||||
Fixed issue MR-560 - Cannot Auto-Update With Open Connections: Unable to find an entry point named 'TaskDialogIndirect' in DLL 'ComCtl32'
|
||||
Fixed issue MR-565 - Double Folder keep heritage on the initial Folder
|
||||
Fixed issue MR-566 - Typo in German UI Automatic Update Settings
|
||||
Fixed duplicated folders possibly being named "New Connection" instead of the original folder's name.
|
||||
|
||||
1.71 Release Candidate 1 (2013-10-01):
|
||||
Fixed issue MR-495 - Having a negative range in port scan creates memory exhaustion.
|
||||
Fixed issue MR-514 - Window Proxy test failed without close button
|
||||
Fixed issue MR-521 - Right-Clicking in "Sessions" panel crashes mRemoteNG
|
||||
Fixed issue MR-525 - Could not start on windows 7 64bit
|
||||
Fixed issue MR-535 - SQL error saving Connections
|
||||
Fixed issue MR-538 - RDP loses connection when hiding config or connections pane
|
||||
Fixed issue MR-542 - Wrapped putty has security flaw
|
||||
Made minor improvements to the port scan functionality.
|
||||
Fixed possible cross-thread operation exception when loading connections from SQL.
|
||||
Fixed PuTTY Saved Sessions not showing after loading a new connections file.
|
||||
Updated PuTTY to version 0.63.
|
||||
Updated translations.
|
||||
Added Chinese (Traditional) translation.
|
||||
Added partial Greek and Hungarian translations.
|
||||
|
||||
1.71 Beta 5 (2013-06-09):
|
||||
Fixed issue MR-491 - Could not start RDP Connection
|
||||
Fixed issue MR-499 - TS Gateway is not working in latest release 1.71
|
||||
Fixed typo in SQL queries.
|
||||
|
||||
1.71 Beta 4 (2013-05-28):
|
||||
Added feature MR-435 - Add digital signature check to updater
|
||||
Fixed issue MR-255 - The version of the RDP AX client should be updated to 7
|
||||
Fixed issue MR-392 - Sessions Panel - context menu entries need to be context aware
|
||||
Fixed issue MR-422 - Gives error Object reference not set to an instance of an object.
|
||||
Fixed issue MR-424 - Import of a few Linux SSH2 hosts discovered via the port scan tool results in a UE
|
||||
Fixed issue MR-439 - MRemoteNG 1.70 does not start
|
||||
Fixed issue MR-440 - RDP import with non-standard port
|
||||
Fixed issue MR-443 - Instructions for eolwtscom.dll registration for Portable version are inaccurate
|
||||
Fixed issue MR-446 - Putty saved sessions show in connection panel
|
||||
Fixed issue MR-459 - Maximized -> Minimized -> Restored results in mangled active display
|
||||
Fixed issue MR-463 - Add support for LoadBalanceInfo to RDP
|
||||
Fixed issue MR-470 - Quick Connect to Linux server uses invalid credentials
|
||||
Fixed issue MR-471 - PuTTY Saved Sessions disappears from connection list
|
||||
Fixed issue MR-487 - Initiate connections on MouseUp event
|
||||
Added PuTTY Session Settings command to the Config panel for PuTTY Saved Sessions.
|
||||
Updated translations.
|
||||
Added Norwegian (Bokmal) and Portuguese (Brazil) translations.
|
||||
Added Spanish translation to the installer.
|
||||
Fixed an exception or crash when choosing unnamed colors for themes.
|
||||
Fixed possible error "Control does not support transparent background colors" when modifying themes.
|
||||
Fixed changes to the active theme not being saved reliably.
|
||||
Fixed handling of the plus (+) character in PuTTY session names.
|
||||
Changed Internet Explorer to no longer force IE7 compatibility mode.
|
||||
Changed the "Launch PuTTY" button in the "Options" dialog to open PuTTY from the path the user has currently set, instead of what was previously saved.
|
||||
Improved update and announcement checking.
|
||||
Improved the PuTTY Saved Sessions list to update automatically when any changes are made.
|
||||
Improved loading time of large connection files.
|
||||
Lowered required version of RDC from 6.1 to 6.0.
|
||||
Updated VncSharpNG to 1.3.4896.25007.
|
||||
|
||||
1.71 Beta 3 (2013-03-20):
|
||||
Fixed issue MR-397 - Putty disappears from the screen
|
||||
Fixed issue MR-398 - Full Screen mode doesn't correctly make use of available space
|
||||
Fixed issue MR-402 - scrollbar touch moves putty window
|
||||
Fixed issue MR-406 - Items disappear from External Tools toolbar when accessing External Tools panel
|
||||
Fixed issue MR-410 - Unhandled exception when clicking New button under Theme
|
||||
Fixed issue MR-413 - Can't use aplication
|
||||
Fixed new connections having a globe icon.
|
||||
Fixed the category names in the themes tab of the options dialog on Windows XP not showing correctly.
|
||||
Fixed PuTTY saved sessions with spaces or special characters not being listed.
|
||||
|
||||
1.71 Beta 2 (2013-03-19):
|
||||
Added feature MR-336 - Customizable background color for the windows/panels
|
||||
Added feature MR-345 - Two separate options for confirming closure of Tabs and Connection Panels
|
||||
Added feature MR-346 - Option to show/hide the description box at the bottom of the Config panel
|
||||
Added feature MR-351 - Import connections from PuTTY
|
||||
Fixed issue MR-354 - Re-ordering tabs doesn't give good, reliable visual feedback
|
||||
Fixed issue MR-375 - Changing a connection's icon using the picture button should immediately update Icon field
|
||||
Fixed issue MR-377 - Several redundant panels can be opened
|
||||
Fixed issue MR-379 - Connection variables not working with external tools
|
||||
Fixed issue MR-381 - Notifications panel - whitespace context menu allows Copy and Delete on nothing
|
||||
Fixed issue MR-401 - Checkbox misaligned
|
||||
The username and domain settings are now hidden for VNC connections since they are not supported.
|
||||
Changed "Automatically get session information" to be disabled by default.
|
||||
RDP connections can now be switched to full screen mode when redirect key combinations is enabled.
|
||||
|
||||
1.71 Beta 1 (2013-03-04):
|
||||
Added feature MR-329 - Create Option to disable the "Quick: " prefix
|
||||
Fixed issue MR-67 - Sort does not recursively sort
|
||||
Fixed issue MR-117 - Remote Session Info Window / Tab does not populate
|
||||
Fixed issue MR-121 - Config pane not sorting properties correctly when switching between alphabetical and categorized view
|
||||
Fixed issue MR-130 - Issues duplicating folders
|
||||
Fixed issue MR-142 - Start of mRemoteNG takes about one minute and consumes excessive CPU
|
||||
Fixed issue MR-158 - Password field not accepting Pipe
|
||||
Fixed issue MR-330 - Portable version saves log to user's profile folder
|
||||
Fixed issue MR-333 - Unnecessary prompt for 'close all open connections?'
|
||||
Fixed issue MR-342 - Incorrect view in config pane of new connection after viewing default inheritance
|
||||
Fixed issue MR-352 - Passwords with " (quotation mark) and # (hash key) characters make mRemoteNG to open PuttyNG dialog
|
||||
Fixed issue MR-362 - Rename 'Screenshot Manager' to 'Screenshots' on the View menu to match Panel name
|
||||
Added detection of newer versions of connection files and database schemata. mRemoteNG will now refuse to open them to avoid data loss.
|
||||
Improved appearance and discoverability of the connection search box.
|
||||
If RDC 7.0 or higher is installed, the connection bar is no longer briefly shown when connecting to an RDP connection with redirect key combinations enabled.
|
||||
If RDC 8.0 or higher is installed, RDP connections automatically adjust their size when the window is resized or when toggling full screen mode.
|
||||
|
||||
1.70 (2013-03-07):
|
||||
Fixed issue MR-339 - Connection group collapses with just one click
|
||||
Fixed issue MR-340 - Object reference not set to an instance of an object.
|
||||
Fixed issue MR-344 - Move "Always show panel tabs" option
|
||||
Fixed issue MR-350 - VerifyDatabaseVersion (Config.Connections.Save) failed. Version string portion was too short or too long.
|
||||
Fixed issue MR-355 - Moving sub folders to top level causes property loss
|
||||
Fixed tabs not closing on double-click when the active tab is a PuTTY connection.
|
||||
|
||||
1.70 Release Candidate 2 (2013-02-25):
|
||||
Fixed issue MR-332 - Can't select different tab with one click after disconnecting existing tab
|
||||
Fixed issue MR-338 - PuTTYNG crashing on fresh install of mRemoteNG
|
||||
Re-enabled PuTTYNG integration enhancements on Windows 8
|
||||
|
||||
1.70 Release Candidate 1 (2013-02-22):
|
||||
Fixed issue MR-183 - Error trying to save connections when using SQL - Invalid column name _parentConstantId
|
||||
Fixed issue MR-225 - Tabs do not open in a panel until multiple panels are displayed.
|
||||
Fixed issue MR-229 - Integrated PuTTY doesn't work in Windows 8 RP
|
||||
Fixed issue MR-264 - Windows 8 support
|
||||
Fixed issue MR-317 - Difficulty right-clicking on Tab
|
||||
Fixed issue MR-318 - Wrong tab gets selected when tab names overflow on the tab bar
|
||||
Fixed issue MR-321 - New connection panel doesn't get panel header if its the only one or is moved
|
||||
Fixed issue MR-322 - Connection Button not listing servers
|
||||
Added option to always show panel tabs.
|
||||
Fixed "Decryption failed. Padding is invalid and cannot be removed." notification.
|
||||
Fixed KiTTY opening in a separate window when using a saved session.
|
||||
|
||||
1.70 Beta 2 (2013-02-18):
|
||||
Fixed issue MR-47 - Silent Installation Prompts for Language
|
||||
Fixed issue MR-54 - Error When disconnecting from SSL channel RDP
|
||||
Fixed issue MR-58 - Bug when duplicating connection in connection view
|
||||
Fixed issue MR-68 - Config Window Loses Options
|
||||
Fixed issue MR-71 - Minimizing mRemoteNG causes temporary re-size of Putty sessions (windows)
|
||||
Fixed issue MR-80 - Reconnect previous sessions
|
||||
Fixed issue MR-81 - Problem Duplicating Folder w/ Sub-Folders
|
||||
Fixed issue MR-85 - Microsoft .NET Framework warning
|
||||
Fixed issue MR-86 - Citrix GDI+ Error when screen is locked
|
||||
Fixed issue MR-96 - When pressing SHIFT+F4 to create a new connection inside a folder, the new connections doesn't inherit any properties from its parent
|
||||
Fixed issue MR-101 - Collapse all folders causes a NullReferenceException
|
||||
Fixed issue MR-165 - Can't close About window if it is the last tab
|
||||
Fixed issue MR-166 - Inheritance button is disabled on some connections
|
||||
Fixed issue MR-167 - Name and description of properties not show in inheritance list
|
||||
Fixed issue MR-171 - Inherit configuration not showing friendly names for each inherit component
|
||||
Fixed issue MR-172 - RDGatewayPassword is unencrypted in confCons.xml file
|
||||
Fixed issue MR-174 - Trailing Space on a Hostname/IP will cause the connection not to happen.
|
||||
Fixed issue MR-175 - Problem with focus when 2 or more PuTTY sessions opened
|
||||
Fixed issue MR-176 - Del key while editing connection name triggers 'Delete Connection'
|
||||
Fixed issue MR-178 - 3 different panels crashes all connections
|
||||
Fixed issue MR-181 - Sessions on startup
|
||||
Fixed issue MR-190 - Can't click on tab/session
|
||||
Fixed issue MR-196 - Cannot export list without usernames and passwords
|
||||
Fixed issue MR-199 - when using screen inside putty, screen becomes dead when reduce mremoteNG
|
||||
Fixed issue MR-202 - The Connection "Tab" show Ampersands as underscores.
|
||||
Fixed issue MR-214 - Hostname/IP reset
|
||||
Fixed issue MR-224 - Session tabs become un-clickable after duplicating a tab or opening a new one in the same panel
|
||||
Fixed issue MR-233 - Backslash at end of password prevents success of putty invocation and corresponding auto-logon
|
||||
Fixed issue MR-235 - Config file gets corrupted when leaving the password entry box with ESC
|
||||
Fixed issue MR-264 - Windows 8 support
|
||||
Fixed issue MR-277 - Inheritance configuration button not appear in configuration tab
|
||||
Fixed issue MR-284 - SSH: Text not showing properly
|
||||
Fixed issue MR-299 - mRemoteNG crashes while using remotely (Windows XP remote desktop)
|
||||
Fixed issue MR-306 - Fatal .NET exception on program start
|
||||
Fixed issue MR-313 - PuTTY window not maximized when loading from saved session
|
||||
mRemoteNG now requires .NET Framework 3.0 instead of 2.0.
|
||||
Updated translations.
|
||||
Added translations for Spanish (Argentina), Italian, Polish, Portuguese, Chinese (Simplified).
|
||||
Improved the use of Tab and Shift-Tab to cycle through entries in the Config grid.
|
||||
Improved loading of XML files from older versions of mRemote/mRemoteNG.
|
||||
|
||||
1.70 Beta 1 (2012-02-27):
|
||||
Fixed issue MR-77 - VerifyDatabaseVersion (Config.Connections.Save) failed. Version string portion was too short or too long.
|
||||
Fixed issue MR-78 - Renaming Connections
|
||||
Fixed issue MR-79 - MoveUp/Down item doesn't work + Sort button broken
|
||||
Fixed issue MR-93 - Regional settings problem when using SQL connection in mRemoteNG
|
||||
Fixed issue MR-97 - Integrate Dutch translation
|
||||
Fixed issue MR-98 - Integrate Russian and Ukranian translations
|
||||
Fixed issue MR-99 - Integrate Spanish translation
|
||||
Fixed issue MR-131 - RD Gateway does not respect setting for use different credentials
|
||||
Added compatibility check for "Use FIPS compliant algorithms" security setting.
|
||||
Improved reporting of errors when encrypting and decrypting connection files.
|
||||
Added partial Polish translation.
|
||||
The panel tabs are now hidden if only one panel is open.
|
||||
Fix focus issue with RDP connections when changing tabs.
|
||||
Show changes live as connection tabs are being dragged around to change their order.
|
||||
Updated PuTTY to version 0.62.
|
||||
Improved error handling when loading connection files.
|
||||
Fixed bugs with creating a new connection file.
|
||||
A backup of the connection file is saved when it is loaded. The most recent ten backup copies are kept.
|
||||
Added the option to use a smart card for RD Gateway credentials.
|
||||
Made the use of CredSSP for RDP connections optional.
|
||||
Updated VncSharpNG to version 1.2.4440.36644.
|
||||
Added debugging symbols for VncSharpNG.
|
||||
|
||||
1.69 (2011-12-09):
|
||||
Fixed issue #66 - Fresh Install Fails to Create Config
|
||||
Fixed issue #69 - Connection file gets erased
|
||||
Fixed issue #72 - scrollbars added to RDP window after minimize/restore of mRemoteNG
|
||||
Disabled automatic updates in the portable edition
|
||||
Fixed file name in window title changing when exporting an XML file.
|
||||
Fixed Use only Notifications panel checkbox.
|
||||
Updated PuTTY to version 0.61
|
||||
Binaries are now digitally signed
|
||||
Added Credits, License, and Version History items to the Start Menu and made Start Menu item names localizable.
|
||||
|
||||
1.68 (2011-07-07):
|
||||
Fixed issue #48 - VerifyDatabaseVersion fails with new (empty) database tables.
|
||||
Fixed issue #60 - Can't save connections file
|
||||
Fixed issue #62 - Connection file error upon launch.
|
||||
|
||||
1.67 (2011-06-05):
|
||||
Fixed migration of external tools configuration and panel layout from Local to Roaming folder.
|
||||
Disable ICA Hotkeys for Citrix connections. Fixes issue with international users.
|
||||
Added a language selection option so users can override the language if they don't want it automatically detected.
|
||||
Added partial French translation to the application.
|
||||
Addded Thai translation to the installer.
|
||||
Updated graphics in the installer to mRemoteNG logo.
|
||||
Fixed RD Gateway default properties and RDP reconnection count setting not being saved.
|
||||
Fixed bug 33 - IPv6 doesn't work in quick Connect box.
|
||||
Moved the items under Tools in the Connections panel context menu up to the top level.
|
||||
Added buttons for Add Connection, Add Folder, and Sort Ascending (A-Z) to the Connections panel toolbar.
|
||||
Fixed rename edit control staying open when collapsing all folders.
|
||||
Changed sorting to sort all subfolders below the selected folder.
|
||||
Allow sorting of connections if a connection entry is selected.
|
||||
Fixed adding a connection entry if nothing is selected in the tree.
|
||||
Added 15-bit Color RDP setting.
|
||||
Fixed loading of RDP Colors setting from SQL.
|
||||
Added Font Smoothing and Desktop Composition RDP settings.
|
||||
Improved error handling when loading XML connection files.
|
||||
Added the mRemoteNG icon to the list of selectable icons for connection entries.
|
||||
Added confirmation before closing connection tabs.
|
||||
Fixed bug 42 - Maximized location not remembered with multiple monitors.
|
||||
Improved loading and saving of window location.
|
||||
Removed flickering on start up.
|
||||
Changed the Options page into a normal dialog.
|
||||
Improved Reset Layout function.
|
||||
Changed to use full four part version numbers with major, minor, build, and revision.
|
||||
Changed hard coded SQL database name into a user configurable setting.
|
||||
Fixed tab order of controls in Options dialog.
|
||||
Fixed bug 45 - Changing some settings in the config file may not save.
|
||||
|
||||
1.66 (2011-05-02):
|
||||
Fixed connections not working
|
||||
|
||||
1.65 (2011-05-02):
|
||||
Fixed Ctrl-Tab and Ctrl-Shift-Tab not working in any other applications while mRemoteNG is running
|
||||
Ctrl-Tab and Ctrl-Shift-Tab no longer work to switch tabs within mRemoteNG
|
||||
Fixed bug 36 - Install creates shortcuts only for the installing user
|
||||
Fixed bug 38 - Application uses the wrong Application Data settings folder (in Local Settings)
|
||||
Added code to the installer to check that the user is in the 'Power Users' or 'Administrators' group
|
||||
|
||||
1.64 (2011-04-27):
|
||||
Fixed bug 6 - VNC CTRL+key & keyboard combo mappings are broken
|
||||
Fixed bug 12 - Tab switch is not working in config panel
|
||||
Fixed bug 14 - RDP Connection authentication problem
|
||||
Fixed bug 22 - External App parameter macro expansion doesn't work with "try to integrate"
|
||||
Fixed bug 25 - Unhandled exception when mRemoteNG opens
|
||||
Added multilanguage support and German translation to the application
|
||||
Added Czech, Dutch, French, German, Polish, and Spanish translations to the installer
|
||||
Added Ctrl-Tab hotkey to switch to the next tab and Ctrl-Shift-Tab to switch to the previous tab
|
||||
Added Tab key to cycle through entries in the Config grid and Shift-Tab to cycle in reverse
|
||||
Added ability to configure external tools to run before or after a connection is established
|
||||
Fixed missing parameters in macro expansion for external tools
|
||||
Fixed RD Gateway and other inheritance bugs
|
||||
Changed how new connection files are created
|
||||
Changed the internal namespace of the application to mRemoteNG instead of mRemote
|
||||
Added credit for the DockPanel Suite to the About page
|
||||
Updated DockPanel Suite to version 2.5 RC1
|
||||
Updated VNCSharpNG to correct Ctrl and Alt key pass-through behavior
|
||||
|
||||
1.63 (2010-02-02):
|
||||
New icon and logo
|
||||
Fixed problems moving or resizing the main window while PuTTY (SSH/telnet/rlogin/raw) connections are open
|
||||
Fixed PuTTY processes not closing on Vista and 7 with UAC enabled
|
||||
Updated DockPanel Suite from 2.2.0 to 2.3.1
|
||||
Fixed error if the mouse is clicked outside of the remote screen area of a VNC connection
|
||||
Fixed flashing and red lines at bottom of the window on first run
|
||||
Added View->Reset Layout menu item
|
||||
Added F11 shortcut key to View->Full Screen
|
||||
Improved RDP error reporting
|
||||
Added support for Credential Security Support Provider (CredSSP) which is required for Network Level Authentication (NLA)
|
||||
Added support for connecting through Remote Desktop Gateway servers
|
||||
Popups can now be allowed in Internet Explorer by holding Ctrl+Alt when clicking a link
|
||||
Added PuTTY Settings item to tab context menu
|
||||
|
||||
1.62 (2010-01-19):
|
||||
Switched to VncSharp, an open source VNC component
|
||||
VNC is supported again except for the following features:
|
||||
Windows authentication
|
||||
Setting the compression, encoding and color settings
|
||||
Connecting through a proxy server
|
||||
Free SmartSize mode (it does the same thing as Aspect SmartSize mode now)
|
||||
Rearranged the Options page and added an Updates tab
|
||||
Added option to change how often updates are checked
|
||||
Open Updates options tab before connecting for the first time
|
||||
No longer show About page on first run
|
||||
Renamed Quicky toolbar to Quick Connect toolbar
|
||||
Changed back to allowing toolbars to dock to the left or right of the menu bar and added gripper to move it around
|
||||
Added RDP, VNC and ICA version numbers to Components Check page
|
||||
Fixed a bug with the inheritance buttons on the Config panel disappearing after awhile
|
||||
|
||||
1.61 (2010-01-14):
|
||||
Removed unlicensed SmartCode Solutions ViewerX VNC Viewer ActiveX
|
||||
This version of mRemoteNG does not support VNC
|
||||
|
||||
1.60 (2010-01-09):
|
||||
Changed name to mRemoteNG
|
||||
Fixed menu bar not staying docked to left side
|
||||
Removed snakes game Easter egg
|
||||
Removed references to visionapp Remote Desktop
|
||||
Changed filename delimiter in title bar from pipe to dash
|
||||
Changed default format for saving screenshot images to PNG
|
||||
Changed website addresses
|
||||
Added Report a Bug and Support Forum links to the Help menu
|
||||
Moved Check for Updates to the Help menu
|
||||
Changed website links in Help menu and About page to load within mRemoteNG instead of launching an external browser
|
||||
|
||||
1.50:
|
||||
Added the following formats to the "Save Connections As" function:
|
||||
mRemote CSV (standard CSV file with all properties)
|
||||
vRD 2008 CSV (standard CSV file with properties relevant for importing connections in vRD 2008)
|
||||
Fixed bug in inheritance code (SmartSize Mode and View Only properies were always shown when using VNC)
|
||||
|
||||
1.49:
|
||||
mRemote and visionapp Remote Desktop 2008 merge!
|
||||
Read more here: http://www.mremote.org/wiki/visionappMerge.ashx
|
||||
or in the Announcement panel.
|
||||
Added features to the update function
|
||||
Added Announcement feature
|
||||
Changed copyright notice in about screen and text when connecting via VNC
|
||||
Fixed some SQL-related problems
|
||||
|
||||
V1.48:
|
||||
ATTENTION! There is a bug in the automatic update code in 1.45 so you will have to download the new version manually from http://www.mremote.org/wiki/Downloads.ashx
|
||||
|
||||
Added startup components check with directions to fix component installation (also available in Tools - Components Check)
|
||||
Added "Try to integrate" option to Ext. Apps. If enabled mRemote will try to integrate the app into a tab container like any other connection protocol.
|
||||
Added Ext. App as protocol. Any Ext. App can be launched just like a normal connection.
|
||||
Example (DameWare Mini Remote Control):
|
||||
Create a new Ext. App with the following properties:
|
||||
Display Name: DameWare
|
||||
Filename: c:\PathToYourDameWareInstallDir\DWRCC.exe
|
||||
Arguments: -c: -h: -m:%hostname% -u:%username% -p:"%password%" -d:%domain%
|
||||
Options: Try to integrate
|
||||
Create a new connection and select Ext. App as protocol
|
||||
Then choose DameWare in the Ext. App field
|
||||
If you have problems with a particular app that takes a long time to start up consider setting a higher PuTTY/Ext. Apps wait time in Tools - Options - Advanced
|
||||
Added option to completely encrypt connection files (tools - options - advancecd)
|
||||
Added Rendering Engine option for HTTP/S protocols
|
||||
You can now use the Gecko (Firefox) rendering engine
|
||||
For this to work you need to download xulrunner (get it here: ftp://ftp.mozilla.org/pub/xulrunner/releases/1.8.1.3/contrib/win32/)
|
||||
It must be the 1.8.1.3 release, 1.9.0.0 does NOT work!
|
||||
Extract the contents to a path of your choice and set the correct path in Tools - Options - Advanced - XULrunner path
|
||||
The interface is tab enabled and usage is generally very firefox-like. So you can open new tabs with Ctrl+T, jump to the location bar with Ctrl+L and so on...
|
||||
Added "MAC Address", "User Field" fields and %MacAddress%, %UserField% variables to use in Ext. Apps
|
||||
Added descriptions for all fields in the config editor
|
||||
Fixed bug in connections loading code when using SQL storage
|
||||
Fixed bug in reconnect code
|
||||
Fixed VNC sessions not refreshing screen automatically when switching between tabs or panels
|
||||
|
||||
WARNING! There have been changes to the connections file/SQL tables
|
||||
Please always backup your whole config before updating to a new mRemote beta release, especially when there have been changes to the config files/SQL tables
|
||||
To get SQL working with the new version please update your tables like in the provided script (Info - Help - SQL Configuration)
|
||||
These are the added lines:
|
||||
[RenderingEngine] [varchar] (10) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
|
||||
[MacAddress] [varchar] (32) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
|
||||
[UserField] [varchar] (256) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
|
||||
[ExtApp] [varchar] (256) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
|
||||
[InheritRenderingEngine] [bit] NOT NULL ,
|
||||
[InheritMacAddress] [bit] NOT NULL ,
|
||||
[InheritUserField] [bit] NOT NULL ,
|
||||
[InheritExtApp] [bit] NOT NULL ,
|
||||
|
||||
V1.45:
|
||||
New german language build available
|
||||
Added support for RDP 6.1 (XP SP3/Vista SP1) features (Server Authentication, Console Session, TS Gateway not yet...)
|
||||
Added basic support for UltraVNC SingleClick (Tools - UltraVNC SingleClick); the listening port is configurable in the options
|
||||
Fixed VNC connections not working on x64
|
||||
Fixed screenshots save all feature overwriting files with the same name (not actually a bug, but rather a new feature ;)
|
||||
Fixed ICA Encryption Strength not inheriting properly
|
||||
|
||||
WARNING! There have been changes to the connections file/SQL tables
|
||||
Please always backup your whole config before updating to a new mRemote beta release, especially when there have been changes to the config files/SQL tables
|
||||
To get SQL working with the new version please update your tables like in the provided script (Info - Help - SQL Configuration)
|
||||
These are the added lines:
|
||||
[RDPAuthenticationLevel] [varchar] (32) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
|
||||
[InheritRDPAuthenticationLevel] [bit] NOT NULL ,
|
||||
|
||||
V1.43:
|
||||
Added function to send either the main form or a connection panel to another screen
|
||||
To send the form to another screen, right click the titlebar or the taskbar button and select "Send to..."
|
||||
If you want to send a panel to another screen, right click the panel's tab and do the same
|
||||
Fixed PuTTY connections not supporting credentials with spaces
|
||||
Fixed form not opening on last position when using multiple screens
|
||||
Fixed kiosk mode not working on secondary screen
|
||||
|
||||
V1.42:
|
||||
IMPORTANT NEWS: Please read the page that opens when you first start this release or go to:
|
||||
http://www.mRemote.org/wiki/MainPage.ashx#Commercial
|
||||
Added minimize to tray option
|
||||
Added option to enable switching to open connections with a single click on the corresponding connection in the tree
|
||||
Added button to test proxy settings
|
||||
Fixed: If the active connection tab is a PuTTY connection, Alt+Tab to mRemote now focuses the PuTTY window
|
||||
Fixed encoding problem with PuTTY sessions that included spaces
|
||||
Fixed problem that made mRemote inaccesible when closing it on a second monitor and then disabling this monitor
|
||||
Fixed: Inheritance defaults of some new VNC properties were not saved in the portable package
|
||||
|
||||
V1.41:
|
||||
Added complete support for SmartCode's ViewerX and removed VncSharp
|
||||
Many thx to everyone who donated to make this happen!!! I didn't think that it wouldn't even take a week! =)
|
||||
I hope everyone will be satisfied by the functions and possibilities this new control provides
|
||||
If you use one of the non-setup packages you must register the control yourself
|
||||
Open a cmd and change to the directory you installed mRemote to
|
||||
Type regsvr32 scvncctrl.dll and click ok
|
||||
Changed shortcuts and added buttons for them to the view menu under "Jump To" because they were causing several problems
|
||||
|
||||
WARNING! There have been changes to the connections file/SQL tables and the Ext. Apps XML file
|
||||
Please always backup your whole config before updating to a new mRemote beta release, especially when there have been changes to the config files/SQL tables
|
||||
To get SQL working with the new version please update your tables like in the provided script (Info - Help - SQL Configuration)
|
||||
|
||||
V1.40:
|
||||
Added (limited) support for the trial version of SmartCode's VNC ActiveX
|
||||
To enable it go to Options - Advanced and check "Try SmartCode VNC ActiveX"
|
||||
When connecting a pop up will open, wait about 10 seconds, then click on "Trial" to continue
|
||||
I will integrate this control fully into mRemote if I get enough Donations to buy the single developer license ($375,-)
|
||||
So if you want to see better VNC support (All UltraVNC, TightVNC and RealVNC functions) in mRemote, please help me and donate some bucks
|
||||
For donations either go to the mRemote Wiki (Info - Website) or click on Info - Donate to directly go to PayPal
|
||||
I will announce the current donation amount every day (or as often as I can) on the Wiki main page
|
||||
If you want to know more about the control go here: http://www.s-code.com/products/viewerx/
|
||||
Added feature to choose the panel a connection will open in when...
|
||||
a. no panel name was assigned in the properties
|
||||
b. you opened a connection with the option to choose the panel before connecting
|
||||
c. you checked "Always show panel selection dialog when opening connectins" in Options - Tabs & Panels
|
||||
Added Shortcuts to focus the standard panels
|
||||
Alt+C: Switch between Connections & Config panel
|
||||
Alt+S: Switch between Sessions & Screenshots panel
|
||||
Alt+E: Switch to Errors & Infos panel
|
||||
Added some new icons
|
||||
|
||||
V1.39:
|
||||
Added MagicLibrary.dll to the release again (forgot it in the 1.38 packages, sorry)
|
||||
Added auto-reconnect for ICA
|
||||
Added feature that automatically clears whitespaces in the Quicky Textfield
|
||||
Added special feature: Go to the set password dialog and type "ijustwannaplay" (without the quotes) in the password field... ;)
|
||||
|
||||
V1.38:
|
||||
Added automatic reconnect feature for RDP (Options - Advanced)
|
||||
Added connections drop-down to the quicky toolbar (same as the tray icon menu)
|
||||
Added setting in the options to enable/disable that double clicking on a connection tab closes it
|
||||
Added option to automatically set the hostname like the display name when creating new connections
|
||||
Fixed bug that caused the properties of a folder to be filled with "Object reference not set to an instance of an object." when adding a folder to the root with Default Inheritance enabled
|
||||
Fixed bug that made the properties of a newly added Connection to the root unavailable when Default Inheritance was enabled
|
||||
Fixed bug that the default settings for Pre/Post Ext. App, and their inheritance settings were not being saved
|
||||
Fixed bug in settings loading methods that caused the application to hang when an error occured
|
||||
Fixed bug in Ext. Apps panel that copied the properties of the previously selected Ext. App when "Wait for exit" was checked
|
||||
Fixed bug in the SQL Query that creates the tables needed by mRemote
|
||||
Attempt to fix the "Drop-Down on Screenshot" bug on some machines
|
||||
|
||||
V1.35:
|
||||
Added single instance mode (look in Options - Startup/Exit) - No cmd arguments supported yet!
|
||||
Added possibilty to start a Ext. App before connecting and on disconnect (e.g. for VPN/RAS)
|
||||
Added option to the Ext. Apps to tell mRemote to wait for the exit of the Ext. App
|
||||
Added encryption setting for ICA
|
||||
|
||||
WARNING! There have been changes to the connections file/SQL tables and the Ext. Apps XML file
|
||||
Please always backup your whole config before updating to a new mRemote beta release, especially when there have been changes to the config files/SQL tables
|
||||
Here's a list of new columns that need to be created before saving connections to an SQL server:
|
||||
Name: ICAEncryptionStrength, Data-Type: varchar, Length: 64, Allow Nulls: No
|
||||
Name: InheritICAEncryptionStrength, Data-Type: bit, Length: 1, Allow Nulls: No
|
||||
Name: PreExtApp, Date-Type: varchar, Length: 512, Allow Nulls: Yes
|
||||
Name: PostExtApp, Date-Type: varchar, Length: 512, Allow Nulls: Yes
|
||||
Name: InheritPreExtApp, Date-Type: bit, Length: 1, Allow Nulls: No
|
||||
Name: InheritPostExtApp, Date-Type: bit, Length: 1, Allow Nulls: No
|
||||
|
||||
V1.33:
|
||||
Fixed problem that caused RDP connections not to initialize properly when using XP SP3
|
||||
Fixed bug in Port Scan that prevented hosts with no hostname from being imported
|
||||
|
||||
V1.32:
|
||||
Added: Inheritance defaults can now be customized (look in the root properties of your connections tree)
|
||||
Fixed bug that made password-secured connection files not load properly because the return value from the password screen was always null
|
||||
Fixed a lot of outdated code in the import functions (Import from XML, Import from AD, Import from RDP files)
|
||||
Fixed bug that caused properties with a ' character not to be saved properly when using SQL Server
|
||||
Changed Target CPU to AnyCPU again as I think the x86 setting caused problems on x64 machines (although it shouldn't)
|
||||
|
||||
V1.31:
|
||||
Small speed improvement to the port scanner
|
||||
Fixed bug that caused SQL live-update to not work when not using AD Authentication
|
||||
Fixed bug that caused Save As not to work
|
||||
|
||||
V1.30:
|
||||
Added experimental SQL Server with live-update (multi-user) support (see Help - Getting started - SQL Configuration)
|
||||
Added bunch of new icons to the UI, most of them by famfamfam.com
|
||||
Added dropdown button to Quicky Toolbar to choose protocol
|
||||
Many smaller changes and additions
|
||||
Fixed: Wrong default PuTTY session name
|
||||
Fixed bug in Port Scanner that caused an error when no DNS name could be resolved
|
||||
|
||||
V1.25:
|
||||
Added inheritance for folders
|
||||
Added port scan feature and possibility to import from a scan
|
||||
Added toolbar for Ext. Apps (see View - External Applications Toolbar)
|
||||
Added quick connect as toolbar
|
||||
Added code that creates a backup of the current connections file every time it is loaded (It's named YourConsFile.xml_BAK)
|
||||
Added description variable to Ext. Apps
|
||||
Fixed bug that allowed inheriting from root node
|
||||
Fixed bug that caused Ext. Apps launched from a connection tab to use the selected tree node instead of the current tab
|
||||
Fixed bug that caused mRemote not to save panel layout and Ext. Apps on exit
|
||||
|
||||
V1.24:
|
||||
Fixed a bug in connections loading mechanism that caused a corrupted connections file when upgrading from a previous version
|
||||
|
||||
V1.23:
|
||||
Added feature to remember which connections were opened on last runtime and reconnect to them on the next start (see Tools - Options - Startup/Exit)
|
||||
A command line switch is also available to cancel reconnecting (/noreconnect or /norc)
|
||||
Added Auto Save feature (Tools - Options - Connections - Auto Save every...)
|
||||
Added Ext. Apps to connection tab context menu
|
||||
Added better error handling for RDP connection creation
|
||||
Fixed problem with Sessions feature on 64bit systems
|
||||
Fixed Sessions feature not working when using global credentials
|
||||
Fixed several problems with the Active Directory OU picker control
|
||||
Fixed bug in Connection duplicate code that caused duplicated connection to still have previous tree node assigned
|
||||
|
||||
V1.20:
|
||||
Added External Applications feature (check the help section for more info)
|
||||
Added duplicate feature to Connections tree
|
||||
Fixed: MagicLibrary.dll was not included in the setup package
|
||||
|
||||
V1.16:
|
||||
New Domain: www.mRemote.org
|
||||
Fixed PuTTY connections appearing in a new window
|
||||
There's a new setting in the options to fine tune the time to wait until the window has been created
|
||||
Fixed export not working
|
||||
Added reconnect feature in tab menu
|
||||
|
||||
V1.15:
|
||||
Added: New portable package
|
||||
Added: Defaults for new connections can now be customized
|
||||
Click the root item and then the new Properties-like button with a small yellow star to get to the settings
|
||||
Fixed Import from Active Directory not working
|
||||
Fixed problem with single click connect not focusing correctly
|
||||
Fixed root node not being renamed after changing name in property grid
|
||||
|
||||
V1.10:
|
||||
Added support for setting a password to protect the connections file with (look in the root of your connections tree)
|
||||
Added RDP file import feature
|
||||
Added new command-line switch to reset panel's positions
|
||||
Added HTTPS as protocol
|
||||
Added HTTP/S basic authentication
|
||||
Added support for setting a Proxy server for automatic updates
|
||||
Some changes in help section
|
||||
Fixed the bug that passwords stored in the options weren't decrypted when a connection was opened
|
||||
Fixed "bug" that prevented "Connect to console session" from working in RDC6.1 (Vista SP1 RC1/XP SP3 RC1)
|
||||
|
||||
V1.00:
|
||||
Merry Christmas! =)
|
||||
|
||||
V1.00 is a (almost) complete rewrite of the whole application
|
||||
The code base is now much cleaner and more (easily) extendable
|
||||
New features include (but are not limited to):
|
||||
Every part of the application is now integrated into panels which can be moved, docked and undocked, hidden, moved to another monitor, etc.
|
||||
This makes many new and exciting ways to manage connection and application windows possible
|
||||
You can for example open up 4 PuTTY sessions in 4 different panels and align them in the main application so you can use all 4 side by side - 2 on the upper side and 2 on the bottom for example
|
||||
This can be done for EVERY part of the application, it's completely modular and customizable
|
||||
Connection and folder (previously called containers) properties have moved to a new property grid control
|
||||
Every setting (with the exclusion of the hostname, which wouldn't make any sense) can now be inherited from the parent folder
|
||||
Connection file saving/loading is now handled a bit different (more in the help section)
|
||||
Application restart is no longer nececary after changing options, they are active with a click of the OK button
|
||||
Smart size can now be activated also if a connection is already open (in the right click menu of the active tab)
|
||||
A panel name can be stored with every connection (or folder, if inherting) to always open the connection in the specified panel
|
||||
And last but not least, many bugs have been fixed, though there are probably many new bugs aswell - Did I already mention this is a rewrite? ;)
|
||||
I hope you like my work and if you do please consider donating on the mRemote website to support me a little. Any amount will do! Thx!
|
||||
|
||||
V0.50:
|
||||
Removed old Terminal (SSH, Telnet) control and embedded PuTTY instead
|
||||
This decision brings mostly good but also some bad news
|
||||
The good news is that now everything that works in putty also works in mRemote
|
||||
This means X11 forwarding, SSH port forwarding, session logging, appearance customization, etc. should be working fine now
|
||||
It also brings some new protocols (Rlogin, RAW)
|
||||
The bad news is that I cannot fully integrate Putty into mRemote because it is a standalone application and thus has it's own window handle
|
||||
This means that you won't be able to use Ctrl+Tab to switch between tabs, catching errors or infos through the new Errors and Infos tab isn't possible, etc.
|
||||
Added possibility to change resolution or display mode (Fit to window, Fullscreen, Smart size)
|
||||
Added new setting in options to show logon info on tab titles
|
||||
Added new feature that catches popup dialogs and puts them in a managed interface. This is another step to make mRemote a single window application.
|
||||
Pressing escape switches back to the connection list
|
||||
There is a context menu that allows you to copy selected errors/warnings/infos to the clipboard (text only) or to delete them
|
||||
There also are settings in the option to change when to switch to the tab and to switch back to the normal behaviour of displaying message popups
|
||||
Added QuickConnect history and auto-complete functions
|
||||
Added a few new Icons (Linux, Windows, ESX, Log, Finance)
|
||||
Improved options tab
|
||||
Connections file version is now 1.2
|
||||
Fixed some form drawing bugs
|
||||
|
||||
V0.35:
|
||||
Added tab switching/closing hotkeys
|
||||
Switch to next tab: Ctrl+Tab
|
||||
Switch to previous tab: Ctrl+Shift+Tab
|
||||
Close active tab: Ctrl+W
|
||||
This does not and will probably never work with RDP connections!
|
||||
Fixed bug in updating code that still displayed the current version in the old format (x.x.x.x instead of x.xx)
|
||||
Fixed bug where the colors setting was not correctly read after saving and reloading a connections file (only with 256 colors setting)
|
||||
Fixed bug that made connect to console session and fullscreen options not work
|
||||
Fixed bug that when opening options, update or about tab caused weird paddings next to the tab or other strange behaviour
|
||||
Changed shortcuts to menu items in main menu as they interfered with some terminal key bindings
|
||||
|
||||
V0.30:
|
||||
Added HTTP as protocol to allow for basic web-based administration
|
||||
Added new connections menu to the toolbar
|
||||
Left click on a connection connects
|
||||
Right click on a container or connection opens the config tab for the selected item
|
||||
Added two new connection context menu entries for quickly connecting to console session or connecting in fullscreen
|
||||
Improved tray icon menu (just like the main connections menu)
|
||||
The connections tree can now be hidden
|
||||
To hide it right click on the splitter (the divider between the connections tree and the tabbing interface)
|
||||
Removed overlay (RDP locking) feature in favor of simply grabbing input when clicking inside the control area
|
||||
I hope nobody is too sad that the nice looking overlay feature had to go, but..., well, it had to! ;-)
|
||||
Changed "Redirect Key combinations (like in fullscreen)" to be disabled when in kiosk mode as it has no effect then anyway
|
||||
Several small bugfixes and code improvements
|
||||
|
||||
V0.20:
|
||||
Added Drag and Drop support for tabs
|
||||
Added tab context menu
|
||||
Switch to/from fullscreen
|
||||
Take a screenshot
|
||||
Transfer files via SCP/SFTP (SSH)
|
||||
Send special keys (VNC)
|
||||
Rename tabs
|
||||
Duplicate tabs (Create another instance of the connection)
|
||||
Show config
|
||||
Close tab (disconnect)
|
||||
Removed Fullscreen and Send special keys buttons from the main toolbar as they are now in the tab context menu
|
||||
Added middle click support for tabs (close/disconnect)
|
||||
Added SSH file transfer (SCP/SFTP) support
|
||||
Added Tools menu to the tree context menu
|
||||
Transfer files via SCP/SFTP (SSH)
|
||||
Import/Export features
|
||||
Sorting
|
||||
Changed version format
|
||||
Fixed the problem that caused mRemote to crash when dragging a parent node of the connections tree onto one of it's child nodes
|
||||
Fixed problem in importing mechanism that allowed importing connections including the root which resulted in multiple root items that couldn't be deleted
|
||||
Fixed problem with quick connect
|
||||
|
||||
V0.0.9.0:
|
||||
Added support for redirecting key combinations (Alt+Tab, Winkey, ...)
|
||||
Added Import/Export features
|
||||
Added Quick Connect Port support, just type the host you want to connect to followed by a ":" and then the port
|
||||
Added Connect/Disconnect buttons to connections context menu
|
||||
Added two new icons (Test Server | TST; Build Server | BS)
|
||||
Many changes to the connections loading/saving mechanisms
|
||||
confCons version is now 1.0
|
||||
Some code cleanup
|
||||
Fixed auto session info to only try to get session information when a RDP connection is selected
|
||||
Fixed AD Import feature (didn't care if imported items were computers, groups, users, ... ;)
|
||||
Fixed settings and connections not saving when installing updates from the auto-updater
|
||||
Fixed form size and location not saving properly when closing the application in minimized state or in maximized state on a secondary monitor
|
||||
|
||||
V0.0.8.2:
|
||||
Added SSH1 to Quick Connect GUI
|
||||
Changed buffer size of terminal control, it's now 500 lines
|
||||
Fixed terminal connections not getting focus when changing tabs
|
||||
Fixed bug in terminal code that caused hitting "home" to show "~" instead of jumping to the start of the line
|
||||
Fixed bug that caused that hitting enter in mRemote wouldn't do anything when options was opened before
|
||||
|
||||
V0.0.8.0:
|
||||
Added code to check if the msrdp com control is registered
|
||||
Many Improvements to the terminal control (ssh1(!), ssh2, telnet)
|
||||
Fixed bug that caused mRemote to crash when moving connection into root node (only with inheritance enabled)
|
||||
Fixed bug: Pressing delete when editing a node's name caused delete messagebox to show
|
||||
|
||||
V0.0.7.5:
|
||||
Added inheritance feature to inherit connection settings from parent container
|
||||
Expanded/Collapsed state of tree nodes will now be saved
|
||||
Reduced auto session info delay to 700ms
|
||||
Some code maintainance
|
||||
Some corrections to connections tree and quick search behaviour
|
||||
Fixed bug in TerminalControl that caused the error message "error loading string"
|
||||
Fixed: Settings saving on exit was broken in V0.0.7.0, this is fixed now
|
||||
Changed connections file version to 0.9
|
||||
Fixed connections context menu bug that made import from ad option inaccessible
|
||||
Fixed session info filling up with infos about hosts previously selected
|
||||
|
||||
V0.0.7.0:
|
||||
Massive GUI redesign and changes, hope you like it! =)
|
||||
Fixed bug that made session info to query immediately after selecting a connection (when enabled), there is now a one second delay to prevent collecting session info for more than one host
|
||||
|
||||
V0.0.6.8:
|
||||
Added connection import feature for Active Directory
|
||||
Tidied up project references
|
||||
Multiple changes to setup routine
|
||||
Improved error handling for auto-update
|
||||
Improved download handling for auto-update
|
||||
Fixed bug that made download finished/failed message box appear multiple times when update was canceled and re-downloaded
|
||||
Fixed bug where double-clicking a container opened all connections inside this container
|
||||
|
||||
V0.0.6.6:
|
||||
Changed port textbox control to only allow digits
|
||||
Small changes to connection code for SSH
|
||||
Fixed port setting not saving (or always displaying default port for selected protocol)
|
||||
|
||||
V0.0.6.5:
|
||||
Added auto update feature
|
||||
Changed: Multiple UI Changes (added shortcuts, rearranged menu items, ...)
|
||||
Fixed the problem where the connections file version was saved either with a dot or a comma, depending on system language
|
||||
Fixed not being able to connect to SSH2 hosts without specifying username and password
|
||||
Fixed several problems with Quick Connect
|
||||
Improved saving of config changes
|
||||
Fixed connections tab not closing when using SSH
|
||||
|
||||
V0.0.6.0:
|
||||
Added new protocols: SSH2 and Telnet
|
||||
Added first command line switch/parameter "/consfile"
|
||||
Ex.: mRemote.exe /consfile "%PathToYourConnectionsFile%"
|
||||
Added button to screenshots to delete a screenshot
|
||||
Added Host Status (Ping) feature
|
||||
Many code rewrites and changes in almost every area
|
||||
Changed the way connections get loaded
|
||||
The default path for the connection file is no longer in the application directory but in the local application data folder.
|
||||
Ex.: c:\Documents and Settings\felix\Local Settings\Application Data\Felix_Deimel\mRemote\
|
||||
If opening a connection file from a custom location (click on open link) saving will also occur in this file and not like in previous versions to the default connections file
|
||||
To import your old connection file please use the following procedure: start mRemote, click on "Open" and find your old connection file. Then click on "Save As" and save it with the default file name to the default location
|
||||
Changed the font and style of context menus
|
||||
Changed Quick Connect UI
|
||||
Fixed connection settings in config tab not saving when clicking another connection before jumping to another config field
|
||||
Fixed a bug where renaming a container caused the first connection in the same container to be renamed too
|
||||
|
||||
V0.0.5.0 R2:
|
||||
Fixed a bug that prevented connections from opening when icon files were assigned in a previous version of mRemote
|
||||
|
||||
V0.0.5.0:
|
||||
Added (Global) fullscreen / kiosk feature
|
||||
Added redirection settings for disk drives, printers, ports, smart cards and sound
|
||||
Added option to write a log file
|
||||
Added option to open new tabs on the right side of the currently selected tab
|
||||
Added possibility to connect to all nodes in a container
|
||||
Changed session functions to work in background
|
||||
Changed icon choosing mechanism and added a bunch of default icons
|
||||
Changed: Containers with connection can now be deleted just like empty containers
|
||||
Changed screenshot functions to now collect all screenshots in one tab
|
||||
Changed: More settings can now be changed on container basis
|
||||
Changed config file version to 0.6
|
||||
Changed: Small internal changes to the connection saving/creating and opening mechanisms
|
||||
Fixed "Display Wallpapers" and "Display Themes" settings, they are working now
|
||||
|
||||
V0.0.3.6:
|
||||
Added Feature to display an overlay when RDP connection tab has lost the focus, clicking on this gives the focus back to the control
|
||||
Added standard handlers for F2 (rename) and DEL (delete) keys in the treeview
|
||||
Added icon preview for connections in config tab
|
||||
Changed the way new connections and containers are being created in the treeview. The pop up window will not be displayed any longer, instead everything is handled inplace by the treeview.
|
||||
Changed some minor UI related stuff
|
||||
Fixed bug in tab closing mechanism that caused icons (play/pause) to not be set on the correct tree nodes
|
||||
|
||||
V0.0.3.5:
|
||||
Added Feature to query and log off sessions on a remote machine and option to do this automatically
|
||||
Added Option to show icon in system tray with connection menu
|
||||
Changed controls to flat style as I think this fits the whole application more than the old 3D look
|
||||
Multiple UI changes to eliminate annoying behaviour
|
||||
|
||||
V0.0.3.3:
|
||||
Added Feature to specify which login information to use when no info is provided in the config of a remote machine
|
||||
Fixed bug in Quick Find where trying to open a connection when no node was found caused an error
|
||||
Fixed bug where the main form was not rendered correctly when hiding top bar and using XP Themes
|
||||
Fixed bug in drag-drop routine that caused application to hang when trying to drop a node on one of it's child nodes
|
||||
Fixed bug where taskbar buttons for fullscreen rdp windows did not disappear after disconnecting
|
||||
|
||||
V0.0.3.2:
|
||||
Added new Save As Dialog with feature to only save specific connection settings
|
||||
Added Option to display Tooltips when hovering over host entries in the connection tree
|
||||
Added Option to ask at exit when there are open connections
|
||||
Fixed bug where saving connections file with spaces in the root node caused an error -> updated Connection File Version to 0.5
|
||||
Fixed bug in options tab where the browse button for a custom connection file didn't do anything
|
||||
|
||||
V0.0.3.0:
|
||||
Added Options Tab
|
||||
Load connections file from different location
|
||||
Save/Don't Save connections file on exit
|
||||
Show current tab name in window title
|
||||
Added drag and drop functionality to the connections tree
|
||||
Added feature to hide top bar
|
||||
Added feature to send special keys (VNC)
|
||||
Updated VncSharp Library to V0.88 (still pretty buggy)
|
||||
|
||||
V0.0.2.7:
|
||||
Added feature to save connection settings to all connections in the selected container
|
||||
Icon choosing bug fixed
|
||||
Taskbar button had no text when in fullscreen - fixed
|
||||
Fixed bug in Quick Connect GUI
|
||||
Disabled "Display Wallpaper" and "Display Themes" checkboxes as these features are not implemented
|
||||
|
||||
V0.0.2.5:
|
||||
Quick connect button bug fixed
|
||||
Search field resize bug fixed
|
||||
Splitter position is now saved on exit
|
||||
Added new connections toolstrip (same functions as context menu)
|
||||
|
||||
V0.0.2.4:
|
||||
Changed default color depth to 16bit
|
||||
Added Keep Alive Interval (1 Minute)
|
||||
Added Options to choose between RDP & VNC
|
||||
Added Port Setting for RDP
|
||||
Added Option to connect to console
|
||||
Added Menu Entries to move Connections & Containers up & down
|
||||
Some small code improvements
|
||||
1684
CHANGELOG.md
Normal file
1684
CHANGELOG.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -278,62 +278,3 @@ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
122
CREDITS.TXT
122
CREDITS.TXT
@@ -1,122 +0,0 @@
|
||||
Contributors
|
||||
============
|
||||
|
||||
Jason Barbier
|
||||
Wiktor Beryt
|
||||
Lionel Caignec
|
||||
Felix Deimel
|
||||
Holger Henke
|
||||
Tom Hiller
|
||||
Riley McArdle
|
||||
Apisitt Rattana
|
||||
Andreas Rehm
|
||||
David Vidmar
|
||||
|
||||
|
||||
Translators
|
||||
===========
|
||||
|
||||
Eugenio "Ryo567" Mart<72>nez
|
||||
Mathieu Pape
|
||||
Emanuel Silva
|
||||
Robert Siwiec
|
||||
|
||||
|
||||
Included Source Code
|
||||
====================
|
||||
|
||||
Command Line Arguments Parser 1.0
|
||||
Copyright <20> 2002 Richard Lopes
|
||||
MIT License
|
||||
http://www.codeproject.com/KB/recipes/command_line.aspx
|
||||
|
||||
DotNetVer
|
||||
Copyright <20> 2010 David Grinberg
|
||||
Copyright <20> 2010-2011 Brandon Hansen
|
||||
http://nsis.sourceforge.net/DotNetVer
|
||||
|
||||
|
||||
Included Components
|
||||
===================
|
||||
|
||||
ADTree 1.0
|
||||
Copyright <20> 2004 Marc Merritt
|
||||
Copyright <20> 2008 Felix Deimel
|
||||
http://www.codeproject.com/KB/selection/ADPickerCtrl.aspx
|
||||
|
||||
DiffieHellman 1.0
|
||||
Copyright <20> 2003 The Mentalis.org Team
|
||||
Modified New BSD License
|
||||
http://www.mentalis.org/
|
||||
|
||||
DockPanel Suite 2.3.1
|
||||
Copyright <20> 2007 Weifen Luo
|
||||
MIT License
|
||||
http://sourceforge.net/projects/dockpanelsuite/
|
||||
|
||||
Emergent OnLine WTSCOM 2.0.6.0
|
||||
Copyright <20> 2001-2003 Emergent OnLine
|
||||
http://www.go-eol.com/
|
||||
|
||||
FilteredPropertyGrid 1.0.0.0
|
||||
Copyright <20> 2006 Azuria
|
||||
http://www.codeproject.com/KB/cs/FilteredPropertyGrid.aspx
|
||||
|
||||
GeckoFX 1.8.1.4
|
||||
Copyright <20> 2008 Skybound Software
|
||||
Mozilla Public License 1.1
|
||||
http://www.geckofx.org/
|
||||
|
||||
IPTextBox 1.1
|
||||
Copyright <20> 2006 Matthew Kleinwaks
|
||||
Copyright <20> 2008 Felix Deimel
|
||||
http://www.vbforums.com/showthread.php?t=430169
|
||||
|
||||
log4net 1.2.10.0
|
||||
Copyright <20> 2001-2006 The Apache Software Foundation
|
||||
Apache License Version 2.0
|
||||
http://logging.apache.org/log4net/
|
||||
|
||||
Magic Library 1.7.4
|
||||
Copyright <20> 2002-2003 Crownwood Consulting, Ltd.
|
||||
Freely redistributable with attribution
|
||||
http://www.dotnetmagic.com/magic_download.html
|
||||
|
||||
Mentalis.org Security Library 1.0.13.715
|
||||
Copyright <20> 2002-2005 The Mentalis.org Team
|
||||
Modified New BSD License
|
||||
http://www.mentalis.org/
|
||||
|
||||
MiniGeckoBrowser 1.0
|
||||
Copyright <20> 2008 Felix Deimel
|
||||
http://www.appjuice.org/
|
||||
|
||||
MiniTabControl 1.0
|
||||
Copyright <20> 2008 Felix Deimel
|
||||
http://www.appjuice.org/
|
||||
|
||||
PuTTY 0.62
|
||||
Copyright <20> 1997-2011 Simon Tatham
|
||||
MIT License
|
||||
http://www.chiark.greenend.org.uk/~sgtatham/putty/
|
||||
|
||||
SharpSSH 1.1.1.13
|
||||
Copyright <20> 2002-2008 Atsuhiko Yamanaka, JCraft, Inc.
|
||||
Copyright <20> 2007 Tamir Gal
|
||||
BSD Style License
|
||||
http://www.tamirgal.com/blog/page/SharpSSH.aspx
|
||||
|
||||
Silk Icon Set
|
||||
Copyright <20> 2005-2008 FAMFAMFAM
|
||||
Creative Commons Attribution 2.5 License
|
||||
http://www.famfamfam.com/
|
||||
|
||||
Vista TaskDialog Wrapper and Emulator 1.0
|
||||
Copyright <20> 2007-2009 Hedley Muscroft
|
||||
Code Project Open License (CPOL)
|
||||
http://www.codeproject.com/KB/vista/Vista_TaskDialog_Wrapper.aspx
|
||||
|
||||
VncSharp 1.0
|
||||
Copyright <20> 2004-2009 David Humphrey
|
||||
GNU General Public License (GPL) Version 2
|
||||
http://cdot.senecac.on.ca/projects/vncsharp/
|
||||
127
CREDITS.md
Normal file
127
CREDITS.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# Contributors
|
||||
|
||||
## Current mRemoteNG dev team
|
||||
|
||||
[David Sparer](http://github.com/sparerd)
|
||||
[Sean Kaim](http://github.com/kmscode)
|
||||
[Faryan Rezagholi](http://github.com/farosch)
|
||||
[Bennett Blodinger](http://github.com/benwa)
|
||||
|
||||
[Joe Cefoli](http://github.com/jcefoli)
|
||||
[countchappy](http://github.com/countchappy)
|
||||
Tony Lambert
|
||||
[Julien Roncaglia](http://github.com/vbfox)
|
||||
[peterchenadded](http://github.com/peterchenadded)
|
||||
[Brandon Wulf](http://github.com/mrwulf)
|
||||
[Pedro Rodrigues](http://github.com/pedro2555)
|
||||
[dekelMP](http://github.com/dekelMP)
|
||||
[Bruce](http://github.com/brucetp)
|
||||
[Camilo Alvarez](http://github.com/jotatsu)
|
||||
[DamianBis](http://github.com/DamianBis)
|
||||
[pfjason](http://github.com/pfjason)
|
||||
[sirLoaf](http://github.com/sirLoaf)
|
||||
[Fyers](http://github.com/Fyers)
|
||||
[Vladimir Semenov](http://github.com/sli-pro)
|
||||
[Stephan](http://github.com/st-schuler)
|
||||
[Aleksey Reytsman](http://github.com/areytsman)
|
||||
[Cristian Abelleira](http://github.com/CrAbelleira)
|
||||
[MitchellBot](http://github.com/MitchellBot)
|
||||
[Filippo Ferrazini](http://github.com/Filippo125)
|
||||
|
||||
## Past Contributors
|
||||
|
||||
Felix Deimel - mRemote original developer
|
||||
Riley McArdle - mRemoteNG original developer
|
||||
|
||||
[Hayato Iriumi](http://github.com/hiriumi)
|
||||
Jason Barbier
|
||||
Wiktor Beryt
|
||||
Lionel Caignec
|
||||
Ruben d'Arco
|
||||
Holger Henke
|
||||
Tom Hiller
|
||||
Apisitt Rattana
|
||||
Andreas Rehm
|
||||
David Vidmar
|
||||
[Brandhor](http://github.com/Brandhor)
|
||||
[Dimitrij](http://github.com/Kvarkas)
|
||||
|
||||
## Translators
|
||||
|
||||
Eugenio "Ryo567" Martínez
|
||||
Mathieu Pape
|
||||
Emanuel Silva
|
||||
Robert Siwiec
|
||||
Hayato Iriumi
|
||||
[Sebastien Thieury](http://github.com/SebThieu)
|
||||
Riza Emet
|
||||
[Lukas Plachy](http://github.com/rheingold)
|
||||
Gyuha Shin
|
||||
[Stefan](http://github.com/polluks)
|
||||
[emazv72](http://github.com/emazv72)
|
||||
[Vladimir Semenov](http://github.com/sli-pro)
|
||||
[Marco Sousa](http://github.com/marcomsousa)
|
||||
[wwj402](http://github.com/wwj402)
|
||||
[Fyers](http://github.com/Fyers)
|
||||
[pablomh](http://github.com/pablomh)
|
||||
[Damian Szczepanik](http://github.com/damianszczepanik)
|
||||
[Mant1kor](http://github.com/Mant1kor)
|
||||
|
||||
# Included Source Code
|
||||
|
||||
**[Command Line Arguments Parser](http://www.codeproject.com/KB/recipes/command_line.aspx)**
|
||||
Copyright © 2002 Richard Lopes
|
||||
MIT License
|
||||
|
||||
**[FilteredPropertyGrid](http://www.codeproject.com/KB/cs/FilteredPropertyGrid.aspx)**
|
||||
Copyright © 2006 Azuria
|
||||
|
||||
**[InputBox](http://www.csharp-examples.net/inputbox/)**
|
||||
Copyright © 2016 Jan Slama
|
||||
|
||||
**[IP TextBox](http://www.codeproject.com/Articles/11576/IP-TextBox)**
|
||||
Copyright © 2005 mawnkay
|
||||
|
||||
**[PortableSettingsProvider](https://github.com/crdx/PortableSettingsProvider)**
|
||||
Copyright © 2014 crdx
|
||||
|
||||
**[ADTree](http://www.codeproject.com/KB/selection/ADPickerCtrl.aspx)**
|
||||
Copyright © 2004 Marc Merritt © 2008 Felix Deimel
|
||||
|
||||
# Included Components
|
||||
|
||||
**[CefSharp](https://github.com/cefsharp/CefSharp)**
|
||||
Copyright © The CefSharp Authors
|
||||
MIT License
|
||||
|
||||
**[DockPanel Suite](https://github.com/dockpanelsuite/dockpanelsuite)**
|
||||
Copyright © 2018 @roken and @lextm (formerly Weifen Luo)
|
||||
MIT License
|
||||
|
||||
**[log4net](http://logging.apache.org/log4net/)**
|
||||
Copyright © 2001-2015 The Apache Software Foundation
|
||||
Apache License Version 2.0
|
||||
|
||||
**[PuTTY](http://www.chiark.greenend.org.uk/~sgtatham/putty/)**
|
||||
Copyright © 1997-2017 Simon Tatham
|
||||
MIT License
|
||||
|
||||
**[Silk Icon Set](http://www.famfamfam.com/)**
|
||||
Copyright © 2005-2008 FAMFAMFAM
|
||||
Creative Commons Attribution 2.5 License
|
||||
|
||||
**[SSH.NET](https://github.com/sshnet/SSH.NET)**
|
||||
Copyright © 2016
|
||||
MIT License
|
||||
|
||||
**[VncSharp](https://github.com/humphd/VncSharp) (Archived)**
|
||||
Copyright © 2004-2009 David Humphrey
|
||||
GNU General Public License (GPL) Version 2
|
||||
|
||||
**[ObjectListView](https://sourceforge.net/projects/objectlistview/)**
|
||||
Copyright © 2006-2016 Phillip Piper
|
||||
GNU General Public License (GPL) Version 3
|
||||
|
||||
**[ConsoleControl](https://github.com/dwmkerr/consolecontrol)**
|
||||
Copyright © 2015 Dave Kerr
|
||||
MIT License
|
||||
180
ExternalConnectors/AWS/AWSConnectionForm.Designer.cs
generated
Normal file
180
ExternalConnectors/AWS/AWSConnectionForm.Designer.cs
generated
Normal file
@@ -0,0 +1,180 @@
|
||||
namespace ExternalConnectors.AWS
|
||||
{
|
||||
partial class AWSConnectionForm
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.tbAccesKeyID = new System.Windows.Forms.TextBox();
|
||||
this.tbAccesKey = new System.Windows.Forms.TextBox();
|
||||
this.btnOK = new System.Windows.Forms.Button();
|
||||
this.btnCancel = new System.Windows.Forms.Button();
|
||||
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel();
|
||||
this.tableLayoutPanel1.SuspendLayout();
|
||||
this.tableLayoutPanel2.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// tbAccesKeyID
|
||||
//
|
||||
this.tbAccesKeyID.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.tbAccesKeyID.Location = new System.Drawing.Point(152, 4);
|
||||
this.tbAccesKeyID.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
|
||||
this.tbAccesKeyID.Name = "tbAccesKeyID";
|
||||
this.tbAccesKeyID.Size = new System.Drawing.Size(490, 23);
|
||||
this.tbAccesKeyID.TabIndex = 0;
|
||||
//
|
||||
// tbAccesKey
|
||||
//
|
||||
this.tbAccesKey.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.tbAccesKey.Location = new System.Drawing.Point(152, 43);
|
||||
this.tbAccesKey.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
|
||||
this.tbAccesKey.Name = "tbAccesKey";
|
||||
this.tbAccesKey.Size = new System.Drawing.Size(490, 23);
|
||||
this.tbAccesKey.TabIndex = 2;
|
||||
//
|
||||
// btnOK
|
||||
//
|
||||
this.btnOK.Anchor = System.Windows.Forms.AnchorStyles.Right;
|
||||
this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK;
|
||||
this.btnOK.Location = new System.Drawing.Point(219, 12);
|
||||
this.btnOK.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
|
||||
this.btnOK.Name = "btnOK";
|
||||
this.btnOK.Size = new System.Drawing.Size(88, 26);
|
||||
this.btnOK.TabIndex = 10;
|
||||
this.btnOK.Text = "OK";
|
||||
this.btnOK.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// btnCancel
|
||||
//
|
||||
this.btnCancel.Anchor = System.Windows.Forms.AnchorStyles.Left;
|
||||
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
||||
this.btnCancel.Location = new System.Drawing.Point(338, 12);
|
||||
this.btnCancel.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
|
||||
this.btnCancel.Name = "btnCancel";
|
||||
this.btnCancel.Size = new System.Drawing.Size(88, 26);
|
||||
this.btnCancel.TabIndex = 11;
|
||||
this.btnCancel.Text = "Cancel";
|
||||
this.btnCancel.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// tableLayoutPanel1
|
||||
//
|
||||
this.tableLayoutPanel1.ColumnCount = 2;
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 22.92994F));
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 77.07006F));
|
||||
this.tableLayoutPanel1.Controls.Add(this.label1, 0, 0);
|
||||
this.tableLayoutPanel1.Controls.Add(this.label2, 0, 1);
|
||||
this.tableLayoutPanel1.Controls.Add(this.tbAccesKeyID, 1, 0);
|
||||
this.tableLayoutPanel1.Controls.Add(this.tbAccesKey, 1, 1);
|
||||
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Top;
|
||||
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
|
||||
this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
|
||||
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
|
||||
this.tableLayoutPanel1.RowCount = 3;
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 23F));
|
||||
this.tableLayoutPanel1.Size = new System.Drawing.Size(646, 102);
|
||||
this.tableLayoutPanel1.TabIndex = 12;
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.AutoSize = true;
|
||||
this.label1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.label1.Location = new System.Drawing.Point(4, 0);
|
||||
this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(140, 39);
|
||||
this.label1.TabIndex = 2;
|
||||
this.label1.Text = "Access Key ID";
|
||||
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// label2
|
||||
//
|
||||
this.label2.AutoSize = true;
|
||||
this.label2.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.label2.Location = new System.Drawing.Point(4, 39);
|
||||
this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(140, 39);
|
||||
this.label2.TabIndex = 4;
|
||||
this.label2.Text = "Access Key";
|
||||
this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// tableLayoutPanel2
|
||||
//
|
||||
this.tableLayoutPanel2.ColumnCount = 5;
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 93F));
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 23F));
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 93F));
|
||||
this.tableLayoutPanel2.Controls.Add(this.btnOK, 1, 0);
|
||||
this.tableLayoutPanel2.Controls.Add(this.btnCancel, 3, 0);
|
||||
this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Bottom;
|
||||
this.tableLayoutPanel2.Location = new System.Drawing.Point(0, 112);
|
||||
this.tableLayoutPanel2.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
|
||||
this.tableLayoutPanel2.Name = "tableLayoutPanel2";
|
||||
this.tableLayoutPanel2.RowCount = 1;
|
||||
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||
this.tableLayoutPanel2.Size = new System.Drawing.Size(646, 50);
|
||||
this.tableLayoutPanel2.TabIndex = 13;
|
||||
//
|
||||
// AWSConnectionForm
|
||||
//
|
||||
this.AcceptButton = this.btnOK;
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(646, 162);
|
||||
this.Controls.Add(this.tableLayoutPanel2);
|
||||
this.Controls.Add(this.tableLayoutPanel1);
|
||||
this.Margin = new System.Windows.Forms.Padding(4, 4, 4, 4);
|
||||
this.Name = "AWSConnectionForm";
|
||||
this.Text = "AWS EC2 API Login Data";
|
||||
this.Activated += new System.EventHandler(this.AWSConnectionForm_Activated);
|
||||
this.tableLayoutPanel1.ResumeLayout(false);
|
||||
this.tableLayoutPanel1.PerformLayout();
|
||||
this.tableLayoutPanel2.ResumeLayout(false);
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public System.Windows.Forms.TextBox tbAccesKeyID;
|
||||
public System.Windows.Forms.TextBox tbAccesKey;
|
||||
private System.Windows.Forms.Button btnOK;
|
||||
private System.Windows.Forms.Button btnCancel;
|
||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
|
||||
private System.Windows.Forms.Label label2;
|
||||
private System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2;
|
||||
}
|
||||
}
|
||||
16
ExternalConnectors/AWS/AWSConnectionForm.cs
Normal file
16
ExternalConnectors/AWS/AWSConnectionForm.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
namespace ExternalConnectors.AWS
|
||||
{
|
||||
public partial class AWSConnectionForm : Form
|
||||
{
|
||||
public AWSConnectionForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
}
|
||||
|
||||
private void AWSConnectionForm_Activated(object sender, EventArgs e)
|
||||
{
|
||||
tbAccesKeyID.Focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
60
ExternalConnectors/AWS/AWSConnectionForm.resx
Normal file
60
ExternalConnectors/AWS/AWSConnectionForm.resx
Normal file
@@ -0,0 +1,60 @@
|
||||
<root>
|
||||
<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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
119
ExternalConnectors/AWS/EC2FetchDataService.cs
Normal file
119
ExternalConnectors/AWS/EC2FetchDataService.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
using Amazon;
|
||||
using Amazon.EC2;
|
||||
using Amazon.EC2.Model;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace ExternalConnectors.AWS
|
||||
{
|
||||
public class EC2FetchDataService
|
||||
{
|
||||
private static DateTime lastFetch;
|
||||
private static List<InstanceInfo>? lastData;
|
||||
|
||||
// input must be in format "AWSAPI:instanceid" where instanceid is the ec2 instance id, e.g. i-066f750a76c97583d
|
||||
public static async Task<string> GetEC2InstanceDataAsync(string input, string region)
|
||||
{
|
||||
// get secret id
|
||||
if (!input.StartsWith("AWSAPI:"))
|
||||
throw new Exception("calling this function requires AWSAPI: input");
|
||||
string InstanceID = input[7..];
|
||||
|
||||
// init connection credentials, display popup if necessary
|
||||
AWSConnectionData.Init();
|
||||
var alldata = await GetEC2IPDataAsync(region);
|
||||
var found = alldata.Where(x => x.InstanceId == InstanceID).SingleOrDefault();
|
||||
return (found == null) ? "" : found.PublicIP;
|
||||
}
|
||||
|
||||
private static async Task<List<InstanceInfo>> GetEC2IPDataAsync(string region)
|
||||
{
|
||||
// caching
|
||||
TimeSpan timeSpan = DateTime.Now - lastFetch;
|
||||
if (timeSpan.TotalMinutes < 1 && lastData != null)
|
||||
return lastData;
|
||||
|
||||
//AWSConfigs.AWSRegion = AWSConnectionData.region;
|
||||
AWSConfigs.AWSRegion = region;
|
||||
string awsAccessKeyId = AWSConnectionData.awsKeyID;
|
||||
string awsSecretAccessKey = AWSConnectionData.awsKey;
|
||||
|
||||
var _client = new AmazonEC2Client(awsAccessKeyId, awsSecretAccessKey, RegionEndpoint.EUCentral1);
|
||||
bool done = false;
|
||||
|
||||
List<InstanceInfo> instanceList = new();
|
||||
var request = new DescribeInstancesRequest();
|
||||
while (!done)
|
||||
{
|
||||
DescribeInstancesResponse response = await _client.DescribeInstancesAsync(request);
|
||||
|
||||
foreach (var reservation in response.Reservations)
|
||||
{
|
||||
foreach (var instance in reservation.Instances)
|
||||
{
|
||||
string vmname = "";
|
||||
foreach (var tag in instance.Tags)
|
||||
{
|
||||
if (tag.Key == "Name")
|
||||
{
|
||||
vmname = tag.Value;
|
||||
}
|
||||
}
|
||||
InstanceInfo inf = new(instance, vmname);
|
||||
instanceList.Add(inf);
|
||||
}
|
||||
}
|
||||
|
||||
request.NextToken = response.NextToken;
|
||||
|
||||
if (response.NextToken == null)
|
||||
{
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
lastData = instanceList.OrderBy(x => x.Name).ToList();
|
||||
lastFetch = DateTime.Now;
|
||||
return lastData;
|
||||
}
|
||||
|
||||
|
||||
public static class AWSConnectionData
|
||||
{
|
||||
private static readonly RegistryKey key = Registry.CurrentUser.CreateSubKey(@"SOFTWARE\mRemoteAWSInterface");
|
||||
|
||||
public static string awsKeyID = "";
|
||||
public static string awsKey = "";
|
||||
//public static string _region = "eu-central-1";
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
if (awsKey != "")
|
||||
return;
|
||||
// display gui and ask for data
|
||||
AWSConnectionForm f = new();
|
||||
f.tbAccesKeyID.Text = "" + key.GetValue("KeyID");
|
||||
f.tbAccesKey.Text = "" + key.GetValue("Key");
|
||||
//f.tbRegion.Text = "" + key.GetValue("Region");
|
||||
//if (f.tbRegion.Text == null || f.tbRegion.Text.Length < 2)
|
||||
// f.tbRegion.Text = region;
|
||||
_ = f.ShowDialog();
|
||||
|
||||
if (f.DialogResult != DialogResult.OK)
|
||||
return;
|
||||
|
||||
// store values to memory
|
||||
awsKeyID = f.tbAccesKeyID.Text;
|
||||
awsKey = f.tbAccesKey.Text;
|
||||
//region = f.tbRegion.Text;
|
||||
|
||||
|
||||
// write values to registry
|
||||
key.SetValue("KeyID", awsKeyID);
|
||||
key.SetValue("Key", awsKey);
|
||||
//key.SetValue("Region", region);
|
||||
key.Close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
34
ExternalConnectors/AWS/InstanceInfo.cs
Normal file
34
ExternalConnectors/AWS/InstanceInfo.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using Amazon.EC2.Model;
|
||||
using System;
|
||||
|
||||
namespace ExternalConnectors.AWS
|
||||
{
|
||||
public class InstanceInfo
|
||||
{
|
||||
public string InstanceId { get; }
|
||||
public string Name { get; }
|
||||
public string Status { get; }
|
||||
public string PublicIP { get; }
|
||||
public string PrivateIP { get; }
|
||||
public InstanceInfo(Instance instance, string name)
|
||||
{
|
||||
InstanceId = instance.InstanceId;
|
||||
Name = name;
|
||||
|
||||
switch(instance.State.Code)
|
||||
{
|
||||
case 0: Status = "Pending"; break;
|
||||
case 16: Status = "Running"; break;
|
||||
case 32: Status = "Shutdown"; break;
|
||||
case 48: Status = "Terminated"; break;
|
||||
case 64: Status = "Stopping"; break;
|
||||
case 80: Status = "Stopped"; break;
|
||||
default: Status = "Unknown"; break;
|
||||
};
|
||||
|
||||
PublicIP = instance.PublicIpAddress ?? "";
|
||||
PrivateIP = instance.PrivateIpAddress ?? "";
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
ExternalConnectors/CPS/CPS.ico
Normal file
BIN
ExternalConnectors/CPS/CPS.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
241
ExternalConnectors/CPS/CPSConnectionForm.Designer.cs
generated
Normal file
241
ExternalConnectors/CPS/CPSConnectionForm.Designer.cs
generated
Normal file
@@ -0,0 +1,241 @@
|
||||
namespace ExternalConnectors.CPS
|
||||
{
|
||||
partial class CPSConnectionForm
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(CPSConnectionForm));
|
||||
tbServerURL = new TextBox();
|
||||
label3 = new Label();
|
||||
tbAPIKey = new TextBox();
|
||||
btnOK = new Button();
|
||||
btnCancel = new Button();
|
||||
tableLayoutPanel1 = new TableLayoutPanel();
|
||||
label1 = new Label();
|
||||
label6 = new Label();
|
||||
tbOTP = new TextBox();
|
||||
cbUseSSO = new CheckBox();
|
||||
tableLayoutPanel2 = new TableLayoutPanel();
|
||||
label4 = new Label();
|
||||
tableLayoutPanel1.SuspendLayout();
|
||||
tableLayoutPanel2.SuspendLayout();
|
||||
SuspendLayout();
|
||||
//
|
||||
// tbServerURL
|
||||
//
|
||||
tbServerURL.Dock = DockStyle.Fill;
|
||||
tbServerURL.Location = new Point(298, 5);
|
||||
tbServerURL.Margin = new Padding(5);
|
||||
tbServerURL.Name = "tbServerURL";
|
||||
tbServerURL.Size = new Size(611, 27);
|
||||
tbServerURL.TabIndex = 0;
|
||||
//
|
||||
// label3
|
||||
//
|
||||
label3.AutoSize = true;
|
||||
label3.Dock = DockStyle.Fill;
|
||||
label3.Location = new Point(5, 84);
|
||||
label3.Margin = new Padding(5, 0, 5, 0);
|
||||
label3.Name = "label3";
|
||||
label3.Size = new Size(283, 42);
|
||||
label3.TabIndex = 5;
|
||||
label3.Text = "API Key";
|
||||
label3.TextAlign = ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// tbAPIKey
|
||||
//
|
||||
tbAPIKey.Dock = DockStyle.Fill;
|
||||
tbAPIKey.Location = new Point(298, 89);
|
||||
tbAPIKey.Margin = new Padding(5);
|
||||
tbAPIKey.Name = "tbAPIKey";
|
||||
tbAPIKey.Size = new Size(611, 27);
|
||||
tbAPIKey.TabIndex = 4;
|
||||
tbAPIKey.UseSystemPasswordChar = true;
|
||||
//
|
||||
// btnOK
|
||||
//
|
||||
btnOK.Anchor = AnchorStyles.Right;
|
||||
btnOK.DialogResult = DialogResult.OK;
|
||||
btnOK.Location = new Point(337, 16);
|
||||
btnOK.Margin = new Padding(5);
|
||||
btnOK.Name = "btnOK";
|
||||
btnOK.Size = new Size(101, 35);
|
||||
btnOK.TabIndex = 6;
|
||||
btnOK.Text = "OK";
|
||||
btnOK.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// btnCancel
|
||||
//
|
||||
btnCancel.Anchor = AnchorStyles.Left;
|
||||
btnCancel.DialogResult = DialogResult.Cancel;
|
||||
btnCancel.Location = new Point(474, 16);
|
||||
btnCancel.Margin = new Padding(5);
|
||||
btnCancel.Name = "btnCancel";
|
||||
btnCancel.Size = new Size(101, 35);
|
||||
btnCancel.TabIndex = 11;
|
||||
btnCancel.Text = "Cancel";
|
||||
btnCancel.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// tableLayoutPanel1
|
||||
//
|
||||
tableLayoutPanel1.ColumnCount = 2;
|
||||
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 32.06997F));
|
||||
tableLayoutPanel1.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 67.93003F));
|
||||
tableLayoutPanel1.Controls.Add(label1, 0, 0);
|
||||
tableLayoutPanel1.Controls.Add(label3, 0, 2);
|
||||
tableLayoutPanel1.Controls.Add(tbServerURL, 1, 0);
|
||||
tableLayoutPanel1.Controls.Add(tbAPIKey, 1, 2);
|
||||
tableLayoutPanel1.Controls.Add(label6, 0, 3);
|
||||
tableLayoutPanel1.Controls.Add(tbOTP, 1, 3);
|
||||
tableLayoutPanel1.Controls.Add(cbUseSSO, 0, 1);
|
||||
tableLayoutPanel1.Dock = DockStyle.Top;
|
||||
tableLayoutPanel1.Location = new Point(0, 0);
|
||||
tableLayoutPanel1.Margin = new Padding(5);
|
||||
tableLayoutPanel1.Name = "tableLayoutPanel1";
|
||||
tableLayoutPanel1.RowCount = 5;
|
||||
tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Percent, 20F));
|
||||
tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Percent, 20F));
|
||||
tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Percent, 20F));
|
||||
tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Percent, 20F));
|
||||
tableLayoutPanel1.RowStyles.Add(new RowStyle(SizeType.Percent, 20F));
|
||||
tableLayoutPanel1.Size = new Size(914, 212);
|
||||
tableLayoutPanel1.TabIndex = 12;
|
||||
//
|
||||
// label1
|
||||
//
|
||||
label1.AutoSize = true;
|
||||
label1.Dock = DockStyle.Fill;
|
||||
label1.Location = new Point(5, 0);
|
||||
label1.Margin = new Padding(5, 0, 5, 0);
|
||||
label1.Name = "label1";
|
||||
label1.Size = new Size(283, 42);
|
||||
label1.TabIndex = 2;
|
||||
label1.Text = "Passwordstate URL";
|
||||
label1.TextAlign = ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// label6
|
||||
//
|
||||
label6.AutoSize = true;
|
||||
label6.Dock = DockStyle.Fill;
|
||||
label6.Location = new Point(3, 126);
|
||||
label6.Name = "label6";
|
||||
label6.Size = new Size(287, 42);
|
||||
label6.TabIndex = 15;
|
||||
label6.Text = "2FA OTP (Optional)";
|
||||
//
|
||||
// tbOTP
|
||||
//
|
||||
tbOTP.Dock = DockStyle.Fill;
|
||||
tbOTP.Location = new Point(298, 131);
|
||||
tbOTP.Margin = new Padding(5);
|
||||
tbOTP.Name = "tbOTP";
|
||||
tbOTP.Size = new Size(611, 27);
|
||||
tbOTP.TabIndex = 5;
|
||||
//
|
||||
// cbUseSSO
|
||||
//
|
||||
cbUseSSO.Anchor = AnchorStyles.Left;
|
||||
cbUseSSO.AutoSize = true;
|
||||
cbUseSSO.Location = new Point(5, 53);
|
||||
cbUseSSO.Margin = new Padding(5, 5, 5, 0);
|
||||
cbUseSSO.Name = "cbUseSSO";
|
||||
cbUseSSO.Size = new Size(157, 24);
|
||||
cbUseSSO.TabIndex = 14;
|
||||
cbUseSSO.Text = "Use SSO / WinAuth";
|
||||
cbUseSSO.UseVisualStyleBackColor = true;
|
||||
cbUseSSO.CheckedChanged += cbUseSSO_CheckedChanged;
|
||||
//
|
||||
// tableLayoutPanel2
|
||||
//
|
||||
tableLayoutPanel2.ColumnCount = 5;
|
||||
tableLayoutPanel2.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 106F));
|
||||
tableLayoutPanel2.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F));
|
||||
tableLayoutPanel2.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 26F));
|
||||
tableLayoutPanel2.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F));
|
||||
tableLayoutPanel2.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 107F));
|
||||
tableLayoutPanel2.Controls.Add(btnOK, 1, 0);
|
||||
tableLayoutPanel2.Controls.Add(btnCancel, 3, 0);
|
||||
tableLayoutPanel2.Dock = DockStyle.Bottom;
|
||||
tableLayoutPanel2.Location = new Point(0, 300);
|
||||
tableLayoutPanel2.Margin = new Padding(5);
|
||||
tableLayoutPanel2.Name = "tableLayoutPanel2";
|
||||
tableLayoutPanel2.RowCount = 1;
|
||||
tableLayoutPanel2.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
|
||||
tableLayoutPanel2.Size = new Size(914, 67);
|
||||
tableLayoutPanel2.TabIndex = 13;
|
||||
//
|
||||
// label4
|
||||
//
|
||||
label4.AutoSize = true;
|
||||
label4.Dock = DockStyle.Fill;
|
||||
label4.Location = new Point(0, 212);
|
||||
label4.Margin = new Padding(5, 0, 5, 0);
|
||||
label4.Name = "label4";
|
||||
label4.Size = new Size(345, 20);
|
||||
label4.TabIndex = 14;
|
||||
label4.Text = "URL is the base URL, like https://pass.domain.local/";
|
||||
label4.TextAlign = ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// CPSConnectionForm
|
||||
//
|
||||
AcceptButton = btnOK;
|
||||
AutoScaleDimensions = new SizeF(8F, 20F);
|
||||
AutoScaleMode = AutoScaleMode.Font;
|
||||
ClientSize = new Size(914, 367);
|
||||
Controls.Add(label4);
|
||||
Controls.Add(tableLayoutPanel2);
|
||||
Controls.Add(tableLayoutPanel1);
|
||||
Icon = (Icon)resources.GetObject("$this.Icon");
|
||||
Margin = new Padding(5);
|
||||
Name = "CPSConnectionForm";
|
||||
Text = "Passwordstate API Login Data";
|
||||
Activated += CPSConnectionForm_Activated;
|
||||
tableLayoutPanel1.ResumeLayout(false);
|
||||
tableLayoutPanel1.PerformLayout();
|
||||
tableLayoutPanel2.ResumeLayout(false);
|
||||
ResumeLayout(false);
|
||||
PerformLayout();
|
||||
}
|
||||
|
||||
#endregion
|
||||
private System.Windows.Forms.Label label3;
|
||||
|
||||
public System.Windows.Forms.TextBox tbServerURL;
|
||||
//public System.Windows.Forms.TextBox tbUsername;
|
||||
public System.Windows.Forms.TextBox tbAPIKey;
|
||||
private System.Windows.Forms.Button btnOK;
|
||||
private System.Windows.Forms.Button btnCancel;
|
||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
|
||||
private System.Windows.Forms.Label label2;
|
||||
private System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2;
|
||||
public System.Windows.Forms.CheckBox cbUseSSO;
|
||||
private System.Windows.Forms.Label label4;
|
||||
private Label label6;
|
||||
public TextBox tbOTP;
|
||||
}
|
||||
}
|
||||
41
ExternalConnectors/CPS/CPSConnectionForm.cs
Normal file
41
ExternalConnectors/CPS/CPSConnectionForm.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
namespace ExternalConnectors.CPS
|
||||
{
|
||||
public partial class CPSConnectionForm : Form
|
||||
{
|
||||
public CPSConnectionForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void CPSConnectionForm_Activated(object sender, EventArgs e)
|
||||
{
|
||||
SetVisibility();
|
||||
if (cbUseSSO.Checked)
|
||||
btnOK.Focus();
|
||||
else
|
||||
{
|
||||
if (tbAPIKey.Text.Length == 0)
|
||||
tbAPIKey.Focus();
|
||||
else
|
||||
tbOTP.Focus();
|
||||
}
|
||||
|
||||
tbAPIKey.Focus();
|
||||
if (!string.IsNullOrEmpty(tbAPIKey.Text) || cbUseSSO.Checked == true)
|
||||
tbOTP.Focus();
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void cbUseSSO_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
SetVisibility();
|
||||
}
|
||||
private void SetVisibility()
|
||||
{
|
||||
bool ch = cbUseSSO.Checked;
|
||||
tbAPIKey.Enabled = !ch;
|
||||
//tbUsername.Enabled = !ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?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
|
||||
|
||||
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>
|
||||
@@ -26,36 +26,36 @@
|
||||
<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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
@@ -112,43 +112,38 @@
|
||||
<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>
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.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>
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="cMenApps.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
<metadata name="dlgOpenFile.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>119, 17</value>
|
||||
</metadata>
|
||||
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>54</value>
|
||||
</metadata>
|
||||
<assembly alias="System.Drawing" name="System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAAAEAAAAAAAAAAAAAAAEAAAAA
|
||||
AAAtLDAA+PDaAP///wD79ugA/PrzAPf39wBGRUkA7NacAM2WAAA3z6kA+Pz/AIKBgwD+//4A4sNtAHXe
|
||||
xAD8+vIAjuTOANOjHgDV6/4AJZf3APn5+QDw37IAIB8jAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAgXdub5CEe9KShHvfjoF134t9cd+HeGzfg3Ro335vZN95bGDfdWhc33BjWd9tYFXfaFxS32VZ
|
||||
UN9gVEvSVEtDQJySifJRUVH/UlJS/1NTU/9UVFT/VVVV/1ZWVv9WVlb/V1dX/1hYWP9ZWVn/Wlpa/1tb
|
||||
W/9bW1v/XFxc/2BVS4yonpX/TU1N/zw8PP88PDz/PT09/z4+Pv9AQED/QUFB/0JCQv9DQ0P/RERE/0VF
|
||||
Rf9FRUX/R0dH/1lZWf9oXFKUraOb/0pKSv83Nzf/ODg4/zk5Of87Ozv/PDw8/z4+Pv8+Pj7/QEBA/0FB
|
||||
Qf9BQUH/Q0ND/0RERP9WVlb/bWBVlLGooP9GRkb/MjIy/zMzM/81NTX/NjY2/zg4OP85OTn/Ojo6/zs7
|
||||
O/89PT3/Pj4+/z8/P/9AQED/VFRU/3JlWpS1rab/QUFB/y4uLv8vLy//MDAw/zIyMv8zMzP/NTU1/zU1
|
||||
Nf83Nzf/OTk5/zo6Ov88PDz/PDw8/1FRUf93al6UubGr/zw8PP8oKCj/0dHR/6ysrP8tLS3/Li4u/zAw
|
||||
MP8xMTH/MzMz/zU1Nf82Njb/Nzc3/zk5Of9NTU3/fW5ilL22sf84ODj/JCQk/yUlJf/e3t7/c3Nz/ykp
|
||||
Kf8rKyv/LCws/y4uLv8wMDD/MTEx/zMzM/80NDT/SkpK/4R1Z5TAubT/MzMz/x8fH//Pz8//p6en/yMj
|
||||
I/8kJCT/JSUl/ycnJ/8pKSn/Kysr/ywsLP8uLi7/MDAw/0VFRf+Kem2Uwr24/zMzM/8fHx//ICAg/yEh
|
||||
If8jIyP/JCQk/yUlJf8nJyf/KSkp/ysrK/8sLCz/Li4u/zAwMP9FRUX/jn5ylMS/uf8wMDD/MTEx/zIy
|
||||
Mv8zMzP/NDQ0/zU1Nf82Njb/ODg4/zo6Ov87Ozv/PDw8/z4+Pv9AQED/QkJC/35vY5TJwr7/t7rx/5Pk
|
||||
9v+x5bP/7enm/+3p5v/t6eb/7enm/+3p5v/t6eb/7enm/+3p5v/t6eb/7enm//X08v+FdmiUwr66/klO
|
||||
0/8WrOL/Pa9B/+Hb1v/h29b/4dvW/+Hb1v/h29b/4dvW/+Hb1v/h29b/4dvW/+Hc1//t6uf/inptk6mi
|
||||
nZnCvrr6yMO+/8jBvf/Ev7n/wLm0/7y1sP+4sar/tKuk/6+mnv+qn5f/pZqR/6CUiv+bjoT/k4Z7+n1y
|
||||
am4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAA//+cQQAAnEEAAJxBAACcQQAAnEEAAJxBAACcQQAAnEEAAJxBAACcQQAAnEEAAJxBAACcQQAA
|
||||
nEEAAJxB//+cQQ==
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAgICAgICCQ4CAgIWAgIUAgICAgICAgkJAgICFhYWAAIIDwICAgICCQwCAhYWFgYCCAgIBwIC
|
||||
AgkJAgsWFhYWBQICCAgIAwIJCQIWFhYWAgICAgINCAgCAgICFhYCAgICAgICAgIRAgICAgICAgICAgIC
|
||||
AgICEwICAgICEhMTExMCAgITExMCAgICAgITExMTAhMTExMCAgICAgICAgICAhMTEwICAgIICAIJCQIC
|
||||
AgIKAgICAgIICAQCAgkJAgICAgICAgICCAgCAgICCQkCAgICAgICFQgBAgICAgwJAgICAgICAggIAgIC
|
||||
AgICEAkCAgICAgIIAgICAgICAgICAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
|
||||
</value>
|
||||
</data>
|
||||
</root>
|
||||
301
ExternalConnectors/CPS/PasswordstateInterface.cs
Normal file
301
ExternalConnectors/CPS/PasswordstateInterface.cs
Normal file
@@ -0,0 +1,301 @@
|
||||
using Microsoft.Win32;
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.OpenSsl;
|
||||
using Org.BouncyCastle.Security;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace ExternalConnectors.CPS;
|
||||
|
||||
public class PasswordstateInterface
|
||||
{
|
||||
private static class CPSConnectionData
|
||||
{
|
||||
public static string ssUsername = "";
|
||||
public static string ssPassword = "";
|
||||
public static string ssUrl = "";
|
||||
public static string ssOTP = "";
|
||||
public static DateTime ssOTPTimeStampExpiration;
|
||||
|
||||
public static bool ssSSO = false;
|
||||
public static bool initdone = false;
|
||||
|
||||
//token
|
||||
//public static string ssTokenBearer = "";
|
||||
//public static DateTime ssTokenExpiresOn = DateTime.UtcNow;
|
||||
//public static string ssTokenRefresh = "";
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
// 2024-05-04 passwordstate currently does not support auth tokens, so we need to re-enter otp codes frequently
|
||||
if (!string.IsNullOrEmpty(ssOTP) && DateTime.Now > ssOTPTimeStampExpiration)
|
||||
{
|
||||
ssOTP = "";
|
||||
initdone = false;
|
||||
}
|
||||
|
||||
if (initdone == true)
|
||||
return;
|
||||
|
||||
RegistryKey key = Registry.CurrentUser.CreateSubKey(@"SOFTWARE\mRemoteCPSInterface");
|
||||
try
|
||||
{
|
||||
// display gui and ask for data
|
||||
CPSConnectionForm f = new CPSConnectionForm();
|
||||
//string? un = key.GetValue("Username") as string;
|
||||
//f.tbUsername.Text = un ?? "";
|
||||
f.tbAPIKey.Text = CPSConnectionData.ssPassword; // in OTP refresh cases, this value might already be filled
|
||||
|
||||
string? url = key.GetValue("URL") as string;
|
||||
if (url == null || !url.Contains("://"))
|
||||
url = "https://cred.domain.local/SecretServer";
|
||||
f.tbServerURL.Text = url;
|
||||
|
||||
var b = key.GetValue("SSO");
|
||||
if (b == null || (string)b != "True")
|
||||
ssSSO = false;
|
||||
else
|
||||
ssSSO = true;
|
||||
f.cbUseSSO.Checked = ssSSO;
|
||||
|
||||
// show dialog
|
||||
while (true)
|
||||
{
|
||||
_ = f.ShowDialog();
|
||||
|
||||
if (f.DialogResult != DialogResult.OK)
|
||||
return;
|
||||
|
||||
// store values to memory
|
||||
//ssUsername = f.tbUsername.Text;
|
||||
ssPassword = f.tbAPIKey.Text;
|
||||
ssUrl = f.tbServerURL.Text;
|
||||
ssSSO = f.cbUseSSO.Checked;
|
||||
ssOTP = f.tbOTP.Text;
|
||||
ssOTPTimeStampExpiration = DateTime.Now.AddSeconds(30);
|
||||
// check connection first
|
||||
try
|
||||
{
|
||||
if (TestCredentials() == true)
|
||||
{
|
||||
initdone = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
MessageBox.Show("Test Credentials failed - please check your credentials");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// write values to registry
|
||||
//key.SetValue("Username", ssUsername);
|
||||
key.SetValue("URL", ssUrl);
|
||||
key.SetValue("SSO", ssSSO);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
key.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TestCredentials()
|
||||
{
|
||||
return ConnectionTest();
|
||||
}
|
||||
private static bool ConnectionTest()
|
||||
{
|
||||
if (CPSConnectionData.ssSSO)
|
||||
{
|
||||
string url = $"{CPSConnectionData.ssUrl}/winapi/passwordlists/";
|
||||
|
||||
using HttpClient client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true });
|
||||
client.DefaultRequestHeaders.Accept.Clear();
|
||||
client.DefaultRequestHeaders.Add("User-Agent", "mRemote");
|
||||
client.DefaultRequestHeaders.Add("OTP", CPSConnectionData.ssOTP);
|
||||
|
||||
var json = client.GetStringAsync(url).Result;
|
||||
JsonNode? data = JsonSerializer.Deserialize<JsonNode>(json);
|
||||
if (data == null)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
string url = $"{CPSConnectionData.ssUrl}/api/passwordlists/";
|
||||
using HttpClient client = new HttpClient();
|
||||
client.DefaultRequestHeaders.Accept.Clear();
|
||||
client.DefaultRequestHeaders.Add("User-Agent", "mRemote");
|
||||
client.DefaultRequestHeaders.Add("APIKey", CPSConnectionData.ssPassword);
|
||||
client.DefaultRequestHeaders.Add("OTP", CPSConnectionData.ssOTP);
|
||||
|
||||
var json = client.GetStringAsync(url).Result;
|
||||
JsonNode? data = JsonSerializer.Deserialize<JsonNode>(json);
|
||||
if (data == null)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static JsonNode? FetchDataWinAuth(int secretID)
|
||||
{
|
||||
string url = $"{CPSConnectionData.ssUrl}/winapi/passwords/{secretID}";
|
||||
|
||||
using HttpClient client = new HttpClient(new HttpClientHandler() { UseDefaultCredentials = true });
|
||||
client.DefaultRequestHeaders.Accept.Clear();
|
||||
client.DefaultRequestHeaders.Add("User-Agent", "mRemote");
|
||||
client.DefaultRequestHeaders.Add("OTP", CPSConnectionData.ssOTP);
|
||||
|
||||
var json = client.GetStringAsync(url).Result;
|
||||
JsonNode? data = JsonSerializer.Deserialize<JsonNode>(json);
|
||||
if (data == null)
|
||||
return null;
|
||||
JsonNode? element = data[0];
|
||||
return element;
|
||||
}
|
||||
private static JsonNode? FetchDataAPIKeyAuth(int secretID)
|
||||
{
|
||||
string url = $"{CPSConnectionData.ssUrl}/api/passwords/{secretID}";
|
||||
|
||||
using HttpClient client = new HttpClient();
|
||||
client.DefaultRequestHeaders.Accept.Clear();
|
||||
client.DefaultRequestHeaders.Add("User-Agent", "mRemote");
|
||||
client.DefaultRequestHeaders.Add("APIKey", CPSConnectionData.ssPassword);
|
||||
client.DefaultRequestHeaders.Add("OTP", CPSConnectionData.ssOTP);
|
||||
|
||||
var json = client.GetStringAsync(url).Result;
|
||||
JsonNode? data = JsonSerializer.Deserialize<JsonNode>(json);
|
||||
if (data == null)
|
||||
return null;
|
||||
JsonNode? element = data[0];
|
||||
return element;
|
||||
}
|
||||
|
||||
private static void FetchSecret(int secretID, out string secretUsername, out string secretPassword, out string secretDomain, out string privatekey)
|
||||
{
|
||||
// clear return variables
|
||||
secretDomain = "";
|
||||
secretUsername = "";
|
||||
secretPassword = "";
|
||||
privatekey = "";
|
||||
string privatekeypassphrase = "";
|
||||
JsonNode? element = null;
|
||||
|
||||
if (CPSConnectionData.ssSSO)
|
||||
element = FetchDataWinAuth(secretID);
|
||||
else
|
||||
element = FetchDataAPIKeyAuth(secretID);
|
||||
|
||||
if (element == null)
|
||||
return;
|
||||
|
||||
var dom = element["Domain"];
|
||||
if (dom != null) secretDomain = dom.ToString();
|
||||
|
||||
var user = element["UserName"];
|
||||
if (user != null) secretUsername = user.ToString();
|
||||
|
||||
var pw = element["Password"];
|
||||
if (pw != null) secretPassword = pw.ToString();
|
||||
|
||||
var privkey = element["GenericField1"];
|
||||
if (privkey != null) privatekey = privkey.ToString();
|
||||
|
||||
var phrase = element["GenericField3"];
|
||||
if (phrase != null) privatekeypassphrase = phrase.ToString();
|
||||
|
||||
// need to decode the private key?
|
||||
if (!string.IsNullOrEmpty(privatekeypassphrase))
|
||||
{
|
||||
try
|
||||
{
|
||||
var key = DecodePrivateKey(privatekey, privatekeypassphrase);
|
||||
privatekey = key;
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// conversion to putty format necessary?
|
||||
if (!string.IsNullOrEmpty(privatekey) && !privatekey.StartsWith("PuTTY-User-Key-File-2"))
|
||||
{
|
||||
try
|
||||
{
|
||||
RSACryptoServiceProvider key = ImportPrivateKey(privatekey);
|
||||
privatekey = PuttyKeyFileGenerator.ToPuttyPrivateKey(key);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region PUTTY KEY HANDLING
|
||||
// decode rsa private key with encryption password
|
||||
private static string DecodePrivateKey(string encryptedPrivateKey, string password)
|
||||
{
|
||||
TextReader textReader = new StringReader(encryptedPrivateKey);
|
||||
PemReader pemReader = new PemReader(textReader, new PasswordFinder(password));
|
||||
|
||||
AsymmetricCipherKeyPair keyPair = (AsymmetricCipherKeyPair)pemReader.ReadObject();
|
||||
|
||||
TextWriter textWriter = new StringWriter();
|
||||
var pemWriter = new PemWriter(textWriter);
|
||||
pemWriter.WriteObject(keyPair.Private);
|
||||
pemWriter.Writer.Flush();
|
||||
|
||||
return ""+textWriter.ToString();
|
||||
}
|
||||
private class PasswordFinder : IPasswordFinder
|
||||
{
|
||||
private string password;
|
||||
|
||||
public PasswordFinder(string password)
|
||||
{
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
|
||||
public char[] GetPassword()
|
||||
{
|
||||
return password.ToCharArray();
|
||||
}
|
||||
}
|
||||
|
||||
// read private key pem string to rsacryptoserviceprovider
|
||||
public static RSACryptoServiceProvider ImportPrivateKey(string pem)
|
||||
{
|
||||
PemReader pr = new PemReader(new StringReader(pem));
|
||||
AsymmetricCipherKeyPair KeyPair = (AsymmetricCipherKeyPair)pr.ReadObject();
|
||||
RSAParameters rsaParams = DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)KeyPair.Private);
|
||||
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
|
||||
rsa.ImportParameters(rsaParams);
|
||||
return rsa;
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
// input: must be the secret id to fetch
|
||||
public static void FetchSecretFromServer(string secretID, out string username, out string password, out string domain, out string privatekey)
|
||||
{
|
||||
// get secret id
|
||||
int sid = Int32.Parse(secretID);
|
||||
|
||||
// init connection credentials, display popup if necessary
|
||||
CPSConnectionData.Init();
|
||||
|
||||
// get the secret
|
||||
FetchSecret(sid, out username, out password, out domain, out privatekey);
|
||||
}
|
||||
}
|
||||
BIN
ExternalConnectors/DSS/DSS.ico
Normal file
BIN
ExternalConnectors/DSS/DSS.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
281
ExternalConnectors/DSS/SSConnectionForm.Designer.cs
generated
Normal file
281
ExternalConnectors/DSS/SSConnectionForm.Designer.cs
generated
Normal file
@@ -0,0 +1,281 @@
|
||||
namespace ExternalConnectors.DSS
|
||||
{
|
||||
partial class SSConnectionForm
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SSConnectionForm));
|
||||
this.tbSSURL = new System.Windows.Forms.TextBox();
|
||||
this.tbUsername = new System.Windows.Forms.TextBox();
|
||||
this.label3 = new System.Windows.Forms.Label();
|
||||
this.tbPassword = new System.Windows.Forms.TextBox();
|
||||
this.btnOK = new System.Windows.Forms.Button();
|
||||
this.btnCancel = new System.Windows.Forms.Button();
|
||||
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
|
||||
this.label5 = new System.Windows.Forms.Label();
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.cbUseSSO = new System.Windows.Forms.CheckBox();
|
||||
this.label6 = new System.Windows.Forms.Label();
|
||||
this.tbOTP = new System.Windows.Forms.TextBox();
|
||||
this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel();
|
||||
this.label4 = new System.Windows.Forms.Label();
|
||||
this.tableLayoutPanel1.SuspendLayout();
|
||||
this.tableLayoutPanel2.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// tbSSURL
|
||||
//
|
||||
this.tbSSURL.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.tbSSURL.Location = new System.Drawing.Point(298, 5);
|
||||
this.tbSSURL.Margin = new System.Windows.Forms.Padding(5, 5, 5, 5);
|
||||
this.tbSSURL.Name = "tbSSURL";
|
||||
this.tbSSURL.Size = new System.Drawing.Size(611, 27);
|
||||
this.tbSSURL.TabIndex = 0;
|
||||
//
|
||||
// tbUsername
|
||||
//
|
||||
this.tbUsername.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.tbUsername.Location = new System.Drawing.Point(298, 47);
|
||||
this.tbUsername.Margin = new System.Windows.Forms.Padding(5, 5, 5, 5);
|
||||
this.tbUsername.Name = "tbUsername";
|
||||
this.tbUsername.Size = new System.Drawing.Size(611, 27);
|
||||
this.tbUsername.TabIndex = 2;
|
||||
//
|
||||
// label3
|
||||
//
|
||||
this.label3.AutoSize = true;
|
||||
this.label3.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.label3.Location = new System.Drawing.Point(5, 84);
|
||||
this.label3.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0);
|
||||
this.label3.Name = "label3";
|
||||
this.label3.Size = new System.Drawing.Size(283, 42);
|
||||
this.label3.TabIndex = 5;
|
||||
this.label3.Text = "Password";
|
||||
this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// tbPassword
|
||||
//
|
||||
this.tbPassword.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.tbPassword.Location = new System.Drawing.Point(298, 89);
|
||||
this.tbPassword.Margin = new System.Windows.Forms.Padding(5, 5, 5, 5);
|
||||
this.tbPassword.Name = "tbPassword";
|
||||
this.tbPassword.Size = new System.Drawing.Size(611, 27);
|
||||
this.tbPassword.TabIndex = 4;
|
||||
this.tbPassword.UseSystemPasswordChar = true;
|
||||
//
|
||||
// btnOK
|
||||
//
|
||||
this.btnOK.Anchor = System.Windows.Forms.AnchorStyles.Right;
|
||||
this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK;
|
||||
this.btnOK.Location = new System.Drawing.Point(337, 16);
|
||||
this.btnOK.Margin = new System.Windows.Forms.Padding(5, 5, 5, 5);
|
||||
this.btnOK.Name = "btnOK";
|
||||
this.btnOK.Size = new System.Drawing.Size(101, 35);
|
||||
this.btnOK.TabIndex = 6;
|
||||
this.btnOK.Text = "OK";
|
||||
this.btnOK.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// btnCancel
|
||||
//
|
||||
this.btnCancel.Anchor = System.Windows.Forms.AnchorStyles.Left;
|
||||
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
||||
this.btnCancel.Location = new System.Drawing.Point(474, 16);
|
||||
this.btnCancel.Margin = new System.Windows.Forms.Padding(5, 5, 5, 5);
|
||||
this.btnCancel.Name = "btnCancel";
|
||||
this.btnCancel.Size = new System.Drawing.Size(101, 35);
|
||||
this.btnCancel.TabIndex = 11;
|
||||
this.btnCancel.Text = "Cancel";
|
||||
this.btnCancel.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// tableLayoutPanel1
|
||||
//
|
||||
this.tableLayoutPanel1.ColumnCount = 2;
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 32.06997F));
|
||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 67.93003F));
|
||||
this.tableLayoutPanel1.Controls.Add(this.label5, 1, 4);
|
||||
this.tableLayoutPanel1.Controls.Add(this.label1, 0, 0);
|
||||
this.tableLayoutPanel1.Controls.Add(this.label2, 0, 1);
|
||||
this.tableLayoutPanel1.Controls.Add(this.label3, 0, 2);
|
||||
this.tableLayoutPanel1.Controls.Add(this.tbSSURL, 1, 0);
|
||||
this.tableLayoutPanel1.Controls.Add(this.cbUseSSO, 0, 4);
|
||||
this.tableLayoutPanel1.Controls.Add(this.tbUsername, 1, 1);
|
||||
this.tableLayoutPanel1.Controls.Add(this.tbPassword, 1, 2);
|
||||
this.tableLayoutPanel1.Controls.Add(this.label6, 0, 3);
|
||||
this.tableLayoutPanel1.Controls.Add(this.tbOTP, 1, 3);
|
||||
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Top;
|
||||
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
|
||||
this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(5, 5, 5, 5);
|
||||
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
|
||||
this.tableLayoutPanel1.RowCount = 5;
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20F));
|
||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20F));
|
||||
this.tableLayoutPanel1.Size = new System.Drawing.Size(914, 212);
|
||||
this.tableLayoutPanel1.TabIndex = 12;
|
||||
//
|
||||
// label5
|
||||
//
|
||||
this.label5.AutoSize = true;
|
||||
this.label5.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.label5.Location = new System.Drawing.Point(298, 168);
|
||||
this.label5.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0);
|
||||
this.label5.Name = "label5";
|
||||
this.label5.Size = new System.Drawing.Size(611, 44);
|
||||
this.label5.TabIndex = 15;
|
||||
this.label5.Text = "For SSO to work, additional IIS configuration is required!";
|
||||
this.label5.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.AutoSize = true;
|
||||
this.label1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.label1.Location = new System.Drawing.Point(5, 0);
|
||||
this.label1.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(283, 42);
|
||||
this.label1.TabIndex = 2;
|
||||
this.label1.Text = "Secret Server URL";
|
||||
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// label2
|
||||
//
|
||||
this.label2.AutoSize = true;
|
||||
this.label2.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.label2.Location = new System.Drawing.Point(5, 42);
|
||||
this.label2.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0);
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(283, 42);
|
||||
this.label2.TabIndex = 4;
|
||||
this.label2.Text = "DOMAIN\\Username";
|
||||
this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// cbUseSSO
|
||||
//
|
||||
this.cbUseSSO.AutoSize = true;
|
||||
this.cbUseSSO.Location = new System.Drawing.Point(5, 173);
|
||||
this.cbUseSSO.Margin = new System.Windows.Forms.Padding(5, 5, 5, 0);
|
||||
this.cbUseSSO.Name = "cbUseSSO";
|
||||
this.cbUseSSO.Size = new System.Drawing.Size(86, 24);
|
||||
this.cbUseSSO.TabIndex = 14;
|
||||
this.cbUseSSO.Text = "Use SSO";
|
||||
this.cbUseSSO.UseVisualStyleBackColor = true;
|
||||
this.cbUseSSO.CheckedChanged += new System.EventHandler(this.cbUseSSO_CheckedChanged);
|
||||
//
|
||||
// label6
|
||||
//
|
||||
this.label6.AutoSize = true;
|
||||
this.label6.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.label6.Location = new System.Drawing.Point(3, 126);
|
||||
this.label6.Name = "label6";
|
||||
this.label6.Size = new System.Drawing.Size(287, 42);
|
||||
this.label6.TabIndex = 15;
|
||||
this.label6.Text = "2FA OTP (Optional)";
|
||||
//
|
||||
// tbOTP
|
||||
//
|
||||
this.tbOTP.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.tbOTP.Location = new System.Drawing.Point(296, 130);
|
||||
this.tbOTP.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
|
||||
this.tbOTP.Name = "tbOTP";
|
||||
this.tbOTP.Size = new System.Drawing.Size(615, 27);
|
||||
this.tbOTP.TabIndex = 5;
|
||||
//
|
||||
// tableLayoutPanel2
|
||||
//
|
||||
this.tableLayoutPanel2.ColumnCount = 5;
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 106F));
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 26F));
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 107F));
|
||||
this.tableLayoutPanel2.Controls.Add(this.btnOK, 1, 0);
|
||||
this.tableLayoutPanel2.Controls.Add(this.btnCancel, 3, 0);
|
||||
this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Bottom;
|
||||
this.tableLayoutPanel2.Location = new System.Drawing.Point(0, 300);
|
||||
this.tableLayoutPanel2.Margin = new System.Windows.Forms.Padding(5, 5, 5, 5);
|
||||
this.tableLayoutPanel2.Name = "tableLayoutPanel2";
|
||||
this.tableLayoutPanel2.RowCount = 1;
|
||||
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
|
||||
this.tableLayoutPanel2.Size = new System.Drawing.Size(914, 67);
|
||||
this.tableLayoutPanel2.TabIndex = 13;
|
||||
//
|
||||
// label4
|
||||
//
|
||||
this.label4.AutoSize = true;
|
||||
this.label4.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.label4.Location = new System.Drawing.Point(0, 212);
|
||||
this.label4.Margin = new System.Windows.Forms.Padding(5, 0, 5, 0);
|
||||
this.label4.Name = "label4";
|
||||
this.label4.Size = new System.Drawing.Size(427, 20);
|
||||
this.label4.TabIndex = 14;
|
||||
this.label4.Text = "URL is the base URL, like https://cred.domain.local/SecretServer";
|
||||
this.label4.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
|
||||
//
|
||||
// SSConnectionForm
|
||||
//
|
||||
this.AcceptButton = this.btnOK;
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 20F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(914, 367);
|
||||
this.Controls.Add(this.label4);
|
||||
this.Controls.Add(this.tableLayoutPanel2);
|
||||
this.Controls.Add(this.tableLayoutPanel1);
|
||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||
this.Margin = new System.Windows.Forms.Padding(5, 5, 5, 5);
|
||||
this.Name = "SSConnectionForm";
|
||||
this.Text = "Secret Server API Login Data";
|
||||
this.Activated += new System.EventHandler(this.SSConnectionForm_Activated);
|
||||
this.tableLayoutPanel1.ResumeLayout(false);
|
||||
this.tableLayoutPanel1.PerformLayout();
|
||||
this.tableLayoutPanel2.ResumeLayout(false);
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
private System.Windows.Forms.Label label3;
|
||||
|
||||
public System.Windows.Forms.TextBox tbSSURL;
|
||||
public System.Windows.Forms.TextBox tbUsername;
|
||||
public System.Windows.Forms.TextBox tbPassword;
|
||||
private System.Windows.Forms.Button btnOK;
|
||||
private System.Windows.Forms.Button btnCancel;
|
||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
|
||||
private System.Windows.Forms.Label label2;
|
||||
private System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2;
|
||||
public System.Windows.Forms.CheckBox cbUseSSO;
|
||||
private System.Windows.Forms.Label label4;
|
||||
private Label label5;
|
||||
private Label label6;
|
||||
public TextBox tbOTP;
|
||||
}
|
||||
}
|
||||
35
ExternalConnectors/DSS/SSConnectionForm.cs
Normal file
35
ExternalConnectors/DSS/SSConnectionForm.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
namespace ExternalConnectors.DSS
|
||||
{
|
||||
public partial class SSConnectionForm : Form
|
||||
{
|
||||
public SSConnectionForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void SSConnectionForm_Activated(object sender, EventArgs e)
|
||||
{
|
||||
SetVisibility();
|
||||
if (cbUseSSO.Checked)
|
||||
btnOK.Focus();
|
||||
else
|
||||
{
|
||||
if (tbPassword.Text.Length == 0)
|
||||
tbPassword.Focus();
|
||||
else
|
||||
tbOTP.Focus();
|
||||
}
|
||||
}
|
||||
|
||||
private void cbUseSSO_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
SetVisibility();
|
||||
}
|
||||
private void SetVisibility()
|
||||
{
|
||||
bool ch = cbUseSSO.Checked;
|
||||
tbPassword.Enabled = !ch;
|
||||
tbUsername.Enabled = !ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
615
ExternalConnectors/DSS/SSConnectionForm.resx
Normal file
615
ExternalConnectors/DSS/SSConnectionForm.resx
Normal file
@@ -0,0 +1,615 @@
|
||||
<root>
|
||||
<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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
AAABAAQAQEAAAAEAIAAoQAAARgAAACAgAAABACAAKBAAAG5AAAAYGAAAAQAgACgJAACWUAAAEBAAAAEA
|
||||
IAAoBAAAvlkAACgAAABAAAAAgAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD9/f3e/f397vz9
|
||||
/e79/f3u7ezr7p6Xju5XTEHuNScV7i8hCe4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isf
|
||||
CO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isf
|
||||
CO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isf
|
||||
CO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHAjuKx8I7jYoFu5XTUHumJGO7u3r6+79/f3u/f397v39
|
||||
/e79/f3e/f399P//////////u7Wy/0M1KP8jEwD/KRgA/ywdBv8tHwj/LiAJ/y4gCf8uIAn/LiAJ/y4g
|
||||
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4g
|
||||
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4g
|
||||
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8sHAT/KRcA/yAR
|
||||
AP9DNSX/ubOt/////////////f399Pv8/e7/////m5aK/xoMAP8lFAD/LSAI/y0gCv8uIAr/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cv8uIAr/LB4J/ysfCf8uIQj/JBYA/xoLAP+elYz///////z9/O79/f3uvLWw/xsNAP8rHAX/LiAK/y0f
|
||||
Cf8tHwj/LR8I/y0fCf8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCf8tHwn/LR8J/y4gCv8pGgP/GgsA/7y3sP/9/f3u6unp7kM0
|
||||
I/8nFwH/LiEL/yweCP8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAr/LR8J/yIU
|
||||
AP9DNSL/6+nq7pqUju4fEQD/LiAI/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8uIAf/HxAA/5qTju5YTT7uJRYA/ywgCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/Kx8I/ygXAP9YTkDuOCkV7iweBv8sHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8sHQX/NykW7ioc
|
||||
CO4uIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8uIAn/Lh8I/yocCO4qHAjuLyEK/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8K/y4gCf8rHAjuKx8I7i4gCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8J/y0fCP8tHwn/LR8J/y0f
|
||||
CP8tHwn/LR8J/y0fCP8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
CP8tHwj/LR8J/y0fCf8tHwj/LR8J/y0fCf8tHwn/LR8J/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8uIAj/Kx8I7isfCO4uIAn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8I/y8fCP8uHwj/LR8G/yweCP8uHgn/LR4H/yweB/8rHgf/Kx4H/yweCP8sHgj/Kx4I/yse
|
||||
CP8sHgj/LB4I/yweCP8sHgn/LB4J/yoeCP8qHgj/LB4I/y4fCP8sIQj/LB8H/y0eCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAJ/ysf
|
||||
CO4rHwjuLiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tIAn/LiAK/y0WAf8sFgH/LRsG/ywbBv8uGwX/MR0B/y8cAv8uGwX/LRkG/ysZ
|
||||
Bf8qGQX/KhkF/ygaBP8oGgX/JxoF/ycaBf8pGgX/KRkG/ykZBv8nGQT/JxkF/ygZCv8jFAb/IBUG/ywe
|
||||
Cf8tHwf/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y4gCf8rHwjuKx8I7i4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/Lh0I/y0XA/84Qyf/P31P/0aSXP9KkVv/UpJT/2OV
|
||||
Sv93mUf/jJ9A/6CjPP+vpjv/rac4/6ilN/+ppDn/qaQ4/62jOP+uozj/sKI2/7KiNv+yojX/s6Az/7Wi
|
||||
NP+0oDP/mIUs/1pIFv8lFQb/Kh4I/y4gCf8sHgn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/Kx8I7isfCO4uIAn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR4I/ygWA/83b0X/SsOE/0zC
|
||||
f/9QvHf/Ubp0/1e4b/9YtGz/WrJl/2KwZP96tWD/lL9X/7fNUf/b2E//3NhP/9nWTP/X00v/29NL/9vS
|
||||
Sv/d0Ur/39FJ/9/RSP/j0Ej/5tBD/+3VRP/r0EX/jHYl/yMUBv8sHwb/LB4I/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAJ/ysfCO4rHwjuLiAJ/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/ysV
|
||||
AP8zWTb/ScmH/0e8ef9Ltnb/TrRx/1Cyb/9VsGr/Wa1m/1mrYf9dqV3/XqRc/1yfWf9ooVL/g6tQ/7XD
|
||||
Tf/R0Ur/ys1H/87LR//Oykb/0MlG/9PKRf/Uy0X/18pF/9jJQf/byEH/4MtD/+7VRv9rWxz/HxMC/y8h
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4g
|
||||
Cf8rHwjuKx8I7i4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/Kx8I/y0cBv8vHQj/QKFs/0fEgP9IuHX/TLZ0/0+0cP9UsW3/Vq9o/1isZP9cqWD/X6hc/2Ok
|
||||
Wf9loVX/aKBT/2iZUP9um0j/obZJ/87RTP/SzUz/yspI/8zLSP/Qykj/0stH/9XJR//VyUT/2clE/9fH
|
||||
RP/izkn/vao2/ywdBv8rHAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8uIAn/Kx8I7isfCO4uIAj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tGwX/MCoV/0azfP9Fvnv/Sbh2/0y2c/9PtG//VbFs/1av
|
||||
Z/9YrGT/XKlg/2KnXP9kpVr/ZaJX/2mhU/9unkz/bZtK/2qVR/+asUj/zdJM/8nOSf/JzUv/zcxJ/8/L
|
||||
R//Rykf/0cpE/9XKRP/WykX/3c5G/9DBQv86Kw//KRoH/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LiAI/ysfCO4rHwjuLiAJ/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwr/LRoE/ywsGP9Dt3//R7x6/0q3
|
||||
dv9OtHL/ULNu/1Wwa/9Xrmb/Watj/12pXv9hplv/ZaRY/2eiVP9qoFH/bp5M/3OcSf9ymUb/b5BC/6m5
|
||||
SP/L0k7/yM5M/8vNS//LzEn/z8xJ/87LR//Rykf/08tH/9nNSP/RxkT/PS8R/ygZBv8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8rHwjuKx8I7i4g
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LB8K/y4a
|
||||
A/8uLRj/RLZ9/0e8ev9LtnX/ULNy/1Gybf9VsGr/Wa5l/1mrYv9dqVz/YKZa/2WkVv9oolL/aqBQ/2+e
|
||||
S/9zm0n/dJpE/3SUQ/+Dmj7/wMxM/8fPTP/IzUz/yc5L/8zNS//LzEr/z8tK/8/LSP/UzUj/0MZG/z0u
|
||||
EP8oGgX/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8uIAj/Kx8I7isfCO4uIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/ywfCv8uGgP/Li0Y/0W2fP9Iu3n/TLZ0/1Czcf9Rsm3/VbBq/1muZf9aq2L/Xqlc/2Gm
|
||||
Wv9lpFb/aKJS/2ugUP9wnkv/c5tI/3WaQ/95l0H/do07/6m6Rf/F007/xM5N/8XOTP/IzUz/yM5L/8zN
|
||||
S//MzEn/0c9J/83HR/89LhD/KBoG/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LiAJ/ysfCO4rHwjuLiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwr/LRsE/zAsGP9HtHr/S7p3/061cv9Qs2//U7Fr/1Wv
|
||||
af9arGP/XKlh/2GnW/9lpFn/ZaNV/2igUf9unk7/cpxK/3WbRv91mEH/e5Y//3mPOP+YrEH/wtVT/7/O
|
||||
Uf/CzlD/xc1Q/8TOTP/IzUz/yc5L/87QTP/HyEn/PS8P/ykZBv8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8rHwjuKx8I7i4gCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8K/y0bBP8wLBj/R7R6/0u6
|
||||
d/9OtXL/ULNv/1Oxa/9Vr2n/Wqxj/1ypYf9hp1v/ZaRZ/2WjVf9ooFH/bp5O/3KcSv91m0b/dZhB/3yW
|
||||
P/97kDj/j6I9/7/VV/+90VH/wNBR/8PPUf/Bz0z/xM5M/8XOS//K0Uz/xclJ/z0vD/8pGQb/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/Kx8I7isf
|
||||
CO4uIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tGwT/MCwa/0qzef9MuXb/TrRx/1Kybv9Tr2n/V61k/12qYP9fqF3/YKZZ/2SkVv9qoVL/bKBP/26d
|
||||
TP9ym0b/dJpE/3iWQf98lD7/fY43/42gPf+111v/t9RU/7nSU/+/z1L/vtBQ/8HPUP/Cz03/xtFN/8HJ
|
||||
TP88MA//KhsF/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LiAJ/ysfCO4rHwjuLiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LRsE/zAsGv9Ks3n/TLl2/060cf9Ssm7/U69p/1etZP9dqmD/X6hd/2Cm
|
||||
Wf9kpFb/aqFS/2ygT/9unUz/cptG/3SaRP94lkH/fJQ+/36NNv+LpkP/qNxh/6zYWv+v1Ff/ttFV/7zS
|
||||
Uf/A0VL/wNFP/8TUT//Ayk3/PDAP/yobBP8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8rHwjuKx8I7i4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4cA/8wLBn/SrN2/024dP9Ps3D/VLFs/1eu
|
||||
Z/9Zq2T/XKlf/2KnXf9jpVj/ZaNV/2qgUf9unk7/cJ1K/3KbRf92mUP/e5U+/3mUPP9/jDP/ibRM/5zi
|
||||
aP+g2mD/pdhe/6zVW/+01Ff/utNU/7vSUv/D1VH/vctN/zswEf8pGgb/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/Kx8I7isfCO4uIAj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8uHQL/MCwY/0qz
|
||||
df9NuHP/T7Nv/1Wwa/9arWb/Wqtj/1qpX/9jplz/ZKRY/2WiVf9qoFH/b55N/3CdSf9ym0T/d5hC/3yW
|
||||
Pf98kjn/g441/4nRZP+N4mv/ld5n/5rcZf+g22D/p9hd/63XWf+y1Fb/utZU/7jMTv86MBH/KBoG/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LiAI/ysf
|
||||
CO4rHwjuLiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/Lh0D/zAsF/9LsnT/Trdz/1Gybv9WsGr/WK1l/1uqYv9dqV3/YqZb/2WkVv9nolT/aqBQ/2+e
|
||||
TP9ynEn/c5pD/3aXQf97lT3/gokz/3uvT/986nr/g+Rw/4nibf+O32v/lN5n/5rcY/+h22D/ptdd/6/Y
|
||||
Wv+xzlX/Oi8R/ygZBv8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y4gCf8rHwjuKx8I7i4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4cBf8wLRb/TbFz/1C2cv9TsWz/V7Bo/1etZP9cqmH/Yahb/2Gl
|
||||
Wv9mo1T/aKFS/2ugTv9wnUr/dJpI/3WZQ/98kz3/gYo3/3WsUP9s7ID/but8/3fneP995XT/geJy/4fh
|
||||
bf+O32r/lN5m/5rbZP+j3GL/pdJd/zkvEv8pFwX/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/Kx8I7isfCO4uIAn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHAX/MC0W/02xc/9QtnL/U7Fs/1ev
|
||||
aP9YrWT/XKpg/1+oW/9hpVr/ZqNV/2mhUv9sn03/cJxJ/3WVRP95kT3/eJZA/2TAYf9Y8Yj/XPOI/2fs
|
||||
gv9r637/cel7/3bneP995XT/g+Nw/4nibP+P32r/l+Bo/5jXYf85MBL/KhgG/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAJ/ysfCO4rHwjuLiAJ/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/KhsE/zAs
|
||||
FP9NsXH/UbVv/1awav9arWf/Wqxi/12oXv9jo1v/Y6FU/2mcUP9tm0r/bptI/2+cSv9rp1P/YMNo/1Do
|
||||
hf9G/ZX/SfaO/1PyjP9b8Ir/X+6F/2btgv9s637/cul6/3fmeP995XT/g+Nw/4vlbv+O22n/NjAU/yca
|
||||
BP8uHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4g
|
||||
Cf8rHwjuKx8I7i4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/yobBP8wLBT/TbJy/1G1b/9WsGr/Wq1n/1uoX/9dp13/YKlf/2CvXv9gt2X/XcBq/1XQ
|
||||
dv9P4YP/SPCO/0T3lP9J85L/R/SR/0n1kf9L9ZD/TfSP/1Tzi/9b8Yj/Yu+E/2ftgP9r637/cel6/3jn
|
||||
dv+A6XT/geBs/zgxFf8pGwX/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8uIAn/Kx8I7isfCO4uIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y8fCf8rHAL/LisU/02ucv9Vs23/VLBp/1ipYP9XuGr/TtOA/0fg
|
||||
if9H5Ir/RuqO/0Xukf9G7pL/Ru2Q/0Ttjf9J7I7/R+6N/0nwj/9L8ZD/S/KR/0nzkP9I85D/TPSN/1X0
|
||||
if9d8If/Ye6D/2fsf/9s6nz/cux7/3Xic/81MRb/KxkE/ywfCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAJ/ysfCO4rHwjuLiAJ/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8sHwn/Lh0D/y4gCP9PnWP/Vbdx/1eq
|
||||
ZP9TwXP/RuSM/0fmjv9F4ov/Q+SJ/0bliv9F5or/RuaM/0bojP9G6or/R+uM/0jtjP9J7o3/SO+O/0nw
|
||||
j/9I8o//RvKP/0f0j/9K947/S/WN/1Tyif9b8YT/YO6D/2n2hP9kz27/KyEM/y4dBv8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8rHwjuKx8I7i4g
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LB8J/y4e
|
||||
B/8sFgH/PGU2/1i9dP9Utm3/RN6J/0Leif9J3on/SN+K/0Xgif9H4ov/R+KL/0fjjP9I5Yz/R+eL/0jo
|
||||
jP9I6o3/SeuO/0jsjf9J7Y7/SO+O/0jwjv9J8Y//S/OQ/0r0kf9M9ZD/T/SO/1Pziv9h/5X/SIJE/yoP
|
||||
Af8sHwf/LR8K/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8uIAn/Kx8I7isfCO4uIAj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwn/LBwH/ywdCf9IfEf/Tsx8/0Ddif9F14X/RduH/0bdiP9E3on/Rd+K/0fl
|
||||
jf9I6JD/SOqQ/0jsj/9J7ZD/SO6R/0nvkv9H8JD/SPKR/0n0kf9K84//SPCP/0fxkP9J8pH/SvWS/0r3
|
||||
lP9M/pn/RKxh/y0YBv8sHAf/LR8J/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LiAI/ysfCO4rHwjuLiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8sGgX/KhUE/0GsbP9E4Y7/QteG/0TZ
|
||||
h/9F2oj/RdyI/0Teiv9Ez3//RMp7/0XNe/9Eznr/RM97/0TQff9F0H3/Q9J7/0PTfP9E0n3/SNyC/0zq
|
||||
iv9M6Yr/TeqL/0zsiv9P8Iz/S7Vp/ywWB/8sGgT/LiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8rHwjuKx8I7i4gCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8sHwn/Lh8G/ywR
|
||||
BP87m2b/ReCR/0PUiP9E1oj/RtiJ/0bZh/9D5o//Nlk1/ywZB/8vJg3/LyYM/y8mDP8vJQ3/LyUN/ywm
|
||||
DP8sJgz/KhgH/z9sO/9c2Hr/Vslx/1fJcf9YyXL/XdF3/0yRUv8pEwT/LyEJ/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/Kx8I7isf
|
||||
CO4uIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LB8J/y0eBv8sEwX/PJ1n/0Tej/9D0of/Q9SG/0TWiP9F2Ib/QuWO/zVPLv8sDgD/MBsI/y8b
|
||||
B/8vGwb/LhsE/y4bBP8sGgb/KxoG/ywKAP87Zjr/U+CD/1HQe/9R0Hv/UtB7/1Pcg/9En17/KxMD/y4g
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LiAJ/ysfCO4rHwjuLiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4fCf8sHwj/LhUD/0CbZP9F2o7/RM+H/0TQhv9F0of/Q9WG/0bk
|
||||
kf8yVzL/KxEA/y4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tDgD/O3ZF/0zslP9I2ob/SdqG/0na
|
||||
hv9L5o7/QaNm/ywVAv8uHgf/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8rHwjuKx8I7i4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8sHwj/LiAJ/ywUAf89jlf/Q9qO/0LN
|
||||
hf9Dz4T/RNCG/0LRg/9H4I//O4dQ/yoOAP8tIAj/LR8J/y0fCf8tHwn/LR8J/y0fCP8tHgj/LBMC/z+n
|
||||
ZP9H6pH/RN+K/0Tfiv9E34r/RuyT/z2bXv8uEwH/LR8H/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/Kx8I7isfCO4uIAn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LB8J/y0f
|
||||
Cf8rDwD/NXNF/0PZjP9DyYX/RcuF/0XNhv9EzYT/RdSJ/0TFfv8uKRH/LBYC/ywfCP8tHwn/LR8J/ywf
|
||||
Cf8sHwj/KBAA/zZAIf9H34n/R+GL/0Xdif9F3Yn/Rd2I/0nvlv86gU3/Kg4A/y0eCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAJ/ysf
|
||||
CO4rHwjuLiAI/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCf8rHwj/LRMB/zZRLv9H0ov/QsmE/0LKhP9Ey4X/Q8uE/0XMhP9H3I7/Pppj/ysa
|
||||
Bv8sEQD/Lh0I/y0fB/8tGgX/Kg4A/y0lD/9AuXH/R+qQ/0Lci/9D34n/Rd6J/0PdiP9C65H/M1wx/y8U
|
||||
Av8uIAf/LR8J/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y4gCP8rHwjuKx8I7i4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LB4I/y4bBv8uJQ//QLV4/0LNh/9Bx4P/Q8iE/0PJ
|
||||
g/9Ey4P/RMuG/0Xajv9EpGn/MEoo/ysfC/8uFgr/LiUN/zNULf8+vHf/SOWQ/0TYh/9F3Ij/Rt6J/0fe
|
||||
i/9E44z/Rc9//y0qDv8vGQX/LSAH/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/Kx8I7isfCO4uIAj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCf8tHwn/KxEA/zt2
|
||||
Sv9F047/QcOC/0PFg/9Cx4L/RMmD/0PKgv9EzIP/RdaL/0fUif8/t3P/Pqpv/0C+dv9H3I//Rt6O/0HW
|
||||
hv9E1oj/RdiI/0Tah/9G3In/R+6U/zuET/8sDwD/LR8J/y0fCf8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LiAI/ysfCO4rHwjuLiAJ/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0aBP8wKhH/QLV4/0HKhv9BxIP/QsWB/0LHgv9CyIL/QsmD/0XKg/9FzoT/Q9KH/0LY
|
||||
iP9G14r/Q9CH/0XRhf9F0oX/RNSI/0LXiP9C14X/ReGM/0XOgv8uKxT/KxgF/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4g
|
||||
Cf8rHwjuKx8I7i4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwf/LRYA/zRTL/9Dy4v/QcWG/0HCgf9CxIL/QsSC/0PE
|
||||
g/9CxoP/Q8iC/0TKhP9Dy4T/RsyF/0PNhf9Cz4b/RNCH/0XSiP9H0oj/QtiJ/0jikP8yWjX/KxAA/ywg
|
||||
B/8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8uIAn/Kx8I7isfCO4uIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LSAH/y8gBv8vEgD/OWlC/0PL
|
||||
if8+xYP/P8KA/0DCgf9Bw4L/QsaC/0LHgf9DyIL/QsqC/0PLg/9CzIT/Q82F/0TOhf9Fz4X/RdaK/0fg
|
||||
lf85dkf/KhEA/yseB/8uHwj/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAJ/ysfCO4rHwjuLiAJ/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8vHwn/Kh4I/y8WAP82Yzj/P7+C/0LLiP9CwoL/QMGC/0HFhP9BxIH/Q8WD/0PGhP9Ex4X/RMmE/0bK
|
||||
hf9DzYT/QtyN/0nTiP86aj3/LRIA/y4eCP8sIAr/Lh8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8rHwjuKx8I7i4g
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8rIAj/KhQA/zU/IP9Bl2X/RseI/0LLiv8/x4b/QMaD/0DF
|
||||
g/9CxoT/Q8mG/0LOh/9G1o3/RtSK/0WjaP8yQiD/LBEA/y8eCP8sIAn/LR8I/y0fCP8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8uIAn/Kx8I7isfCO4uIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8sHwn/LR4I/y4gCv8sFQD/KxoE/zRJ
|
||||
Kf9Cg1P/Q6tv/0O6ev9EwoL/QsOC/0a+fv9FsHD/QoZS/zZJJ/8sGgT/KxMB/y4fCf8wIAj/Lh8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LiAJ/ysfCO4rHwjuLiAI/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8J/y0f
|
||||
Cf8tHwj/LR8J/y0dCP8rEwL/LBQA/zEjCf8yLxP/Njce/zQ3H/8yMBT/MiMJ/y8UAP8rEgD/LB0H/y4g
|
||||
Cf8tHwn/LB8J/y0fCf8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y4gCP8rHwjuKx8I7i4gCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAI/yseB/8rHQj/LBoF/ysXAv8sGAP/KxsF/ygb
|
||||
Bv8wHgf/LR8J/y4fCv8uIAr/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/Kx8I7isf
|
||||
CO4uIAj/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8I/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LB8J/y0fCf8tHwj/LR8I/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LiAJ/ysfCO4rHwjuLyEJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8rHwjuLh8J7i8hCP8tIAj/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwj/LyEJ7jgqFu4sHgX/LR8H/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/LB4G/zUn
|
||||
FO5YTT7uJhUA/y4gCv8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LSAL/yoZAP9XTD7um5SO7iMRAP8vIAn/LR8J/y0fCf8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwn/LR8I/ywfBv8iEgD/npeO7urq6e5ENSX/JBQA/y0hCP8tIAf/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/Kx0H/y4gCv8lFQD/QzYm/+vr6e79/f3uvLax/xoM
|
||||
AP8pGwL/LiEI/y0fCf8tHwj/LR8J/y0fCf8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0f
|
||||
CP8tHwj/LR8I/y0fCP8tHwj/LR8I/y0fCP8tHwj/LR8J/y0fCf8tHwn/LR8J/y4gCf8rHQX/Gw0A/723
|
||||
tP/9/f3u+vv87v////+ak4n/GQsA/yQUAP8uHwn/LR8J/y0fCP8uIAn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0eCf8sHgj/LB8J/y4g
|
||||
B/8mFwD/Gw0A/5qViv//////+/z87v39/fT//////////7q1sP9DNCb/IxEA/ycWAP8rHgX/LiAH/y8h
|
||||
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4g
|
||||
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4g
|
||||
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/zAh
|
||||
Cv8uHwj/LB0G/yQWAP8fEQD/QzQm/7q0r/////////////39/fT9/f3e/f397vz9/e79/f3u6+rp7puU
|
||||
ju5YTj/uOCkW7i4fCu4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isf
|
||||
CO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isf
|
||||
CO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isfCO4rHwjuKx8I7isf
|
||||
CO4rHwjuKx8I7isfCO4rHwjuKyAI7jcpFu5YTj7umpOO7uvr6u79/f3u/Pz97v39/e79/f3eKAAAACAA
|
||||
AABAAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP39/dzt6+vtdGte7TQmFu0rHQjtLR8I7S0f
|
||||
CO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0f
|
||||
CO0tHwjtLR8I7S0fCO0tHwjtLR8I7SsdBu00Jhbtc2le7e3r6e39/f3c6ubl81ZKOP8dDgD/LB4G/y4g
|
||||
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4g
|
||||
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LyAK/ywdBf8dDgD/VUk4/+rn5vN0a13tHg4A/y4g
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8dDQD/dWtd7TQm
|
||||
Eu0rHQb/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/ywd
|
||||
Bf80JhLtKx0G7S8gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LiAJ/ysdBu0tHwjtLiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwj/LBcC/ysT
|
||||
AP8pFAH/JhMC/yISA/8hEQP/IBID/yASA/8gEgT/HxIE/x8RBP8jFgb/LB4I/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8uIAn/LR8I7S0fCO0uIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR4I/ywY
|
||||
BP81QiP/Olw1/0RbLP9WXSb/bGMj/3RnIP9vZCD/bmIg/3FiIP9yYh7/dWMf/1ZFFv8lFwb/LB4I/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8rFgL/N2c//0vDgf9SwHr/W7pv/2a2Zf9+t13/p8RT/9bYT//g20z/3tZL/+LWSv/p10f/69RF/39r
|
||||
Iv8iFQT/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAJ/y0fCO0tHwjtLiAJ/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LBwG/y4lD/9Esnb/Sr55/1Kybv9XrWf/Xahe/1+iWP9imlH/faJJ/8DHSv/Pz0r/z8tI/9PK
|
||||
Rf/f0Uf/z75A/zQmC/8pGwj/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/LR8I7S0fCO0uIAn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8sGgX/Ly0W/0S0ef9NuHb/U7Fs/1itZP9ep1z/ZqNW/2yfTv9slUb/hqFE/8fP
|
||||
TP/Lzkv/zcxJ/9TOSf/Ow0T/PS4P/ygaB/8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4g
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/ywbBf8wLBX/RrF1/0+4dP9UsGv/Wqxj/2CmWv9molT/b55N/3WZ
|
||||
RP93kTz/r8BI/8fST//Hzkz/zdFM/8bERv87LQ7/KRoH/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAJ/y0f
|
||||
CO0tHwjtLiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LBsF/zAsFf9Jr3T/ULdz/1WvaP9dqmD/YqVZ/2mg
|
||||
Uv9wnEv/dplD/3mOOf+iuEn/wNZV/8HPT//H0k7/v8ZJ/zstDv8pGgf/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8uIAn/LR8I7S0fCO0uIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8sGwX/MCwV/0mvc/9RtnH/Vq1m/16p
|
||||
Xv9jpFf/bKBQ/3CcSP93mEH/fIw2/5bCU/+q3F//tdNW/8HWUv+7yEv/Oi4O/ykaB/8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/ywbBf8wLRT/S65w/1O1
|
||||
bv9ZrGT/YKdc/2ajVv9sn0//cpxG/3uPOf+AmT7/hd5t/5Hhav+e22L/rdtc/6/KUf85LQ//KRoH/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LiAJ/y0fCO0tHwjtLiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LBsG/zAs
|
||||
E/9NrW7/VrRt/1qrYf9ipFj/aZ5R/3GXR/94kj//dKFI/2bddv9t74H/eeZ2/4Xib/+U4mr/mdFe/zgt
|
||||
EP8qGQb/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/LR8I7S0fCO0uIAn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8sGwb/LiwU/06ubf9ZsGn/Xahf/2CqXv9jr13/YLtj/1jRdP9K8o7/SvqT/1byi/9j7oP/b+p8/3zq
|
||||
d/+D22r/NjAS/yoaBv8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/ywcBv8vJw7/Ualp/1e3bP9N0n//R+GJ/0bojf9E75H/RPKR/0jwj/9J8ZD/R/OR/0z1
|
||||
jv9W8on/ZfWF/2vhd/8yKg//KxoG/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAJ/y0fCO0tHwjtLiAJ/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8I/ysXAv9CaTr/TNSC/0PijP9F34n/RueP/0n0lv9J95b/SvqY/0n9
|
||||
mP9J+5b/R/OR/0j4lP9Q/pb/RI5N/ysTAv8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/LR8I7S0f
|
||||
CO0uIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LRwG/ywbB/9Aunf/RN6M/0bijf9At3D/OXJC/zp7
|
||||
Rf86fEb/NnhD/z+SUv9S3H//U+KD/068bf8sGgj/LRsH/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4g
|
||||
Cf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHQf/LRwI/0CydP9E2oz/RuWR/zqO
|
||||
Vv8qAAD/LA8A/ywOAP8pAgD/Mzkb/0/bhP9Q24T/SLRs/y0dCP8tHQf/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LiAJ/y0fCO0tHwjtLiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0eCP8tGQT/PqZp/0TW
|
||||
jP9F1or/Qbh0/ywbBv8sFQL/LBwG/ykGAP86eEX/Ru6U/0Xpkf9At3H/LRkE/y0eB/8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8uIAn/LR8I7S0fCO0uIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/ysR
|
||||
AP86fk//RNeP/0TJg/9G14z/PI1Y/y4pEP8tHQn/M1ky/0TWhv9F4Yz/R/CV/zqOU/8sEAD/LR8I/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LBcD/zE5HP9CxoX/QsqF/0PIgv9F1ov/QsR+/0C7d/9F2oz/RNmK/0Xdiv9G34z/MT0d/ywW
|
||||
A/8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAJ/y0fCO0tHwjtLiAJ/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LBQA/zdmPf9C0Iz/QcmG/0LFgv9DzIX/RNGI/0POhv9F14v/R+WU/zdv
|
||||
Qv8qEQD/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8uIAn/LR8I7S0fCO0uIAn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHgj/LBUB/zVeNv9Ct3v/Q8+M/0LRjP9E1I7/RteO/0TE
|
||||
fv84ZDr/LRQB/y0eCP8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4g
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHgj/LBMA/y8qEf84Xjb/O3lM/zt6
|
||||
Tf86YDf/MCoR/ysSAP8tHgj/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LiAJ/y0f
|
||||
CO0tHwjtLiAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LBoF/ysT
|
||||
AP8sEQD/KxEA/ysSAP8sGgX/LSAJ/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8uIAn/LR8I7SweBu0vIAn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y4gCf8rHQbtNCYT7SwdBv8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LB4G/zQmEu11a17tHQ4A/y4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0gCf8dDgD/dmxe7ern5vNVSTf/Hg4A/ywdBv8uIAn/LiAJ/y4g
|
||||
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4g
|
||||
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8rHQX/Hg4A/1ZKOP/q5+fz/f393Ozr6e10aV7tNSYU7Swe
|
||||
CO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0f
|
||||
CO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0tHwjtLB4I7TQmFO1zaV7t7evp7f39/dwoAAAAGAAAADAA
|
||||
AAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/f393JSPhO00JhbtKx0G7S0fCO0tHwjtLR8I7S0f
|
||||
CO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0rHQbtMyUW7ZSO
|
||||
g+39/f3ckouC8yESAP8qHAT/LyAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4g
|
||||
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8vIAn/KhwE/yERAP+Si4LzMyUR7SocBP8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/yocBP8zJRLtKx0F7S4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0eCP8sGgX/KxoG/yka
|
||||
B/8oGgf/KBoH/ygaB/8nGgf/KBoH/yweCP8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8rHQXtLR8I7S4g
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LRwH/y0aBf8wLRP/Ni8Q/0ExDv9FMw3/PjAN/z4vDv8/MA3/PS4N/ycZ
|
||||
B/8qHAj/LR8J/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8tHQf/LB8K/z6E
|
||||
VP9NsnL/Wapi/3GoVv+bskv/x8VG/87FRP/PwkL/2cZB/6GMLf8tHwj/Kx0I/y0fCf8tHwn/LR8J/y4g
|
||||
Cf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8sFgH/MkYn/0nIhP9Rt3L/WK9m/16nXf9jnlP/iKlK/8nO
|
||||
S//T0Er/2M9H/+jYSv9XSRf/IxUF/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0f
|
||||
Cf8sFQH/M0os/0vBfv9Tsm3/Wqxi/2OlWP9sn07/bJJD/5uuRP/M0k3/y81L/9nWTP9cTxv/IhQF/y0f
|
||||
Cf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8rFQH/NEkq/02+e/9UsGr/Xalg/2Wj
|
||||
Vv9unkz/dJRB/4ufPv+/01P/xM9O/8/XT/9YThr/IxQE/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4g
|
||||
Cf8tHwn/LR8J/y0fCf8rFgH/NUkp/068ef9Xrmf/X6dc/2iiVP9wnUn/eI86/4ilRP+k3mL/tNVX/8ba
|
||||
U/9WTxv/JBQE/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8rFgH/Nkgo/1K7
|
||||
dv9ZrWT/YqRZ/2ycTf93kj//epU+/3bVa/+D53T/lN5n/63hYv9RTx//JRMD/y0fCf8tHwn/LR8J/y4g
|
||||
Cf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8qFQL/NUop/1a6c/9cqWH/Yatd/2SwW/9gv2b/UeeE/1L4
|
||||
kP9k7oL/dOh5/4vvdf9JViX/JxIC/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0f
|
||||
Cf8rFwP/NT4e/1a5cP9N0H3/R+GI/0fwkv9F+5n/SPuY/0j7lv9J95H/VfaM/2f6iP88SyH/KhQC/y0f
|
||||
Cf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8tHgj/LBUC/z6ETv9E6JL/Rd+L/0LG
|
||||
ef9DwHP/QsR1/0LHdP9L4oX/T/aS/0KaV/8rFgT/LR4I/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4g
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/Kw8A/zVgOv9F4pL/Rd+N/zJGJP8tEAD/LRoF/ywaBv9Lum3/VOeL/zle
|
||||
M/8qDwD/LR8J/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8tHwn/KxMA/zVX
|
||||
Mf9F2I7/RtuO/zl3R/8qBQD/KQQA/zJDIv9F4Iv/RvCW/zVfNf8rEQD/LR8J/y0fCf8tHwn/LR8J/y4g
|
||||
Cf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8tHwn/LBoF/zAtE/9Cv3//RNCJ/0TNhf86g1D/N29C/0LF
|
||||
fP9G6JH/RNeF/y8wFP8sGQT/LR8J/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/ywTAP82Yzv/QtKN/0PPif9F2I3/Rd2Q/0XekP9H5ZP/N2w//ysRAP8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0eCP8sFQH/NVs1/0Cu
|
||||
dP9CxoT/RMqG/0O3dv83YTj/LBQB/y0eCP8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8tHwjtLR8I7S4g
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHgj/LBMA/y4iC/8zPB7/Mj0e/y8hCv8rEgD/LR4I/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4gCf8tHwjtKx0F7S4gCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/ywcB/8sFwP/LBcD/ywcB/8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y4g
|
||||
Cf8rHQbtNCUS7SocBP8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0f
|
||||
Cf8tHwn/LR8J/y0fCf8tHwn/LR8J/y0fCf8tHwn/LR8J/yocBP80JhLtkouD8yERAP8qHAT/LyAJ/y4g
|
||||
Cf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4gCf8uIAn/LiAJ/y4g
|
||||
Cf8vIAn/KhwE/yERAP+VjITz/f393JSOhO00JhbtKx0G7S0fCO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0f
|
||||
CO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0rHQbtMyUW7ZSOg+39/f3cKAAAABAA
|
||||
AAAgAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL65stw/MR/tKBkF7S0fCO0tHwjtLR8I7S0f
|
||||
CO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0oGQXtPzEf7b65stw8MR3zJxgA/y8gCf8uIAn/LiAJ/y4g
|
||||
Cf8uHwn/Lh8J/y4fCf8uHwn/LiAJ/y4gCf8uIAn/LyAJ/ycYAP88MR3zKBoE7S8gCf8tHwn/LSAJ/ywa
|
||||
Bf8rFAH/LBcD/ysXBP8kFQT/IxUF/yATBP8oGgf/LiAJ/y0fCf8vIQn/KBkE7S0fCO0uIAn/LR8J/ywa
|
||||
Bf8wMxj/QYNR/1OFSP94iTr/npg0/6aYM/+hji7/RDQP/ycaB/8tHwn/LiAJ/y0fCO0tHwjtLiAJ/y0f
|
||||
Cf8rEQD/OW9F/1LNhP9dtGj/aahY/5u2TP/Z2U3/7+ZP/4V2KP8eEAT/LR8J/y4gCf8tHwjtLR8I7S4g
|
||||
Cf8tHwn/KxIA/ztvRf9Tv3f/Xqhd/2ufT/91kj7/tMNK/9fgU/+AeSr/IBAD/y0fCf8uIAn/LR8I7S0f
|
||||
CO0uIAn/LR8J/ysTAP89bUL/Vrxz/2OjWP9ylkX/fZQ7/5XTX/+952D/d3ot/yEQA/8tHwn/LiAJ/y0f
|
||||
CO0tHwjtLiAJ/y0fCf8qEwD/P29C/1y6bv9iqlv/Z7Fa/1zad/9l8oX/ifZ6/2OGPP8lDgH/LR8J/y4g
|
||||
Cf8tHwjtLR8I7S4gCf8tHwn/KhQB/zxULf9P0oD/SOSL/0b1lf9F/Jn/SPaS/1j7kP9DbTb/KRAB/y0f
|
||||
Cf8uIAn/LR8I7S0fCO0uIAn/LR8J/y0cB/8uIwz/QcuC/0HCef8zRSL/MD0e/0SmYP9M0n7/LSMN/ywc
|
||||
Bv8tHwn/LiAJ/y0fCO0tHwjtLiAJ/y0fCf8tHQj/LRsG/0Cxc/9Ez4b/MDca/yweCf9Cxnr/Qsd6/y0b
|
||||
Bv8tHQj/LR8J/y4gCf8tHwjtLR8I7S4gCf8tHwn/LR8J/ywUAf81XTf/RNiR/0TTiv9E1In/R+2Y/zZn
|
||||
Ov8rEgH/LR8J/y0fCf8uIAn/LR8I7S0fCO0uIAn/LR8J/y0fCf8tHgj/LBcC/zRWMv8+nWb/P6Jo/zZb
|
||||
NP8sFgL/LR4I/y0fCf8tHwn/LiAJ/y0fCO0oGgTtLyEJ/y0fCf8tHwn/LR8J/y0fCP8sFAH/LBYC/ywW
|
||||
Av8rEwD/LR4I/y0fCf8tHwn/LR8J/y8hCf8oGQTtPTEd8ycYAP8vIAn/LiAJ/y4gCf8uIAn/LiAJ/y4f
|
||||
CP8uHwn/LiAJ/y4gCf8uIAn/LiAJ/y8gCf8nGAD/PTEd87u5stw/MR/tKBkF7S0fCO0tHwjtLR8I7S0f
|
||||
CO0tHwjtLR8I7S0fCO0tHwjtLR8I7S0fCO0oGQXtPzEf7b65stwAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
</value>
|
||||
</data>
|
||||
</root>
|
||||
383
ExternalConnectors/DSS/SecretServerAuthentication.cs
Normal file
383
ExternalConnectors/DSS/SecretServerAuthentication.cs
Normal file
@@ -0,0 +1,383 @@
|
||||
//----------------------
|
||||
// <auto-generated>
|
||||
// Generated using the NSwag toolchain v13.14.8.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0)) (http://NSwag.org)
|
||||
// </auto-generated>
|
||||
//----------------------
|
||||
|
||||
#pragma warning disable 108 // Disable "CS0108 '{derivedDto}.ToJson()' hides inherited member '{dtoBase}.ToJson()'. Use the new keyword if hiding was intended."
|
||||
#pragma warning disable 114 // Disable "CS0114 '{derivedDto}.RaisePropertyChanged(String)' hides inherited member 'dtoBase.RaisePropertyChanged(String)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword."
|
||||
#pragma warning disable 472 // Disable "CS0472 The result of the expression is always 'false' since a value of type 'Int32' is never equal to 'null' of type 'Int32?'
|
||||
#pragma warning disable 1573 // Disable "CS1573 Parameter '...' has no matching param tag in the XML comment for ...
|
||||
#pragma warning disable 1591 // Disable "CS1591 Missing XML comment for publicly visible type or member ..."
|
||||
#pragma warning disable 8073 // Disable "CS8073 The result of the expression is always 'false' since a value of type 'T' is never equal to 'null' of type 'T?'"
|
||||
#pragma warning disable 3016 // Disable "CS3016 Arrays as attribute arguments is not CLS-compliant"
|
||||
|
||||
namespace SecretServerAuthentication.DSS
|
||||
{
|
||||
using System = global::System;
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("NSwag", "13.14.8.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")]
|
||||
public partial class OAuth2ServiceClient
|
||||
{
|
||||
private string _baseUrl = "";
|
||||
private System.Net.Http.HttpClient _httpClient;
|
||||
private System.Lazy<Newtonsoft.Json.JsonSerializerSettings> _settings;
|
||||
|
||||
public OAuth2ServiceClient(string baseUrl, System.Net.Http.HttpClient httpClient)
|
||||
{
|
||||
BaseUrl = baseUrl;
|
||||
_httpClient = httpClient;
|
||||
_settings = new System.Lazy<Newtonsoft.Json.JsonSerializerSettings>(CreateSerializerSettings);
|
||||
}
|
||||
|
||||
private Newtonsoft.Json.JsonSerializerSettings CreateSerializerSettings()
|
||||
{
|
||||
var settings = new Newtonsoft.Json.JsonSerializerSettings();
|
||||
UpdateJsonSerializerSettings(settings);
|
||||
return settings;
|
||||
}
|
||||
|
||||
public string BaseUrl
|
||||
{
|
||||
get { return _baseUrl; }
|
||||
set { _baseUrl = value; }
|
||||
}
|
||||
|
||||
protected Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get { return _settings.Value; } }
|
||||
|
||||
partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings);
|
||||
|
||||
|
||||
partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, string url);
|
||||
partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder);
|
||||
partial void ProcessResponse(System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response);
|
||||
/// <summary>Retrieve or Refresh Access Token</summary>
|
||||
/// <param name="grant_type">Authentication grant type. Use 'password' when authenticating, and 'refresh_token' when refreshing a token.</param>
|
||||
/// <param name="username">Secret Server authentication username. Required when authenticating.</param>
|
||||
/// <param name="password">Secret Server authentication password. Required when authenticating.</param>
|
||||
/// <param name="refresh_token">The refresh token. Required when refreshing a token.</param>
|
||||
/// <returns>Successful retrieval of an access token</returns>
|
||||
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||
public System.Threading.Tasks.Task<TokenResponse> AuthorizeAsync(Grant_type grant_type, string username, string password, string refresh_token, string OTP)
|
||||
{
|
||||
return AuthorizeAsync(grant_type, username, password, refresh_token, System.Threading.CancellationToken.None, OTP);
|
||||
}
|
||||
|
||||
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
|
||||
/// <summary>Retrieve or Refresh Access Token</summary>
|
||||
/// <param name="grant_type">Authentication grant type. Use 'password' when authenticating, and 'refresh_token' when refreshing a token.</param>
|
||||
/// <param name="username">Secret Server authentication username. Required when authenticating.</param>
|
||||
/// <param name="password">Secret Server authentication password. Required when authenticating.</param>
|
||||
/// <param name="refresh_token">The refresh token. Required when refreshing a token.</param>
|
||||
/// <returns>Successful retrieval of an access token</returns>
|
||||
/// <exception cref="ApiException">A server side error occurred.</exception>
|
||||
public async System.Threading.Tasks.Task<TokenResponse> AuthorizeAsync(Grant_type grant_type, string username, string password, string refresh_token, System.Threading.CancellationToken cancellationToken, string OTP)
|
||||
{
|
||||
var urlBuilder_ = new System.Text.StringBuilder();
|
||||
urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/oauth2/token");
|
||||
|
||||
var client_ = _httpClient;
|
||||
var disposeClient_ = false;
|
||||
try
|
||||
{
|
||||
using (var request_ = new System.Net.Http.HttpRequestMessage())
|
||||
{
|
||||
var keyValues_ = new System.Collections.Generic.List<System.Collections.Generic.KeyValuePair<string, string>>();
|
||||
if (grant_type == null)
|
||||
throw new System.ArgumentNullException("grant_type");
|
||||
else
|
||||
keyValues_.Add(new System.Collections.Generic.KeyValuePair<string, string>("grant_type", ConvertToString(grant_type, System.Globalization.CultureInfo.InvariantCulture)));
|
||||
if (username != null)
|
||||
keyValues_.Add(new System.Collections.Generic.KeyValuePair<string, string>("username", ConvertToString(username, System.Globalization.CultureInfo.InvariantCulture)));
|
||||
if (password != null)
|
||||
keyValues_.Add(new System.Collections.Generic.KeyValuePair<string, string>("password", ConvertToString(password, System.Globalization.CultureInfo.InvariantCulture)));
|
||||
if (refresh_token != null)
|
||||
keyValues_.Add(new System.Collections.Generic.KeyValuePair<string, string>("refresh_token", ConvertToString(refresh_token, System.Globalization.CultureInfo.InvariantCulture)));
|
||||
if (OTP != null)
|
||||
request_.Headers.Add("OTP", ConvertToString(OTP, System.Globalization.CultureInfo.InvariantCulture));
|
||||
|
||||
request_.Content = new System.Net.Http.FormUrlEncodedContent(keyValues_);
|
||||
request_.Method = new System.Net.Http.HttpMethod("POST");
|
||||
request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json"));
|
||||
|
||||
PrepareRequest(client_, request_, urlBuilder_);
|
||||
|
||||
var url_ = urlBuilder_.ToString();
|
||||
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
|
||||
|
||||
PrepareRequest(client_, request_, url_);
|
||||
|
||||
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
|
||||
var disposeResponse_ = true;
|
||||
try
|
||||
{
|
||||
var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
|
||||
if (response_.Content != null && response_.Content.Headers != null)
|
||||
{
|
||||
foreach (var item_ in response_.Content.Headers)
|
||||
headers_[item_.Key] = item_.Value;
|
||||
}
|
||||
|
||||
ProcessResponse(client_, response_);
|
||||
|
||||
var status_ = (int)response_.StatusCode;
|
||||
if (status_ == 200)
|
||||
{
|
||||
var objectResponse_ = await ReadObjectResponseAsync<TokenResponse>(response_, headers_, cancellationToken).ConfigureAwait(false);
|
||||
if (objectResponse_.Object == null)
|
||||
{
|
||||
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
|
||||
}
|
||||
|
||||
return objectResponse_.Object;
|
||||
}
|
||||
else
|
||||
if (status_ == 400)
|
||||
{
|
||||
var objectResponse_ = await ReadObjectResponseAsync<TokenErrorResponse>(response_, headers_, cancellationToken).ConfigureAwait(false);
|
||||
if (objectResponse_.Object == null)
|
||||
{
|
||||
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
|
||||
}
|
||||
throw new ApiException<TokenErrorResponse>("An error occurred", status_, objectResponse_.Text, headers_, objectResponse_.Object, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (disposeResponse_)
|
||||
response_.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (disposeClient_)
|
||||
client_.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
protected struct ObjectResponseResult<T>
|
||||
{
|
||||
public ObjectResponseResult(T responseObject, string responseText)
|
||||
{
|
||||
this.Object = responseObject;
|
||||
this.Text = responseText;
|
||||
}
|
||||
|
||||
public T Object { get; }
|
||||
|
||||
public string Text { get; }
|
||||
}
|
||||
|
||||
public bool ReadResponseAsString { get; set; }
|
||||
|
||||
protected virtual async System.Threading.Tasks.Task<ObjectResponseResult<T>> ReadObjectResponseAsync<T>(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IEnumerable<string>> headers, System.Threading.CancellationToken cancellationToken)
|
||||
{
|
||||
if (response == null || response.Content == null)
|
||||
{
|
||||
return new ObjectResponseResult<T>(default(T), string.Empty);
|
||||
}
|
||||
|
||||
if (ReadResponseAsString)
|
||||
{
|
||||
var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
var typedBody = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(responseText, JsonSerializerSettings);
|
||||
return new ObjectResponseResult<T>(typedBody, responseText);
|
||||
}
|
||||
catch (Newtonsoft.Json.JsonException exception)
|
||||
{
|
||||
var message = "Could not deserialize the response body string as " + typeof(T).FullName + ".";
|
||||
throw new ApiException(message, (int)response.StatusCode, responseText, headers, exception);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
|
||||
using (var streamReader = new System.IO.StreamReader(responseStream))
|
||||
using (var jsonTextReader = new Newtonsoft.Json.JsonTextReader(streamReader))
|
||||
{
|
||||
var serializer = Newtonsoft.Json.JsonSerializer.Create(JsonSerializerSettings);
|
||||
var typedBody = serializer.Deserialize<T>(jsonTextReader);
|
||||
return new ObjectResponseResult<T>(typedBody, string.Empty);
|
||||
}
|
||||
}
|
||||
catch (Newtonsoft.Json.JsonException exception)
|
||||
{
|
||||
var message = "Could not deserialize the response body stream as " + typeof(T).FullName + ".";
|
||||
throw new ApiException(message, (int)response.StatusCode, string.Empty, headers, exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string ConvertToString(object value, System.Globalization.CultureInfo cultureInfo)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
if (value is System.Enum)
|
||||
{
|
||||
var name = System.Enum.GetName(value.GetType(), value);
|
||||
if (name != null)
|
||||
{
|
||||
var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name);
|
||||
if (field != null)
|
||||
{
|
||||
var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute))
|
||||
as System.Runtime.Serialization.EnumMemberAttribute;
|
||||
if (attribute != null)
|
||||
{
|
||||
return attribute.Value != null ? attribute.Value : name;
|
||||
}
|
||||
}
|
||||
|
||||
var converted = System.Convert.ToString(System.Convert.ChangeType(value, System.Enum.GetUnderlyingType(value.GetType()), cultureInfo));
|
||||
return converted == null ? string.Empty : converted;
|
||||
}
|
||||
}
|
||||
else if (value is bool)
|
||||
{
|
||||
return System.Convert.ToString((bool)value, cultureInfo).ToLowerInvariant();
|
||||
}
|
||||
else if (value is byte[])
|
||||
{
|
||||
return System.Convert.ToBase64String((byte[])value);
|
||||
}
|
||||
else if (value.GetType().IsArray)
|
||||
{
|
||||
var array = System.Linq.Enumerable.OfType<object>((System.Array)value);
|
||||
return string.Join(",", System.Linq.Enumerable.Select(array, o => ConvertToString(o, cultureInfo)));
|
||||
}
|
||||
|
||||
var result = System.Convert.ToString(value, cultureInfo);
|
||||
return result == null ? "" : result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>API access token response</summary>
|
||||
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")]
|
||||
public partial class TokenResponse
|
||||
{
|
||||
/// <summary>Authentication token</summary>
|
||||
[Newtonsoft.Json.JsonProperty("access_token", Required = Newtonsoft.Json.Required.Always)]
|
||||
[System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
|
||||
public string Access_token { get; set; }
|
||||
|
||||
/// <summary>Authentication token type</summary>
|
||||
[Newtonsoft.Json.JsonProperty("token_type", Required = Newtonsoft.Json.Required.Always)]
|
||||
[System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
|
||||
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
|
||||
public TokenResponseToken_type Token_type { get; set; }
|
||||
|
||||
|
||||
private string _Expires_in;
|
||||
/// <summary>Authentication token expiration time, in seconds</summary>
|
||||
[Newtonsoft.Json.JsonProperty("expires_in", Required = Newtonsoft.Json.Required.Always)]
|
||||
[System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
|
||||
// public string Expires_in { get; set; }
|
||||
|
||||
public string Expires_in
|
||||
{
|
||||
get { return _Expires_in; }
|
||||
set
|
||||
{
|
||||
_Expires_in = value;
|
||||
Expires_on = DateTime.UtcNow.AddSeconds(Double.Parse(value) - 60);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Authentication token expiration time in UTC</summary>
|
||||
public DateTime Expires_on { get; set; }
|
||||
|
||||
/// <summary>Refresh token. This is only provided when the server is set to allow refresh tokens for web services and when the session timeout duration is not set to Unlimited.</summary>
|
||||
[Newtonsoft.Json.JsonProperty("refresh_token", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
|
||||
public string Refresh_token { get; set; }
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>API access token error response</summary>
|
||||
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")]
|
||||
public partial class TokenErrorResponse
|
||||
{
|
||||
/// <summary>Authentication token</summary>
|
||||
[Newtonsoft.Json.JsonProperty("message", Required = Newtonsoft.Json.Required.Always)]
|
||||
[System.ComponentModel.DataAnnotations.Required(AllowEmptyStrings = true)]
|
||||
public string Message { get; set; }
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>Authentication grant type. Use 'password' when authenticating, and 'refresh_token' when refreshing a token.</summary>
|
||||
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")]
|
||||
public enum Grant_type
|
||||
{
|
||||
[System.Runtime.Serialization.EnumMember(Value = @"password")]
|
||||
Password = 0,
|
||||
|
||||
[System.Runtime.Serialization.EnumMember(Value = @"refresh_token")]
|
||||
Refresh_token = 1,
|
||||
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.5.2.0 (Newtonsoft.Json v11.0.0.0)")]
|
||||
public enum TokenResponseToken_type
|
||||
{
|
||||
[System.Runtime.Serialization.EnumMember(Value = @"bearer")]
|
||||
Bearer = 0,
|
||||
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("NSwag", "13.14.8.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")]
|
||||
public partial class ApiException : System.Exception
|
||||
{
|
||||
public int StatusCode { get; private set; }
|
||||
|
||||
public string Response { get; private set; }
|
||||
|
||||
public System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IEnumerable<string>> Headers { get; private set; }
|
||||
|
||||
public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IEnumerable<string>> headers, System.Exception innerException)
|
||||
: base(message + "\n\nStatus: " + statusCode + "\nResponse: \n" + ((response == null) ? "(null)" : response.Substring(0, response.Length >= 512 ? 512 : response.Length)), innerException)
|
||||
{
|
||||
StatusCode = statusCode;
|
||||
Response = response;
|
||||
Headers = headers;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("HTTP Response: \n\n{0}\n\n{1}", Response, base.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
[System.CodeDom.Compiler.GeneratedCode("NSwag", "13.14.8.0 (NJsonSchema v10.5.2.0 (Newtonsoft.Json v11.0.0.0))")]
|
||||
public partial class ApiException<TResult> : ApiException
|
||||
{
|
||||
public TResult Result { get; private set; }
|
||||
|
||||
public ApiException(string message, int statusCode, string response, System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IEnumerable<string>> headers, TResult result, System.Exception innerException)
|
||||
: base(message, statusCode, response, headers, innerException)
|
||||
{
|
||||
Result = result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#pragma warning restore 1591
|
||||
#pragma warning restore 1573
|
||||
#pragma warning restore 472
|
||||
#pragma warning restore 114
|
||||
#pragma warning restore 108
|
||||
#pragma warning restore 3016
|
||||
341
ExternalConnectors/DSS/SecretServerInterface.cs
Normal file
341
ExternalConnectors/DSS/SecretServerInterface.cs
Normal file
@@ -0,0 +1,341 @@
|
||||
using Microsoft.Win32;
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using Org.BouncyCastle.Crypto.Parameters;
|
||||
using Org.BouncyCastle.OpenSsl;
|
||||
using Org.BouncyCastle.Security;
|
||||
using SecretServerAuthentication.DSS;
|
||||
using SecretServerRestClient.DSS;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace ExternalConnectors.DSS;
|
||||
|
||||
public class SecretServerInterface
|
||||
{
|
||||
private static class SSConnectionData
|
||||
{
|
||||
public static string ssUsername = "";
|
||||
public static string ssPassword = "";
|
||||
public static string ssUrl = "";
|
||||
public static string ssOTP = "";
|
||||
public static bool ssSSO = false;
|
||||
public static bool initdone = false;
|
||||
|
||||
//token
|
||||
public static string ssTokenBearer = "";
|
||||
public static DateTime ssTokenExpiresOn = DateTime.UtcNow;
|
||||
public static string ssTokenRefresh = "";
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
if (initdone == true)
|
||||
return;
|
||||
|
||||
RegistryKey key = Registry.CurrentUser.CreateSubKey(@"SOFTWARE\mRemoteSSInterface");
|
||||
try
|
||||
{
|
||||
// display gui and ask for data
|
||||
SSConnectionForm f = new();
|
||||
string? un = key.GetValue("Username") as string;
|
||||
f.tbUsername.Text = un ?? "";
|
||||
f.tbPassword.Text = SSConnectionData.ssPassword; // in OTP refresh cases, this value might already be filled
|
||||
|
||||
string? url = key.GetValue("URL") as string;
|
||||
if (url == null || !url.Contains("://"))
|
||||
url = "https://cred.domain.local/SecretServer";
|
||||
f.tbSSURL.Text = url;
|
||||
|
||||
var b = key.GetValue("SSO");
|
||||
if (b == null || (string)b != "True")
|
||||
ssSSO = false;
|
||||
else
|
||||
ssSSO = true;
|
||||
f.cbUseSSO.Checked = ssSSO;
|
||||
|
||||
// show dialog
|
||||
while (true)
|
||||
{
|
||||
_ = f.ShowDialog();
|
||||
|
||||
if (f.DialogResult != DialogResult.OK)
|
||||
return;
|
||||
|
||||
// store values to memory
|
||||
ssUsername = f.tbUsername.Text;
|
||||
ssPassword = f.tbPassword.Text;
|
||||
ssUrl = f.tbSSURL.Text;
|
||||
ssSSO = f.cbUseSSO.Checked;
|
||||
ssOTP = f.tbOTP.Text;
|
||||
// check connection first
|
||||
try
|
||||
{
|
||||
if (TestCredentials() == true)
|
||||
{
|
||||
initdone = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
MessageBox.Show("Test Credentials failed - please check your credentials");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// write values to registry
|
||||
key.SetValue("Username", ssUsername);
|
||||
key.SetValue("URL", ssUrl);
|
||||
key.SetValue("SSO", ssSSO);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
key.Close();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TestCredentials()
|
||||
{
|
||||
if (SSConnectionData.ssSSO)
|
||||
{
|
||||
// checking creds doesn't really make sense here, as we can't modify them anyway if something is wrong
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (!String.IsNullOrEmpty(GetToken()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static SecretsServiceClient ConstructSecretsServiceClient()
|
||||
{
|
||||
string baseURL = SSConnectionData.ssUrl;
|
||||
if (SSConnectionData.ssSSO)
|
||||
{
|
||||
// REQUIRES IIS CONFIG! https://docs.thycotic.com/ss/11.0.0/api-scripting/webservice-iwa-powershell
|
||||
var handler = new HttpClientHandler() { UseDefaultCredentials = true };
|
||||
var httpClient = new HttpClient(handler);
|
||||
{
|
||||
// Call REST API:
|
||||
return new SecretsServiceClient($"{baseURL}/winauthwebservices/api", httpClient);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var httpClient = new HttpClient();
|
||||
{
|
||||
|
||||
var token = GetToken();
|
||||
// Set credentials (token):
|
||||
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
|
||||
|
||||
// Call REST API:
|
||||
return new SecretsServiceClient($"{baseURL}/api", httpClient);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
private static void FetchSecret(int secretID, out string secretUsername, out string secretPassword, out string secretDomain, out string privatekey)
|
||||
{
|
||||
var client = ConstructSecretsServiceClient();
|
||||
SecretModel secret = client.GetSecretAsync(false, true, secretID, null).Result;
|
||||
|
||||
// clear return variables
|
||||
secretDomain = "";
|
||||
secretUsername = "";
|
||||
secretPassword = "";
|
||||
privatekey = "";
|
||||
string privatekeypassphrase = "";
|
||||
|
||||
// parse data and extract what we need
|
||||
foreach (var item in secret.Items)
|
||||
{
|
||||
if (item.FieldName.ToLower().Equals("domain"))
|
||||
secretDomain = item.ItemValue;
|
||||
else if (item.FieldName.ToLower().Equals("username"))
|
||||
secretUsername = item.ItemValue;
|
||||
else if (item.FieldName.ToLower().Equals("password"))
|
||||
secretPassword = item.ItemValue;
|
||||
else if (item.FieldName.ToLower().Equals("private key"))
|
||||
{
|
||||
client.ReadResponseNoJSONConvert = true;
|
||||
privatekey = client.GetFieldAsync(false, false, secretID, "private-key").Result;
|
||||
client.ReadResponseNoJSONConvert = false;
|
||||
}
|
||||
else if (item.FieldName.ToLower().Equals("private key passphrase"))
|
||||
privatekeypassphrase = item.ItemValue;
|
||||
}
|
||||
|
||||
// need to decode the private key?
|
||||
if (!string.IsNullOrEmpty(privatekeypassphrase))
|
||||
{
|
||||
try
|
||||
{
|
||||
var key = DecodePrivateKey(privatekey, privatekeypassphrase);
|
||||
privatekey = key;
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// conversion to putty format necessary?
|
||||
if (!string.IsNullOrEmpty(privatekey) && !privatekey.StartsWith("PuTTY-User-Key-File-2"))
|
||||
{
|
||||
try
|
||||
{
|
||||
RSACryptoServiceProvider key = ImportPrivateKey(privatekey);
|
||||
privatekey = PuttyKeyFileGenerator.ToPuttyPrivateKey(key);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region PUTTY KEY HANDLING
|
||||
// decode rsa private key with encryption password
|
||||
private static string DecodePrivateKey(string encryptedPrivateKey, string password)
|
||||
{
|
||||
TextReader textReader = new StringReader(encryptedPrivateKey);
|
||||
PemReader pemReader = new(textReader, new PasswordFinder(password));
|
||||
|
||||
AsymmetricCipherKeyPair keyPair = (AsymmetricCipherKeyPair)pemReader.ReadObject();
|
||||
|
||||
TextWriter textWriter = new StringWriter();
|
||||
var pemWriter = new PemWriter(textWriter);
|
||||
pemWriter.WriteObject(keyPair.Private);
|
||||
pemWriter.Writer.Flush();
|
||||
|
||||
return ""+textWriter.ToString();
|
||||
}
|
||||
private class PasswordFinder : IPasswordFinder
|
||||
{
|
||||
private string password;
|
||||
|
||||
public PasswordFinder(string password)
|
||||
{
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
|
||||
public char[] GetPassword()
|
||||
{
|
||||
return password.ToCharArray();
|
||||
}
|
||||
}
|
||||
|
||||
// read private key pem string to rsacryptoserviceprovider
|
||||
public static RSACryptoServiceProvider ImportPrivateKey(string pem)
|
||||
{
|
||||
PemReader pr = new(new StringReader(pem));
|
||||
AsymmetricCipherKeyPair KeyPair = (AsymmetricCipherKeyPair)pr.ReadObject();
|
||||
RSAParameters rsaParams = DotNetUtilities.ToRSAParameters((RsaPrivateCrtKeyParameters)KeyPair.Private);
|
||||
RSACryptoServiceProvider rsa = new();
|
||||
rsa.ImportParameters(rsaParams);
|
||||
return rsa;
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region TOKEN
|
||||
private static string GetToken()
|
||||
{
|
||||
// if there is no token, fetch a fresh one
|
||||
if (String.IsNullOrEmpty(SSConnectionData.ssTokenBearer))
|
||||
{
|
||||
return GetTokenFresh();
|
||||
}
|
||||
// if there is a token, check if it is valid
|
||||
if (SSConnectionData.ssTokenExpiresOn >= DateTime.UtcNow)
|
||||
{
|
||||
return SSConnectionData.ssTokenBearer;
|
||||
}
|
||||
else
|
||||
{
|
||||
// try using refresh token
|
||||
using (var httpClient = new HttpClient())
|
||||
{
|
||||
var tokenClient = new OAuth2ServiceClient(SSConnectionData.ssUrl, httpClient);
|
||||
TokenResponse token = new();
|
||||
try
|
||||
{
|
||||
token = tokenClient.AuthorizeAsync(Grant_type.Refresh_token, null, null, SSConnectionData.ssTokenRefresh, null).Result;
|
||||
var tokenResult = token.Access_token;
|
||||
|
||||
SSConnectionData.ssTokenBearer = tokenResult;
|
||||
SSConnectionData.ssTokenRefresh = token.Refresh_token;
|
||||
SSConnectionData.ssTokenExpiresOn = token.Expires_on;
|
||||
return tokenResult;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// refresh token failed. clean memory and start fresh
|
||||
SSConnectionData.ssTokenBearer = "";
|
||||
SSConnectionData.ssTokenRefresh = "";
|
||||
SSConnectionData.ssTokenExpiresOn = DateTime.Now;
|
||||
// if OTP is required we need to ask user for a new OTP
|
||||
if (!String.IsNullOrEmpty(SSConnectionData.ssOTP))
|
||||
{
|
||||
SSConnectionData.initdone = false;
|
||||
// the call below executes a connection test, which fetches a valid token
|
||||
SSConnectionData.Init();
|
||||
// we now have a fresh token in memory. return it to caller
|
||||
return SSConnectionData.ssTokenBearer;
|
||||
}
|
||||
else
|
||||
{
|
||||
// no user interaction required. get a fresh token and return it to caller
|
||||
return GetTokenFresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
static string GetTokenFresh()
|
||||
{
|
||||
using (var httpClient = new HttpClient())
|
||||
{
|
||||
// Authenticate:
|
||||
var tokenClient = new OAuth2ServiceClient(SSConnectionData.ssUrl, httpClient);
|
||||
// call below will throw an exception if the creds are invalid
|
||||
var token = tokenClient.AuthorizeAsync(Grant_type.Password, SSConnectionData.ssUsername, SSConnectionData.ssPassword, null, SSConnectionData.ssOTP).Result;
|
||||
// here we can be sure the creds are ok - return success state
|
||||
var tokenResult = token.Access_token;
|
||||
|
||||
SSConnectionData.ssTokenBearer = tokenResult;
|
||||
SSConnectionData.ssTokenRefresh = token.Refresh_token;
|
||||
SSConnectionData.ssTokenExpiresOn = token.Expires_on;
|
||||
return tokenResult;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
// input must be the secret id to fetch
|
||||
public static void FetchSecretFromServer(string input, out string username, out string password, out string domain, out string privatekey)
|
||||
{
|
||||
// get secret id
|
||||
int secretID = Int32.Parse(input);
|
||||
|
||||
// init connection credentials, display popup if necessary
|
||||
SSConnectionData.Init();
|
||||
|
||||
// get the secret
|
||||
FetchSecret(secretID, out username, out password, out domain, out privatekey);
|
||||
}
|
||||
}
|
||||
130353
ExternalConnectors/DSS/SecretServerRestClient.cs
Normal file
130353
ExternalConnectors/DSS/SecretServerRestClient.cs
Normal file
File diff suppressed because it is too large
Load Diff
31
ExternalConnectors/ExternalConnectors.csproj
Normal file
31
ExternalConnectors/ExternalConnectors.csproj
Normal file
@@ -0,0 +1,31 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0-windows7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<OutputType>Library</OutputType>
|
||||
<UseWindowsForms>True</UseWindowsForms>
|
||||
<Platforms>x64</Platforms>
|
||||
<Configurations>Debug;Release;Debug Portable;Release Portable;Deploy to github</Configurations>
|
||||
<SupportedOSPlatformVersion>7.0</SupportedOSPlatformVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Portable|x64'">
|
||||
<Optimize>True</Optimize>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AWSSDK.Core" Version="3.7.401.8" />
|
||||
<PackageReference Include="AWSSDK.EC2" Version="3.7.429.3" />
|
||||
<PackageReference Include="BouncyCastle.Cryptography" Version="2.5.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="AWS\AWSConnectionForm.cs" />
|
||||
<Compile Update="CPS\CPSConnectionForm.cs" />
|
||||
<Compile Update="DSS\SSConnectionForm.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
111
ExternalConnectors/PuttyKeyFileGenerator.cs
Normal file
111
ExternalConnectors/PuttyKeyFileGenerator.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ExternalConnectors;
|
||||
|
||||
public class PuttyKeyFileGenerator
|
||||
{
|
||||
private const int prefixSize = 4;
|
||||
private const int paddedPrefixSize = prefixSize + 1;
|
||||
private const int lineLength = 64;
|
||||
private const string keyType = "ssh-rsa";
|
||||
private const string encryptionType = "none";
|
||||
|
||||
public static string ToPuttyPrivateKey(RSACryptoServiceProvider cryptoServiceProvider, string Comment = "imported-openssh-key")
|
||||
{
|
||||
var publicParameters = cryptoServiceProvider.ExportParameters(false);
|
||||
byte[] publicBuffer = new byte[3 + keyType.Length + GetPrefixSize(publicParameters.Exponent) + publicParameters.Exponent!.Length + GetPrefixSize(publicParameters.Modulus) + publicParameters.Modulus!.Length + 1];
|
||||
|
||||
using (var bw = new BinaryWriter(new MemoryStream(publicBuffer)))
|
||||
{
|
||||
bw.Write(new byte[] { 0x00, 0x00, 0x00 });
|
||||
bw.Write(Encoding.ASCII.GetBytes(keyType));
|
||||
PutPrefixed(bw, publicParameters.Exponent, CheckIsNeddPadding(publicParameters.Exponent));
|
||||
PutPrefixed(bw, publicParameters.Modulus, CheckIsNeddPadding(publicParameters.Modulus));
|
||||
}
|
||||
var publicBlob = System.Convert.ToBase64String(publicBuffer);
|
||||
|
||||
var privateParameters = cryptoServiceProvider.ExportParameters(true);
|
||||
|
||||
byte[] privateBuffer = new byte[paddedPrefixSize + privateParameters.D!.Length + paddedPrefixSize + privateParameters.P!.Length + paddedPrefixSize + privateParameters.Q!.Length + paddedPrefixSize + privateParameters.InverseQ!.Length];
|
||||
|
||||
using (var bw = new BinaryWriter(new MemoryStream(privateBuffer)))
|
||||
{
|
||||
PutPrefixed(bw, privateParameters.D, true);
|
||||
PutPrefixed(bw, privateParameters.P, true);
|
||||
PutPrefixed(bw, privateParameters.Q, true);
|
||||
PutPrefixed(bw, privateParameters.InverseQ, true);
|
||||
}
|
||||
var privateBlob = System.Convert.ToBase64String(privateBuffer);
|
||||
|
||||
HMACSHA1 hmacSha1 = new(SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes("putty-private-key-file-mac-key")));
|
||||
byte[] bytesToHash = new byte[prefixSize + keyType.Length + prefixSize + encryptionType.Length + prefixSize + Comment.Length + prefixSize + publicBuffer.Length + prefixSize + privateBuffer.Length];
|
||||
|
||||
using (var bw = new BinaryWriter(new MemoryStream(bytesToHash)))
|
||||
{
|
||||
PutPrefixed(bw, Encoding.ASCII.GetBytes(keyType));
|
||||
PutPrefixed(bw, Encoding.ASCII.GetBytes(encryptionType));
|
||||
PutPrefixed(bw, Encoding.ASCII.GetBytes(Comment));
|
||||
PutPrefixed(bw, publicBuffer);
|
||||
PutPrefixed(bw, privateBuffer);
|
||||
}
|
||||
|
||||
var hash = string.Join("", hmacSha1.ComputeHash(bytesToHash).Select(x => $"{x:x2}"));
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("PuTTY-User-Key-File-2: " + keyType);
|
||||
sb.AppendLine("Encryption: " + encryptionType);
|
||||
sb.AppendLine("Comment: " + Comment);
|
||||
|
||||
var publicLines = SpliceText(publicBlob, lineLength);
|
||||
sb.AppendLine("Public-Lines: " + publicLines.Length);
|
||||
foreach (var line in publicLines)
|
||||
{
|
||||
sb.AppendLine(line);
|
||||
}
|
||||
|
||||
var privateLines = SpliceText(privateBlob, lineLength);
|
||||
sb.AppendLine("Private-Lines: " + privateLines.Length);
|
||||
foreach (var line in privateLines)
|
||||
{
|
||||
sb.AppendLine(line);
|
||||
}
|
||||
|
||||
sb.AppendLine("Private-MAC: " + hash);
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static void PutPrefixed(BinaryWriter bw, byte[] bytes, bool addLeadingNull = false)
|
||||
{
|
||||
bw.Write(BitConverter.GetBytes(bytes.Length + (addLeadingNull ? 1 : 0)).Reverse().ToArray());
|
||||
if (addLeadingNull)
|
||||
bw.Write(new byte[] { 0x00 });
|
||||
bw.Write(bytes);
|
||||
}
|
||||
|
||||
private static string[] SpliceText(string text, int lineLength)
|
||||
{
|
||||
return Regex.Matches(text, ".{1," + lineLength + "}").Cast<Match>().Select(m => m.Value).ToArray();
|
||||
}
|
||||
|
||||
private static int GetPrefixSize(byte[]? bytes)
|
||||
{
|
||||
if (bytes is null)
|
||||
return 0;
|
||||
|
||||
return CheckIsNeddPadding(bytes) ? paddedPrefixSize : prefixSize;
|
||||
}
|
||||
|
||||
private static bool CheckIsNeddPadding(byte[] bytes)
|
||||
{
|
||||
if (bytes is null || bytes.Length == 0)
|
||||
return false;
|
||||
|
||||
// 128 == 10000000
|
||||
// This means that the number of bits can be divided by 8.
|
||||
// According to the algorithm in putty, you need to add a padding.
|
||||
return bytes[0] >= 128;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,296 +0,0 @@
|
||||
; ---------------------
|
||||
; DotNetVer.nsh
|
||||
; Written by: David Grinberg
|
||||
; Homepage: http://ontheperiphery.veraida.com/
|
||||
; Updated By: Brandon Hansen (http://www.remotehams.com/)
|
||||
; ---------------------
|
||||
;
|
||||
; LogicLib extensions for checking Microsoft .NET Framework versions and service packs.
|
||||
;
|
||||
; Latests Updates by Brandon Hansen, KG6YPI (RemoteHams.com)
|
||||
; Dec 26, 2011 - .NET Framework 4.0 detection fixes - client profile not being found
|
||||
; Dec 07, 2010 - .NET Framework 4.0 detection added by Brandon Hansen (KG6YPI)
|
||||
;
|
||||
; Usage examples:
|
||||
;
|
||||
; ${If} ${HasDotNet4.0}
|
||||
; DetailPrint "Microsoft .NET Framework 4.0 installed."
|
||||
; ${If} ${DOTNETVER_4_0} AtLeastDotNetServicePack 1
|
||||
; DetailPrint "Microsoft .NET Framework 4.0 is at least SP1."
|
||||
; ${Else}
|
||||
; DetailPrint "Microsoft .NET Framework 4.0 SP1 not installed."
|
||||
; ${EndIf}
|
||||
; ${If} ${DOTNETVER_4_0} HasDotNetClientProfile 1
|
||||
; DetailPrint "Microsoft .NET Framework 4.0 (Client Profile) available."
|
||||
; ${EndIf}
|
||||
; ${If} ${DOTNETVER_4_0} HasDotNetFullProfile 1
|
||||
; DetailPrint "Microsoft .NET Framework 4.0 (Full Profile) available."
|
||||
; ${EndIf}
|
||||
; ${If} ${DOTNETVER_4_0} HasDotNetFullProfile 0
|
||||
; DetailPrint "Microsoft .NET Framework 4.0 (Full Profile) not available."
|
||||
; ${EndIf}
|
||||
; ${EndIf}
|
||||
|
||||
|
||||
!verbose push
|
||||
!verbose 3
|
||||
|
||||
!ifndef ___DOTNETVER__NSH___
|
||||
!define ___DOTNETVER__NSH___
|
||||
|
||||
!include LogicLib.nsh
|
||||
!include Util.nsh
|
||||
|
||||
# constants
|
||||
|
||||
!define DOTNETVER_1_0 "1.0"
|
||||
!define DOTNETVER_1_1 "1.1"
|
||||
!define DOTNETVER_2_0 "2.0"
|
||||
!define DOTNETVER_3_0 "3.0"
|
||||
!define DOTNETVER_3_5 "3.5"
|
||||
!define DOTNETVER_4_0 "4.0"
|
||||
|
||||
# variable declaration
|
||||
|
||||
Var /GLOBAL __DONTNET_FOUNDVER
|
||||
|
||||
!macro __DotNetVer_DeclareVars
|
||||
!ifndef __DOTNETVER_VARS_DECLARED
|
||||
!define __DOTNETVER_VARS_DECLARED
|
||||
Var /GLOBAL __DOTNET_1.0
|
||||
Var /GLOBAL __DOTNET_1.1
|
||||
Var /GLOBAL __DOTNET_2.0
|
||||
Var /GLOBAL __DOTNET_3.0
|
||||
Var /GLOBAL __DOTNET_3.5
|
||||
Var /GLOBAL __DOTNET_4.0
|
||||
|
||||
Var /GLOBAL __DOTNETVER_1.0_SP
|
||||
Var /GLOBAL __DOTNETVER_1.1_SP
|
||||
Var /GLOBAL __DOTNETVER_2.0_SP
|
||||
Var /GLOBAL __DOTNETVER_3.0_SP
|
||||
Var /GLOBAL __DOTNETVER_3.5_SP
|
||||
Var /GLOBAL __DOTNETVER_4.0_SP
|
||||
|
||||
Var /GLOBAL __DOTNET_1.0_CLIENT
|
||||
Var /GLOBAL __DOTNET_1.1_CLIENT
|
||||
Var /GLOBAL __DOTNET_2.0_CLIENT
|
||||
Var /GLOBAL __DOTNET_3.0_CLIENT
|
||||
Var /GLOBAL __DOTNET_3.5_CLIENT
|
||||
Var /GLOBAL __DOTNET_4.0_CLIENT
|
||||
|
||||
Var /GLOBAL __DOTNET_1.0_FULL
|
||||
Var /GLOBAL __DOTNET_1.1_FULL
|
||||
Var /GLOBAL __DOTNET_2.0_FULL
|
||||
Var /GLOBAL __DOTNET_3.0_FULL
|
||||
Var /GLOBAL __DOTNET_3.5_FULL
|
||||
Var /GLOBAL __DOTNET_4.0_FULL
|
||||
|
||||
StrCpy $__DOTNET_1.0 0
|
||||
StrCpy $__DOTNET_1.1 0
|
||||
StrCpy $__DOTNET_2.0 0
|
||||
StrCpy $__DOTNET_3.0 0
|
||||
StrCpy $__DOTNET_3.5 0
|
||||
StrCpy $__DOTNET_4.0 0
|
||||
|
||||
StrCpy $__DOTNETVER_1.0_SP 0
|
||||
StrCpy $__DOTNETVER_1.1_SP 0
|
||||
StrCpy $__DOTNETVER_2.0_SP 0
|
||||
StrCpy $__DOTNETVER_3.0_SP 0
|
||||
StrCpy $__DOTNETVER_3.5_SP 0
|
||||
StrCpy $__DOTNETVER_4.0_SP 0
|
||||
|
||||
StrCpy $__DOTNET_1.0_CLIENT 0
|
||||
StrCpy $__DOTNET_1.1_CLIENT 0
|
||||
StrCpy $__DOTNET_2.0_CLIENT 0
|
||||
StrCpy $__DOTNET_3.0_CLIENT 0
|
||||
StrCpy $__DOTNET_3.5_CLIENT 0
|
||||
StrCpy $__DOTNET_4.0_CLIENT 0
|
||||
|
||||
StrCpy $__DOTNET_1.0_FULL 0
|
||||
StrCpy $__DOTNET_1.1_FULL 0
|
||||
StrCpy $__DOTNET_2.0_FULL 0
|
||||
StrCpy $__DOTNET_3.0_FULL 0
|
||||
StrCpy $__DOTNET_3.5_FULL 0
|
||||
StrCpy $__DOTNET_4.0_FULL 0
|
||||
|
||||
!endif
|
||||
!macroend
|
||||
|
||||
|
||||
# lazy initialization macro
|
||||
|
||||
!macro __DotNetVer_InitVars
|
||||
# only calculate version once
|
||||
StrCmp $__DONTNET_FOUNDVER "" dotnetver.noveryet
|
||||
Return
|
||||
|
||||
dotnetver.noveryet:
|
||||
!insertmacro __DotNetVer_DeclareVars
|
||||
|
||||
Push $0 ;registry count
|
||||
Push $1 ;registry key
|
||||
Push $2 ;version number
|
||||
Push $3 ;installed
|
||||
Push $4 ;service pack number
|
||||
Push $8 ;strLen helper var
|
||||
|
||||
StrCpy $0 0
|
||||
|
||||
dotnetver.startenum:
|
||||
|
||||
EnumRegKey $1 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP" $0
|
||||
StrCmp $1 "" dotnetver.done
|
||||
|
||||
IntOp $0 $0 + 1
|
||||
|
||||
StrCpy $2 $1 1 0
|
||||
StrCmp $2 "v" +1 dotnetver.startenum
|
||||
StrCpy $2 $1 3 1
|
||||
|
||||
; Check for .NET 1.0 to 3.5
|
||||
ReadRegDWORD $3 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\$1" "Install"
|
||||
ReadRegDWORD $4 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\$1" "SP"
|
||||
IntCmp $3 1 dotnetcheck.skipalt
|
||||
; Alternate check for versions that don't set the Install key
|
||||
ReadRegDWORD $3 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\$1\Setup" "InstallSuccess"
|
||||
dotnetcheck.skipalt:
|
||||
; This is a sanity check that works on .NET 1.0 to 3.5
|
||||
; if it fails check for dotnet 4
|
||||
IntCmp $3 0 dotnetcheck.40
|
||||
StrCmp $2 ${DOTNETVER_1_0} dotnetver.10
|
||||
StrCmp $2 ${DOTNETVER_1_1} dotnetver.11
|
||||
StrCmp $2 ${DOTNETVER_2_0} dotnetver.20
|
||||
StrCmp $2 ${DOTNETVER_3_0} dotnetver.30
|
||||
StrCmp $2 ${DOTNETVER_3_5} dotnetver.35
|
||||
dotnetcheck.40:
|
||||
StrCmp $2 ${DOTNETVER_4_0} dotnetver.40
|
||||
StrCmp $2 "4" dotnetver.40
|
||||
|
||||
Goto dotnetver.startenum
|
||||
|
||||
dotnetver.10:
|
||||
StrCpy $__DOTNET_1.0 1
|
||||
StrCpy $__DOTNETVER_1.0_SP $4
|
||||
StrCpy $__DOTNET_1.0_FULL 1
|
||||
Goto dotnetver.startenum
|
||||
dotnetver.11:
|
||||
StrCpy $__DOTNET_1.1 1
|
||||
StrCpy $__DOTNETVER_1.1_SP $4
|
||||
StrCpy $__DOTNET_1.1_FULL 1
|
||||
Goto dotnetver.startenum
|
||||
dotnetver.20:
|
||||
StrCpy $__DOTNET_2.0 1
|
||||
StrCpy $__DOTNETVER_2.0_SP $4
|
||||
StrCpy $__DOTNET_2.0_FULL 1
|
||||
Goto dotnetver.startenum
|
||||
dotnetver.30:
|
||||
StrCpy $__DOTNET_3.0 1
|
||||
StrCpy $__DOTNETVER_3.0_SP $4
|
||||
StrCpy $__DOTNET_3.0_FULL 1
|
||||
Goto dotnetver.startenum
|
||||
dotnetver.35:
|
||||
StrCpy $__DOTNET_3.5 1
|
||||
StrCpy $__DOTNETVER_3.5_SP $4
|
||||
StrCpy $__DOTNET_3.5_FULL 1
|
||||
Goto dotnetver.startenum
|
||||
dotnetver.40:
|
||||
; Check for .NET 4.0 (Full Profile)
|
||||
ReadRegDWORD $3 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" "Install"
|
||||
ReadRegDWORD $4 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" "SP"
|
||||
StrLen $8 $3
|
||||
IntCmp $8 0 dotnetcheck.40c
|
||||
IntCmp $3 0 dotnetcheck.40c
|
||||
StrCmp $2 ${DOTNETVER_4_0} dotnetver.40_Full
|
||||
StrCmp $2 "4" dotnetver.40_Full
|
||||
dotnetcheck.40c:
|
||||
; Check for .NET 4.0 (Client Profile)
|
||||
ReadRegDWORD $3 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Client" "Install"
|
||||
ReadRegDWORD $4 HKLM "SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Client" "SP"
|
||||
StrLen $8 $3
|
||||
IntCmp $8 0 dotnetver.startenum
|
||||
IntCmp $3 0 dotnetver.startenum
|
||||
StrCmp $2 ${DOTNETVER_4_0} dotnetver.40_Client
|
||||
StrCmp $2 "4" dotnetver.40_Client
|
||||
Goto dotnetver.startenum
|
||||
dotnetver.40_Full:
|
||||
StrCpy $__DOTNET_4.0 1
|
||||
StrCpy $__DOTNETVER_4.0_SP $4
|
||||
StrCpy $__DOTNET_4.0_FULL 1
|
||||
Goto dotnetcheck.40c ; continue looking for other profiles
|
||||
dotnetver.40_Client:
|
||||
StrCpy $__DOTNET_4.0 1
|
||||
StrCpy $__DOTNETVER_4.0_SP $4
|
||||
StrCpy $__DOTNET_4.0_CLIENT 1
|
||||
Goto dotnetver.startenum
|
||||
|
||||
dotnetver.done:
|
||||
|
||||
StrCpy $__DONTNET_FOUNDVER "1"
|
||||
|
||||
Pop $8
|
||||
Pop $4
|
||||
Pop $3
|
||||
Pop $2
|
||||
Pop $1
|
||||
Pop $0
|
||||
!macroend
|
||||
|
||||
!macro _HasDotNet _a _b _t _f
|
||||
${CallArtificialFunction} __DotNetVer_InitVars
|
||||
|
||||
!insertmacro _= `$__DOTNET_${_b}` `1` `${_t}` `${_f}`
|
||||
!macroend
|
||||
|
||||
!macro __DotNetVer_DefineTest Ver
|
||||
!define HasDotNet${Ver} `"" HasDotNet ${Ver}`
|
||||
!macroend
|
||||
|
||||
!insertmacro __DotNetVer_DefineTest ${DOTNETVER_1_0}
|
||||
!insertmacro __DotNetVer_DefineTest ${DOTNETVER_1_1}
|
||||
!insertmacro __DotNetVer_DefineTest ${DOTNETVER_2_0}
|
||||
!insertmacro __DotNetVer_DefineTest ${DOTNETVER_3_0}
|
||||
!insertmacro __DotNetVer_DefineTest ${DOTNETVER_3_5}
|
||||
!insertmacro __DotNetVer_DefineTest ${DOTNETVER_4_0}
|
||||
|
||||
!macro _AtLeastDotNetServicePack _a _b _t _f
|
||||
${CallArtificialFunction} __DotNetVer_InitVars
|
||||
|
||||
!insertmacro _>= `$__DOTNETVER_${_a}_SP` `${_b}` `${_t}` `${_f}`
|
||||
!macroend
|
||||
!define AtLeastDotNetServicePack `AtLeastDotNetServicePack`
|
||||
|
||||
|
||||
!macro _AtMostDotNetServicePack _a _b _t _f
|
||||
${CallArtificialFunction} __DotNetVer_InitVars
|
||||
|
||||
!insertmacro _<= `$__DOTNETVER_${_a}_SP` `${_b}` `${_t}` `${_f}`
|
||||
!macroend
|
||||
!define AtMostDotNetServicePack `AtMostDotNetServicePack`
|
||||
|
||||
|
||||
!macro _IsDotNetServicePack _a _b _t _f
|
||||
${CallArtificialFunction} __DotNetVer_InitVars
|
||||
|
||||
!insertmacro _= `$__DOTNETVER_${_a}_SP` `${_b}` `${_t}` `${_f}`
|
||||
!macroend
|
||||
!define IsDotNetServicePack `IsDotNetServicePack`
|
||||
|
||||
!macro _HasDotNetClientProfile _a _b _t _f
|
||||
${CallArtificialFunction} __DotNetVer_InitVars
|
||||
|
||||
!insertmacro _= `$__DOTNET_${_a}_CLIENT` `${_b}` `${_t}` `${_f}`
|
||||
!macroend
|
||||
!define HasDotNetClientProfile `HasDotNetClientProfile`
|
||||
|
||||
!macro _HasDotNetFullProfile _a _b _t _f
|
||||
${CallArtificialFunction} __DotNetVer_InitVars
|
||||
|
||||
!insertmacro _= `$__DOTNET_${_a}_FULL` `${_b}` `${_t}` `${_f}`
|
||||
!macroend
|
||||
!define HasDotNetFullProfile `HasDotNetFullProfile`
|
||||
|
||||
# done
|
||||
|
||||
!endif # !___DOTNETVER__NSH___
|
||||
|
||||
!verbose pop
|
||||
@@ -1,22 +0,0 @@
|
||||
; Czech installer translation
|
||||
|
||||
; Start mRemoteNG after installation
|
||||
LangString LaunchMremoteNow ${LANG_CZECH} "mRemoteNG run CZECH message"
|
||||
|
||||
; Installer Language
|
||||
LangString InstallerLanguage ${LANG_CZECH} "Installer Language"
|
||||
|
||||
; Select installer Language
|
||||
LangString SelectInstallerLanguage ${LANG_CZECH} "Please select the language of the installer"
|
||||
|
||||
; Requires .NET Framework
|
||||
LangString RequiresNetFramework ${LANG_CZECH} "mRemoteNG requires Microsoft .NET Framework 3.0."
|
||||
|
||||
; User needs to be Admin
|
||||
LangString RequiresAdminUser ${LANG_CZECH} "You must be a member of the 'Power Users' or 'Administrators' group to install mRemoteNG."
|
||||
|
||||
; Start Menu items
|
||||
LangString CreditsLinkName ${LANG_CZECH} "Credits"
|
||||
LangString CopyingLinkName ${LANG_CZECH} "License"
|
||||
LangString UninstallLinkName ${LANG_CZECH} "Uninstall"
|
||||
LangString ChangeLogLinkName ${LANG_CZECH} "Version History"
|
||||
@@ -1,22 +0,0 @@
|
||||
; Dutch installer translation
|
||||
|
||||
; Start mRemoteNG after installation
|
||||
LangString LaunchMremoteNow ${LANG_DUTCH} "mRemoteNG run DUTCH message"
|
||||
|
||||
; Installer Language
|
||||
LangString InstallerLanguage ${LANG_DUTCH} "Installer Language"
|
||||
|
||||
; Select installer Language
|
||||
LangString SelectInstallerLanguage ${LANG_DUTCH} "Please select the language of the installer"
|
||||
|
||||
; Requires .NET Framework
|
||||
LangString RequiresNetFramework ${LANG_DUTCH} "mRemoteNG requires Microsoft .NET Framework 3.0."
|
||||
|
||||
; User needs to be Admin
|
||||
LangString RequiresAdminUser ${LANG_DUTCH} "You must be a member of the 'Power Users' or 'Administrators' group to install mRemoteNG."
|
||||
|
||||
; Start Menu items
|
||||
LangString CreditsLinkName ${LANG_DUTCH} "Credits"
|
||||
LangString CopyingLinkName ${LANG_DUTCH} "License"
|
||||
LangString UninstallLinkName ${LANG_DUTCH} "Uninstall"
|
||||
LangString ChangeLogLinkName ${LANG_DUTCH} "Version History"
|
||||
@@ -1,22 +0,0 @@
|
||||
; English installer translation
|
||||
|
||||
; Start mRemoteNG after installation
|
||||
LangString LaunchMremoteNow ${LANG_ENGLISH} "Launch mRemoteNG Now"
|
||||
|
||||
; Installer Language
|
||||
LangString InstallerLanguage ${LANG_ENGLISH} "Installer Language"
|
||||
|
||||
; Select installer Language
|
||||
LangString SelectInstallerLanguage ${LANG_ENGLISH} "Please select the language of the installer"
|
||||
|
||||
; Requires .NET Framework
|
||||
LangString RequiresNetFramework ${LANG_ENGLISH} "mRemoteNG requires Microsoft .NET Framework 3.0."
|
||||
|
||||
; User needs to be Admin
|
||||
LangString RequiresAdminUser ${LANG_ENGLISH} "You must be a member of the 'Power Users' or 'Administrators' group to install mRemoteNG."
|
||||
|
||||
; Start Menu items
|
||||
LangString CreditsLinkName ${LANG_ENGLISH} "Credits"
|
||||
LangString CopyingLinkName ${LANG_ENGLISH} "License"
|
||||
LangString UninstallLinkName ${LANG_ENGLISH} "Uninstall"
|
||||
LangString ChangeLogLinkName ${LANG_ENGLISH} "Version History"
|
||||
@@ -1,22 +0,0 @@
|
||||
; French installer translation
|
||||
|
||||
; Start mRemoteNG after installation
|
||||
LangString LaunchMremoteNow ${LANG_FRENCH} "mRemoteNG run FRENCH message"
|
||||
|
||||
; Installer Language
|
||||
LangString InstallerLanguage ${LANG_FRENCH} "Installer Language"
|
||||
|
||||
; Select installer Language
|
||||
LangString SelectInstallerLanguage ${LANG_FRENCH} "Please select the language of the installer"
|
||||
|
||||
; Requires .NET Framework
|
||||
LangString RequiresNetFramework ${LANG_FRENCH} "mRemoteNG requires Microsoft .NET Framework 3.0."
|
||||
|
||||
; User needs to be Admin
|
||||
LangString RequiresAdminUser ${LANG_FRENCH} "You must be a member of the 'Power Users' or 'Administrators' group to install mRemoteNG."
|
||||
|
||||
; Start Menu items
|
||||
LangString CreditsLinkName ${LANG_FRENCH} "Credits"
|
||||
LangString CopyingLinkName ${LANG_FRENCH} "License"
|
||||
LangString UninstallLinkName ${LANG_FRENCH} "Uninstall"
|
||||
LangString ChangeLogLinkName ${LANG_FRENCH} "Version History"
|
||||
@@ -1,22 +0,0 @@
|
||||
; German installer translation
|
||||
|
||||
; Start mRemoteNG after installation
|
||||
LangString LaunchMremoteNow ${LANG_GERMAN} "mRemoteNG jetzt Starten"
|
||||
|
||||
; Installer Language
|
||||
LangString InstallerLanguage ${LANG_GERMAN} "Installationsprogamm Sprache"
|
||||
|
||||
; Select installer Language
|
||||
LangString SelectInstallerLanguage ${LANG_GERMAN} "Bitte w<>hlen Sie die Sprache f<>r das Installationsprogramm"
|
||||
|
||||
; Requires .NET Framework
|
||||
LangString RequiresNetFramework ${LANG_GERMAN} "mRemoteNG ben<65>tigt das Microsoft .NET Framework 3.0."
|
||||
|
||||
; User needs to be Admin
|
||||
LangString RequiresAdminUser ${LANG_GERMAN} "Sie m<>ssen Mitglied der Grupper 'Power Users' or 'Administratoren' sein, damit Sie mRemoteNG installieren k<>nnen."
|
||||
|
||||
; Start Menu items
|
||||
LangString CreditsLinkName ${LANG_GERMAN} "Credits"
|
||||
LangString CopyingLinkName ${LANG_GERMAN} "License"
|
||||
LangString UninstallLinkName ${LANG_GERMAN} "Uninstall"
|
||||
LangString ChangeLogLinkName ${LANG_GERMAN} "Version History"
|
||||
@@ -1,31 +0,0 @@
|
||||
!define LanguageNameEnglish "English"
|
||||
!insertmacro MUI_LANGUAGE "English"
|
||||
!include "Language\english.nsi"
|
||||
|
||||
!define LanguageNameCzech "Čeština"
|
||||
!insertmacro MUI_LANGUAGE "Czech"
|
||||
!include "Language\czech.nsi"
|
||||
|
||||
!define LanguageNameGerman "Deutsch"
|
||||
!insertmacro MUI_LANGUAGE "German"
|
||||
!include "Language\german.nsi"
|
||||
|
||||
!define LanguageNameSpanish "Español"
|
||||
!insertmacro MUI_LANGUAGE "Spanish"
|
||||
!include "Language\spanish.nsi"
|
||||
|
||||
!define LanguageNameFrench "Français"
|
||||
!insertmacro MUI_LANGUAGE "French"
|
||||
!include "Language\french.nsi"
|
||||
|
||||
!define LanguageNameDutch "Nederlands"
|
||||
!insertmacro MUI_LANGUAGE "Dutch"
|
||||
!include "Language\dutch.nsi"
|
||||
|
||||
!define LanguageNamePolish "Polski"
|
||||
!insertmacro MUI_LANGUAGE "Polish"
|
||||
!include "Language\polish.nsi"
|
||||
|
||||
!define LanguageNameThai "ภาษาไทย"
|
||||
!insertmacro MUI_LANGUAGE "Thai"
|
||||
!include "Language\thai.nsi"
|
||||
@@ -1,22 +0,0 @@
|
||||
; Polish installer translation
|
||||
|
||||
; Start mRemoteNG after installation
|
||||
LangString LaunchMremoteNow ${LANG_POLISH} "mRemoteNG run POLISH message"
|
||||
|
||||
; Installer Language
|
||||
LangString InstallerLanguage ${LANG_POLISH} "Installer Language"
|
||||
|
||||
; Select installer Language
|
||||
LangString SelectInstallerLanguage ${LANG_POLISH} "Please select the language of the installer"
|
||||
|
||||
; Requires .NET Framework
|
||||
LangString RequiresNetFramework ${LANG_POLISH} "mRemoteNG requires Microsoft .NET Framework 3.0."
|
||||
|
||||
; User needs to be Admin
|
||||
LangString RequiresAdminUser ${LANG_POLISH} "You must be a member of the 'Power Users' or 'Administrators' group to install mRemoteNG."
|
||||
|
||||
; Start Menu items
|
||||
LangString CreditsLinkName ${LANG_POLISH} "Credits"
|
||||
LangString CopyingLinkName ${LANG_POLISH} "License"
|
||||
LangString UninstallLinkName ${LANG_POLISH} "Uninstall"
|
||||
LangString ChangeLogLinkName ${LANG_POLISH} "Version History"
|
||||
@@ -1,22 +0,0 @@
|
||||
; Spanish installer translation
|
||||
|
||||
; Start mRemoteNG after installation
|
||||
LangString LaunchMremoteNow ${LANG_SPANISH} "Iniciando de mRemoteNG"
|
||||
|
||||
; Installer Language
|
||||
LangString InstallerLanguage ${LANG_SPANISH} "Lenguaje de Instalación"
|
||||
|
||||
; Select installer Language
|
||||
LangString SelectInstallerLanguage ${LANG_SPANISH} "Seleccione el lenguaje de instalación"
|
||||
|
||||
; Requires .NET Framework
|
||||
LangString RequiresNetFramework ${LANG_SPANISH} "mRemoteNG requiere Microsoft .NET Framework 3.0."
|
||||
|
||||
; User needs to be Admin
|
||||
LangString RequiresAdminUser ${LANG_SPANISH} "Debe ser miembro del grupo 'Administradores' para poder instalar mRemoteNG."
|
||||
|
||||
; Start Menu items
|
||||
LangString CreditsLinkName ${LANG_SPANISH} "Créditos"
|
||||
LangString CopyingLinkName ${LANG_SPANISH} "Licencia"
|
||||
LangString UninstallLinkName ${LANG_SPANISH} "Desinstalar"
|
||||
LangString ChangeLogLinkName ${LANG_SPANISH} "Historial de Versiones"
|
||||
@@ -1,23 +0,0 @@
|
||||
; Thai installer translation
|
||||
; Contributed by Apisitt Rattana
|
||||
|
||||
; Start mRemoteNG after installation
|
||||
LangString LaunchMremoteNow ${LANG_THAI} "ขณะนี้กำลังติดตั้ง mRemoteNG"
|
||||
|
||||
; Installer Language
|
||||
LangString InstallerLanguage ${LANG_THAI} "ภาษาสำหรับการติดตั้ง"
|
||||
|
||||
; Select installer Language
|
||||
LangString SelectInstallerLanguage ${LANG_THAI} "กรุณาเลือกภาษาสำหรับการติดตั้ง"
|
||||
|
||||
; Requires .NET Framework
|
||||
LangString RequiresNetFramework ${LANG_THAI} "mRemoteNG มีความต้องการ Microsoft .NET Framework 3.0. เป็นพื้นฐาน"
|
||||
|
||||
; User needs to be Admin
|
||||
LangString RequiresAdminUser ${LANG_THAI} "คุณต้องเป็นสมาชิกในกลุ่มของ 'Power Users' หรือ 'Administrators' เพื่อการติดตั้ง mRemoteNG"
|
||||
|
||||
; Start Menu items
|
||||
LangString CreditsLinkName ${LANG_THAI} "Credits"
|
||||
LangString CopyingLinkName ${LANG_THAI} "License"
|
||||
LangString UninstallLinkName ${LANG_THAI} "Uninstall"
|
||||
LangString ChangeLogLinkName ${LANG_THAI} "Version History"
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 78 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 79 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 83 KiB |
@@ -1,197 +0,0 @@
|
||||
!include "MUI.nsh"
|
||||
!include "WordFunc.nsh"
|
||||
!insertmacro VersionCompare
|
||||
|
||||
!include "DotNetVer.nsh"
|
||||
!include "..\Release\Version.nsh"
|
||||
|
||||
; This will be passed in using the /D switch by BUILD.CMD
|
||||
!ifdef PRODUCT_VERSION_TAG
|
||||
!define PRODUCT_VERSION_FRIENDLY "${PRODUCT_VERSION_SHORT}"
|
||||
!define PRODUCT_VERSION_TAGGED "${PRODUCT_VERSION_SHORT}-${PRODUCT_VERSION_TAG}"
|
||||
!else
|
||||
!define PRODUCT_VERSION_FRIENDLY "${PRODUCT_VERSION_SHORT}"
|
||||
!define PRODUCT_VERSION_TAGGED "${PRODUCT_VERSION_SHORT}"
|
||||
!endif
|
||||
|
||||
; Basic Config
|
||||
Name "mRemoteNG ${PRODUCT_VERSION_FRIENDLY}"
|
||||
OutFile "..\Release\mRemoteNG-Installer-${PRODUCT_VERSION_TAGGED}.exe"
|
||||
SetCompressor /SOLID lzma
|
||||
InstallDir "$PROGRAMFILES\mRemoteNG"
|
||||
InstallDirRegKey HKLM "Software\mRemoteNG" "InstallPath"
|
||||
RequestExecutionLevel admin
|
||||
|
||||
; Version Information
|
||||
VIProductVersion ${PRODUCT_VERSION}
|
||||
VIAddVersionKey "CompanyName" "Next Generation Software"
|
||||
VIAddVersionKey "ProductName" "mRemoteNG"
|
||||
VIAddVersionKey "ProductVersion" ${PRODUCT_VERSION}
|
||||
VIAddVersionKey "LegalCopyright" "Copyright © 2007-2009 Felix Deimel, 2010-2013 Riley McArdle"
|
||||
VIAddVersionKey "FileDescription" "mRemoteNG ${PRODUCT_VERSION_FRIENDLY} Installer"
|
||||
VIAddVersionKey "FileVersion" ${PRODUCT_VERSION}
|
||||
|
||||
; Design
|
||||
!define MUI_ICON "Setup_Install.ico"
|
||||
!define MUI_UNICON "RecycleBin.ico"
|
||||
!define MUI_HEADERIMAGE
|
||||
!define MUI_HEADERIMAGE_BITMAP "header.bmp" ; optional
|
||||
!define MUI_HEADERIMAGE_BITMAP_NOSTRETCH
|
||||
!define MUI_HEADERIMAGE_UNBITMAP "header.bmp" ; optional
|
||||
!define MUI_HEADERIMAGE_UNBITMAP_NOSTRETCH
|
||||
!define MUI_HEADER_TRANSPARENT_TEXT
|
||||
!define MUI_WELCOMEFINISHPAGE_BITMAP "welcomefinish.bmp"
|
||||
!define MUI_WELCOMEFINISHPAGE_BITMAP_NOSTRETCH
|
||||
!define MUI_UNWELCOMEFINISHPAGE_BITMAP "welcomefinish.bmp"
|
||||
!define MUI_UNWELCOMEFINISHPAGE_BITMAP_NOSTRETCH
|
||||
|
||||
; Install Pages
|
||||
!insertmacro MUI_PAGE_LICENSE "..\COPYING.TXT"
|
||||
!insertmacro MUI_PAGE_DIRECTORY
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
!define MUI_FINISHPAGE_NOAUTOCLOSE
|
||||
|
||||
; Finish Page
|
||||
!define MUI_FINISHPAGE_RUN_NOTCHECKED
|
||||
!define MUI_FINISHPAGE_RUN "$INSTDIR\mRemoteNG.exe"
|
||||
!insertmacro MUI_PAGE_FINISH
|
||||
|
||||
; Uninstall Pages
|
||||
!insertmacro MUI_UNPAGE_CONFIRM
|
||||
!insertmacro MUI_UNPAGE_INSTFILES
|
||||
|
||||
; Get Languages
|
||||
!include "Language\languages.nsi"
|
||||
|
||||
; Set finish page text
|
||||
!define MUI_FINISHPAGE_RUN_Text "$(LaunchMremoteNow)"
|
||||
|
||||
Function .onInit
|
||||
ClearErrors
|
||||
UserInfo::GetName
|
||||
IfErrors Win9x
|
||||
Pop $0
|
||||
UserInfo::GetAccountType
|
||||
Pop $1
|
||||
# GetOriginalAccountType will check the tokens of the original user of the
|
||||
# current thread/process. If the user tokens were elevated or limited for
|
||||
# this process, GetOriginalAccountType will return the non-restricted
|
||||
# account type.
|
||||
# On Vista with UAC, for example, this is not the same value when running
|
||||
# with `RequestExecutionLevel user`. GetOriginalAccountType will return
|
||||
# "admin" while GetAccountType will return "user".
|
||||
StrCmp $1 "Admin" 0 +3
|
||||
Goto doit
|
||||
StrCmp $1 "Power" 0 +3
|
||||
Goto doit
|
||||
StrCmp $1 "User" 0 +3
|
||||
Goto noop
|
||||
StrCmp $1 "Guest" 0 +3
|
||||
Goto noop
|
||||
MessageBox MB_OK "Unknown error"
|
||||
Goto doit
|
||||
|
||||
Win9x:
|
||||
doit:
|
||||
# We can install
|
||||
IfSilent +2
|
||||
Call SelectLanguage
|
||||
Goto end
|
||||
noop:
|
||||
MessageBox MB_OK "$(RequiresAdminUser)"
|
||||
Quit
|
||||
end:
|
||||
FunctionEnd
|
||||
|
||||
Function SelectLanguage
|
||||
;Language selection dialog
|
||||
Push ""
|
||||
Push ${LANG_ENGLISH}
|
||||
Push ${LanguageNameEnglish}
|
||||
Push ${LANG_GERMAN}
|
||||
Push ${LanguageNameGerman}
|
||||
Push ${LANG_DUTCH}
|
||||
Push ${LanguageNameDutch}
|
||||
Push ${LANG_FRENCH}
|
||||
Push ${LanguageNameFrench}
|
||||
Push ${LANG_POLISH}
|
||||
Push ${LanguageNamePolish}
|
||||
Push ${LANG_SPANISH}
|
||||
Push ${LanguageNameSpanish}
|
||||
Push ${LANG_CZECH}
|
||||
Push ${LanguageNameCzech}
|
||||
Push ${LANG_THAI}
|
||||
Push ${LanguageNameThai}
|
||||
Push A ; A means auto count languages
|
||||
; for the auto count to work the first empty push (Push "") must remain
|
||||
LangDLL::LangDialog "$(InstallerLanguage)" "$(SelectInstallerLanguage)"
|
||||
|
||||
Pop $LANGUAGE
|
||||
StrCmp $LANGUAGE "cancel" 0 +2
|
||||
Abort
|
||||
|
||||
; Check .NET version
|
||||
${IfNot} ${HasDotNet3.0}
|
||||
MessageBox MB_OK|MB_ICONEXCLAMATION "$(RequiresNetFramework)"
|
||||
Quit
|
||||
${EndIf}
|
||||
FunctionEnd
|
||||
|
||||
Section "" ; Install
|
||||
SetOutPath $INSTDIR
|
||||
SetShellVarContext all
|
||||
|
||||
; AddFiles
|
||||
File /r /x "mRemoteNG.vshost.*" "..\mRemoteV1\bin\Release\*.*"
|
||||
File /r "Dependencies\*.*"
|
||||
File "..\*.txt"
|
||||
|
||||
; Uninstaller
|
||||
WriteUninstaller "$INSTDIR\Uninstall.exe"
|
||||
|
||||
; Register ActiveX components
|
||||
RegDLL "$INSTDIR\eolwtscom.dll"
|
||||
|
||||
; Start Menu
|
||||
CreateDirectory "$SMPROGRAMS\mRemoteNG"
|
||||
CreateShortCut "$SMPROGRAMS\mRemoteNG\$(CreditsLinkName).lnk" "$INSTDIR\CREDITS.TXT"
|
||||
CreateShortCut "$SMPROGRAMS\mRemoteNG\$(CopyingLinkName).lnk" "$INSTDIR\COPYING.TXT"
|
||||
CreateShortCut "$SMPROGRAMS\mRemoteNG\mRemoteNG.lnk" "$INSTDIR\mRemoteNG.exe"
|
||||
CreateShortCut "$SMPROGRAMS\mRemoteNG\$(UninstallLinkName).lnk" "$INSTDIR\Uninstall.exe"
|
||||
CreateShortCut "$SMPROGRAMS\mRemoteNG\$(ChangeLogLinkName).lnk" "$INSTDIR\CHANGELOG.TXT"
|
||||
|
||||
; Registry
|
||||
WriteRegStr HKLM "Software\mRemoteNG" "InstallPath" $INSTDIR
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\mRemoteNG" "DisplayName" "mRemoteNG"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\mRemoteNG" "Publisher" "Next Generation Software"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\mRemoteNG" "DisplayIcon" "$INSTDIR\mRemoteNG.exe"
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\mRemoteNG" "EstimatedSize" 7080
|
||||
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\mRemoteNG" "DisplayVersion" ${PRODUCT_VERSION}
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\mRemoteNG" "VersionMajor" ${PRODUCT_VERSION_MAJOR}
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\mRemoteNG" "VersionMinor" ${PRODUCT_VERSION_MINOR}
|
||||
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\mRemoteNG" "UninstallString" '"$INSTDIR\Uninstall.exe"'
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\mRemoteNG" "NoModify" 1
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\mRemoteNG" "NoRepair" 1
|
||||
SectionEnd
|
||||
|
||||
Section "un.Uninstall"
|
||||
; Unregister ActiveX components
|
||||
UnregDLL "$INSTDIR\eolwtscom.dll"
|
||||
|
||||
; Delete Files
|
||||
RMDIR /r $INSTDIR
|
||||
|
||||
; Start Menu
|
||||
SetShellVarContext all
|
||||
RMDir /r "$SMPROGRAMS\mRemoteNG"
|
||||
SetShellVarContext current
|
||||
RMDir /r "$SMPROGRAMS\mRemoteNG"
|
||||
|
||||
; Registry
|
||||
DeleteRegValue HKLM "Software\mRemoteNG" "InstallPath"
|
||||
DeleteRegKey /ifempty HKLM "Software\mRemoteNG"
|
||||
DeleteRegKey /ifempty HKCU "Software\mRemoteNG"
|
||||
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\mRemoteNG"
|
||||
SectionEnd
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 151 KiB |
15
ObjectListView/AssemblyInfo.Version.cs
Normal file
15
ObjectListView/AssemblyInfo.Version.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System.Reflection;
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Revision and Build Numbers
|
||||
// by using the '*' as shown below:
|
||||
[assembly: AssemblyVersion("2.9.3.*")]
|
||||
[assembly: AssemblyFileVersion("2.9.3")]
|
||||
[assembly: AssemblyInformationalVersion("2.9.3")]
|
||||
[assembly: System.CLSCompliant(true)]
|
||||
520
ObjectListView/CellEditing/CellEditKeyEngine.cs
Normal file
520
ObjectListView/CellEditing/CellEditKeyEngine.cs
Normal file
@@ -0,0 +1,520 @@
|
||||
/*
|
||||
* CellEditKeyEngine - A engine that allows the behaviour of arbitrary keys to be configured
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 3-March-2011 10:53 pm
|
||||
*
|
||||
* Change log:
|
||||
* v2.8
|
||||
* 2014-05-30 JPP - When a row is disabled, skip over it when looking for another cell to edit
|
||||
* v2.5
|
||||
* 2012-04-14 JPP - Fixed bug where, on a OLV with only a single editable column, tabbing
|
||||
* to change rows would edit the cell above rather than the cell below
|
||||
* the cell being edited.
|
||||
* 2.5
|
||||
* 2011-03-03 JPP - First version
|
||||
*
|
||||
* Copyright (C) 2011-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using BrightIdeasSoftware;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
/// <summary>
|
||||
/// Indicates the behavior of a key when a cell "on the edge" is being edited.
|
||||
/// and the normal behavior of that key would exceed the edge. For example,
|
||||
/// for a key that normally moves one column to the left, the "edge" would be
|
||||
/// the left most column, since the normal action of the key cannot be taken
|
||||
/// (since there are no more columns to the left).
|
||||
/// </summary>
|
||||
public enum CellEditAtEdgeBehaviour {
|
||||
/// <summary>
|
||||
/// The key press will be ignored
|
||||
/// </summary>
|
||||
Ignore,
|
||||
|
||||
/// <summary>
|
||||
/// The key press will result in the cell editing wrapping to the
|
||||
/// cell on the opposite edge.
|
||||
/// </summary>
|
||||
Wrap,
|
||||
|
||||
/// <summary>
|
||||
/// The key press will wrap, but the column will be changed to the
|
||||
/// appropiate adjacent column. This only makes sense for keys where
|
||||
/// the normal action is ChangeRow.
|
||||
/// </summary>
|
||||
ChangeColumn,
|
||||
|
||||
/// <summary>
|
||||
/// The key press will wrap, but the row will be changed to the
|
||||
/// appropiate adjacent row. This only makes sense for keys where
|
||||
/// the normal action is ChangeColumn.
|
||||
/// </summary>
|
||||
ChangeRow,
|
||||
|
||||
/// <summary>
|
||||
/// The key will result in the current edit operation being ended.
|
||||
/// </summary>
|
||||
EndEdit
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the normal behaviour of a key when used during a cell edit
|
||||
/// operation.
|
||||
/// </summary>
|
||||
public enum CellEditCharacterBehaviour {
|
||||
/// <summary>
|
||||
/// The key press will be ignored
|
||||
/// </summary>
|
||||
Ignore,
|
||||
|
||||
/// <summary>
|
||||
/// The key press will end the current edit and begin an edit
|
||||
/// operation on the next editable cell to the left.
|
||||
/// </summary>
|
||||
ChangeColumnLeft,
|
||||
|
||||
/// <summary>
|
||||
/// The key press will end the current edit and begin an edit
|
||||
/// operation on the next editable cell to the right.
|
||||
/// </summary>
|
||||
ChangeColumnRight,
|
||||
|
||||
/// <summary>
|
||||
/// The key press will end the current edit and begin an edit
|
||||
/// operation on the row above.
|
||||
/// </summary>
|
||||
ChangeRowUp,
|
||||
|
||||
/// <summary>
|
||||
/// The key press will end the current edit and begin an edit
|
||||
/// operation on the row below
|
||||
/// </summary>
|
||||
ChangeRowDown,
|
||||
|
||||
/// <summary>
|
||||
/// The key press will cancel the current edit
|
||||
/// </summary>
|
||||
CancelEdit,
|
||||
|
||||
/// <summary>
|
||||
/// The key press will finish the current edit operation
|
||||
/// </summary>
|
||||
EndEdit,
|
||||
|
||||
/// <summary>
|
||||
/// Custom verb that can be used for specialized actions.
|
||||
/// </summary>
|
||||
CustomVerb1,
|
||||
|
||||
/// <summary>
|
||||
/// Custom verb that can be used for specialized actions.
|
||||
/// </summary>
|
||||
CustomVerb2,
|
||||
|
||||
/// <summary>
|
||||
/// Custom verb that can be used for specialized actions.
|
||||
/// </summary>
|
||||
CustomVerb3,
|
||||
|
||||
/// <summary>
|
||||
/// Custom verb that can be used for specialized actions.
|
||||
/// </summary>
|
||||
CustomVerb4,
|
||||
|
||||
/// <summary>
|
||||
/// Custom verb that can be used for specialized actions.
|
||||
/// </summary>
|
||||
CustomVerb5,
|
||||
|
||||
/// <summary>
|
||||
/// Custom verb that can be used for specialized actions.
|
||||
/// </summary>
|
||||
CustomVerb6,
|
||||
|
||||
/// <summary>
|
||||
/// Custom verb that can be used for specialized actions.
|
||||
/// </summary>
|
||||
CustomVerb7,
|
||||
|
||||
/// <summary>
|
||||
/// Custom verb that can be used for specialized actions.
|
||||
/// </summary>
|
||||
CustomVerb8,
|
||||
|
||||
/// <summary>
|
||||
/// Custom verb that can be used for specialized actions.
|
||||
/// </summary>
|
||||
CustomVerb9,
|
||||
|
||||
/// <summary>
|
||||
/// Custom verb that can be used for specialized actions.
|
||||
/// </summary>
|
||||
CustomVerb10,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Instances of this class handle key presses during a cell edit operation.
|
||||
/// </summary>
|
||||
public class CellEditKeyEngine {
|
||||
|
||||
#region Public interface
|
||||
|
||||
/// <summary>
|
||||
/// Sets the behaviour of a given key
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="normalBehaviour"></param>
|
||||
/// <param name="atEdgeBehaviour"></param>
|
||||
public virtual void SetKeyBehaviour(Keys key, CellEditCharacterBehaviour normalBehaviour, CellEditAtEdgeBehaviour atEdgeBehaviour) {
|
||||
this.CellEditKeyMap[key] = normalBehaviour;
|
||||
this.CellEditKeyAtEdgeBehaviourMap[key] = atEdgeBehaviour;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle a key press
|
||||
/// </summary>
|
||||
/// <param name="olv"></param>
|
||||
/// <param name="keyData"></param>
|
||||
/// <returns>True if the key was completely handled.</returns>
|
||||
public virtual bool HandleKey(ObjectListView olv, Keys keyData) {
|
||||
if (olv == null) throw new ArgumentNullException("olv");
|
||||
|
||||
CellEditCharacterBehaviour behaviour;
|
||||
if (!CellEditKeyMap.TryGetValue(keyData, out behaviour))
|
||||
return false;
|
||||
|
||||
this.ListView = olv;
|
||||
|
||||
switch (behaviour) {
|
||||
case CellEditCharacterBehaviour.Ignore:
|
||||
break;
|
||||
case CellEditCharacterBehaviour.CancelEdit:
|
||||
this.HandleCancelEdit();
|
||||
break;
|
||||
case CellEditCharacterBehaviour.EndEdit:
|
||||
this.HandleEndEdit();
|
||||
break;
|
||||
case CellEditCharacterBehaviour.ChangeColumnLeft:
|
||||
case CellEditCharacterBehaviour.ChangeColumnRight:
|
||||
this.HandleColumnChange(keyData, behaviour);
|
||||
break;
|
||||
case CellEditCharacterBehaviour.ChangeRowDown:
|
||||
case CellEditCharacterBehaviour.ChangeRowUp:
|
||||
this.HandleRowChange(keyData, behaviour);
|
||||
break;
|
||||
default:
|
||||
return this.HandleCustomVerb(keyData, behaviour);
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ObjectListView on which the current key is being handled.
|
||||
/// This cannot be null.
|
||||
/// </summary>
|
||||
protected ObjectListView ListView {
|
||||
get { return listView; }
|
||||
set { listView = value; }
|
||||
}
|
||||
private ObjectListView listView;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the row of the cell that is currently being edited
|
||||
/// </summary>
|
||||
protected OLVListItem ItemBeingEdited {
|
||||
get {
|
||||
return (this.ListView == null || this.ListView.CellEditEventArgs == null) ? null : this.ListView.CellEditEventArgs.ListViewItem;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the column of the cell that is being edited
|
||||
/// </summary>
|
||||
protected int SubItemIndexBeingEdited {
|
||||
get {
|
||||
return (this.ListView == null || this.ListView.CellEditEventArgs == null) ? -1 : this.ListView.CellEditEventArgs.SubItemIndex;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the map that remembers the normal behaviour of keys
|
||||
/// </summary>
|
||||
protected IDictionary<Keys, CellEditCharacterBehaviour> CellEditKeyMap {
|
||||
get {
|
||||
if (cellEditKeyMap == null)
|
||||
this.InitializeCellEditKeyMaps();
|
||||
return cellEditKeyMap;
|
||||
}
|
||||
set {
|
||||
cellEditKeyMap = value;
|
||||
}
|
||||
}
|
||||
private IDictionary<Keys, CellEditCharacterBehaviour> cellEditKeyMap;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the map that remembers the desired behaviour of keys
|
||||
/// on edge cases.
|
||||
/// </summary>
|
||||
protected IDictionary<Keys, CellEditAtEdgeBehaviour> CellEditKeyAtEdgeBehaviourMap {
|
||||
get {
|
||||
if (cellEditKeyAtEdgeBehaviourMap == null)
|
||||
this.InitializeCellEditKeyMaps();
|
||||
return cellEditKeyAtEdgeBehaviourMap;
|
||||
}
|
||||
set {
|
||||
cellEditKeyAtEdgeBehaviourMap = value;
|
||||
}
|
||||
}
|
||||
private IDictionary<Keys, CellEditAtEdgeBehaviour> cellEditKeyAtEdgeBehaviourMap;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Initialization
|
||||
|
||||
/// <summary>
|
||||
/// Setup the default key mapping
|
||||
/// </summary>
|
||||
protected virtual void InitializeCellEditKeyMaps() {
|
||||
this.cellEditKeyMap = new Dictionary<Keys, CellEditCharacterBehaviour>();
|
||||
this.cellEditKeyMap[Keys.Escape] = CellEditCharacterBehaviour.CancelEdit;
|
||||
this.cellEditKeyMap[Keys.Return] = CellEditCharacterBehaviour.EndEdit;
|
||||
this.cellEditKeyMap[Keys.Enter] = CellEditCharacterBehaviour.EndEdit;
|
||||
this.cellEditKeyMap[Keys.Tab] = CellEditCharacterBehaviour.ChangeColumnRight;
|
||||
this.cellEditKeyMap[Keys.Tab | Keys.Shift] = CellEditCharacterBehaviour.ChangeColumnLeft;
|
||||
this.cellEditKeyMap[Keys.Left | Keys.Alt] = CellEditCharacterBehaviour.ChangeColumnLeft;
|
||||
this.cellEditKeyMap[Keys.Right | Keys.Alt] = CellEditCharacterBehaviour.ChangeColumnRight;
|
||||
this.cellEditKeyMap[Keys.Up | Keys.Alt] = CellEditCharacterBehaviour.ChangeRowUp;
|
||||
this.cellEditKeyMap[Keys.Down | Keys.Alt] = CellEditCharacterBehaviour.ChangeRowDown;
|
||||
|
||||
this.cellEditKeyAtEdgeBehaviourMap = new Dictionary<Keys, CellEditAtEdgeBehaviour>();
|
||||
this.cellEditKeyAtEdgeBehaviourMap[Keys.Tab] = CellEditAtEdgeBehaviour.Wrap;
|
||||
this.cellEditKeyAtEdgeBehaviourMap[Keys.Tab | Keys.Shift] = CellEditAtEdgeBehaviour.Wrap;
|
||||
this.cellEditKeyAtEdgeBehaviourMap[Keys.Left | Keys.Alt] = CellEditAtEdgeBehaviour.Wrap;
|
||||
this.cellEditKeyAtEdgeBehaviourMap[Keys.Right | Keys.Alt] = CellEditAtEdgeBehaviour.Wrap;
|
||||
this.cellEditKeyAtEdgeBehaviourMap[Keys.Up | Keys.Alt] = CellEditAtEdgeBehaviour.ChangeColumn;
|
||||
this.cellEditKeyAtEdgeBehaviourMap[Keys.Down | Keys.Alt] = CellEditAtEdgeBehaviour.ChangeColumn;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Command handling
|
||||
|
||||
/// <summary>
|
||||
/// Handle the end edit command
|
||||
/// </summary>
|
||||
protected virtual void HandleEndEdit() {
|
||||
this.ListView.PossibleFinishCellEditing();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle the cancel edit command
|
||||
/// </summary>
|
||||
protected virtual void HandleCancelEdit() {
|
||||
this.ListView.CancelCellEdit();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Placeholder that subclasses can override to handle any custom verbs
|
||||
/// </summary>
|
||||
/// <param name="keyData"></param>
|
||||
/// <param name="behaviour"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual bool HandleCustomVerb(Keys keyData, CellEditCharacterBehaviour behaviour) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle a change row command
|
||||
/// </summary>
|
||||
/// <param name="keyData"></param>
|
||||
/// <param name="behaviour"></param>
|
||||
protected virtual void HandleRowChange(Keys keyData, CellEditCharacterBehaviour behaviour) {
|
||||
// If we couldn't finish editing the current cell, don't try to move it
|
||||
if (!this.ListView.PossibleFinishCellEditing())
|
||||
return;
|
||||
|
||||
OLVListItem olvi = this.ItemBeingEdited;
|
||||
int subItemIndex = this.SubItemIndexBeingEdited;
|
||||
bool isGoingUp = behaviour == CellEditCharacterBehaviour.ChangeRowUp;
|
||||
|
||||
// Try to find a row above (or below) the currently edited cell
|
||||
// If we find one, start editing it and we're done.
|
||||
OLVListItem adjacentOlvi = this.GetAdjacentItemOrNull(olvi, isGoingUp);
|
||||
if (adjacentOlvi != null) {
|
||||
this.StartCellEditIfDifferent(adjacentOlvi, subItemIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
// There is no adjacent row in the direction we want, so we must be on an edge.
|
||||
CellEditAtEdgeBehaviour atEdgeBehaviour;
|
||||
if (!this.CellEditKeyAtEdgeBehaviourMap.TryGetValue(keyData, out atEdgeBehaviour))
|
||||
atEdgeBehaviour = CellEditAtEdgeBehaviour.Wrap;
|
||||
switch (atEdgeBehaviour) {
|
||||
case CellEditAtEdgeBehaviour.Ignore:
|
||||
break;
|
||||
case CellEditAtEdgeBehaviour.EndEdit:
|
||||
this.ListView.PossibleFinishCellEditing();
|
||||
break;
|
||||
case CellEditAtEdgeBehaviour.Wrap:
|
||||
adjacentOlvi = this.GetAdjacentItemOrNull(null, isGoingUp);
|
||||
this.StartCellEditIfDifferent(adjacentOlvi, subItemIndex);
|
||||
break;
|
||||
case CellEditAtEdgeBehaviour.ChangeColumn:
|
||||
// Figure out the next editable column
|
||||
List<OLVColumn> editableColumnsInDisplayOrder = this.EditableColumnsInDisplayOrder;
|
||||
int displayIndex = Math.Max(0, editableColumnsInDisplayOrder.IndexOf(this.ListView.GetColumn(subItemIndex)));
|
||||
if (isGoingUp)
|
||||
displayIndex = (editableColumnsInDisplayOrder.Count + displayIndex - 1) % editableColumnsInDisplayOrder.Count;
|
||||
else
|
||||
displayIndex = (displayIndex + 1) % editableColumnsInDisplayOrder.Count;
|
||||
subItemIndex = editableColumnsInDisplayOrder[displayIndex].Index;
|
||||
|
||||
// Wrap to the next row and start the cell edit
|
||||
adjacentOlvi = this.GetAdjacentItemOrNull(null, isGoingUp);
|
||||
this.StartCellEditIfDifferent(adjacentOlvi, subItemIndex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle a change column command
|
||||
/// </summary>
|
||||
/// <param name="keyData"></param>
|
||||
/// <param name="behaviour"></param>
|
||||
protected virtual void HandleColumnChange(Keys keyData, CellEditCharacterBehaviour behaviour)
|
||||
{
|
||||
// If we couldn't finish editing the current cell, don't try to move it
|
||||
if (!this.ListView.PossibleFinishCellEditing())
|
||||
return;
|
||||
|
||||
// Changing columns only works in details mode
|
||||
if (this.ListView.View != View.Details)
|
||||
return;
|
||||
|
||||
List<OLVColumn> editableColumns = this.EditableColumnsInDisplayOrder;
|
||||
OLVListItem olvi = this.ItemBeingEdited;
|
||||
int displayIndex = Math.Max(0,
|
||||
editableColumns.IndexOf(this.ListView.GetColumn(this.SubItemIndexBeingEdited)));
|
||||
bool isGoingLeft = behaviour == CellEditCharacterBehaviour.ChangeColumnLeft;
|
||||
|
||||
// Are we trying to continue past one of the edges?
|
||||
if ((isGoingLeft && displayIndex == 0) ||
|
||||
(!isGoingLeft && displayIndex == editableColumns.Count - 1))
|
||||
{
|
||||
// Yes, so figure out our at edge behaviour
|
||||
CellEditAtEdgeBehaviour atEdgeBehaviour;
|
||||
if (!this.CellEditKeyAtEdgeBehaviourMap.TryGetValue(keyData, out atEdgeBehaviour))
|
||||
atEdgeBehaviour = CellEditAtEdgeBehaviour.Wrap;
|
||||
switch (atEdgeBehaviour)
|
||||
{
|
||||
case CellEditAtEdgeBehaviour.Ignore:
|
||||
return;
|
||||
case CellEditAtEdgeBehaviour.EndEdit:
|
||||
this.HandleEndEdit();
|
||||
return;
|
||||
case CellEditAtEdgeBehaviour.ChangeRow:
|
||||
case CellEditAtEdgeBehaviour.Wrap:
|
||||
if (atEdgeBehaviour == CellEditAtEdgeBehaviour.ChangeRow)
|
||||
olvi = GetAdjacentItem(olvi, isGoingLeft && displayIndex == 0);
|
||||
if (isGoingLeft)
|
||||
displayIndex = editableColumns.Count - 1;
|
||||
else
|
||||
displayIndex = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isGoingLeft)
|
||||
displayIndex -= 1;
|
||||
else
|
||||
displayIndex += 1;
|
||||
}
|
||||
|
||||
int subItemIndex = editableColumns[displayIndex].Index;
|
||||
this.StartCellEditIfDifferent(olvi, subItemIndex);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Utilities
|
||||
|
||||
/// <summary>
|
||||
/// Start editing the indicated cell if that cell is not already being edited
|
||||
/// </summary>
|
||||
/// <param name="olvi">The row to edit</param>
|
||||
/// <param name="subItemIndex">The cell within that row to edit</param>
|
||||
protected void StartCellEditIfDifferent(OLVListItem olvi, int subItemIndex) {
|
||||
if (this.ItemBeingEdited == olvi && this.SubItemIndexBeingEdited == subItemIndex)
|
||||
return;
|
||||
|
||||
this.ListView.EnsureVisible(olvi.Index);
|
||||
this.ListView.StartCellEdit(olvi, subItemIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the adjacent item to the given item in the given direction.
|
||||
/// If that item is disabled, continue in that direction until an enabled item is found.
|
||||
/// </summary>
|
||||
/// <param name="olvi">The row whose neighbour is sought</param>
|
||||
/// <param name="up">The direction of the adjacentness</param>
|
||||
/// <returns>An OLVListView adjacent to the given item, or null if there are no more enabled items in that direction.</returns>
|
||||
protected OLVListItem GetAdjacentItemOrNull(OLVListItem olvi, bool up) {
|
||||
OLVListItem item = up ? this.ListView.GetPreviousItem(olvi) : this.ListView.GetNextItem(olvi);
|
||||
while (item != null && !item.Enabled)
|
||||
item = up ? this.ListView.GetPreviousItem(item) : this.ListView.GetNextItem(item);
|
||||
return item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the adjacent item to the given item in the given direction, wrapping if needed.
|
||||
/// </summary>
|
||||
/// <param name="olvi">The row whose neighbour is sought</param>
|
||||
/// <param name="up">The direction of the adjacentness</param>
|
||||
/// <returns>An OLVListView adjacent to the given item, or null if there are no more items in that direction.</returns>
|
||||
protected OLVListItem GetAdjacentItem(OLVListItem olvi, bool up) {
|
||||
return this.GetAdjacentItemOrNull(olvi, up) ?? this.GetAdjacentItemOrNull(null, up);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of columns that are editable in the order they are shown to the user
|
||||
/// </summary>
|
||||
protected List<OLVColumn> EditableColumnsInDisplayOrder {
|
||||
get {
|
||||
List<OLVColumn> editableColumnsInDisplayOrder = new List<OLVColumn>();
|
||||
foreach (OLVColumn x in this.ListView.ColumnsInDisplayOrder)
|
||||
if (x.IsEditable)
|
||||
editableColumnsInDisplayOrder.Add(x);
|
||||
return editableColumnsInDisplayOrder;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
288
ObjectListView/CellEditing/CellEditors.cs
Normal file
288
ObjectListView/CellEditing/CellEditors.cs
Normal file
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
* CellEditors - Several slightly modified controls that are used as celleditors within ObjectListView.
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 20/10/2008 5:15 PM
|
||||
*
|
||||
* Change log:
|
||||
* v2.6
|
||||
* 2012-08-02 JPP - Make most editors public so they can be reused/subclassed
|
||||
* v2.3
|
||||
* 2009-08-13 JPP - Standardized code formatting
|
||||
* v2.2.1
|
||||
* 2008-01-18 JPP - Added special handling for enums
|
||||
* 2008-01-16 JPP - Added EditorRegistry
|
||||
* v2.0.1
|
||||
* 2008-10-20 JPP - Separated from ObjectListView.cs
|
||||
*
|
||||
* Copyright (C) 2006-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Reflection;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace BrightIdeasSoftware
|
||||
{
|
||||
/// <summary>
|
||||
/// These items allow combo boxes to remember a value and its description.
|
||||
/// </summary>
|
||||
public class ComboBoxItem
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="description"></param>
|
||||
public ComboBoxItem(Object key, String description) {
|
||||
this.key = key;
|
||||
this.description = description;
|
||||
}
|
||||
private readonly String description;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public Object Key {
|
||||
get { return key; }
|
||||
}
|
||||
private readonly Object key;
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string that represents the current object.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string that represents the current object.
|
||||
/// </returns>
|
||||
/// <filterpriority>2</filterpriority>
|
||||
public override string ToString() {
|
||||
return this.description;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
// Cell editors
|
||||
// These classes are simple cell editors that make it easier to get and set
|
||||
// the value that the control is showing.
|
||||
// In many cases, you can intercept the CellEditStarting event to
|
||||
// change the characteristics of the editor. For example, changing
|
||||
// the acceptable range for a numeric editor or changing the strings
|
||||
// that respresent true and false values for a boolean editor.
|
||||
|
||||
/// <summary>
|
||||
/// This editor shows and auto completes values from the given listview column.
|
||||
/// </summary>
|
||||
[ToolboxItem(false)]
|
||||
public class AutoCompleteCellEditor : ComboBox
|
||||
{
|
||||
/// <summary>
|
||||
/// Create an AutoCompleteCellEditor
|
||||
/// </summary>
|
||||
/// <param name="lv"></param>
|
||||
/// <param name="column"></param>
|
||||
public AutoCompleteCellEditor(ObjectListView lv, OLVColumn column) {
|
||||
this.DropDownStyle = ComboBoxStyle.DropDown;
|
||||
|
||||
Dictionary<String, bool> alreadySeen = new Dictionary<string, bool>();
|
||||
for (int i = 0; i < Math.Min(lv.GetItemCount(), 1000); i++) {
|
||||
String str = column.GetStringValue(lv.GetModelObject(i));
|
||||
if (!alreadySeen.ContainsKey(str)) {
|
||||
this.Items.Add(str);
|
||||
alreadySeen[str] = true;
|
||||
}
|
||||
}
|
||||
|
||||
this.Sorted = true;
|
||||
this.AutoCompleteSource = AutoCompleteSource.ListItems;
|
||||
this.AutoCompleteMode = AutoCompleteMode.Append;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This combo box is specialised to allow editing of an enum.
|
||||
/// </summary>
|
||||
[ToolboxItem(false)]
|
||||
public class EnumCellEditor : ComboBox
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
public EnumCellEditor(Type type) {
|
||||
this.DropDownStyle = ComboBoxStyle.DropDownList;
|
||||
this.ValueMember = "Key";
|
||||
|
||||
ArrayList values = new ArrayList();
|
||||
foreach (object value in Enum.GetValues(type))
|
||||
values.Add(new ComboBoxItem(value, Enum.GetName(type, value)));
|
||||
|
||||
this.DataSource = values;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This editor simply shows and edits integer values.
|
||||
/// </summary>
|
||||
[ToolboxItem(false)]
|
||||
public class IntUpDown : NumericUpDown
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public IntUpDown() {
|
||||
this.DecimalPlaces = 0;
|
||||
this.Minimum = -9999999;
|
||||
this.Maximum = 9999999;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value shown by this editor
|
||||
/// </summary>
|
||||
new public int Value {
|
||||
get { return Decimal.ToInt32(base.Value); }
|
||||
set { base.Value = new Decimal(value); }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This editor simply shows and edits unsigned integer values.
|
||||
/// </summary>
|
||||
/// <remarks>This class can't be made public because unsigned int is not a
|
||||
/// CLS-compliant type. If you want to use, just copy the code to this class
|
||||
/// into your project and use it from there.</remarks>
|
||||
[ToolboxItem(false)]
|
||||
internal class UintUpDown : NumericUpDown
|
||||
{
|
||||
public UintUpDown() {
|
||||
this.DecimalPlaces = 0;
|
||||
this.Minimum = 0;
|
||||
this.Maximum = 9999999;
|
||||
}
|
||||
|
||||
new public uint Value {
|
||||
get { return Decimal.ToUInt32(base.Value); }
|
||||
set { base.Value = new Decimal(value); }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This editor simply shows and edits boolean values.
|
||||
/// </summary>
|
||||
[ToolboxItem(false)]
|
||||
public class BooleanCellEditor : ComboBox
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public BooleanCellEditor() {
|
||||
this.DropDownStyle = ComboBoxStyle.DropDownList;
|
||||
this.ValueMember = "Key";
|
||||
|
||||
ArrayList values = new ArrayList();
|
||||
values.Add(new ComboBoxItem(false, "False"));
|
||||
values.Add(new ComboBoxItem(true, "True"));
|
||||
|
||||
this.DataSource = values;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This editor simply shows and edits boolean values using a checkbox
|
||||
/// </summary>
|
||||
[ToolboxItem(false)]
|
||||
public class BooleanCellEditor2 : CheckBox
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the value shown by this editor
|
||||
/// </summary>
|
||||
public bool? Value {
|
||||
get {
|
||||
switch (this.CheckState) {
|
||||
case CheckState.Checked: return true;
|
||||
case CheckState.Indeterminate: return null;
|
||||
case CheckState.Unchecked:
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
set {
|
||||
if (value.HasValue)
|
||||
this.CheckState = value.Value ? CheckState.Checked : CheckState.Unchecked;
|
||||
else
|
||||
this.CheckState = CheckState.Indeterminate;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets how the checkbox will be aligned
|
||||
/// </summary>
|
||||
public new HorizontalAlignment TextAlign {
|
||||
get {
|
||||
switch (this.CheckAlign) {
|
||||
case ContentAlignment.MiddleRight: return HorizontalAlignment.Right;
|
||||
case ContentAlignment.MiddleCenter: return HorizontalAlignment.Center;
|
||||
case ContentAlignment.MiddleLeft:
|
||||
default: return HorizontalAlignment.Left;
|
||||
}
|
||||
}
|
||||
set {
|
||||
switch (value) {
|
||||
case HorizontalAlignment.Left:
|
||||
this.CheckAlign = ContentAlignment.MiddleLeft;
|
||||
break;
|
||||
case HorizontalAlignment.Center:
|
||||
this.CheckAlign = ContentAlignment.MiddleCenter;
|
||||
break;
|
||||
case HorizontalAlignment.Right:
|
||||
this.CheckAlign = ContentAlignment.MiddleRight;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This editor simply shows and edits floating point values.
|
||||
/// </summary>
|
||||
/// <remarks>You can intercept the CellEditStarting event if you want
|
||||
/// to change the characteristics of the editor. For example, by increasing
|
||||
/// the number of decimal places.</remarks>
|
||||
[ToolboxItem(false)]
|
||||
public class FloatCellEditor : NumericUpDown
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public FloatCellEditor() {
|
||||
this.DecimalPlaces = 2;
|
||||
this.Minimum = -9999999;
|
||||
this.Maximum = 9999999;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value shown by this editor
|
||||
/// </summary>
|
||||
new public double Value {
|
||||
get { return Convert.ToDouble(base.Value); }
|
||||
set { base.Value = Convert.ToDecimal(value); }
|
||||
}
|
||||
}
|
||||
}
|
||||
213
ObjectListView/CellEditing/EditorRegistry.cs
Normal file
213
ObjectListView/CellEditing/EditorRegistry.cs
Normal file
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* EditorRegistry - A registry mapping types to cell editors.
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 6-March-2011 7:53 am
|
||||
*
|
||||
* Change log:
|
||||
* 2011-03-31 JPP - Use OLVColumn.DataType if the value to be edited is null
|
||||
* 2011-03-06 JPP - Separated from CellEditors.cs
|
||||
*
|
||||
* Copyright (C) 2011-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using System.Reflection;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
/// <summary>
|
||||
/// A delegate that creates an editor for the given value
|
||||
/// </summary>
|
||||
/// <param name="model">The model from which that value came</param>
|
||||
/// <param name="column">The column for which the editor is being created</param>
|
||||
/// <param name="value">A representative value of the type to be edited. This value may not be the exact
|
||||
/// value for the column/model combination. It could be simply representative of
|
||||
/// the appropriate type of value.</param>
|
||||
/// <returns>A control which can edit the given value</returns>
|
||||
public delegate Control EditorCreatorDelegate(Object model, OLVColumn column, Object value);
|
||||
|
||||
/// <summary>
|
||||
/// An editor registry gives a way to decide what cell editor should be used to edit
|
||||
/// the value of a cell. Programmers can register non-standard types and the control that
|
||||
/// should be used to edit instances of that type.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>All ObjectListViews share the same editor registry.</para>
|
||||
/// </remarks>
|
||||
public class EditorRegistry {
|
||||
#region Initializing
|
||||
|
||||
/// <summary>
|
||||
/// Create an EditorRegistry
|
||||
/// </summary>
|
||||
public EditorRegistry() {
|
||||
this.InitializeStandardTypes();
|
||||
}
|
||||
|
||||
private void InitializeStandardTypes() {
|
||||
this.Register(typeof(Boolean), typeof(BooleanCellEditor));
|
||||
this.Register(typeof(Int16), typeof(IntUpDown));
|
||||
this.Register(typeof(Int32), typeof(IntUpDown));
|
||||
this.Register(typeof(Int64), typeof(IntUpDown));
|
||||
this.Register(typeof(UInt16), typeof(UintUpDown));
|
||||
this.Register(typeof(UInt32), typeof(UintUpDown));
|
||||
this.Register(typeof(UInt64), typeof(UintUpDown));
|
||||
this.Register(typeof(Single), typeof(FloatCellEditor));
|
||||
this.Register(typeof(Double), typeof(FloatCellEditor));
|
||||
this.Register(typeof(DateTime), delegate(Object model, OLVColumn column, Object value) {
|
||||
DateTimePicker c = new DateTimePicker();
|
||||
c.Format = DateTimePickerFormat.Short;
|
||||
return c;
|
||||
});
|
||||
this.Register(typeof(Boolean), delegate(Object model, OLVColumn column, Object value) {
|
||||
CheckBox c = new BooleanCellEditor2();
|
||||
c.ThreeState = column.TriStateCheckBoxes;
|
||||
return c;
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Registering
|
||||
|
||||
/// <summary>
|
||||
/// Register that values of 'type' should be edited by instances of 'controlType'.
|
||||
/// </summary>
|
||||
/// <param name="type">The type of value to be edited</param>
|
||||
/// <param name="controlType">The type of the Control that will edit values of 'type'</param>
|
||||
/// <example>
|
||||
/// ObjectListView.EditorRegistry.Register(typeof(Color), typeof(MySpecialColorEditor));
|
||||
/// </example>
|
||||
public void Register(Type type, Type controlType) {
|
||||
this.Register(type, delegate(Object model, OLVColumn column, Object value) {
|
||||
return controlType.InvokeMember("", BindingFlags.CreateInstance, null, null, null) as Control;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register the given delegate so that it is called to create editors
|
||||
/// for values of the given type
|
||||
/// </summary>
|
||||
/// <param name="type">The type of value to be edited</param>
|
||||
/// <param name="creator">The delegate that will create a control that can edit values of 'type'</param>
|
||||
/// <example>
|
||||
/// ObjectListView.EditorRegistry.Register(typeof(Color), CreateColorEditor);
|
||||
/// ...
|
||||
/// public Control CreateColorEditor(Object model, OLVColumn column, Object value)
|
||||
/// {
|
||||
/// return new MySpecialColorEditor();
|
||||
/// }
|
||||
/// </example>
|
||||
public void Register(Type type, EditorCreatorDelegate creator) {
|
||||
this.creatorMap[type] = creator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register a delegate that will be called to create an editor for values
|
||||
/// that have not been handled.
|
||||
/// </summary>
|
||||
/// <param name="creator">The delegate that will create a editor for all other types</param>
|
||||
public void RegisterDefault(EditorCreatorDelegate creator) {
|
||||
this.defaultCreator = creator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register a delegate that will be given a chance to create a control
|
||||
/// before any other option is considered.
|
||||
/// </summary>
|
||||
/// <param name="creator">The delegate that will create a control</param>
|
||||
public void RegisterFirstChance(EditorCreatorDelegate creator) {
|
||||
this.firstChanceCreator = creator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the registered handler for the given type
|
||||
/// </summary>
|
||||
/// <remarks>Does nothing if the given type doesn't exist</remarks>
|
||||
/// <param name="type">The type whose registration is to be removed</param>
|
||||
public void Unregister(Type type) {
|
||||
if (this.creatorMap.ContainsKey(type))
|
||||
this.creatorMap.Remove(type);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Accessing
|
||||
|
||||
/// <summary>
|
||||
/// Create and return an editor that is appropriate for the given value.
|
||||
/// Return null if no appropriate editor can be found.
|
||||
/// </summary>
|
||||
/// <param name="model">The model involved</param>
|
||||
/// <param name="column">The column to be edited</param>
|
||||
/// <param name="value">The value to be edited. This value may not be the exact
|
||||
/// value for the column/model combination. It could be simply representative of
|
||||
/// the appropriate type of value.</param>
|
||||
/// <returns>A Control that can edit the given type of values</returns>
|
||||
public Control GetEditor(Object model, OLVColumn column, Object value) {
|
||||
Control editor;
|
||||
|
||||
// Give the first chance delegate a chance to decide
|
||||
if (this.firstChanceCreator != null) {
|
||||
editor = this.firstChanceCreator(model, column, value);
|
||||
if (editor != null)
|
||||
return editor;
|
||||
}
|
||||
|
||||
// Try to find a creator based on the type of the value (or the column)
|
||||
Type type = value == null ? column.DataType : value.GetType();
|
||||
if (type != null && this.creatorMap.ContainsKey(type)) {
|
||||
editor = this.creatorMap[type](model, column, value);
|
||||
if (editor != null)
|
||||
return editor;
|
||||
}
|
||||
|
||||
// Enums without other processing get a special editor
|
||||
if (value != null && value.GetType().IsEnum)
|
||||
return this.CreateEnumEditor(value.GetType());
|
||||
|
||||
// Give any default creator a final chance
|
||||
if (this.defaultCreator != null)
|
||||
return this.defaultCreator(model, column, value);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create and return an editor that will edit values of the given type
|
||||
/// </summary>
|
||||
/// <param name="type">A enum type</param>
|
||||
protected Control CreateEnumEditor(Type type) {
|
||||
return new EnumCellEditor(type);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private variables
|
||||
|
||||
private EditorCreatorDelegate firstChanceCreator;
|
||||
private EditorCreatorDelegate defaultCreator;
|
||||
private Dictionary<Type, EditorCreatorDelegate> creatorMap = new Dictionary<Type, EditorCreatorDelegate>();
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
46
ObjectListView/CustomDictionary.xml
Normal file
46
ObjectListView/CustomDictionary.xml
Normal file
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Dictionary>
|
||||
<Words>
|
||||
<Recognized>
|
||||
<Word>br</Word>
|
||||
<Word>Canceled</Word>
|
||||
<Word>Center</Word>
|
||||
<Word>Color</Word>
|
||||
<Word>Colors</Word>
|
||||
<Word>f</Word>
|
||||
<Word>fmt</Word>
|
||||
<Word>g</Word>
|
||||
<Word>gdi</Word>
|
||||
<Word>hti</Word>
|
||||
<Word>i</Word>
|
||||
<Word>lightbox</Word>
|
||||
<Word>lv</Word>
|
||||
<Word>lvi</Word>
|
||||
<Word>lvsi</Word>
|
||||
<Word>m</Word>
|
||||
<Word>multi</Word>
|
||||
<Word>Munger</Word>
|
||||
<Word>n</Word>
|
||||
<Word>olv</Word>
|
||||
<Word>olvi</Word>
|
||||
<Word>p</Word>
|
||||
<Word>parms</Word>
|
||||
<Word>r</Word>
|
||||
<Word>Renderer</Word>
|
||||
<Word>s</Word>
|
||||
<Word>SubItem</Word>
|
||||
<Word>Unapply</Word>
|
||||
<Word>Unpause</Word>
|
||||
<Word>x</Word>
|
||||
<Word>y</Word>
|
||||
</Recognized>
|
||||
<Deprecated>
|
||||
<Term PreferredAlternate="EnterpriseServices">ComPlus</Term>
|
||||
</Deprecated>
|
||||
</Words>
|
||||
<Acronyms>
|
||||
<CasingExceptions>
|
||||
<Acronym>OLV</Acronym>
|
||||
</CasingExceptions>
|
||||
</Acronyms>
|
||||
</Dictionary>
|
||||
236
ObjectListView/DataListView.cs
Normal file
236
ObjectListView/DataListView.cs
Normal file
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* DataListView - A data-bindable listview
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 27/09/2008 9:15 AM
|
||||
*
|
||||
* Change log:
|
||||
* 2015-02-02 JPP - Made Unfreezing more efficient by removing a redundant BuildList() call
|
||||
* v2.6
|
||||
* 2011-02-27 JPP - Moved most of the logic to DataSourceAdapter (where it
|
||||
* can be used by FastDataListView too)
|
||||
* v2.3
|
||||
* 2009-01-18 JPP - Boolean columns are now handled as checkboxes
|
||||
* - Auto-generated columns would fail if the data source was
|
||||
* reseated, even to the same data source
|
||||
* v2.0.1
|
||||
* 2009-01-07 JPP - Made all public and protected methods virtual
|
||||
* 2008-10-03 JPP - Separated from ObjectListView.cs
|
||||
*
|
||||
* Copyright (C) 2006-2015 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing.Design;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace BrightIdeasSoftware
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// A DataListView is a ListView that can be bound to a datasource (which would normally be a DataTable or DataView).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>This listview keeps itself in sync with its source datatable by listening for change events.</para>
|
||||
/// <para>The DataListView will automatically create columns to show all of the data source's columns/properties, if there is not already
|
||||
/// a column showing that property. This allows you to define one or two columns in the designer and then have the others generated automatically.
|
||||
/// If you don't want any column to be auto generated, set <see cref="AutoGenerateColumns"/> to false.
|
||||
/// These generated columns will be only the simplest view of the world, and would look more interesting with a few delegates installed.</para>
|
||||
/// <para>This listview will also automatically generate missing aspect getters to fetch the values from the data view.</para>
|
||||
/// <para>Changing data sources is possible, but error prone. Before changing data sources, the programmer is responsible for modifying/resetting
|
||||
/// the column collection to be valid for the new data source.</para>
|
||||
/// <para>Internally, a CurrencyManager controls keeping the data source in-sync with other users of the data source (as per normal .NET
|
||||
/// behavior). This means that the model objects in the DataListView are DataRowView objects. If you write your own AspectGetters/Setters,
|
||||
/// they will be given DataRowView objects.</para>
|
||||
/// </remarks>
|
||||
public class DataListView : ObjectListView
|
||||
{
|
||||
#region Life and death
|
||||
|
||||
/// <summary>
|
||||
/// Make a DataListView
|
||||
/// </summary>
|
||||
public DataListView()
|
||||
{
|
||||
this.Adapter = new DataSourceAdapter(this);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing) {
|
||||
this.Adapter.Dispose();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether or not columns will be automatically generated to show the
|
||||
/// columns when the DataSource is set.
|
||||
/// </summary>
|
||||
/// <remarks>This must be set before the DataSource is set. It has no effect afterwards.</remarks>
|
||||
[Category("Data"),
|
||||
Description("Should the control automatically generate columns from the DataSource"),
|
||||
DefaultValue(true)]
|
||||
public bool AutoGenerateColumns {
|
||||
get { return this.Adapter.AutoGenerateColumns; }
|
||||
set { this.Adapter.AutoGenerateColumns = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get or set the DataSource that will be displayed in this list view.
|
||||
/// </summary>
|
||||
/// <remarks>The DataSource should implement either <see cref="IList"/>, <see cref="IBindingList"/>,
|
||||
/// or <see cref="IListSource"/>. Some common examples are the following types of objects:
|
||||
/// <list type="unordered">
|
||||
/// <item><description><see cref="DataView"/></description></item>
|
||||
/// <item><description><see cref="DataTable"/></description></item>
|
||||
/// <item><description><see cref="DataSet"/></description></item>
|
||||
/// <item><description><see cref="DataViewManager"/></description></item>
|
||||
/// <item><description><see cref="BindingSource"/></description></item>
|
||||
/// </list>
|
||||
/// <para>When binding to a list container (i.e. one that implements the
|
||||
/// <see cref="IListSource"/> interface, such as <see cref="DataSet"/>)
|
||||
/// you must also set the <see cref="DataMember"/> property in order
|
||||
/// to identify which particular list you would like to display. You
|
||||
/// may also set the <see cref="DataMember"/> property even when
|
||||
/// DataSource refers to a list, since <see cref="DataMember"/> can
|
||||
/// also be used to navigate relations between lists.</para>
|
||||
/// <para>When a DataSource is set, the control will create OLVColumns to show any
|
||||
/// data source columns that are not already shown.</para>
|
||||
/// <para>If the DataSource is changed, you will have to remove any previously
|
||||
/// created columns, since they will be configured for the previous DataSource.
|
||||
/// <see cref="ObjectListView.Reset()"/>.</para>
|
||||
/// </remarks>
|
||||
[Category("Data"),
|
||||
TypeConverter("System.Windows.Forms.Design.DataSourceConverter, System.Design")]
|
||||
public virtual Object DataSource
|
||||
{
|
||||
get { return this.Adapter.DataSource; }
|
||||
set { this.Adapter.DataSource = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the list or table in the data source for which the DataListView is displaying data.
|
||||
/// </summary>
|
||||
/// <remarks>If the data source is not a DataSet or DataViewManager, this property has no effect</remarks>
|
||||
[Category("Data"),
|
||||
Editor("System.Windows.Forms.Design.DataMemberListEditor, System.Design", typeof(UITypeEditor)),
|
||||
DefaultValue("")]
|
||||
public virtual string DataMember
|
||||
{
|
||||
get { return this.Adapter.DataMember; }
|
||||
set { this.Adapter.DataMember = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the DataSourceAdaptor that does the bulk of the work needed
|
||||
/// for data binding.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Adaptors cannot be shared between controls. Each DataListView needs its own adapter.
|
||||
/// </remarks>
|
||||
protected DataSourceAdapter Adapter {
|
||||
get {
|
||||
Debug.Assert(adapter != null, "Data adapter should not be null");
|
||||
return adapter;
|
||||
}
|
||||
set { adapter = value; }
|
||||
}
|
||||
private DataSourceAdapter adapter;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Object manipulations
|
||||
|
||||
/// <summary>
|
||||
/// Add the given collection of model objects to this control.
|
||||
/// </summary>
|
||||
/// <param name="modelObjects">A collection of model objects</param>
|
||||
/// <remarks>This is a no-op for data lists, since the data
|
||||
/// is controlled by the DataSource. Manipulate the data source
|
||||
/// rather than this view of the data source.</remarks>
|
||||
public override void AddObjects(ICollection modelObjects)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Insert the given collection of objects before the given position
|
||||
/// </summary>
|
||||
/// <param name="index">Where to insert the objects</param>
|
||||
/// <param name="modelObjects">The objects to be inserted</param>
|
||||
/// <remarks>This is a no-op for data lists, since the data
|
||||
/// is controlled by the DataSource. Manipulate the data source
|
||||
/// rather than this view of the data source.</remarks>
|
||||
public override void InsertObjects(int index, ICollection modelObjects) {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the given collection of model objects from this control.
|
||||
/// </summary>
|
||||
/// <remarks>This is a no-op for data lists, since the data
|
||||
/// is controlled by the DataSource. Manipulate the data source
|
||||
/// rather than this view of the data source.</remarks>
|
||||
public override void RemoveObjects(ICollection modelObjects)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
/// <summary>
|
||||
/// Change the Unfreeze behaviour
|
||||
/// </summary>
|
||||
protected override void DoUnfreeze() {
|
||||
|
||||
// Copied from base method, but we don't need to BuildList() since we know that our
|
||||
// data adaptor is going to do that immediately after this method exits.
|
||||
this.EndUpdate();
|
||||
this.ResizeFreeSpaceFillingColumns();
|
||||
// this.BuildList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles parent binding context changes
|
||||
/// </summary>
|
||||
/// <param name="e">Unused EventArgs.</param>
|
||||
protected override void OnParentBindingContextChanged(EventArgs e)
|
||||
{
|
||||
base.OnParentBindingContextChanged(e);
|
||||
|
||||
// BindingContext is an ambient property - by default it simply picks
|
||||
// up the parent control's context (unless something has explicitly
|
||||
// given us our own). So we must respond to changes in our parent's
|
||||
// binding context in the same way we would changes to our own
|
||||
// binding context.
|
||||
|
||||
// THINK: Do we need to forward this to the adapter?
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
240
ObjectListView/DataTreeListView.cs
Normal file
240
ObjectListView/DataTreeListView.cs
Normal file
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* DataTreeListView - A data bindable TreeListView
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 05/05/2012 3:26 PM
|
||||
*
|
||||
* Change log:
|
||||
|
||||
* 2012-05-05 JPP Initial version
|
||||
*
|
||||
* TO DO:
|
||||
|
||||
*
|
||||
* Copyright (C) 2012 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing.Design;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace BrightIdeasSoftware
|
||||
{
|
||||
/// <summary>
|
||||
/// A DataTreeListView is a TreeListView that calculates its hierarchy based on
|
||||
/// information in the data source.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Like a <see cref="DataListView"/>, a DataTreeListView sources all its information
|
||||
/// from a combination of <see cref="DataSource"/> and <see cref="DataMember"/>.
|
||||
/// <see cref="DataSource"/> can be a DataTable, DataSet,
|
||||
/// or anything that implements <see cref="IList"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// To function properly, the DataTreeListView requires:
|
||||
/// <list type="bullet">
|
||||
/// <item>the table to have a column which holds a unique for the row. The name of this column must be set in <see cref="KeyAspectName"/>.</item>
|
||||
/// <item>the table to have a column which holds id of the hierarchical parent of the row. The name of this column must be set in <see cref="ParentKeyAspectName"/>.</item>
|
||||
/// <item>a value which identifies which rows are the roots of the tree (<see cref="RootKeyValue"/>).</item>
|
||||
/// </list>
|
||||
/// The hierarchy structure is determined finding all the rows where the parent key is equal to <see cref="RootKeyValue"/>. These rows
|
||||
/// become the root objects of the hierarchy.
|
||||
/// </para>
|
||||
/// <para>Like a TreeListView, the hierarchy must not contain cycles. Bad things will happen if the data is cyclic.</para>
|
||||
/// </remarks>
|
||||
public partial class DataTreeListView : TreeListView
|
||||
{
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether or not columns will be automatically generated to show the
|
||||
/// columns when the DataSource is set.
|
||||
/// </summary>
|
||||
/// <remarks>This must be set before the DataSource is set. It has no effect afterwards.</remarks>
|
||||
[Category("Data"),
|
||||
Description("Should the control automatically generate columns from the DataSource"),
|
||||
DefaultValue(true)]
|
||||
public bool AutoGenerateColumns
|
||||
{
|
||||
get { return this.Adapter.AutoGenerateColumns; }
|
||||
set { this.Adapter.AutoGenerateColumns = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get or set the DataSource that will be displayed in this list view.
|
||||
/// </summary>
|
||||
/// <remarks>The DataSource should implement either <see cref="IList"/>, <see cref="IBindingList"/>,
|
||||
/// or <see cref="IListSource"/>. Some common examples are the following types of objects:
|
||||
/// <list type="unordered">
|
||||
/// <item><description><see cref="DataView"/></description></item>
|
||||
/// <item><description><see cref="DataTable"/></description></item>
|
||||
/// <item><description><see cref="DataSet"/></description></item>
|
||||
/// <item><description><see cref="DataViewManager"/></description></item>
|
||||
/// <item><description><see cref="BindingSource"/></description></item>
|
||||
/// </list>
|
||||
/// <para>When binding to a list container (i.e. one that implements the
|
||||
/// <see cref="IListSource"/> interface, such as <see cref="DataSet"/>)
|
||||
/// you must also set the <see cref="DataMember"/> property in order
|
||||
/// to identify which particular list you would like to display. You
|
||||
/// may also set the <see cref="DataMember"/> property even when
|
||||
/// DataSource refers to a list, since <see cref="DataMember"/> can
|
||||
/// also be used to navigate relations between lists.</para>
|
||||
/// </remarks>
|
||||
[Category("Data"),
|
||||
TypeConverter("System.Windows.Forms.Design.DataSourceConverter, System.Design")]
|
||||
public virtual Object DataSource {
|
||||
get { return this.Adapter.DataSource; }
|
||||
set { this.Adapter.DataSource = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the list or table in the data source for which the DataListView is displaying data.
|
||||
/// </summary>
|
||||
/// <remarks>If the data source is not a DataSet or DataViewManager, this property has no effect</remarks>
|
||||
[Category("Data"),
|
||||
Editor("System.Windows.Forms.Design.DataMemberListEditor, System.Design", typeof(UITypeEditor)),
|
||||
DefaultValue("")]
|
||||
public virtual string DataMember {
|
||||
get { return this.Adapter.DataMember; }
|
||||
set { this.Adapter.DataMember = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the property/column that uniquely identifies each row.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The value contained by this column must be unique across all rows
|
||||
/// in the data source. Odd and unpredictable things will happen if two
|
||||
/// rows have the same id.
|
||||
/// </para>
|
||||
/// <para>Null cannot be a valid key value.</para>
|
||||
/// </remarks>
|
||||
[Category("Data"),
|
||||
Description("The name of the property/column that holds the key of a row"),
|
||||
DefaultValue(null)]
|
||||
public virtual string KeyAspectName {
|
||||
get { return this.Adapter.KeyAspectName; }
|
||||
set { this.Adapter.KeyAspectName = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the property/column that contains the key of
|
||||
/// the parent of a row.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The test condition for deciding if one row is the parent of another is functionally
|
||||
/// equivilent to this:
|
||||
/// <code>
|
||||
/// Object.Equals(candidateParentRow[this.KeyAspectName], row[this.ParentKeyAspectName])
|
||||
/// </code>
|
||||
/// </para>
|
||||
/// <para>Unlike key value, parent keys can be null but a null parent key can only be used
|
||||
/// to identify root objects.</para>
|
||||
/// </remarks>
|
||||
[Category("Data"),
|
||||
Description("The name of the property/column that holds the key of the parent of a row"),
|
||||
DefaultValue(null)]
|
||||
public virtual string ParentKeyAspectName {
|
||||
get { return this.Adapter.ParentKeyAspectName; }
|
||||
set { this.Adapter.ParentKeyAspectName = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value that identifies a row as a root object.
|
||||
/// When the ParentKey of a row equals the RootKeyValue, that row will
|
||||
/// be treated as root of the TreeListView.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The test condition for deciding a root object is functionally
|
||||
/// equivilent to this:
|
||||
/// <code>
|
||||
/// Object.Equals(candidateRow[this.ParentKeyAspectName], this.RootKeyValue)
|
||||
/// </code>
|
||||
/// </para>
|
||||
/// <para>The RootKeyValue can be null. Actually, it can be any value that can
|
||||
/// be compared for equality against a basic type.</para>
|
||||
/// <para>If this is set to the wrong value (i.e. to a value that no row
|
||||
/// has in the parent id column), the list will be empty.</para>
|
||||
/// </remarks>
|
||||
[Browsable(false),
|
||||
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
public virtual object RootKeyValue {
|
||||
get { return this.Adapter.RootKeyValue; }
|
||||
set { this.Adapter.RootKeyValue = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value that identifies a row as a root object.
|
||||
/// <see cref="RootKeyValue"/>. The RootKeyValue can be of any type,
|
||||
/// but the IDE cannot sensibly represent a value of any type,
|
||||
/// so this is a typed wrapper around that property.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If you want the root value to be something other than a string,
|
||||
/// you will have set it yourself.
|
||||
/// </remarks>
|
||||
[Category("Data"),
|
||||
Description("The parent id value that identifies a row as a root object"),
|
||||
DefaultValue(null)]
|
||||
public virtual string RootKeyValueString {
|
||||
get { return Convert.ToString(this.Adapter.RootKeyValue); }
|
||||
set { this.Adapter.RootKeyValue = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether or not the key columns (id and parent id) should
|
||||
/// be shown to the user.
|
||||
/// </summary>
|
||||
/// <remarks>This must be set before the DataSource is set. It has no effect
|
||||
/// afterwards.</remarks>
|
||||
[Category("Data"),
|
||||
Description("Should the keys columns (id and parent id) be shown to the user?"),
|
||||
DefaultValue(true)]
|
||||
public virtual bool ShowKeyColumns {
|
||||
get { return this.Adapter.ShowKeyColumns; }
|
||||
set { this.Adapter.ShowKeyColumns = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the DataSourceAdaptor that does the bulk of the work needed
|
||||
/// for data binding.
|
||||
/// </summary>
|
||||
protected TreeDataSourceAdapter Adapter {
|
||||
get {
|
||||
if (this.adapter == null)
|
||||
this.adapter = new TreeDataSourceAdapter(this);
|
||||
return adapter;
|
||||
}
|
||||
set { adapter = value; }
|
||||
}
|
||||
private TreeDataSourceAdapter adapter;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
219
ObjectListView/DragDrop/DragSource.cs
Normal file
219
ObjectListView/DragDrop/DragSource.cs
Normal file
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* DragSource.cs - Add drag source functionality to an ObjectListView
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 2009-03-17 5:15 PM
|
||||
*
|
||||
* Change log:
|
||||
* 2011-03-29 JPP - Separate OLVDataObject.cs
|
||||
* v2.3
|
||||
* 2009-07-06 JPP - Make sure Link is acceptable as an drop effect by default
|
||||
* (since MS didn't make it part of the 'All' value)
|
||||
* v2.2
|
||||
* 2009-04-15 JPP - Separated DragSource.cs into DropSink.cs
|
||||
* 2009-03-17 JPP - Initial version
|
||||
*
|
||||
* Copyright (C) 2009-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
|
||||
namespace BrightIdeasSoftware
|
||||
{
|
||||
/// <summary>
|
||||
/// An IDragSource controls how drag out from the ObjectListView will behave
|
||||
/// </summary>
|
||||
public interface IDragSource
|
||||
{
|
||||
/// <summary>
|
||||
/// A drag operation is beginning. Return the data object that will be used
|
||||
/// for data transfer. Return null to prevent the drag from starting. The data
|
||||
/// object will normally include all the selected objects.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The returned object is later passed to the GetAllowedEffect() and EndDrag()
|
||||
/// methods.
|
||||
/// </remarks>
|
||||
/// <param name="olv">What ObjectListView is being dragged from.</param>
|
||||
/// <param name="button">Which mouse button is down?</param>
|
||||
/// <param name="item">What item was directly dragged by the user? There may be more than just this
|
||||
/// item selected.</param>
|
||||
/// <returns>The data object that will be used for data transfer. This will often be a subclass
|
||||
/// of DataObject, but does not need to be.</returns>
|
||||
Object StartDrag(ObjectListView olv, MouseButtons button, OLVListItem item);
|
||||
|
||||
/// <summary>
|
||||
/// What operations are possible for this drag? This controls the icon shown during the drag
|
||||
/// </summary>
|
||||
/// <param name="dragObject">The data object returned by StartDrag()</param>
|
||||
/// <returns>A combination of DragDropEffects flags</returns>
|
||||
DragDropEffects GetAllowedEffects(Object dragObject);
|
||||
|
||||
/// <summary>
|
||||
/// The drag operation is complete. Do whatever is necessary to complete the action.
|
||||
/// </summary>
|
||||
/// <param name="dragObject">The data object returned by StartDrag()</param>
|
||||
/// <param name="effect">The value returned from GetAllowedEffects()</param>
|
||||
void EndDrag(Object dragObject, DragDropEffects effect);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A do-nothing implementation of IDragSource that can be safely subclassed.
|
||||
/// </summary>
|
||||
public class AbstractDragSource : IDragSource
|
||||
{
|
||||
#region IDragSource Members
|
||||
|
||||
/// <summary>
|
||||
/// See IDragSource documentation
|
||||
/// </summary>
|
||||
/// <param name="olv"></param>
|
||||
/// <param name="button"></param>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
public virtual Object StartDrag(ObjectListView olv, MouseButtons button, OLVListItem item) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See IDragSource documentation
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <returns></returns>
|
||||
public virtual DragDropEffects GetAllowedEffects(Object data) {
|
||||
return DragDropEffects.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// See IDragSource documentation
|
||||
/// </summary>
|
||||
/// <param name="dragObject"></param>
|
||||
/// <param name="effect"></param>
|
||||
public virtual void EndDrag(Object dragObject, DragDropEffects effect) {
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A reasonable implementation of IDragSource that provides normal
|
||||
/// drag source functionality. It creates a data object that supports
|
||||
/// inter-application dragging of text and HTML representation of
|
||||
/// the dragged rows. It can optionally force a refresh of all dragged
|
||||
/// rows when the drag is complete.
|
||||
/// </summary>
|
||||
/// <remarks>Subclasses can override GetDataObject() to add new
|
||||
/// data formats to the data transfer object.</remarks>
|
||||
public class SimpleDragSource : IDragSource
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Construct a SimpleDragSource
|
||||
/// </summary>
|
||||
public SimpleDragSource() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a SimpleDragSource that refreshes the dragged rows when
|
||||
/// the drag is complete
|
||||
/// </summary>
|
||||
/// <param name="refreshAfterDrop"></param>
|
||||
public SimpleDragSource(bool refreshAfterDrop) {
|
||||
this.RefreshAfterDrop = refreshAfterDrop;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the dragged rows should be refreshed when the
|
||||
/// drag operation is complete.
|
||||
/// </summary>
|
||||
public bool RefreshAfterDrop {
|
||||
get { return refreshAfterDrop; }
|
||||
set { refreshAfterDrop = value; }
|
||||
}
|
||||
private bool refreshAfterDrop;
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDragSource Members
|
||||
|
||||
/// <summary>
|
||||
/// Create a DataObject when the user does a left mouse drag operation.
|
||||
/// See IDragSource for further information.
|
||||
/// </summary>
|
||||
/// <param name="olv"></param>
|
||||
/// <param name="button"></param>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
public virtual Object StartDrag(ObjectListView olv, MouseButtons button, OLVListItem item) {
|
||||
// We only drag on left mouse
|
||||
if (button != MouseButtons.Left)
|
||||
return null;
|
||||
|
||||
return this.CreateDataObject(olv);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Which operations are allowed in the operation? By default, all operations are supported.
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <returns>All opertions are supported</returns>
|
||||
public virtual DragDropEffects GetAllowedEffects(Object data) {
|
||||
return DragDropEffects.All | DragDropEffects.Link; // why didn't MS include 'Link' in 'All'??
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The drag operation is finished. Refreshe the dragged rows if so configured.
|
||||
/// </summary>
|
||||
/// <param name="dragObject"></param>
|
||||
/// <param name="effect"></param>
|
||||
public virtual void EndDrag(Object dragObject, DragDropEffects effect) {
|
||||
OLVDataObject data = dragObject as OLVDataObject;
|
||||
if (data == null)
|
||||
return;
|
||||
|
||||
if (this.RefreshAfterDrop)
|
||||
data.ListView.RefreshObjects(data.ModelObjects);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a data object that will be used to as the data object
|
||||
/// for the drag operation.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Subclasses can override this method add new formats to the data object.
|
||||
/// </remarks>
|
||||
/// <param name="olv">The ObjectListView that is the source of the drag</param>
|
||||
/// <returns>A data object for the drag</returns>
|
||||
protected virtual object CreateDataObject(ObjectListView olv) {
|
||||
return new OLVDataObject(olv);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
1454
ObjectListView/DragDrop/DropSink.cs
Normal file
1454
ObjectListView/DragDrop/DropSink.cs
Normal file
File diff suppressed because it is too large
Load Diff
185
ObjectListView/DragDrop/OLVDataObject.cs
Normal file
185
ObjectListView/DragDrop/OLVDataObject.cs
Normal file
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* OLVDataObject.cs - An OLE DataObject that knows how to convert rows of an OLV to text and HTML
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 2011-03-29 3:34PM
|
||||
*
|
||||
* Change log:
|
||||
* v2.8
|
||||
* 2014-05-02 JPP - When the listview is completely empty, don't try to set CSV text in the clipboard.
|
||||
* v2.6
|
||||
* 2012-08-08 JPP - Changed to use OLVExporter.
|
||||
* - Added CSV to formats exported to Clipboard
|
||||
* v2.4
|
||||
* 2011-03-29 JPP - Initial version
|
||||
*
|
||||
* Copyright (C) 2011-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
/// <summary>
|
||||
/// A data transfer object that knows how to transform a list of model
|
||||
/// objects into a text and HTML representation.
|
||||
/// </summary>
|
||||
public class OLVDataObject : DataObject {
|
||||
#region Life and death
|
||||
|
||||
/// <summary>
|
||||
/// Create a data object from the selected objects in the given ObjectListView
|
||||
/// </summary>
|
||||
/// <param name="olv">The source of the data object</param>
|
||||
public OLVDataObject(ObjectListView olv)
|
||||
: this(olv, olv.SelectedObjects) {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a data object which operates on the given model objects
|
||||
/// in the given ObjectListView
|
||||
/// </summary>
|
||||
/// <param name="olv">The source of the data object</param>
|
||||
/// <param name="modelObjects">The model objects to be put into the data object</param>
|
||||
public OLVDataObject(ObjectListView olv, IList modelObjects) {
|
||||
this.objectListView = olv;
|
||||
this.modelObjects = modelObjects;
|
||||
this.includeHiddenColumns = olv.IncludeHiddenColumnsInDataTransfer;
|
||||
this.includeColumnHeaders = olv.IncludeColumnHeadersInCopy;
|
||||
this.CreateTextFormats();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether hidden columns will also be included in the text
|
||||
/// and HTML representation. If this is false, only visible columns will
|
||||
/// be included.
|
||||
/// </summary>
|
||||
public bool IncludeHiddenColumns {
|
||||
get { return includeHiddenColumns; }
|
||||
}
|
||||
private readonly bool includeHiddenColumns;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether column headers will also be included in the text
|
||||
/// and HTML representation.
|
||||
/// </summary>
|
||||
public bool IncludeColumnHeaders {
|
||||
get { return includeColumnHeaders; }
|
||||
}
|
||||
private readonly bool includeColumnHeaders;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ObjectListView that is being used as the source of the data
|
||||
/// </summary>
|
||||
public ObjectListView ListView {
|
||||
get { return objectListView; }
|
||||
}
|
||||
private readonly ObjectListView objectListView;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the model objects that are to be placed in the data object
|
||||
/// </summary>
|
||||
public IList ModelObjects {
|
||||
get { return modelObjects; }
|
||||
}
|
||||
private readonly IList modelObjects;
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Put a text and HTML representation of our model objects
|
||||
/// into the data object.
|
||||
/// </summary>
|
||||
public void CreateTextFormats() {
|
||||
|
||||
OLVExporter exporter = this.CreateExporter();
|
||||
|
||||
// Put both the text and html versions onto the clipboard.
|
||||
// For some reason, SetText() with UnicodeText doesn't set the basic CF_TEXT format,
|
||||
// but using SetData() does.
|
||||
//this.SetText(sbText.ToString(), TextDataFormat.UnicodeText);
|
||||
this.SetData(exporter.ExportTo(OLVExporter.ExportFormat.TabSeparated));
|
||||
string exportTo = exporter.ExportTo(OLVExporter.ExportFormat.CSV);
|
||||
if (!String.IsNullOrEmpty(exportTo))
|
||||
this.SetText(exportTo, TextDataFormat.CommaSeparatedValue);
|
||||
this.SetText(ConvertToHtmlFragment(exporter.ExportTo(OLVExporter.ExportFormat.HTML)), TextDataFormat.Html);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an exporter for the data contained in this object
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected OLVExporter CreateExporter() {
|
||||
OLVExporter exporter = new OLVExporter(this.ListView);
|
||||
exporter.IncludeColumnHeaders = this.IncludeColumnHeaders;
|
||||
exporter.IncludeHiddenColumns = this.IncludeHiddenColumns;
|
||||
exporter.ModelObjects = this.ModelObjects;
|
||||
return exporter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make a HTML representation of our model objects
|
||||
/// </summary>
|
||||
[Obsolete("Use OLVExporter directly instead", false)]
|
||||
public string CreateHtml() {
|
||||
OLVExporter exporter = this.CreateExporter();
|
||||
return exporter.ExportTo(OLVExporter.ExportFormat.HTML);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert the fragment of HTML into the Clipboards HTML format.
|
||||
/// </summary>
|
||||
/// <remarks>The HTML format is found here http://msdn2.microsoft.com/en-us/library/aa767917.aspx
|
||||
/// </remarks>
|
||||
/// <param name="fragment">The HTML to put onto the clipboard. It must be valid HTML!</param>
|
||||
/// <returns>A string that can be put onto the clipboard and will be recognized as HTML</returns>
|
||||
private string ConvertToHtmlFragment(string fragment) {
|
||||
// Minimal implementation of HTML clipboard format
|
||||
const string SOURCE = "http://www.codeproject.com/Articles/16009/A-Much-Easier-to-Use-ListView";
|
||||
|
||||
const String MARKER_BLOCK =
|
||||
"Version:1.0\r\n" +
|
||||
"StartHTML:{0,8}\r\n" +
|
||||
"EndHTML:{1,8}\r\n" +
|
||||
"StartFragment:{2,8}\r\n" +
|
||||
"EndFragment:{3,8}\r\n" +
|
||||
"StartSelection:{2,8}\r\n" +
|
||||
"EndSelection:{3,8}\r\n" +
|
||||
"SourceURL:{4}\r\n" +
|
||||
"{5}";
|
||||
|
||||
int prefixLength = String.Format(MARKER_BLOCK, 0, 0, 0, 0, SOURCE, "").Length;
|
||||
|
||||
const String DEFAULT_HTML_BODY =
|
||||
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">" +
|
||||
"<HTML><HEAD></HEAD><BODY><!--StartFragment-->{0}<!--EndFragment--></BODY></HTML>";
|
||||
|
||||
string html = String.Format(DEFAULT_HTML_BODY, fragment);
|
||||
int startFragment = prefixLength + html.IndexOf(fragment, StringComparison.Ordinal);
|
||||
int endFragment = startFragment + fragment.Length;
|
||||
|
||||
return String.Format(MARKER_BLOCK, prefixLength, prefixLength + html.Length, startFragment, endFragment, SOURCE, html);
|
||||
}
|
||||
}
|
||||
}
|
||||
165
ObjectListView/FastDataListView.cs
Normal file
165
ObjectListView/FastDataListView.cs
Normal file
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* FastDataListView - A data bindable listview that has the speed of a virtual list
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 22/09/2010 8:11 AM
|
||||
*
|
||||
* Change log:
|
||||
* 2015-02-02 JPP - Made Unfreezing more efficient by removing a redundant BuildList() call
|
||||
* v2.6
|
||||
* 2010-09-22 JPP - Initial version
|
||||
*
|
||||
* Copyright (C) 2006-2015 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.ComponentModel;
|
||||
using System.Windows.Forms;
|
||||
using System.Drawing.Design;
|
||||
|
||||
namespace BrightIdeasSoftware
|
||||
{
|
||||
/// <summary>
|
||||
/// A FastDataListView virtualizes the display of data from a DataSource. It operates on
|
||||
/// DataSets and DataTables in the same way as a DataListView, but does so much more efficiently.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// A FastDataListView still has to load all its data from the DataSource. If you have SQL statement
|
||||
/// that returns 1 million rows, all 1 million rows will still need to read from the database.
|
||||
/// However, once the rows are loaded, the FastDataListView will only build rows as they are displayed.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class FastDataListView : FastObjectListView
|
||||
{
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (this.adapter != null) {
|
||||
this.adapter.Dispose();
|
||||
this.adapter = null;
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether or not columns will be automatically generated to show the
|
||||
/// columns when the DataSource is set.
|
||||
/// </summary>
|
||||
/// <remarks>This must be set before the DataSource is set. It has no effect afterwards.</remarks>
|
||||
[Category("Data"),
|
||||
Description("Should the control automatically generate columns from the DataSource"),
|
||||
DefaultValue(true)]
|
||||
public bool AutoGenerateColumns
|
||||
{
|
||||
get { return this.Adapter.AutoGenerateColumns; }
|
||||
set { this.Adapter.AutoGenerateColumns = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get or set the VirtualListDataSource that will be displayed in this list view.
|
||||
/// </summary>
|
||||
/// <remarks>The VirtualListDataSource should implement either <see cref="IList"/>, <see cref="IBindingList"/>,
|
||||
/// or <see cref="IListSource"/>. Some common examples are the following types of objects:
|
||||
/// <list type="unordered">
|
||||
/// <item><description><see cref="DataView"/></description></item>
|
||||
/// <item><description><see cref="DataTable"/></description></item>
|
||||
/// <item><description><see cref="DataSet"/></description></item>
|
||||
/// <item><description><see cref="DataViewManager"/></description></item>
|
||||
/// <item><description><see cref="BindingSource"/></description></item>
|
||||
/// </list>
|
||||
/// <para>When binding to a list container (i.e. one that implements the
|
||||
/// <see cref="IListSource"/> interface, such as <see cref="DataSet"/>)
|
||||
/// you must also set the <see cref="DataMember"/> property in order
|
||||
/// to identify which particular list you would like to display. You
|
||||
/// may also set the <see cref="DataMember"/> property even when
|
||||
/// VirtualListDataSource refers to a list, since <see cref="DataMember"/> can
|
||||
/// also be used to navigate relations between lists.</para>
|
||||
/// </remarks>
|
||||
[Category("Data"),
|
||||
TypeConverter("System.Windows.Forms.Design.DataSourceConverter, System.Design")]
|
||||
public virtual Object DataSource {
|
||||
get { return this.Adapter.DataSource; }
|
||||
set { this.Adapter.DataSource = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the list or table in the data source for which the DataListView is displaying data.
|
||||
/// </summary>
|
||||
/// <remarks>If the data source is not a DataSet or DataViewManager, this property has no effect</remarks>
|
||||
[Category("Data"),
|
||||
Editor("System.Windows.Forms.Design.DataMemberListEditor, System.Design", typeof(UITypeEditor)),
|
||||
DefaultValue("")]
|
||||
public virtual string DataMember {
|
||||
get { return this.Adapter.DataMember; }
|
||||
set { this.Adapter.DataMember = value; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the DataSourceAdaptor that does the bulk of the work needed
|
||||
/// for data binding.
|
||||
/// </summary>
|
||||
protected DataSourceAdapter Adapter {
|
||||
get {
|
||||
if (adapter == null)
|
||||
adapter = this.CreateDataSourceAdapter();
|
||||
return adapter;
|
||||
}
|
||||
set { adapter = value; }
|
||||
}
|
||||
private DataSourceAdapter adapter;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation
|
||||
|
||||
/// <summary>
|
||||
/// Create the DataSourceAdapter that this control will use.
|
||||
/// </summary>
|
||||
/// <returns>A DataSourceAdapter configured for this list</returns>
|
||||
/// <remarks>Subclasses should override this to create their
|
||||
/// own specialized adapters</remarks>
|
||||
protected virtual DataSourceAdapter CreateDataSourceAdapter() {
|
||||
return new DataSourceAdapter(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Change the Unfreeze behaviour
|
||||
/// </summary>
|
||||
protected override void DoUnfreeze()
|
||||
{
|
||||
|
||||
// Copied from base method, but we don't need to BuildList() since we know that our
|
||||
// data adaptor is going to do that immediately after this method exits.
|
||||
this.EndUpdate();
|
||||
this.ResizeFreeSpaceFillingColumns();
|
||||
// this.BuildList();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
422
ObjectListView/FastObjectListView.cs
Normal file
422
ObjectListView/FastObjectListView.cs
Normal file
@@ -0,0 +1,422 @@
|
||||
/*
|
||||
* FastObjectListView - A listview that behaves like an ObjectListView but has the speed of a virtual list
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 27/09/2008 9:15 AM
|
||||
*
|
||||
* Change log:
|
||||
* 2014-10-15 JPP - Fire Filter event when applying filters
|
||||
* v2.8
|
||||
* 2012-06-11 JPP - Added more efficient version of FilteredObjects
|
||||
* v2.5.1
|
||||
* 2011-04-25 JPP - Fixed problem with removing objects from filtered or sorted list
|
||||
* v2.4
|
||||
* 2010-04-05 JPP - Added filtering
|
||||
* v2.3
|
||||
* 2009-08-27 JPP - Added GroupingStrategy
|
||||
* - Added optimized Objects property
|
||||
* v2.2.1
|
||||
* 2009-01-07 JPP - Made all public and protected methods virtual
|
||||
* 2008-09-27 JPP - Separated from ObjectListView.cs
|
||||
*
|
||||
* Copyright (C) 2006-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace BrightIdeasSoftware
|
||||
{
|
||||
/// <summary>
|
||||
/// A FastObjectListView trades function for speed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>On my mid-range laptop, this view builds a list of 10,000 objects in 0.1 seconds,
|
||||
/// as opposed to a normal ObjectListView which takes 10-15 seconds. Lists of up to 50,000 items should be
|
||||
/// able to be handled with sub-second response times even on low end machines.</para>
|
||||
/// <para>
|
||||
/// A FastObjectListView is implemented as a virtual list with many of the virtual modes limits (e.g. no sorting)
|
||||
/// fixed through coding. There are some functions that simply cannot be provided. Specifically, a FastObjectListView cannot:
|
||||
/// <list type="bullet">
|
||||
/// <item><description>use Tile view</description></item>
|
||||
/// <item><description>show groups on XP</description></item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class FastObjectListView : VirtualObjectListView
|
||||
{
|
||||
/// <summary>
|
||||
/// Make a FastObjectListView
|
||||
/// </summary>
|
||||
public FastObjectListView() {
|
||||
this.VirtualListDataSource = new FastObjectListDataSource(this);
|
||||
this.GroupingStrategy = new FastListGroupingStrategy();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of objects that survive any filtering that may be in place.
|
||||
/// </summary>
|
||||
[Browsable(false),
|
||||
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
public override IEnumerable FilteredObjects {
|
||||
get {
|
||||
// This is much faster than the base method
|
||||
return ((FastObjectListDataSource)this.VirtualListDataSource).FilteredObjectList;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get/set the collection of objects that this list will show
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The contents of the control will be updated immediately after setting this property.
|
||||
/// </para>
|
||||
/// <para>This method preserves selection, if possible. Use SetObjects() if
|
||||
/// you do not want to preserve the selection. Preserving selection is the slowest part of this
|
||||
/// code and performance is O(n) where n is the number of selected rows.</para>
|
||||
/// <para>This method is not thread safe.</para>
|
||||
/// </remarks>
|
||||
[Browsable(false),
|
||||
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
public override IEnumerable Objects {
|
||||
get {
|
||||
// This is much faster than the base method
|
||||
return ((FastObjectListDataSource)this.VirtualListDataSource).ObjectList;
|
||||
}
|
||||
set { base.Objects = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move the given collection of objects to the given index.
|
||||
/// </summary>
|
||||
/// <remarks>This operation only makes sense on non-grouped ObjectListViews.</remarks>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="modelObjects"></param>
|
||||
public override void MoveObjects(int index, ICollection modelObjects) {
|
||||
if (this.InvokeRequired) {
|
||||
this.Invoke((MethodInvoker)delegate() { this.MoveObjects(index, modelObjects); });
|
||||
return;
|
||||
}
|
||||
|
||||
// If any object that is going to be moved is before the point where the insertion
|
||||
// will occur, then we have to reduce the location of our insertion point
|
||||
int displacedObjectCount = 0;
|
||||
foreach (object modelObject in modelObjects) {
|
||||
int i = this.IndexOf(modelObject);
|
||||
if (i >= 0 && i <= index)
|
||||
displacedObjectCount++;
|
||||
}
|
||||
index -= displacedObjectCount;
|
||||
|
||||
this.BeginUpdate();
|
||||
try {
|
||||
this.RemoveObjects(modelObjects);
|
||||
this.InsertObjects(index, modelObjects);
|
||||
}
|
||||
finally {
|
||||
this.EndUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove any sorting and revert to the given order of the model objects
|
||||
/// </summary>
|
||||
/// <remarks>To be really honest, Unsort() doesn't work on FastObjectListViews since
|
||||
/// the original ordering of model objects is lost when Sort() is called. So this method
|
||||
/// effectively just turns off sorting.</remarks>
|
||||
public override void Unsort() {
|
||||
this.ShowGroups = false;
|
||||
this.PrimarySortColumn = null;
|
||||
this.PrimarySortOrder = SortOrder.None;
|
||||
this.SetObjects(this.Objects);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provide a data source for a FastObjectListView
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class isn't intended to be used directly, but it is left as a public
|
||||
/// class just in case someone wants to subclass it.
|
||||
/// </remarks>
|
||||
public class FastObjectListDataSource : AbstractVirtualListDataSource
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a FastObjectListDataSource
|
||||
/// </summary>
|
||||
/// <param name="listView"></param>
|
||||
public FastObjectListDataSource(FastObjectListView listView)
|
||||
: base(listView) {
|
||||
}
|
||||
|
||||
#region IVirtualListDataSource Members
|
||||
|
||||
/// <summary>
|
||||
/// Get n'th object
|
||||
/// </summary>
|
||||
/// <param name="n"></param>
|
||||
/// <returns></returns>
|
||||
public override object GetNthObject(int n) {
|
||||
if (n >= 0 && n < this.filteredObjectList.Count)
|
||||
return this.filteredObjectList[n];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// How many items are in the data source
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override int GetObjectCount() {
|
||||
return this.filteredObjectList.Count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the index of the given model
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <returns></returns>
|
||||
public override int GetObjectIndex(object model) {
|
||||
int index;
|
||||
|
||||
if (model != null && this.objectsToIndexMap.TryGetValue(model, out index))
|
||||
return index;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <param name="first"></param>
|
||||
/// <param name="last"></param>
|
||||
/// <param name="column"></param>
|
||||
/// <returns></returns>
|
||||
public override int SearchText(string text, int first, int last, OLVColumn column) {
|
||||
if (first <= last) {
|
||||
for (int i = first; i <= last; i++) {
|
||||
string data = column.GetStringValue(this.listView.GetNthItemInDisplayOrder(i).RowObject);
|
||||
if (data.StartsWith(text, StringComparison.CurrentCultureIgnoreCase))
|
||||
return i;
|
||||
}
|
||||
} else {
|
||||
for (int i = first; i >= last; i--) {
|
||||
string data = column.GetStringValue(this.listView.GetNthItemInDisplayOrder(i).RowObject);
|
||||
if (data.StartsWith(text, StringComparison.CurrentCultureIgnoreCase))
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="column"></param>
|
||||
/// <param name="sortOrder"></param>
|
||||
public override void Sort(OLVColumn column, SortOrder sortOrder) {
|
||||
if (sortOrder != SortOrder.None) {
|
||||
ModelObjectComparer comparer = new ModelObjectComparer(column, sortOrder, this.listView.SecondarySortColumn, this.listView.SecondarySortOrder);
|
||||
this.fullObjectList.Sort(comparer);
|
||||
this.filteredObjectList.Sort(comparer);
|
||||
}
|
||||
this.RebuildIndexMap();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="modelObjects"></param>
|
||||
public override void AddObjects(ICollection modelObjects) {
|
||||
foreach (object modelObject in modelObjects) {
|
||||
if (modelObject != null)
|
||||
this.fullObjectList.Add(modelObject);
|
||||
}
|
||||
this.FilterObjects();
|
||||
this.RebuildIndexMap();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="modelObjects"></param>
|
||||
public override void InsertObjects(int index, ICollection modelObjects) {
|
||||
this.fullObjectList.InsertRange(index, modelObjects);
|
||||
this.FilterObjects();
|
||||
this.RebuildIndexMap();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the given collection of models from this source.
|
||||
/// </summary>
|
||||
/// <param name="modelObjects"></param>
|
||||
public override void RemoveObjects(ICollection modelObjects) {
|
||||
|
||||
// We have to unselect any object that is about to be deleted
|
||||
List<int> indicesToRemove = new List<int>();
|
||||
foreach (object modelObject in modelObjects) {
|
||||
int i = this.GetObjectIndex(modelObject);
|
||||
if (i >= 0)
|
||||
indicesToRemove.Add(i);
|
||||
}
|
||||
|
||||
// Sort the indices from highest to lowest so that we
|
||||
// remove latter ones before earlier ones. In this way, the
|
||||
// indices of the rows doesn't change after the deletes.
|
||||
indicesToRemove.Sort();
|
||||
indicesToRemove.Reverse();
|
||||
|
||||
foreach (int i in indicesToRemove)
|
||||
this.listView.SelectedIndices.Remove(i);
|
||||
|
||||
// Remove the objects from the unfiltered list
|
||||
foreach (object modelObject in modelObjects)
|
||||
this.fullObjectList.Remove(modelObject);
|
||||
|
||||
this.FilterObjects();
|
||||
this.RebuildIndexMap();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="collection"></param>
|
||||
public override void SetObjects(IEnumerable collection) {
|
||||
ArrayList newObjects = ObjectListView.EnumerableToArray(collection, true);
|
||||
|
||||
this.fullObjectList = newObjects;
|
||||
this.FilterObjects();
|
||||
this.RebuildIndexMap();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update/replace the nth object with the given object
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="modelObject"></param>
|
||||
public override void UpdateObject(int index, object modelObject) {
|
||||
if (index < 0 || index >= this.filteredObjectList.Count)
|
||||
return;
|
||||
|
||||
int i = this.fullObjectList.IndexOf(this.filteredObjectList[index]);
|
||||
if (i < 0)
|
||||
return;
|
||||
|
||||
if (ReferenceEquals(this.fullObjectList[i], modelObject))
|
||||
return;
|
||||
|
||||
this.fullObjectList[i] = modelObject;
|
||||
this.filteredObjectList[index] = modelObject;
|
||||
this.objectsToIndexMap[modelObject] = index;
|
||||
}
|
||||
|
||||
private ArrayList fullObjectList = new ArrayList();
|
||||
private ArrayList filteredObjectList = new ArrayList();
|
||||
private IModelFilter modelFilter;
|
||||
private IListFilter listFilter;
|
||||
|
||||
#endregion
|
||||
|
||||
#region IFilterableDataSource Members
|
||||
|
||||
/// <summary>
|
||||
/// Apply the given filters to this data source. One or both may be null.
|
||||
/// </summary>
|
||||
/// <param name="iModelFilter"></param>
|
||||
/// <param name="iListFilter"></param>
|
||||
public override void ApplyFilters(IModelFilter iModelFilter, IListFilter iListFilter) {
|
||||
this.modelFilter = iModelFilter;
|
||||
this.listFilter = iListFilter;
|
||||
this.SetObjects(this.fullObjectList);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full list of objects being used for this fast list.
|
||||
/// This list is unfiltered.
|
||||
/// </summary>
|
||||
public ArrayList ObjectList {
|
||||
get { return fullObjectList; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of objects from ObjectList which survive any installed filters.
|
||||
/// </summary>
|
||||
public ArrayList FilteredObjectList {
|
||||
get { return filteredObjectList; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rebuild the map that remembers which model object is displayed at which line
|
||||
/// </summary>
|
||||
protected void RebuildIndexMap() {
|
||||
this.objectsToIndexMap.Clear();
|
||||
for (int i = 0; i < this.filteredObjectList.Count; i++)
|
||||
this.objectsToIndexMap[this.filteredObjectList[i]] = i;
|
||||
}
|
||||
readonly Dictionary<Object, int> objectsToIndexMap = new Dictionary<Object, int>();
|
||||
|
||||
/// <summary>
|
||||
/// Build our filtered list from our full list.
|
||||
/// </summary>
|
||||
protected void FilterObjects() {
|
||||
|
||||
// If this list isn't filtered, we don't need to do anything else
|
||||
if (!this.listView.UseFiltering) {
|
||||
this.filteredObjectList = new ArrayList(this.fullObjectList);
|
||||
return;
|
||||
}
|
||||
|
||||
// Tell the world to filter the objects. If they do so, don't do anything else
|
||||
// ReSharper disable PossibleMultipleEnumeration
|
||||
FilterEventArgs args = new FilterEventArgs(this.fullObjectList);
|
||||
this.listView.OnFilter(args);
|
||||
if (args.FilteredObjects != null) {
|
||||
this.filteredObjectList = ObjectListView.EnumerableToArray(args.FilteredObjects, false);
|
||||
return;
|
||||
}
|
||||
|
||||
IEnumerable objects = (this.listFilter == null) ?
|
||||
this.fullObjectList : this.listFilter.Filter(this.fullObjectList);
|
||||
|
||||
// Apply the object filter if there is one
|
||||
if (this.modelFilter == null) {
|
||||
this.filteredObjectList = ObjectListView.EnumerableToArray(objects, false);
|
||||
} else {
|
||||
this.filteredObjectList = new ArrayList();
|
||||
foreach (object model in objects) {
|
||||
if (this.modelFilter.Filter(model))
|
||||
this.filteredObjectList.Add(model);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
||||
125
ObjectListView/Filtering/Cluster.cs
Normal file
125
ObjectListView/Filtering/Cluster.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Cluster - Implements a simple cluster
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 3-March-2011 10:53 pm
|
||||
*
|
||||
* Change log:
|
||||
* 2011-03-03 JPP - First version
|
||||
*
|
||||
* Copyright (C) 2011-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
/// <summary>
|
||||
/// Concrete implementation of the ICluster interface.
|
||||
/// </summary>
|
||||
public class Cluster : ICluster {
|
||||
|
||||
#region Life and death
|
||||
|
||||
/// <summary>
|
||||
/// Create a cluster
|
||||
/// </summary>
|
||||
/// <param name="key">The key for the cluster</param>
|
||||
public Cluster(object key) {
|
||||
this.Count = 1;
|
||||
this.ClusterKey = key;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public overrides
|
||||
|
||||
/// <summary>
|
||||
/// Return a string representation of this cluster
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override string ToString() {
|
||||
return this.DisplayLabel ?? "[empty]";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation of ICluster
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets how many items belong to this cluster
|
||||
/// </summary>
|
||||
public int Count {
|
||||
get { return count; }
|
||||
set { count = value; }
|
||||
}
|
||||
private int count;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the label that will be shown to the user to represent
|
||||
/// this cluster
|
||||
/// </summary>
|
||||
public string DisplayLabel {
|
||||
get { return displayLabel; }
|
||||
set { displayLabel = value; }
|
||||
}
|
||||
private string displayLabel;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the actual data object that all members of this cluster
|
||||
/// have commonly returned.
|
||||
/// </summary>
|
||||
public object ClusterKey {
|
||||
get { return clusterKey; }
|
||||
set { clusterKey = value; }
|
||||
}
|
||||
private object clusterKey;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation of IComparable
|
||||
|
||||
/// <summary>
|
||||
/// Return an indication of the ordering between this object and the given one
|
||||
/// </summary>
|
||||
/// <param name="other"></param>
|
||||
/// <returns></returns>
|
||||
public int CompareTo(object other) {
|
||||
if (other == null || other == System.DBNull.Value)
|
||||
return 1;
|
||||
|
||||
ICluster otherCluster = other as ICluster;
|
||||
if (otherCluster == null)
|
||||
return 1;
|
||||
|
||||
string keyAsString = this.ClusterKey as string;
|
||||
if (keyAsString != null)
|
||||
return String.Compare(keyAsString, otherCluster.ClusterKey as string, StringComparison.CurrentCultureIgnoreCase);
|
||||
|
||||
IComparable keyAsComparable = this.ClusterKey as IComparable;
|
||||
if (keyAsComparable != null)
|
||||
return keyAsComparable.CompareTo(otherCluster.ClusterKey);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
189
ObjectListView/Filtering/ClusteringStrategy.cs
Normal file
189
ObjectListView/Filtering/ClusteringStrategy.cs
Normal file
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* ClusteringStrategy - Implements a simple clustering strategy
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 3-March-2011 10:53 pm
|
||||
*
|
||||
* Change log:
|
||||
* 2011-03-03 JPP - First version
|
||||
*
|
||||
* Copyright (C) 2011-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Text;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
/// <summary>
|
||||
/// This class provides a useful base implemention of a clustering
|
||||
/// strategy where the clusters are grouped around the value of a given column.
|
||||
/// </summary>
|
||||
public class ClusteringStrategy : IClusteringStrategy {
|
||||
|
||||
#region Static properties
|
||||
|
||||
/// <summary>
|
||||
/// This field is the text that will be shown to the user when a cluster
|
||||
/// key is null. It is exposed so it can be localized.
|
||||
/// </summary>
|
||||
static public string NULL_LABEL = "[null]";
|
||||
|
||||
/// <summary>
|
||||
/// This field is the text that will be shown to the user when a cluster
|
||||
/// key is empty (i.e. a string of zero length). It is exposed so it can be localized.
|
||||
/// </summary>
|
||||
static public string EMPTY_LABEL = "[empty]";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the format that will be used by default for clusters that only
|
||||
/// contain 1 item. The format string must accept two placeholders:
|
||||
/// - {0} is the cluster key converted to a string
|
||||
/// - {1} is the number of items in the cluster (always 1 in this case)
|
||||
/// </summary>
|
||||
static public string DefaultDisplayLabelFormatSingular {
|
||||
get { return defaultDisplayLabelFormatSingular; }
|
||||
set { defaultDisplayLabelFormatSingular = value; }
|
||||
}
|
||||
static private string defaultDisplayLabelFormatSingular = "{0} ({1} item)";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the format that will be used by default for clusters that
|
||||
/// contain 0 or two or more items. The format string must accept two placeholders:
|
||||
/// - {0} is the cluster key converted to a string
|
||||
/// - {1} is the number of items in the cluster
|
||||
/// </summary>
|
||||
static public string DefaultDisplayLabelFormatPlural {
|
||||
get { return defaultDisplayLabelFormatPural; }
|
||||
set { defaultDisplayLabelFormatPural = value; }
|
||||
}
|
||||
static private string defaultDisplayLabelFormatPural = "{0} ({1} items)";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Life and death
|
||||
|
||||
/// <summary>
|
||||
/// Create a clustering strategy
|
||||
/// </summary>
|
||||
public ClusteringStrategy() {
|
||||
this.DisplayLabelFormatSingular = DefaultDisplayLabelFormatSingular;
|
||||
this.DisplayLabelFormatPlural = DefaultDisplayLabelFormatPlural;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the column upon which this strategy is operating
|
||||
/// </summary>
|
||||
public OLVColumn Column {
|
||||
get { return column; }
|
||||
set { column = value; }
|
||||
}
|
||||
private OLVColumn column;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the format that will be used when the cluster
|
||||
/// contains only 1 item. The format string must accept two placeholders:
|
||||
/// - {0} is the cluster key converted to a string
|
||||
/// - {1} is the number of items in the cluster (always 1 in this case)
|
||||
/// </summary>
|
||||
/// <remarks>If this is not set, the value from
|
||||
/// ClusteringStrategy.DefaultDisplayLabelFormatSingular will be used</remarks>
|
||||
public string DisplayLabelFormatSingular {
|
||||
get { return displayLabelFormatSingular; }
|
||||
set { displayLabelFormatSingular = value; }
|
||||
}
|
||||
private string displayLabelFormatSingular;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the format that will be used when the cluster
|
||||
/// contains 0 or two or more items. The format string must accept two placeholders:
|
||||
/// - {0} is the cluster key converted to a string
|
||||
/// - {1} is the number of items in the cluster
|
||||
/// </summary>
|
||||
/// <remarks>If this is not set, the value from
|
||||
/// ClusteringStrategy.DefaultDisplayLabelFormatPlural will be used</remarks>
|
||||
public string DisplayLabelFormatPlural {
|
||||
get { return displayLabelFormatPural; }
|
||||
set { displayLabelFormatPural = value; }
|
||||
}
|
||||
private string displayLabelFormatPural;
|
||||
|
||||
#endregion
|
||||
|
||||
#region ICluster implementation
|
||||
|
||||
/// <summary>
|
||||
/// Get the cluster key by which the given model will be partitioned by this strategy
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <returns></returns>
|
||||
virtual public object GetClusterKey(object model) {
|
||||
return this.Column.GetValue(model);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a cluster to hold the given cluster key
|
||||
/// </summary>
|
||||
/// <param name="clusterKey"></param>
|
||||
/// <returns></returns>
|
||||
virtual public ICluster CreateCluster(object clusterKey) {
|
||||
return new Cluster(clusterKey);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the display label that the given cluster should use
|
||||
/// </summary>
|
||||
/// <param name="cluster"></param>
|
||||
/// <returns></returns>
|
||||
virtual public string GetClusterDisplayLabel(ICluster cluster) {
|
||||
string s = this.Column.ValueToString(cluster.ClusterKey) ?? NULL_LABEL;
|
||||
if (String.IsNullOrEmpty(s))
|
||||
s = EMPTY_LABEL;
|
||||
return this.ApplyDisplayFormat(cluster, s);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a filter that will include only model objects that
|
||||
/// match one or more of the given values.
|
||||
/// </summary>
|
||||
/// <param name="valuesChosenForFiltering"></param>
|
||||
/// <returns></returns>
|
||||
virtual public IModelFilter CreateFilter(IList valuesChosenForFiltering) {
|
||||
return new OneOfFilter(this.GetClusterKey, valuesChosenForFiltering);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a label that combines the string representation of the cluster
|
||||
/// key with a format string that holds an "X [N items in cluster]" type layout.
|
||||
/// </summary>
|
||||
/// <param name="cluster"></param>
|
||||
/// <param name="s"></param>
|
||||
/// <returns></returns>
|
||||
virtual protected string ApplyDisplayFormat(ICluster cluster, string s) {
|
||||
string format = (cluster.Count == 1) ? this.DisplayLabelFormatSingular : this.DisplayLabelFormatPlural;
|
||||
return String.IsNullOrEmpty(format) ? s : String.Format(format, s, cluster.Count);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
70
ObjectListView/Filtering/ClustersFromGroupsStrategy.cs
Normal file
70
ObjectListView/Filtering/ClustersFromGroupsStrategy.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* ClusteringStrategy - Implements a simple clustering strategy
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 1-April-2011 8:12am
|
||||
*
|
||||
* Change log:
|
||||
* 2011-04-01 JPP - First version
|
||||
*
|
||||
* Copyright (C) 2011-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
/// <summary>
|
||||
/// This class calculates clusters from the groups that the column uses.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This is the default strategy for all non-date, filterable columns.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This class does not strictly mimic the groups created by the given column.
|
||||
/// In particular, if the programmer changes the default grouping technique
|
||||
/// by listening for grouping events, this class will not mimic that behaviour.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class ClustersFromGroupsStrategy : ClusteringStrategy {
|
||||
|
||||
/// <summary>
|
||||
/// Get the cluster key by which the given model will be partitioned by this strategy
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <returns></returns>
|
||||
public override object GetClusterKey(object model) {
|
||||
return this.Column.GetGroupKey(model);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the display label that the given cluster should use
|
||||
/// </summary>
|
||||
/// <param name="cluster"></param>
|
||||
/// <returns></returns>
|
||||
public override string GetClusterDisplayLabel(ICluster cluster) {
|
||||
string s = this.Column.ConvertGroupKeyToTitle(cluster.ClusterKey);
|
||||
if (String.IsNullOrEmpty(s))
|
||||
s = EMPTY_LABEL;
|
||||
return this.ApplyDisplayFormat(cluster, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
187
ObjectListView/Filtering/DateTimeClusteringStrategy.cs
Normal file
187
ObjectListView/Filtering/DateTimeClusteringStrategy.cs
Normal file
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* DateTimeClusteringStrategy - A strategy to cluster objects by a date time
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 30-March-2011 9:40am
|
||||
*
|
||||
* Change log:
|
||||
* 2011-03-30 JPP - First version
|
||||
*
|
||||
* Copyright (C) 2011-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
/// <summary>
|
||||
/// This enum is used to indicate various portions of a datetime
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum DateTimePortion {
|
||||
/// <summary>
|
||||
/// Year
|
||||
/// </summary>
|
||||
Year = 0x01,
|
||||
|
||||
/// <summary>
|
||||
/// Month
|
||||
/// </summary>
|
||||
Month = 0x02,
|
||||
|
||||
/// <summary>
|
||||
/// Day of the month
|
||||
/// </summary>
|
||||
Day = 0x04,
|
||||
|
||||
/// <summary>
|
||||
/// Hour
|
||||
/// </summary>
|
||||
Hour = 0x08,
|
||||
|
||||
/// <summary>
|
||||
/// Minute
|
||||
/// </summary>
|
||||
Minute = 0x10,
|
||||
|
||||
/// <summary>
|
||||
/// Second
|
||||
/// </summary>
|
||||
Second = 0x20
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This class implements a strategy where the model objects are clustered
|
||||
/// according to some portion of the datetime value in the configured column.
|
||||
/// </summary>
|
||||
/// <remarks>To create a strategy that grouped people who were born in
|
||||
/// the same month, you would create a strategy that extracted just
|
||||
/// the month, and formatted it to show just the month's name. Like this:
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// someColumn.ClusteringStrategy = new DateTimeClusteringStrategy(DateTimePortion.Month, "MMMM");
|
||||
/// </example>
|
||||
public class DateTimeClusteringStrategy : ClusteringStrategy {
|
||||
#region Life and death
|
||||
|
||||
/// <summary>
|
||||
/// Create a strategy that clusters by month/year
|
||||
/// </summary>
|
||||
public DateTimeClusteringStrategy()
|
||||
: this(DateTimePortion.Year | DateTimePortion.Month, "MMMM yyyy") {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a strategy that clusters around the given parts
|
||||
/// </summary>
|
||||
/// <param name="portions"></param>
|
||||
/// <param name="format"></param>
|
||||
public DateTimeClusteringStrategy(DateTimePortion portions, string format) {
|
||||
this.Portions = portions;
|
||||
this.Format = format;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the format string will will be used to create a user-presentable
|
||||
/// version of the cluster key.
|
||||
/// </summary>
|
||||
/// <remarks>The format should use the date/time format strings, as documented
|
||||
/// in the Windows SDK. Both standard formats and custom format will work.</remarks>
|
||||
/// <example>"D" - long date pattern</example>
|
||||
/// <example>"MMMM, yyyy" - "January, 1999"</example>
|
||||
public string Format {
|
||||
get { return format; }
|
||||
set { format = value; }
|
||||
}
|
||||
private string format;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the parts of the DateTime that will be extracted when
|
||||
/// determining the clustering key for an object.
|
||||
/// </summary>
|
||||
public DateTimePortion Portions {
|
||||
get { return portions; }
|
||||
set { portions = value; }
|
||||
}
|
||||
private DateTimePortion portions = DateTimePortion.Year | DateTimePortion.Month;
|
||||
|
||||
#endregion
|
||||
|
||||
#region IClusterStrategy implementation
|
||||
|
||||
/// <summary>
|
||||
/// Get the cluster key by which the given model will be partitioned by this strategy
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <returns></returns>
|
||||
public override object GetClusterKey(object model) {
|
||||
// Get the data attribute we want from the given model
|
||||
// Make sure the returned value is a DateTime
|
||||
DateTime? dateTime = this.Column.GetValue(model) as DateTime?;
|
||||
if (!dateTime.HasValue)
|
||||
return null;
|
||||
|
||||
// Extract the parts of the datetime that we are intereted in.
|
||||
// Even if we aren't interested in a particular portion, we still have to give it a reasonable default
|
||||
// otherwise we won't be able to build a DateTime object for it
|
||||
int year = ((this.Portions & DateTimePortion.Year) == DateTimePortion.Year) ? dateTime.Value.Year : 1;
|
||||
int month = ((this.Portions & DateTimePortion.Month) == DateTimePortion.Month) ? dateTime.Value.Month : 1;
|
||||
int day = ((this.Portions & DateTimePortion.Day) == DateTimePortion.Day) ? dateTime.Value.Day : 1;
|
||||
int hour = ((this.Portions & DateTimePortion.Hour) == DateTimePortion.Hour) ? dateTime.Value.Hour : 0;
|
||||
int minute = ((this.Portions & DateTimePortion.Minute) == DateTimePortion.Minute) ? dateTime.Value.Minute : 0;
|
||||
int second = ((this.Portions & DateTimePortion.Second) == DateTimePortion.Second) ? dateTime.Value.Second : 0;
|
||||
|
||||
return new DateTime(year, month, day, hour, minute, second);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the display label that the given cluster should use
|
||||
/// </summary>
|
||||
/// <param name="cluster"></param>
|
||||
/// <returns></returns>
|
||||
public override string GetClusterDisplayLabel(ICluster cluster) {
|
||||
DateTime? dateTime = cluster.ClusterKey as DateTime?;
|
||||
|
||||
return this.ApplyDisplayFormat(cluster, dateTime.HasValue ? this.DateToString(dateTime.Value) : NULL_LABEL);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert the given date into a user presentable string
|
||||
/// </summary>
|
||||
/// <param name="dateTime"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual string DateToString(DateTime dateTime) {
|
||||
if (String.IsNullOrEmpty(this.Format))
|
||||
return dateTime.ToString(CultureInfo.CurrentUICulture);
|
||||
|
||||
try {
|
||||
return dateTime.ToString(this.Format);
|
||||
}
|
||||
catch (FormatException) {
|
||||
return String.Format("Bad format string '{0}' for value '{1}'", this.Format, dateTime);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
369
ObjectListView/Filtering/FilterMenuBuilder.cs
Normal file
369
ObjectListView/Filtering/FilterMenuBuilder.cs
Normal file
@@ -0,0 +1,369 @@
|
||||
/*
|
||||
* FilterMenuBuilder - Responsible for creating a Filter menu
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 4-March-2011 11:59 pm
|
||||
*
|
||||
* Change log:
|
||||
* 2012-05-20 JPP - Allow the same model object to be in multiple clusters
|
||||
* Useful for xor'ed flag fields, and multi-value strings
|
||||
* (e.g. hobbies that are stored as comma separated values).
|
||||
* v2.5.1
|
||||
* 2012-04-14 JPP - Fixed rare bug with clustering an empty list (SF #3445118)
|
||||
* v2.5
|
||||
* 2011-04-12 JPP - Added some images to menu
|
||||
* 2011-03-04 JPP - First version
|
||||
*
|
||||
* Copyright (C) 2011-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using System.Collections;
|
||||
using System.Drawing;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
/// <summary>
|
||||
/// Instances of this class know how to build a Filter menu.
|
||||
/// It is responsible for clustering the values in the target column,
|
||||
/// build a menu that shows those clusters, and then constructing
|
||||
/// a filter that will enact the users choices.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Almost all of the methods in this class are declared as "virtual protected"
|
||||
/// so that subclasses can provide alternative behaviours.
|
||||
/// </remarks>
|
||||
public class FilterMenuBuilder {
|
||||
|
||||
#region Static properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the string that labels the Apply button.
|
||||
/// Exposed so it can be localized.
|
||||
/// </summary>
|
||||
static public string APPLY_LABEL = "Apply";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the string that labels the Clear All menu item.
|
||||
/// Exposed so it can be localized.
|
||||
/// </summary>
|
||||
static public string CLEAR_ALL_FILTERS_LABEL = "Clear All Filters";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the string that labels the Filtering menu as a whole..
|
||||
/// Exposed so it can be localized.
|
||||
/// </summary>
|
||||
static public string FILTERING_LABEL = "Filtering";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the string that represents Select All values.
|
||||
/// If this is set to null or empty, no Select All option will be included.
|
||||
/// Exposed so it can be localized.
|
||||
/// </summary>
|
||||
static public string SELECT_ALL_LABEL = "Select All";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the image that will be placed next to the Clear Filtering menu item
|
||||
/// </summary>
|
||||
static public Bitmap ClearFilteringImage = BrightIdeasSoftware.Properties.Resources.ClearFiltering;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the image that will be placed next to all "Apply" menu items on the filtering menu
|
||||
/// </summary>
|
||||
static public Bitmap FilteringImage = BrightIdeasSoftware.Properties.Resources.Filtering;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether null should be considered as a valid data value.
|
||||
/// If this is true (the default), then a cluster will null as a key will be allow.
|
||||
/// If this is false, object that return a cluster key of null will ignored.
|
||||
/// </summary>
|
||||
public bool TreatNullAsDataValue {
|
||||
get { return treatNullAsDataValue; }
|
||||
set { treatNullAsDataValue = value; }
|
||||
}
|
||||
private bool treatNullAsDataValue = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum number of objects that the clustering strategy
|
||||
/// will consider. This should be large enough to collect all unique clusters,
|
||||
/// but small enough to finish in a reasonable time.
|
||||
/// </summary>
|
||||
/// <remarks>The default value is 10,000. This should be perfectly
|
||||
/// acceptable for almost all lists.</remarks>
|
||||
public int MaxObjectsToConsider {
|
||||
get { return maxObjectsToConsider; }
|
||||
set { maxObjectsToConsider = value; }
|
||||
}
|
||||
private int maxObjectsToConsider = 10000;
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Create a Filter menu on the given tool tip for the given column in the given ObjectListView.
|
||||
/// </summary>
|
||||
/// <remarks>This is the main entry point into this class.</remarks>
|
||||
/// <param name="strip"></param>
|
||||
/// <param name="listView"></param>
|
||||
/// <param name="column"></param>
|
||||
/// <returns>The strip that should be shown to the user</returns>
|
||||
virtual public ToolStripDropDown MakeFilterMenu(ToolStripDropDown strip, ObjectListView listView, OLVColumn column) {
|
||||
if (strip == null) throw new ArgumentNullException("strip");
|
||||
if (listView == null) throw new ArgumentNullException("listView");
|
||||
if (column == null) throw new ArgumentNullException("column");
|
||||
|
||||
if (!column.UseFiltering || column.ClusteringStrategy == null || listView.Objects == null)
|
||||
return strip;
|
||||
|
||||
List<ICluster> clusters = this.Cluster(column.ClusteringStrategy, listView, column);
|
||||
if (clusters.Count > 0) {
|
||||
this.SortClusters(column.ClusteringStrategy, clusters);
|
||||
strip.Items.Add(this.CreateFilteringMenuItem(column, clusters));
|
||||
}
|
||||
|
||||
return strip;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a collection of clusters that should be presented to the user
|
||||
/// </summary>
|
||||
/// <param name="strategy"></param>
|
||||
/// <param name="listView"></param>
|
||||
/// <param name="column"></param>
|
||||
/// <returns></returns>
|
||||
virtual protected List<ICluster> Cluster(IClusteringStrategy strategy, ObjectListView listView, OLVColumn column) {
|
||||
// Build a map that correlates cluster key to clusters
|
||||
NullableDictionary<object, ICluster> map = new NullableDictionary<object, ICluster>();
|
||||
int count = 0;
|
||||
foreach (object model in listView.ObjectsForClustering) {
|
||||
this.ClusterOneModel(strategy, map, model);
|
||||
|
||||
if (count++ > this.MaxObjectsToConsider)
|
||||
break;
|
||||
}
|
||||
|
||||
// Now that we know exactly how many items are in each cluster, create a label for it
|
||||
foreach (ICluster cluster in map.Values)
|
||||
cluster.DisplayLabel = strategy.GetClusterDisplayLabel(cluster);
|
||||
|
||||
return new List<ICluster>(map.Values);
|
||||
}
|
||||
|
||||
private void ClusterOneModel(IClusteringStrategy strategy, NullableDictionary<object, ICluster> map, object model) {
|
||||
object clusterKey = strategy.GetClusterKey(model);
|
||||
|
||||
// If the returned value is an IEnumerable, that means the given model can belong to more than one cluster
|
||||
IEnumerable keyEnumerable = clusterKey as IEnumerable;
|
||||
if (clusterKey is string || keyEnumerable == null)
|
||||
keyEnumerable = new object[] {clusterKey};
|
||||
|
||||
// Deal with nulls and DBNulls
|
||||
ArrayList nullCorrected = new ArrayList();
|
||||
foreach (object key in keyEnumerable) {
|
||||
if (key == null || key == System.DBNull.Value) {
|
||||
if (this.TreatNullAsDataValue)
|
||||
nullCorrected.Add(null);
|
||||
} else nullCorrected.Add(key);
|
||||
}
|
||||
|
||||
// Group by key
|
||||
foreach (object key in nullCorrected) {
|
||||
if (map.ContainsKey(key))
|
||||
map[key].Count += 1;
|
||||
else
|
||||
map[key] = strategy.CreateCluster(key);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Order the given list of clusters in the manner in which they should be presented to the user.
|
||||
/// </summary>
|
||||
/// <param name="strategy"></param>
|
||||
/// <param name="clusters"></param>
|
||||
virtual protected void SortClusters(IClusteringStrategy strategy, List<ICluster> clusters) {
|
||||
clusters.Sort();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Do the work of making a menu that shows the clusters to the users
|
||||
/// </summary>
|
||||
/// <param name="column"></param>
|
||||
/// <param name="clusters"></param>
|
||||
/// <returns></returns>
|
||||
virtual protected ToolStripMenuItem CreateFilteringMenuItem(OLVColumn column, List<ICluster> clusters) {
|
||||
ToolStripCheckedListBox checkedList = new ToolStripCheckedListBox();
|
||||
checkedList.Tag = column;
|
||||
foreach (ICluster cluster in clusters)
|
||||
checkedList.AddItem(cluster, column.ValuesChosenForFiltering.Contains(cluster.ClusterKey));
|
||||
if (!String.IsNullOrEmpty(SELECT_ALL_LABEL)) {
|
||||
int checkedCount = checkedList.CheckedItems.Count;
|
||||
if (checkedCount == 0)
|
||||
checkedList.AddItem(SELECT_ALL_LABEL, CheckState.Unchecked);
|
||||
else
|
||||
checkedList.AddItem(SELECT_ALL_LABEL, checkedCount == clusters.Count ? CheckState.Checked : CheckState.Indeterminate);
|
||||
}
|
||||
checkedList.ItemCheck += new ItemCheckEventHandler(HandleItemCheckedWrapped);
|
||||
|
||||
ToolStripMenuItem clearAll = new ToolStripMenuItem(CLEAR_ALL_FILTERS_LABEL, ClearFilteringImage, delegate(object sender, EventArgs args) {
|
||||
this.ClearAllFilters(column);
|
||||
});
|
||||
ToolStripMenuItem apply = new ToolStripMenuItem(APPLY_LABEL, FilteringImage, delegate(object sender, EventArgs args) {
|
||||
this.EnactFilter(checkedList, column);
|
||||
});
|
||||
ToolStripMenuItem subMenu = new ToolStripMenuItem(FILTERING_LABEL, null, new ToolStripItem[] {
|
||||
clearAll, new ToolStripSeparator(), checkedList, apply });
|
||||
return subMenu;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wrap a protected section around the real HandleItemChecked method, so that if
|
||||
/// that method tries to change a "checkedness" of an item, we don't get a recursive
|
||||
/// stack error. Effectively, this ensure that HandleItemChecked is only called
|
||||
/// in response to a user action.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void HandleItemCheckedWrapped(object sender, ItemCheckEventArgs e) {
|
||||
if (alreadyInHandleItemChecked)
|
||||
return;
|
||||
|
||||
try {
|
||||
alreadyInHandleItemChecked = true;
|
||||
this.HandleItemChecked(sender, e);
|
||||
}
|
||||
finally {
|
||||
alreadyInHandleItemChecked = false;
|
||||
}
|
||||
}
|
||||
bool alreadyInHandleItemChecked = false;
|
||||
|
||||
/// <summary>
|
||||
/// Handle a user-generated ItemCheck event
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
virtual protected void HandleItemChecked(object sender, ItemCheckEventArgs e) {
|
||||
|
||||
ToolStripCheckedListBox checkedList = sender as ToolStripCheckedListBox;
|
||||
if (checkedList == null) return;
|
||||
OLVColumn column = checkedList.Tag as OLVColumn;
|
||||
if (column == null) return;
|
||||
ObjectListView listView = column.ListView as ObjectListView;
|
||||
if (listView == null) return;
|
||||
|
||||
// Deal with the "Select All" item if there is one
|
||||
int selectAllIndex = checkedList.Items.IndexOf(SELECT_ALL_LABEL);
|
||||
if (selectAllIndex >= 0)
|
||||
HandleSelectAllItem(e, checkedList, selectAllIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle any checking/unchecking of the Select All option, and keep
|
||||
/// its checkedness in sync with everything else that is checked.
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
/// <param name="checkedList"></param>
|
||||
/// <param name="selectAllIndex"></param>
|
||||
virtual protected void HandleSelectAllItem(ItemCheckEventArgs e, ToolStripCheckedListBox checkedList, int selectAllIndex) {
|
||||
// Did they check/uncheck the "Select All"?
|
||||
if (e.Index == selectAllIndex) {
|
||||
if (e.NewValue == CheckState.Checked)
|
||||
checkedList.CheckAll();
|
||||
if (e.NewValue == CheckState.Unchecked)
|
||||
checkedList.UncheckAll();
|
||||
return;
|
||||
}
|
||||
|
||||
// OK. The user didn't check/uncheck SelectAll. Now we have to update it's
|
||||
// checkedness to reflect the state of everything else
|
||||
// If all clusters are checked, we check the Select All.
|
||||
// If no clusters are checked, the uncheck the Select All.
|
||||
// For everything else, Select All is set to indeterminate.
|
||||
|
||||
// How many items are currenty checked?
|
||||
int count = checkedList.CheckedItems.Count;
|
||||
|
||||
// First complication.
|
||||
// The value of the Select All itself doesn't count
|
||||
if (checkedList.GetItemCheckState(selectAllIndex) != CheckState.Unchecked)
|
||||
count -= 1;
|
||||
|
||||
// Another complication.
|
||||
// CheckedItems does not yet know about the item the user has just
|
||||
// clicked, so we have to adjust the count of checked items to what
|
||||
// it is going to be
|
||||
if (e.NewValue != e.CurrentValue) {
|
||||
if (e.NewValue == CheckState.Checked)
|
||||
count += 1;
|
||||
else
|
||||
count -= 1;
|
||||
}
|
||||
|
||||
// Update the state of the Select All item
|
||||
if (count == 0)
|
||||
checkedList.SetItemState(selectAllIndex, CheckState.Unchecked);
|
||||
else if (count == checkedList.Items.Count - 1)
|
||||
checkedList.SetItemState(selectAllIndex, CheckState.Checked);
|
||||
else
|
||||
checkedList.SetItemState(selectAllIndex, CheckState.Indeterminate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear all the filters that are applied to the given column
|
||||
/// </summary>
|
||||
/// <param name="column">The column from which filters are to be removed</param>
|
||||
virtual protected void ClearAllFilters(OLVColumn column) {
|
||||
|
||||
ObjectListView olv = column.ListView as ObjectListView;
|
||||
if (olv == null || olv.IsDisposed)
|
||||
return;
|
||||
|
||||
olv.ResetColumnFiltering();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply the selected values from the given list as a filter on the given column
|
||||
/// </summary>
|
||||
/// <param name="checkedList">A list in which the checked items should be used as filters</param>
|
||||
/// <param name="column">The column for which a filter should be generated</param>
|
||||
virtual protected void EnactFilter(ToolStripCheckedListBox checkedList, OLVColumn column) {
|
||||
|
||||
ObjectListView olv = column.ListView as ObjectListView;
|
||||
if (olv == null || olv.IsDisposed)
|
||||
return;
|
||||
|
||||
// Collect all the checked values
|
||||
ArrayList chosenValues = new ArrayList();
|
||||
foreach (object x in checkedList.CheckedItems) {
|
||||
ICluster cluster = x as ICluster;
|
||||
if (cluster != null) {
|
||||
chosenValues.Add(cluster.ClusterKey);
|
||||
}
|
||||
}
|
||||
column.ValuesChosenForFiltering = chosenValues;
|
||||
|
||||
olv.UpdateColumnFiltering();
|
||||
}
|
||||
}
|
||||
}
|
||||
489
ObjectListView/Filtering/Filters.cs
Normal file
489
ObjectListView/Filtering/Filters.cs
Normal file
@@ -0,0 +1,489 @@
|
||||
/*
|
||||
* Filters - Filtering on ObjectListViews
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 03/03/2010 17:00
|
||||
*
|
||||
* Change log:
|
||||
* 2011-03-01 JPP Added CompositeAllFilter, CompositeAnyFilter and OneOfFilter
|
||||
* v2.4.1
|
||||
* 2010-06-23 JPP Extended TextMatchFilter to handle regular expressions and string prefix matching.
|
||||
* v2.4
|
||||
* 2010-03-03 JPP Initial version
|
||||
*
|
||||
* TO DO:
|
||||
*
|
||||
* Copyright (C) 2010-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Reflection;
|
||||
using System.Drawing;
|
||||
|
||||
namespace BrightIdeasSoftware
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for model-by-model filtering
|
||||
/// </summary>
|
||||
public interface IModelFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// Should the given model be included when this filter is installed
|
||||
/// </summary>
|
||||
/// <param name="modelObject">The model object to consider</param>
|
||||
/// <returns>Returns true if the model will be included by the filter</returns>
|
||||
bool Filter(object modelObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface for whole list filtering
|
||||
/// </summary>
|
||||
public interface IListFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// Return a subset of the given list of model objects as the new
|
||||
/// contents of the ObjectListView
|
||||
/// </summary>
|
||||
/// <param name="modelObjects">The collection of model objects that the list will possibly display</param>
|
||||
/// <returns>The filtered collection that holds the model objects that will be displayed.</returns>
|
||||
IEnumerable Filter(IEnumerable modelObjects);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base class for model-by-model filters
|
||||
/// </summary>
|
||||
public class AbstractModelFilter : IModelFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// Should the given model be included when this filter is installed
|
||||
/// </summary>
|
||||
/// <param name="modelObject">The model object to consider</param>
|
||||
/// <returns>Returns true if the model will be included by the filter</returns>
|
||||
virtual public bool Filter(object modelObject) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This filter calls a given Predicate to decide if a model object should be included
|
||||
/// </summary>
|
||||
public class ModelFilter : IModelFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a filter based on the given predicate
|
||||
/// </summary>
|
||||
/// <param name="predicate">The function that will filter objects</param>
|
||||
public ModelFilter(Predicate<object> predicate) {
|
||||
this.Predicate = predicate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the predicate used to filter model objects
|
||||
/// </summary>
|
||||
protected Predicate<object> Predicate {
|
||||
get { return predicate; }
|
||||
set { predicate = value; }
|
||||
}
|
||||
private Predicate<object> predicate;
|
||||
|
||||
/// <summary>
|
||||
/// Should the given model object be included?
|
||||
/// </summary>
|
||||
/// <param name="modelObject"></param>
|
||||
/// <returns></returns>
|
||||
virtual public bool Filter(object modelObject) {
|
||||
return this.Predicate == null ? true : this.Predicate(modelObject);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A CompositeFilter joins several other filters together.
|
||||
/// If there are no filters, all model objects are included
|
||||
/// </summary>
|
||||
abstract public class CompositeFilter : IModelFilter {
|
||||
|
||||
/// <summary>
|
||||
/// Create an empty filter
|
||||
/// </summary>
|
||||
public CompositeFilter() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a composite filter from the given list of filters
|
||||
/// </summary>
|
||||
/// <param name="filters">A list of filters</param>
|
||||
public CompositeFilter(IEnumerable<IModelFilter> filters) {
|
||||
foreach (IModelFilter filter in filters) {
|
||||
if (filter != null)
|
||||
Filters.Add(filter);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the filters used by this composite
|
||||
/// </summary>
|
||||
public IList<IModelFilter> Filters {
|
||||
get { return filters; }
|
||||
set { filters = value; }
|
||||
}
|
||||
private IList<IModelFilter> filters = new List<IModelFilter>();
|
||||
|
||||
/// <summary>
|
||||
/// Get the sub filters that are text match filters
|
||||
/// </summary>
|
||||
public IEnumerable<TextMatchFilter> TextFilters {
|
||||
get {
|
||||
foreach (IModelFilter filter in this.Filters) {
|
||||
TextMatchFilter textFilter = filter as TextMatchFilter;
|
||||
if (textFilter != null)
|
||||
yield return textFilter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decide whether or not the given model should be included by the filter
|
||||
/// </summary>
|
||||
/// <param name="modelObject"></param>
|
||||
/// <returns>True if the object is included by the filter</returns>
|
||||
virtual public bool Filter(object modelObject) {
|
||||
if (this.Filters == null || this.Filters.Count == 0)
|
||||
return true;
|
||||
|
||||
return this.FilterObject(modelObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decide whether or not the given model should be included by the filter
|
||||
/// </summary>
|
||||
/// <remarks>Filters is guaranteed to be non-empty when this method is called</remarks>
|
||||
/// <param name="modelObject">The model object under consideration</param>
|
||||
/// <returns>True if the object is included by the filter</returns>
|
||||
abstract public bool FilterObject(object modelObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A CompositeAllFilter joins several other filters together.
|
||||
/// A model object must satisfy all filters to be included.
|
||||
/// If there are no filters, all model objects are included
|
||||
/// </summary>
|
||||
public class CompositeAllFilter : CompositeFilter {
|
||||
|
||||
/// <summary>
|
||||
/// Create a filter
|
||||
/// </summary>
|
||||
/// <param name="filters"></param>
|
||||
public CompositeAllFilter(List<IModelFilter> filters)
|
||||
: base(filters) {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decide whether or not the given model should be included by the filter
|
||||
/// </summary>
|
||||
/// <remarks>Filters is guaranteed to be non-empty when this method is called</remarks>
|
||||
/// <param name="modelObject">The model object under consideration</param>
|
||||
/// <returns>True if the object is included by the filter</returns>
|
||||
override public bool FilterObject(object modelObject) {
|
||||
foreach (IModelFilter filter in this.Filters)
|
||||
if (!filter.Filter(modelObject))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A CompositeAllFilter joins several other filters together.
|
||||
/// A model object must only satisfy one of the filters to be included.
|
||||
/// If there are no filters, all model objects are included
|
||||
/// </summary>
|
||||
public class CompositeAnyFilter : CompositeFilter {
|
||||
|
||||
/// <summary>
|
||||
/// Create a filter from the given filters
|
||||
/// </summary>
|
||||
/// <param name="filters"></param>
|
||||
public CompositeAnyFilter(List<IModelFilter> filters)
|
||||
: base(filters) {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decide whether or not the given model should be included by the filter
|
||||
/// </summary>
|
||||
/// <remarks>Filters is guaranteed to be non-empty when this method is called</remarks>
|
||||
/// <param name="modelObject">The model object under consideration</param>
|
||||
/// <returns>True if the object is included by the filter</returns>
|
||||
override public bool FilterObject(object modelObject) {
|
||||
foreach (IModelFilter filter in this.Filters)
|
||||
if (filter.Filter(modelObject))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instances of this class extract a value from the model object
|
||||
/// and compare that value to a list of fixed values. The model
|
||||
/// object is included if the extracted value is in the list
|
||||
/// </summary>
|
||||
/// <remarks>If there is no delegate installed or there are
|
||||
/// no values to match, no model objects will be matched</remarks>
|
||||
public class OneOfFilter : IModelFilter {
|
||||
|
||||
/// <summary>
|
||||
/// Create a filter that will use the given delegate to extract values
|
||||
/// </summary>
|
||||
/// <param name="valueGetter"></param>
|
||||
public OneOfFilter(AspectGetterDelegate valueGetter) :
|
||||
this(valueGetter, new ArrayList()) {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a filter that will extract values using the given delegate
|
||||
/// and compare them to the values in the given list.
|
||||
/// </summary>
|
||||
/// <param name="valueGetter"></param>
|
||||
/// <param name="possibleValues"></param>
|
||||
public OneOfFilter(AspectGetterDelegate valueGetter, ICollection possibleValues) {
|
||||
this.ValueGetter = valueGetter;
|
||||
this.PossibleValues = new ArrayList(possibleValues);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the delegate that will be used to extract values
|
||||
/// from model objects
|
||||
/// </summary>
|
||||
virtual public AspectGetterDelegate ValueGetter {
|
||||
get { return valueGetter; }
|
||||
set { valueGetter = value; }
|
||||
}
|
||||
private AspectGetterDelegate valueGetter;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of values that the value extracted from
|
||||
/// the model object must match in order to be included.
|
||||
/// </summary>
|
||||
virtual public IList PossibleValues {
|
||||
get { return possibleValues; }
|
||||
set { possibleValues = value; }
|
||||
}
|
||||
private IList possibleValues;
|
||||
|
||||
/// <summary>
|
||||
/// Should the given model object be included?
|
||||
/// </summary>
|
||||
/// <param name="modelObject"></param>
|
||||
/// <returns></returns>
|
||||
public virtual bool Filter(object modelObject) {
|
||||
if (this.ValueGetter == null || this.PossibleValues == null || this.PossibleValues.Count == 0)
|
||||
return false;
|
||||
|
||||
object result = this.ValueGetter(modelObject);
|
||||
IEnumerable enumerable = result as IEnumerable;
|
||||
if (result is string || enumerable == null)
|
||||
return this.DoesValueMatch(result);
|
||||
|
||||
foreach (object x in enumerable) {
|
||||
if (this.DoesValueMatch(x))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decides if the given property is a match for the values in the PossibleValues collection
|
||||
/// </summary>
|
||||
/// <param name="result"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual bool DoesValueMatch(object result) {
|
||||
return this.PossibleValues.Contains(result);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instances of this class match a property of a model objects against
|
||||
/// a list of bit flags. The property should be an xor-ed collection
|
||||
/// of bits flags.
|
||||
/// </summary>
|
||||
/// <remarks>Both the property compared and the list of possible values
|
||||
/// must be convertible to ulongs.</remarks>
|
||||
public class FlagBitSetFilter : OneOfFilter {
|
||||
|
||||
/// <summary>
|
||||
/// Create an instance
|
||||
/// </summary>
|
||||
/// <param name="valueGetter"></param>
|
||||
/// <param name="possibleValues"></param>
|
||||
public FlagBitSetFilter(AspectGetterDelegate valueGetter, ICollection possibleValues) : base(valueGetter, possibleValues) {
|
||||
this.ConvertPossibleValues();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the collection of values that will be matched.
|
||||
/// These must be ulongs (or convertible to ulongs).
|
||||
/// </summary>
|
||||
public override IList PossibleValues {
|
||||
get { return base.PossibleValues; }
|
||||
set {
|
||||
base.PossibleValues = value;
|
||||
this.ConvertPossibleValues();
|
||||
}
|
||||
}
|
||||
|
||||
private void ConvertPossibleValues() {
|
||||
this.possibleValuesAsUlongs = new List<UInt64>();
|
||||
foreach (object x in this.PossibleValues)
|
||||
this.possibleValuesAsUlongs.Add(Convert.ToUInt64(x));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decides if the given property is a match for the values in the PossibleValues collection
|
||||
/// </summary>
|
||||
/// <param name="result"></param>
|
||||
/// <returns></returns>
|
||||
protected override bool DoesValueMatch(object result) {
|
||||
try {
|
||||
UInt64 value = Convert.ToUInt64(result);
|
||||
foreach (ulong flag in this.possibleValuesAsUlongs) {
|
||||
if ((value & flag) == flag)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch (InvalidCastException) {
|
||||
return false;
|
||||
}
|
||||
catch (FormatException) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private List<UInt64> possibleValuesAsUlongs = new List<UInt64>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base class for whole list filters
|
||||
/// </summary>
|
||||
public class AbstractListFilter : IListFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// Return a subset of the given list of model objects as the new
|
||||
/// contents of the ObjectListView
|
||||
/// </summary>
|
||||
/// <param name="modelObjects">The collection of model objects that the list will possibly display</param>
|
||||
/// <returns>The filtered collection that holds the model objects that will be displayed.</returns>
|
||||
virtual public IEnumerable Filter(IEnumerable modelObjects) {
|
||||
return modelObjects;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instance of this class implement delegate based whole list filtering
|
||||
/// </summary>
|
||||
public class ListFilter : AbstractListFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// A delegate that filters on a whole list
|
||||
/// </summary>
|
||||
/// <param name="rowObjects"></param>
|
||||
/// <returns></returns>
|
||||
public delegate IEnumerable ListFilterDelegate(IEnumerable rowObjects);
|
||||
|
||||
/// <summary>
|
||||
/// Create a ListFilter
|
||||
/// </summary>
|
||||
/// <param name="function"></param>
|
||||
public ListFilter(ListFilterDelegate function) {
|
||||
this.Function = function;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the delegate that will filter the list
|
||||
/// </summary>
|
||||
public ListFilterDelegate Function {
|
||||
get { return function; }
|
||||
set { function = value; }
|
||||
}
|
||||
private ListFilterDelegate function;
|
||||
|
||||
/// <summary>
|
||||
/// Do the actual work of filtering
|
||||
/// </summary>
|
||||
/// <param name="modelObjects"></param>
|
||||
/// <returns></returns>
|
||||
public override IEnumerable Filter(IEnumerable modelObjects) {
|
||||
if (this.Function == null)
|
||||
return modelObjects;
|
||||
|
||||
return this.Function(modelObjects);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Filter the list so only the last N entries are displayed
|
||||
/// </summary>
|
||||
public class TailFilter : AbstractListFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a no-op tail filter
|
||||
/// </summary>
|
||||
public TailFilter() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a filter that includes on the last N model objects
|
||||
/// </summary>
|
||||
/// <param name="numberOfObjects"></param>
|
||||
public TailFilter(int numberOfObjects) {
|
||||
this.Count = numberOfObjects;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of model objects that will be
|
||||
/// returned from the tail of the list
|
||||
/// </summary>
|
||||
public int Count {
|
||||
get { return count; }
|
||||
set { count = value; }
|
||||
}
|
||||
private int count;
|
||||
|
||||
/// <summary>
|
||||
/// Return the last N subset of the model objects
|
||||
/// </summary>
|
||||
/// <param name="modelObjects"></param>
|
||||
/// <returns></returns>
|
||||
public override IEnumerable Filter(IEnumerable modelObjects) {
|
||||
if (this.Count <= 0)
|
||||
return modelObjects;
|
||||
|
||||
ArrayList list = ObjectListView.EnumerableToArray(modelObjects, false);
|
||||
|
||||
if (this.Count > list.Count)
|
||||
return list;
|
||||
|
||||
object[] tail = new object[this.Count];
|
||||
list.CopyTo(list.Count - this.Count, tail, 0, this.Count);
|
||||
return new ArrayList(tail);
|
||||
}
|
||||
}
|
||||
}
|
||||
160
ObjectListView/Filtering/FlagClusteringStrategy.cs
Normal file
160
ObjectListView/Filtering/FlagClusteringStrategy.cs
Normal file
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* FlagClusteringStrategy - Implements a clustering strategy for a field which is a single integer
|
||||
* containing an XOR'ed collection of bit flags
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 23-March-2012 8:33 am
|
||||
*
|
||||
* Change log:
|
||||
* 2012-03-23 JPP - First version
|
||||
*
|
||||
* Copyright (C) 2012 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
/// <summary>
|
||||
/// Instances of this class cluster model objects on the basis of a
|
||||
/// property that holds an xor-ed collection of bit flags.
|
||||
/// </summary>
|
||||
public class FlagClusteringStrategy : ClusteringStrategy
|
||||
{
|
||||
#region Life and death
|
||||
|
||||
/// <summary>
|
||||
/// Create a clustering strategy that operates on the flags of the given enum
|
||||
/// </summary>
|
||||
/// <param name="enumType"></param>
|
||||
public FlagClusteringStrategy(Type enumType) {
|
||||
if (enumType == null) throw new ArgumentNullException("enumType");
|
||||
if (!enumType.IsEnum) throw new ArgumentException("Type must be enum", "enumType");
|
||||
if (enumType.GetCustomAttributes(typeof(FlagsAttribute), false) == null) throw new ArgumentException("Type must have [Flags] attribute", "enumType");
|
||||
|
||||
List<long> flags = new List<long>();
|
||||
foreach (object x in Enum.GetValues(enumType))
|
||||
flags.Add(Convert.ToInt64(x));
|
||||
|
||||
List<string> flagLabels = new List<string>();
|
||||
foreach (string x in Enum.GetNames(enumType))
|
||||
flagLabels.Add(x);
|
||||
|
||||
this.SetValues(flags.ToArray(), flagLabels.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a clustering strategy around the given collections of flags and their display labels.
|
||||
/// There must be the same number of elements in both collections.
|
||||
/// </summary>
|
||||
/// <param name="values">The list of flags. </param>
|
||||
/// <param name="labels"></param>
|
||||
public FlagClusteringStrategy(long[] values, string[] labels) {
|
||||
this.SetValues(values, labels);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value that will be xor-ed to test for the presence of a particular value.
|
||||
/// </summary>
|
||||
public long[] Values {
|
||||
get { return this.values; }
|
||||
private set { this.values = value; }
|
||||
}
|
||||
private long[] values;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the labels that will be used when the corresponding Value is XOR present in the data.
|
||||
/// </summary>
|
||||
public string[] Labels {
|
||||
get { return this.labels; }
|
||||
private set { this.labels = value; }
|
||||
}
|
||||
private string[] labels;
|
||||
|
||||
private void SetValues(long[] flags, string[] flagLabels) {
|
||||
if (flags == null || flags.Length == 0) throw new ArgumentNullException("flags");
|
||||
if (flagLabels == null || flagLabels.Length == 0) throw new ArgumentNullException("flagLabels");
|
||||
if (flags.Length != flagLabels.Length) throw new ArgumentException("values and labels must have the same number of entries", "flags");
|
||||
|
||||
this.Values = flags;
|
||||
this.Labels = flagLabels;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation of IClusteringStrategy
|
||||
|
||||
/// <summary>
|
||||
/// Get the cluster key by which the given model will be partitioned by this strategy
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <returns></returns>
|
||||
public override object GetClusterKey(object model) {
|
||||
List<long> flags = new List<long>();
|
||||
try {
|
||||
long modelValue = Convert.ToInt64(this.Column.GetValue(model));
|
||||
foreach (long x in this.Values) {
|
||||
if ((x & modelValue) == x)
|
||||
flags.Add(x);
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
catch (InvalidCastException ex) {
|
||||
System.Diagnostics.Debug.Write(ex);
|
||||
return flags;
|
||||
}
|
||||
catch (FormatException ex) {
|
||||
System.Diagnostics.Debug.Write(ex);
|
||||
return flags;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the display label that the given cluster should use
|
||||
/// </summary>
|
||||
/// <param name="cluster"></param>
|
||||
/// <returns></returns>
|
||||
public override string GetClusterDisplayLabel(ICluster cluster) {
|
||||
long clusterKeyAsUlong = Convert.ToInt64(cluster.ClusterKey);
|
||||
for (int i = 0; i < this.Values.Length; i++ ) {
|
||||
if (clusterKeyAsUlong == this.Values[i])
|
||||
return this.ApplyDisplayFormat(cluster, this.Labels[i]);
|
||||
}
|
||||
return this.ApplyDisplayFormat(cluster, clusterKeyAsUlong.ToString(CultureInfo.CurrentUICulture));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a filter that will include only model objects that
|
||||
/// match one or more of the given values.
|
||||
/// </summary>
|
||||
/// <param name="valuesChosenForFiltering"></param>
|
||||
/// <returns></returns>
|
||||
public override IModelFilter CreateFilter(IList valuesChosenForFiltering) {
|
||||
return new FlagBitSetFilter(this.GetClusterKey, valuesChosenForFiltering);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
56
ObjectListView/Filtering/ICluster.cs
Normal file
56
ObjectListView/Filtering/ICluster.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* ICluster - A cluster is a group of objects that can be included or excluded as a whole
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 4-March-2011 11:59 pm
|
||||
*
|
||||
* Change log:
|
||||
* 2011-03-04 JPP - First version
|
||||
*
|
||||
* Copyright (C) 2011-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
/// <summary>
|
||||
/// A cluster is a like collection of objects that can be usefully filtered
|
||||
/// as whole using the filtering UI provided by the ObjectListView.
|
||||
/// </summary>
|
||||
public interface ICluster : IComparable {
|
||||
/// <summary>
|
||||
/// Gets or sets how many items belong to this cluster
|
||||
/// </summary>
|
||||
int Count { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the label that will be shown to the user to represent
|
||||
/// this cluster
|
||||
/// </summary>
|
||||
string DisplayLabel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the actual data object that all members of this cluster
|
||||
/// have commonly returned.
|
||||
/// </summary>
|
||||
object ClusterKey { get; set; }
|
||||
}
|
||||
}
|
||||
80
ObjectListView/Filtering/IClusteringStrategy.cs
Normal file
80
ObjectListView/Filtering/IClusteringStrategy.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* IClusterStrategy - Encapsulates the ability to create a list of clusters from an ObjectListView
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 4-March-2011 11:59 pm
|
||||
*
|
||||
* Change log:
|
||||
* 2012-05-23 JPP - Added CreateFilter() method to interface to allow the strategy
|
||||
* to control the actual model filter that is created.
|
||||
* v2.5
|
||||
* 2011-03-04 JPP - First version
|
||||
*
|
||||
* Copyright (C) 2011-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BrightIdeasSoftware{
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of this interface control the selecting of cluster keys
|
||||
/// and how those clusters will be presented to the user
|
||||
/// </summary>
|
||||
public interface IClusteringStrategy {
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the column upon which this strategy will operate
|
||||
/// </summary>
|
||||
OLVColumn Column { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the cluster key by which the given model will be partitioned by this strategy
|
||||
/// </summary>
|
||||
/// <remarks>If the returned value is an IEnumerable, the given model is considered
|
||||
/// to belong to multiple clusters</remarks>
|
||||
/// <param name="model"></param>
|
||||
/// <returns></returns>
|
||||
object GetClusterKey(object model);
|
||||
|
||||
/// <summary>
|
||||
/// Create a cluster to hold the given cluster key
|
||||
/// </summary>
|
||||
/// <param name="clusterKey"></param>
|
||||
/// <returns></returns>
|
||||
ICluster CreateCluster(object clusterKey);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the display label that the given cluster should use
|
||||
/// </summary>
|
||||
/// <param name="cluster"></param>
|
||||
/// <returns></returns>
|
||||
string GetClusterDisplayLabel(ICluster cluster);
|
||||
|
||||
/// <summary>
|
||||
/// Create a filter that will include only model objects that
|
||||
/// match one or more of the given values.
|
||||
/// </summary>
|
||||
/// <param name="valuesChosenForFiltering"></param>
|
||||
/// <returns></returns>
|
||||
IModelFilter CreateFilter(IList valuesChosenForFiltering);
|
||||
}
|
||||
}
|
||||
629
ObjectListView/Filtering/TextMatchFilter.cs
Normal file
629
ObjectListView/Filtering/TextMatchFilter.cs
Normal file
@@ -0,0 +1,629 @@
|
||||
/*
|
||||
* TextMatchFilter - Text based filtering on ObjectListViews
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 31/05/2011 7:45am
|
||||
*
|
||||
* Change log:
|
||||
* v2.6
|
||||
* 2012-10-13 JPP Allow filtering to consider additional columns
|
||||
* v2.5.1
|
||||
* 2011-06-22 JPP Handle searching for empty strings
|
||||
* v2.5.0
|
||||
* 2011-05-31 JPP Initial version
|
||||
*
|
||||
* TO DO:
|
||||
*
|
||||
* Copyright (C) 2011-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Drawing;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
/// <summary>
|
||||
/// Instances of this class include only those rows of the listview
|
||||
/// that match one or more given strings.
|
||||
/// </summary>
|
||||
/// <remarks>This class can match strings by prefix, regex, or simple containment.
|
||||
/// There are factory methods for each of these matching strategies.</remarks>
|
||||
public class TextMatchFilter : AbstractModelFilter {
|
||||
|
||||
#region Life and death
|
||||
|
||||
/// <summary>
|
||||
/// Create a text filter that will include rows where any cell matches
|
||||
/// any of the given regex expressions.
|
||||
/// </summary>
|
||||
/// <param name="olv"></param>
|
||||
/// <param name="texts"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>Any string that is not a valid regex expression will be ignored.</remarks>
|
||||
public static TextMatchFilter Regex(ObjectListView olv, params string[] texts) {
|
||||
TextMatchFilter filter = new TextMatchFilter(olv);
|
||||
filter.RegexStrings = texts;
|
||||
return filter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a text filter that includes rows where any cell begins with one of the given strings
|
||||
/// </summary>
|
||||
/// <param name="olv"></param>
|
||||
/// <param name="texts"></param>
|
||||
/// <returns></returns>
|
||||
public static TextMatchFilter Prefix(ObjectListView olv, params string[] texts) {
|
||||
TextMatchFilter filter = new TextMatchFilter(olv);
|
||||
filter.PrefixStrings = texts;
|
||||
return filter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a text filter that includes rows where any cell contains any of the given strings.
|
||||
/// </summary>
|
||||
/// <param name="olv"></param>
|
||||
/// <param name="texts"></param>
|
||||
/// <returns></returns>
|
||||
public static TextMatchFilter Contains(ObjectListView olv, params string[] texts) {
|
||||
TextMatchFilter filter = new TextMatchFilter(olv);
|
||||
filter.ContainsStrings = texts;
|
||||
return filter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a TextFilter
|
||||
/// </summary>
|
||||
/// <param name="olv"></param>
|
||||
public TextMatchFilter(ObjectListView olv) {
|
||||
this.ListView = olv;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a TextFilter that finds the given string
|
||||
/// </summary>
|
||||
/// <param name="olv"></param>
|
||||
/// <param name="text"></param>
|
||||
public TextMatchFilter(ObjectListView olv, string text) {
|
||||
this.ListView = olv;
|
||||
this.ContainsStrings = new string[] { text };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a TextFilter that finds the given string using the given comparison
|
||||
/// </summary>
|
||||
/// <param name="olv"></param>
|
||||
/// <param name="text"></param>
|
||||
/// <param name="comparison"></param>
|
||||
public TextMatchFilter(ObjectListView olv, string text, StringComparison comparison) {
|
||||
this.ListView = olv;
|
||||
this.ContainsStrings = new string[] { text };
|
||||
this.StringComparison = comparison;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets which columns will be used for the comparisons? If this is null, all columns will be used
|
||||
/// </summary>
|
||||
public OLVColumn[] Columns {
|
||||
get { return columns; }
|
||||
set { columns = value; }
|
||||
}
|
||||
private OLVColumn[] columns;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets additional columns which will be used in the comparison. These will be used
|
||||
/// in addition to either the Columns property or to all columns taken from the control.
|
||||
/// </summary>
|
||||
public OLVColumn[] AdditionalColumns {
|
||||
get { return additionalColumns; }
|
||||
set { additionalColumns = value; }
|
||||
}
|
||||
private OLVColumn[] additionalColumns;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the collection of strings that will be used for
|
||||
/// contains matching. Setting this replaces all previous texts
|
||||
/// of any kind.
|
||||
/// </summary>
|
||||
public IEnumerable<string> ContainsStrings {
|
||||
get {
|
||||
foreach (TextMatchingStrategy component in this.MatchingStrategies)
|
||||
yield return component.Text;
|
||||
}
|
||||
set {
|
||||
this.MatchingStrategies = new List<TextMatchingStrategy>();
|
||||
if (value != null) {
|
||||
foreach (string text in value)
|
||||
this.MatchingStrategies.Add(new TextContainsMatchingStrategy(this, text));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether or not this filter has any search criteria
|
||||
/// </summary>
|
||||
public bool HasComponents {
|
||||
get {
|
||||
return this.MatchingStrategies.Count > 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or set the ObjectListView upon which this filter will work
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// You cannot really rebase a filter after it is created, so do not change this value.
|
||||
/// It is included so that it can be set in an object initializer.
|
||||
/// </remarks>
|
||||
public ObjectListView ListView {
|
||||
get { return listView; }
|
||||
set { listView = value; }
|
||||
}
|
||||
private ObjectListView listView;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the collection of strings that will be used for
|
||||
/// prefix matching. Setting this replaces all previous texts
|
||||
/// of any kind.
|
||||
/// </summary>
|
||||
public IEnumerable<string> PrefixStrings {
|
||||
get {
|
||||
foreach (TextMatchingStrategy component in this.MatchingStrategies)
|
||||
yield return component.Text;
|
||||
}
|
||||
set {
|
||||
this.MatchingStrategies = new List<TextMatchingStrategy>();
|
||||
if (value != null) {
|
||||
foreach (string text in value)
|
||||
this.MatchingStrategies.Add(new TextBeginsMatchingStrategy(this, text));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the options that will be used when compiling the regular expression.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is only used when doing Regex matching (obviously).
|
||||
/// If this is not set specifically, the appropriate options are chosen to match the
|
||||
/// StringComparison setting (culture invariant, case sensitive).
|
||||
/// </remarks>
|
||||
public RegexOptions RegexOptions {
|
||||
get {
|
||||
if (!regexOptions.HasValue) {
|
||||
switch (this.StringComparison) {
|
||||
case StringComparison.CurrentCulture:
|
||||
regexOptions = RegexOptions.None;
|
||||
break;
|
||||
case StringComparison.CurrentCultureIgnoreCase:
|
||||
regexOptions = RegexOptions.IgnoreCase;
|
||||
break;
|
||||
case StringComparison.Ordinal:
|
||||
case StringComparison.InvariantCulture:
|
||||
regexOptions = RegexOptions.CultureInvariant;
|
||||
break;
|
||||
case StringComparison.OrdinalIgnoreCase:
|
||||
case StringComparison.InvariantCultureIgnoreCase:
|
||||
regexOptions = RegexOptions.CultureInvariant | RegexOptions.IgnoreCase;
|
||||
break;
|
||||
default:
|
||||
regexOptions = RegexOptions.None;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return regexOptions.Value;
|
||||
}
|
||||
set {
|
||||
regexOptions = value;
|
||||
}
|
||||
}
|
||||
private RegexOptions? regexOptions;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the collection of strings that will be used for
|
||||
/// regex pattern matching. Setting this replaces all previous texts
|
||||
/// of any kind.
|
||||
/// </summary>
|
||||
public IEnumerable<string> RegexStrings {
|
||||
get {
|
||||
foreach (TextMatchingStrategy component in this.MatchingStrategies)
|
||||
yield return component.Text;
|
||||
}
|
||||
set {
|
||||
this.MatchingStrategies = new List<TextMatchingStrategy>();
|
||||
if (value != null) {
|
||||
foreach (string text in value)
|
||||
this.MatchingStrategies.Add(new TextRegexMatchingStrategy(this, text));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets how the filter will match text
|
||||
/// </summary>
|
||||
public StringComparison StringComparison {
|
||||
get { return this.stringComparison; }
|
||||
set { this.stringComparison = value; }
|
||||
}
|
||||
private StringComparison stringComparison = StringComparison.InvariantCultureIgnoreCase;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation
|
||||
|
||||
/// <summary>
|
||||
/// Loop over the columns that are being considering by the filter
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual IEnumerable<OLVColumn> IterateColumns() {
|
||||
if (this.Columns == null) {
|
||||
foreach (OLVColumn column in this.ListView.Columns)
|
||||
yield return column;
|
||||
} else {
|
||||
foreach (OLVColumn column in this.Columns)
|
||||
yield return column;
|
||||
}
|
||||
if (this.AdditionalColumns != null) {
|
||||
foreach (OLVColumn column in this.AdditionalColumns)
|
||||
yield return column;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public interface
|
||||
|
||||
/// <summary>
|
||||
/// Do the actual work of filtering
|
||||
/// </summary>
|
||||
/// <param name="modelObject"></param>
|
||||
/// <returns></returns>
|
||||
public override bool Filter(object modelObject) {
|
||||
if (this.ListView == null || !this.HasComponents)
|
||||
return true;
|
||||
|
||||
foreach (OLVColumn column in this.IterateColumns()) {
|
||||
if (column.IsVisible && column.Searchable) {
|
||||
string[] cellTexts = column.GetSearchValues(modelObject);
|
||||
if (cellTexts != null && cellTexts.Length > 0) {
|
||||
foreach (TextMatchingStrategy filter in this.MatchingStrategies) {
|
||||
if (String.IsNullOrEmpty(filter.Text))
|
||||
return true;
|
||||
foreach (string cellText in cellTexts) {
|
||||
if (filter.MatchesText(cellText))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find all the ways in which this filter matches the given string.
|
||||
/// </summary>
|
||||
/// <remarks>This is used by the renderer to decide which bits of
|
||||
/// the string should be highlighted</remarks>
|
||||
/// <param name="cellText"></param>
|
||||
/// <returns>A list of character ranges indicating the matched substrings</returns>
|
||||
public IEnumerable<CharacterRange> FindAllMatchedRanges(string cellText) {
|
||||
List<CharacterRange> ranges = new List<CharacterRange>();
|
||||
|
||||
foreach (TextMatchingStrategy filter in this.MatchingStrategies) {
|
||||
if (!String.IsNullOrEmpty(filter.Text))
|
||||
ranges.AddRange(filter.FindAllMatchedRanges(cellText));
|
||||
}
|
||||
|
||||
return ranges;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is the given column one of the columns being used by this filter?
|
||||
/// </summary>
|
||||
/// <param name="column"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsIncluded(OLVColumn column) {
|
||||
if (this.Columns == null) {
|
||||
return column.ListView == this.ListView;
|
||||
}
|
||||
|
||||
foreach (OLVColumn x in this.Columns) {
|
||||
if (x == column)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation members
|
||||
|
||||
private List<TextMatchingStrategy> MatchingStrategies = new List<TextMatchingStrategy>();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Components
|
||||
|
||||
/// <summary>
|
||||
/// Base class for the various types of string matching that TextMatchFilter provides
|
||||
/// </summary>
|
||||
abstract protected class TextMatchingStrategy {
|
||||
|
||||
/// <summary>
|
||||
/// Gets how the filter will match text
|
||||
/// </summary>
|
||||
public StringComparison StringComparison {
|
||||
get { return this.TextFilter.StringComparison; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the text filter to which this component belongs
|
||||
/// </summary>
|
||||
public TextMatchFilter TextFilter {
|
||||
get { return textFilter; }
|
||||
set { textFilter = value; }
|
||||
}
|
||||
private TextMatchFilter textFilter;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the text that will be matched
|
||||
/// </summary>
|
||||
public string Text {
|
||||
get { return this.text; }
|
||||
set { this.text = value; }
|
||||
}
|
||||
private string text;
|
||||
|
||||
/// <summary>
|
||||
/// Find all the ways in which this filter matches the given string.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This is used by the renderer to decide which bits of
|
||||
/// the string should be highlighted.
|
||||
/// </para>
|
||||
/// <para>this.Text will not be null or empty when this is called.</para>
|
||||
/// </remarks>
|
||||
/// <param name="cellText">The text of the cell we want to search</param>
|
||||
/// <returns>A list of character ranges indicating the matched substrings</returns>
|
||||
abstract public IEnumerable<CharacterRange> FindAllMatchedRanges(string cellText);
|
||||
|
||||
/// <summary>
|
||||
/// Does the given text match the filter
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>this.Text will not be null or empty when this is called.</para>
|
||||
/// </remarks>
|
||||
/// <param name="cellText">The text of the cell we want to search</param>
|
||||
/// <returns>Return true if the given cellText matches our strategy</returns>
|
||||
abstract public bool MatchesText(string cellText);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This component provides text contains matching strategy.
|
||||
/// </summary>
|
||||
protected class TextContainsMatchingStrategy : TextMatchingStrategy {
|
||||
|
||||
/// <summary>
|
||||
/// Create a text contains strategy
|
||||
/// </summary>
|
||||
/// <param name="filter"></param>
|
||||
/// <param name="text"></param>
|
||||
public TextContainsMatchingStrategy(TextMatchFilter filter, string text) {
|
||||
this.TextFilter = filter;
|
||||
this.Text = text;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does the given text match the filter
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>this.Text will not be null or empty when this is called.</para>
|
||||
/// </remarks>
|
||||
/// <param name="cellText">The text of the cell we want to search</param>
|
||||
/// <returns>Return true if the given cellText matches our strategy</returns>
|
||||
override public bool MatchesText(string cellText) {
|
||||
return cellText.IndexOf(this.Text, this.StringComparison) != -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find all the ways in which this filter matches the given string.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This is used by the renderer to decide which bits of
|
||||
/// the string should be highlighted.
|
||||
/// </para>
|
||||
/// <para>this.Text will not be null or empty when this is called.</para>
|
||||
/// </remarks>
|
||||
/// <param name="cellText">The text of the cell we want to search</param>
|
||||
/// <returns>A list of character ranges indicating the matched substrings</returns>
|
||||
override public IEnumerable<CharacterRange> FindAllMatchedRanges(string cellText) {
|
||||
List<CharacterRange> ranges = new List<CharacterRange>();
|
||||
|
||||
int matchIndex = cellText.IndexOf(this.Text, this.StringComparison);
|
||||
while (matchIndex != -1) {
|
||||
ranges.Add(new CharacterRange(matchIndex, this.Text.Length));
|
||||
matchIndex = cellText.IndexOf(this.Text, matchIndex + this.Text.Length, this.StringComparison);
|
||||
}
|
||||
|
||||
return ranges;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This component provides text begins with matching strategy.
|
||||
/// </summary>
|
||||
protected class TextBeginsMatchingStrategy : TextMatchingStrategy {
|
||||
|
||||
/// <summary>
|
||||
/// Create a text begins strategy
|
||||
/// </summary>
|
||||
/// <param name="filter"></param>
|
||||
/// <param name="text"></param>
|
||||
public TextBeginsMatchingStrategy(TextMatchFilter filter, string text) {
|
||||
this.TextFilter = filter;
|
||||
this.Text = text;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does the given text match the filter
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>this.Text will not be null or empty when this is called.</para>
|
||||
/// </remarks>
|
||||
/// <param name="cellText">The text of the cell we want to search</param>
|
||||
/// <returns>Return true if the given cellText matches our strategy</returns>
|
||||
override public bool MatchesText(string cellText) {
|
||||
return cellText.StartsWith(this.Text, this.StringComparison);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find all the ways in which this filter matches the given string.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This is used by the renderer to decide which bits of
|
||||
/// the string should be highlighted.
|
||||
/// </para>
|
||||
/// <para>this.Text will not be null or empty when this is called.</para>
|
||||
/// </remarks>
|
||||
/// <param name="cellText">The text of the cell we want to search</param>
|
||||
/// <returns>A list of character ranges indicating the matched substrings</returns>
|
||||
override public IEnumerable<CharacterRange> FindAllMatchedRanges(string cellText) {
|
||||
List<CharacterRange> ranges = new List<CharacterRange>();
|
||||
|
||||
if (cellText.StartsWith(this.Text, this.StringComparison))
|
||||
ranges.Add(new CharacterRange(0, this.Text.Length));
|
||||
|
||||
return ranges;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This component provides regex matching strategy.
|
||||
/// </summary>
|
||||
protected class TextRegexMatchingStrategy : TextMatchingStrategy {
|
||||
|
||||
/// <summary>
|
||||
/// Creates a regex strategy
|
||||
/// </summary>
|
||||
/// <param name="filter"></param>
|
||||
/// <param name="text"></param>
|
||||
public TextRegexMatchingStrategy(TextMatchFilter filter, string text) {
|
||||
this.TextFilter = filter;
|
||||
this.Text = text;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the options that will be used when compiling the regular expression.
|
||||
/// </summary>
|
||||
public RegexOptions RegexOptions {
|
||||
get {
|
||||
return this.TextFilter.RegexOptions;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a compilex regular expression, based on our current Text and RegexOptions.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If Text fails to compile as a regular expression, this will return a Regex object
|
||||
/// that will match all strings.
|
||||
/// </remarks>
|
||||
protected Regex Regex {
|
||||
get {
|
||||
if (this.regex == null) {
|
||||
try {
|
||||
this.regex = new Regex(this.Text, this.RegexOptions);
|
||||
}
|
||||
catch (ArgumentException) {
|
||||
this.regex = TextRegexMatchingStrategy.InvalidRegexMarker;
|
||||
}
|
||||
}
|
||||
return this.regex;
|
||||
}
|
||||
set {
|
||||
this.regex = value;
|
||||
}
|
||||
}
|
||||
private Regex regex;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether or not our current regular expression is a valid regex
|
||||
/// </summary>
|
||||
protected bool IsRegexInvalid {
|
||||
get {
|
||||
return this.Regex == TextRegexMatchingStrategy.InvalidRegexMarker;
|
||||
}
|
||||
}
|
||||
static private Regex InvalidRegexMarker = new Regex(".*");
|
||||
|
||||
/// <summary>
|
||||
/// Does the given text match the filter
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>this.Text will not be null or empty when this is called.</para>
|
||||
/// </remarks>
|
||||
/// <param name="cellText">The text of the cell we want to search</param>
|
||||
/// <returns>Return true if the given cellText matches our strategy</returns>
|
||||
public override bool MatchesText(string cellText) {
|
||||
if (this.IsRegexInvalid)
|
||||
return true;
|
||||
return this.Regex.Match(cellText).Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find all the ways in which this filter matches the given string.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This is used by the renderer to decide which bits of
|
||||
/// the string should be highlighted.
|
||||
/// </para>
|
||||
/// <para>this.Text will not be null or empty when this is called.</para>
|
||||
/// </remarks>
|
||||
/// <param name="cellText">The text of the cell we want to search</param>
|
||||
/// <returns>A list of character ranges indicating the matched substrings</returns>
|
||||
override public IEnumerable<CharacterRange> FindAllMatchedRanges(string cellText) {
|
||||
List<CharacterRange> ranges = new List<CharacterRange>();
|
||||
|
||||
if (!this.IsRegexInvalid) {
|
||||
foreach (Match match in this.Regex.Matches(cellText)) {
|
||||
if (match.Length > 0)
|
||||
ranges.Add(new CharacterRange(match.Index, match.Length));
|
||||
}
|
||||
}
|
||||
|
||||
return ranges;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
1261
ObjectListView/FullClassDiagram.cd
Normal file
1261
ObjectListView/FullClassDiagram.cd
Normal file
File diff suppressed because it is too large
Load Diff
335
ObjectListView/Implementation/Attributes.cs
Normal file
335
ObjectListView/Implementation/Attributes.cs
Normal file
@@ -0,0 +1,335 @@
|
||||
/*
|
||||
* Attributes - Attributes that can be attached to properties of models to allow columns to be
|
||||
* built from them directly
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 15/08/2009 22:01
|
||||
*
|
||||
* Change log:
|
||||
* v2.6
|
||||
* 2012-08-16 JPP - Added [OLVChildren] and [OLVIgnore]
|
||||
* - OLV attributes can now only be set on properties
|
||||
* v2.4
|
||||
* 2010-04-14 JPP - Allow Name property to be set
|
||||
*
|
||||
* v2.3
|
||||
* 2009-08-15 JPP - Initial version
|
||||
*
|
||||
* To do:
|
||||
*
|
||||
* Copyright (C) 2009-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace BrightIdeasSoftware
|
||||
{
|
||||
/// <summary>
|
||||
/// This attribute is used to mark a property of a model
|
||||
/// class that should be noticed by Generator class.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// All the attributes of this class match their equivilent properties on OLVColumn.
|
||||
/// </remarks>
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class OLVColumnAttribute : Attribute
|
||||
{
|
||||
#region Constructor
|
||||
|
||||
// There are several property where we actually want nullable value (bool?, int?),
|
||||
// but it seems attribute properties can't be nullable types.
|
||||
// So we explicitly track if those properties have been set.
|
||||
|
||||
/// <summary>
|
||||
/// Create a new OLVColumnAttribute
|
||||
/// </summary>
|
||||
public OLVColumnAttribute() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new OLVColumnAttribute with the given title
|
||||
/// </summary>
|
||||
/// <param name="title">The title of the column</param>
|
||||
public OLVColumnAttribute(string title) {
|
||||
this.Title = title;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public properties
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string AspectToStringFormat {
|
||||
get { return aspectToStringFormat; }
|
||||
set { aspectToStringFormat = value; }
|
||||
}
|
||||
private string aspectToStringFormat;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool CheckBoxes {
|
||||
get { return checkBoxes; }
|
||||
set {
|
||||
checkBoxes = value;
|
||||
this.IsCheckBoxesSet = true;
|
||||
}
|
||||
}
|
||||
private bool checkBoxes;
|
||||
internal bool IsCheckBoxesSet = false;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public int DisplayIndex {
|
||||
get { return displayIndex; }
|
||||
set { displayIndex = value; }
|
||||
}
|
||||
private int displayIndex = -1;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool FillsFreeSpace {
|
||||
get { return fillsFreeSpace; }
|
||||
set { fillsFreeSpace = value; }
|
||||
}
|
||||
private bool fillsFreeSpace;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public int FreeSpaceProportion {
|
||||
get { return freeSpaceProportion; }
|
||||
set {
|
||||
freeSpaceProportion = value;
|
||||
IsFreeSpaceProportionSet = true;
|
||||
}
|
||||
}
|
||||
private int freeSpaceProportion;
|
||||
internal bool IsFreeSpaceProportionSet = false;
|
||||
|
||||
/// <summary>
|
||||
/// An array of IComparables that mark the cutoff points for values when
|
||||
/// grouping on this column.
|
||||
/// </summary>
|
||||
public object[] GroupCutoffs {
|
||||
get { return groupCutoffs; }
|
||||
set { groupCutoffs = value; }
|
||||
}
|
||||
private object[] groupCutoffs;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string[] GroupDescriptions {
|
||||
get { return groupDescriptions; }
|
||||
set { groupDescriptions = value; }
|
||||
}
|
||||
private string[] groupDescriptions;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string GroupWithItemCountFormat {
|
||||
get { return groupWithItemCountFormat; }
|
||||
set { groupWithItemCountFormat = value; }
|
||||
}
|
||||
private string groupWithItemCountFormat;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string GroupWithItemCountSingularFormat {
|
||||
get { return groupWithItemCountSingularFormat; }
|
||||
set { groupWithItemCountSingularFormat = value; }
|
||||
}
|
||||
private string groupWithItemCountSingularFormat;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool Hyperlink {
|
||||
get { return hyperlink; }
|
||||
set { hyperlink = value; }
|
||||
}
|
||||
private bool hyperlink;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string ImageAspectName {
|
||||
get { return imageAspectName; }
|
||||
set { imageAspectName = value; }
|
||||
}
|
||||
private string imageAspectName;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool IsEditable {
|
||||
get { return isEditable; }
|
||||
set {
|
||||
isEditable = value;
|
||||
this.IsEditableSet = true;
|
||||
}
|
||||
}
|
||||
private bool isEditable = true;
|
||||
internal bool IsEditableSet = false;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool IsVisible {
|
||||
get { return isVisible; }
|
||||
set { isVisible = value; }
|
||||
}
|
||||
private bool isVisible = true;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool IsTileViewColumn {
|
||||
get { return isTileViewColumn; }
|
||||
set { isTileViewColumn = value; }
|
||||
}
|
||||
private bool isTileViewColumn;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public int MaximumWidth {
|
||||
get { return maximumWidth; }
|
||||
set { maximumWidth = value; }
|
||||
}
|
||||
private int maximumWidth = -1;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public int MinimumWidth {
|
||||
get { return minimumWidth; }
|
||||
set { minimumWidth = value; }
|
||||
}
|
||||
private int minimumWidth = -1;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public String Name {
|
||||
get { return name; }
|
||||
set { name = value; }
|
||||
}
|
||||
private String name;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public HorizontalAlignment TextAlign {
|
||||
get { return this.textAlign; }
|
||||
set {
|
||||
this.textAlign = value;
|
||||
IsTextAlignSet = true;
|
||||
}
|
||||
}
|
||||
private HorizontalAlignment textAlign = HorizontalAlignment.Left;
|
||||
internal bool IsTextAlignSet = false;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public String Tag {
|
||||
get { return tag; }
|
||||
set { tag = value; }
|
||||
}
|
||||
private String tag;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public String Title {
|
||||
get { return title; }
|
||||
set { title = value; }
|
||||
}
|
||||
private String title;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public String ToolTipText {
|
||||
get { return toolTipText; }
|
||||
set { toolTipText = value; }
|
||||
}
|
||||
private String toolTipText;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool TriStateCheckBoxes {
|
||||
get { return triStateCheckBoxes; }
|
||||
set {
|
||||
triStateCheckBoxes = value;
|
||||
this.IsTriStateCheckBoxesSet = true;
|
||||
}
|
||||
}
|
||||
private bool triStateCheckBoxes;
|
||||
internal bool IsTriStateCheckBoxesSet = false;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool UseInitialLetterForGroup {
|
||||
get { return useInitialLetterForGroup; }
|
||||
set { useInitialLetterForGroup = value; }
|
||||
}
|
||||
private bool useInitialLetterForGroup;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public int Width {
|
||||
get { return width; }
|
||||
set { width = value; }
|
||||
}
|
||||
private int width = 150;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Properties marked with [OLVChildren] will be used as the children source in a TreeListView.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class OLVChildrenAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Properties marked with [OLVIgnore] will not have columns generated for them.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property)]
|
||||
public class OLVIgnoreAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
330
ObjectListView/Implementation/Comparers.cs
Normal file
330
ObjectListView/Implementation/Comparers.cs
Normal file
@@ -0,0 +1,330 @@
|
||||
/*
|
||||
* Comparers - Various Comparer classes used within ObjectListView
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 25/11/2008 17:15
|
||||
*
|
||||
* Change log:
|
||||
* v2.8.1
|
||||
* 2014-12-03 JPP - Added StringComparer
|
||||
* v2.3
|
||||
* 2009-08-24 JPP - Added OLVGroupComparer
|
||||
* 2009-06-01 JPP - ModelObjectComparer would crash if secondary sort column was null.
|
||||
* 2008-12-20 JPP - Fixed bug with group comparisons when a group key was null (SF#2445761)
|
||||
* 2008-11-25 JPP Initial version
|
||||
*
|
||||
* TO DO:
|
||||
*
|
||||
* Copyright (C) 2006-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace BrightIdeasSoftware
|
||||
{
|
||||
/// <summary>
|
||||
/// ColumnComparer is the workhorse for all comparison between two values of a particular column.
|
||||
/// If the column has a specific comparer, use that to compare the values. Otherwise, do
|
||||
/// a case insensitive string compare of the string representations of the values.
|
||||
/// </summary>
|
||||
/// <remarks><para>This class inherits from both IComparer and its generic counterpart
|
||||
/// so that it can be used on untyped and typed collections.</para>
|
||||
/// <para>This is used by normal (non-virtual) ObjectListViews. Virtual lists use
|
||||
/// ModelObjectComparer</para>
|
||||
/// </remarks>
|
||||
public class ColumnComparer : IComparer, IComparer<OLVListItem>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the method that will be used to compare two strings.
|
||||
/// The default is to compare on the current culture, case-insensitive
|
||||
/// </summary>
|
||||
public static StringCompareDelegate StringComparer
|
||||
{
|
||||
get { return stringComparer; }
|
||||
set { stringComparer = value; }
|
||||
}
|
||||
private static StringCompareDelegate stringComparer;
|
||||
|
||||
/// <summary>
|
||||
/// Create a ColumnComparer that will order the rows in a list view according
|
||||
/// to the values in a given column
|
||||
/// </summary>
|
||||
/// <param name="col">The column whose values will be compared</param>
|
||||
/// <param name="order">The ordering for column values</param>
|
||||
public ColumnComparer(OLVColumn col, SortOrder order)
|
||||
{
|
||||
this.column = col;
|
||||
this.sortOrder = order;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a ColumnComparer that will order the rows in a list view according
|
||||
/// to the values in a given column, and by a secondary column if the primary
|
||||
/// column is equal.
|
||||
/// </summary>
|
||||
/// <param name="col">The column whose values will be compared</param>
|
||||
/// <param name="order">The ordering for column values</param>
|
||||
/// <param name="col2">The column whose values will be compared for secondary sorting</param>
|
||||
/// <param name="order2">The ordering for secondary column values</param>
|
||||
public ColumnComparer(OLVColumn col, SortOrder order, OLVColumn col2, SortOrder order2)
|
||||
: this(col, order)
|
||||
{
|
||||
// There is no point in secondary sorting on the same column
|
||||
if (col != col2)
|
||||
this.secondComparer = new ColumnComparer(col2, order2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare two rows
|
||||
/// </summary>
|
||||
/// <param name="x">row1</param>
|
||||
/// <param name="y">row2</param>
|
||||
/// <returns>An ordering indication: -1, 0, 1</returns>
|
||||
public int Compare(object x, object y)
|
||||
{
|
||||
return this.Compare((OLVListItem)x, (OLVListItem)y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare two rows
|
||||
/// </summary>
|
||||
/// <param name="x">row1</param>
|
||||
/// <param name="y">row2</param>
|
||||
/// <returns>An ordering indication: -1, 0, 1</returns>
|
||||
public int Compare(OLVListItem x, OLVListItem y)
|
||||
{
|
||||
if (this.sortOrder == SortOrder.None)
|
||||
return 0;
|
||||
|
||||
int result = 0;
|
||||
object x1 = this.column.GetValue(x.RowObject);
|
||||
object y1 = this.column.GetValue(y.RowObject);
|
||||
|
||||
// Handle nulls. Null values come last
|
||||
bool xIsNull = (x1 == null || x1 == System.DBNull.Value);
|
||||
bool yIsNull = (y1 == null || y1 == System.DBNull.Value);
|
||||
if (xIsNull || yIsNull) {
|
||||
if (xIsNull && yIsNull)
|
||||
result = 0;
|
||||
else
|
||||
result = (xIsNull ? -1 : 1);
|
||||
} else {
|
||||
result = this.CompareValues(x1, y1);
|
||||
}
|
||||
|
||||
if (this.sortOrder == SortOrder.Descending)
|
||||
result = 0 - result;
|
||||
|
||||
// If the result was equality, use the secondary comparer to resolve it
|
||||
if (result == 0 && this.secondComparer != null)
|
||||
result = this.secondComparer.Compare(x, y);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare the actual values to be used for sorting
|
||||
/// </summary>
|
||||
/// <param name="x">The aspect extracted from the first row</param>
|
||||
/// <param name="y">The aspect extracted from the second row</param>
|
||||
/// <returns>An ordering indication: -1, 0, 1</returns>
|
||||
public int CompareValues(object x, object y)
|
||||
{
|
||||
// Force case insensitive compares on strings
|
||||
String xAsString = x as String;
|
||||
if (xAsString != null)
|
||||
return CompareStrings(xAsString, y as String);
|
||||
|
||||
IComparable comparable = x as IComparable;
|
||||
return comparable != null ? comparable.CompareTo(y) : 0;
|
||||
}
|
||||
|
||||
private static int CompareStrings(string x, string y)
|
||||
{
|
||||
if (StringComparer == null)
|
||||
return String.Compare(x, y, StringComparison.CurrentCultureIgnoreCase);
|
||||
else
|
||||
return StringComparer(x, y);
|
||||
}
|
||||
|
||||
private OLVColumn column;
|
||||
private SortOrder sortOrder;
|
||||
private ColumnComparer secondComparer;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This comparer sort list view groups. OLVGroups have a "SortValue" property,
|
||||
/// which is used if present. Otherwise, the titles of the groups will be compared.
|
||||
/// </summary>
|
||||
public class OLVGroupComparer : IComparer<OLVGroup>
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a group comparer
|
||||
/// </summary>
|
||||
/// <param name="order">The ordering for column values</param>
|
||||
public OLVGroupComparer(SortOrder order) {
|
||||
this.sortOrder = order;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare the two groups. OLVGroups have a "SortValue" property,
|
||||
/// which is used if present. Otherwise, the titles of the groups will be compared.
|
||||
/// </summary>
|
||||
/// <param name="x">group1</param>
|
||||
/// <param name="y">group2</param>
|
||||
/// <returns>An ordering indication: -1, 0, 1</returns>
|
||||
public int Compare(OLVGroup x, OLVGroup y) {
|
||||
// If we can compare the sort values, do that.
|
||||
// Otherwise do a case insensitive compare on the group header.
|
||||
int result;
|
||||
if (x.SortValue != null && y.SortValue != null)
|
||||
result = x.SortValue.CompareTo(y.SortValue);
|
||||
else
|
||||
result = String.Compare(x.Header, y.Header, StringComparison.CurrentCultureIgnoreCase);
|
||||
|
||||
if (this.sortOrder == SortOrder.Descending)
|
||||
result = 0 - result;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private SortOrder sortOrder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This comparer can be used to sort a collection of model objects by a given column
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>This is used by virtual ObjectListViews. Non-virtual lists use
|
||||
/// ColumnComparer</para>
|
||||
/// </remarks>
|
||||
public class ModelObjectComparer : IComparer, IComparer<object>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the method that will be used to compare two strings.
|
||||
/// The default is to compare on the current culture, case-insensitive
|
||||
/// </summary>
|
||||
public static StringCompareDelegate StringComparer
|
||||
{
|
||||
get { return stringComparer; }
|
||||
set { stringComparer = value; }
|
||||
}
|
||||
private static StringCompareDelegate stringComparer;
|
||||
|
||||
/// <summary>
|
||||
/// Create a model object comparer
|
||||
/// </summary>
|
||||
/// <param name="col"></param>
|
||||
/// <param name="order"></param>
|
||||
public ModelObjectComparer(OLVColumn col, SortOrder order)
|
||||
{
|
||||
this.column = col;
|
||||
this.sortOrder = order;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a model object comparer with a secondary sorting column
|
||||
/// </summary>
|
||||
/// <param name="col"></param>
|
||||
/// <param name="order"></param>
|
||||
/// <param name="col2"></param>
|
||||
/// <param name="order2"></param>
|
||||
public ModelObjectComparer(OLVColumn col, SortOrder order, OLVColumn col2, SortOrder order2)
|
||||
: this(col, order)
|
||||
{
|
||||
// There is no point in secondary sorting on the same column
|
||||
if (col != col2 && col2 != null && order2 != SortOrder.None)
|
||||
this.secondComparer = new ModelObjectComparer(col2, order2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare the two model objects
|
||||
/// </summary>
|
||||
/// <param name="x"></param>
|
||||
/// <param name="y"></param>
|
||||
/// <returns></returns>
|
||||
public int Compare(object x, object y)
|
||||
{
|
||||
int result = 0;
|
||||
object x1 = this.column.GetValue(x);
|
||||
object y1 = this.column.GetValue(y);
|
||||
|
||||
if (this.sortOrder == SortOrder.None)
|
||||
return 0;
|
||||
|
||||
// Handle nulls. Null values come last
|
||||
bool xIsNull = (x1 == null || x1 == System.DBNull.Value);
|
||||
bool yIsNull = (y1 == null || y1 == System.DBNull.Value);
|
||||
if (xIsNull || yIsNull) {
|
||||
if (xIsNull && yIsNull)
|
||||
result = 0;
|
||||
else
|
||||
result = (xIsNull ? -1 : 1);
|
||||
} else {
|
||||
result = this.CompareValues(x1, y1);
|
||||
}
|
||||
|
||||
if (this.sortOrder == SortOrder.Descending)
|
||||
result = 0 - result;
|
||||
|
||||
// If the result was equality, use the secondary comparer to resolve it
|
||||
if (result == 0 && this.secondComparer != null)
|
||||
result = this.secondComparer.Compare(x, y);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare the actual values
|
||||
/// </summary>
|
||||
/// <param name="x"></param>
|
||||
/// <param name="y"></param>
|
||||
/// <returns></returns>
|
||||
public int CompareValues(object x, object y)
|
||||
{
|
||||
// Force case insensitive compares on strings
|
||||
String xStr = x as String;
|
||||
if (xStr != null)
|
||||
return CompareStrings(xStr, y as String);
|
||||
|
||||
IComparable comparable = x as IComparable;
|
||||
return comparable != null ? comparable.CompareTo(y) : 0;
|
||||
}
|
||||
|
||||
private static int CompareStrings(string x, string y)
|
||||
{
|
||||
if (StringComparer == null)
|
||||
return String.Compare(x, y, StringComparison.CurrentCultureIgnoreCase);
|
||||
else
|
||||
return StringComparer(x, y);
|
||||
}
|
||||
|
||||
private OLVColumn column;
|
||||
private SortOrder sortOrder;
|
||||
private ModelObjectComparer secondComparer;
|
||||
|
||||
#region IComparer<object> Members
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
||||
630
ObjectListView/Implementation/DataSourceAdapter.cs
Normal file
630
ObjectListView/Implementation/DataSourceAdapter.cs
Normal file
@@ -0,0 +1,630 @@
|
||||
/*
|
||||
* DataSourceAdapter - A helper class that translates DataSource events for an ObjectListView
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 20/09/2010 7:42 AM
|
||||
*
|
||||
* Change log:
|
||||
* v2.9
|
||||
* 2015-10-31 JPP - Put back sanity check on upper limit of source items
|
||||
* 2015-02-02 JPP - Made CreateColumnsFromSource() only rebuild columns when new ones were added
|
||||
* v2.8.1
|
||||
* 2014-11-23 JPP - Honour initial CurrencyManager.Position when setting DataSource.
|
||||
* 2014-10-27 JPP - Fix issue where SelectedObject was not sync'ed with CurrencyManager.Position (SF #129)
|
||||
* v2.6
|
||||
* 2012-08-16 JPP - Unify common column creation functionality with Generator when possible
|
||||
*
|
||||
* 2010-09-20 JPP - Initial version
|
||||
*
|
||||
* Copyright (C) 2010-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Windows.Forms;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace BrightIdeasSoftware
|
||||
{
|
||||
/// <summary>
|
||||
/// A helper class that translates DataSource events for an ObjectListView
|
||||
/// </summary>
|
||||
public class DataSourceAdapter : IDisposable
|
||||
{
|
||||
#region Life and death
|
||||
|
||||
/// <summary>
|
||||
/// Make a DataSourceAdapter
|
||||
/// </summary>
|
||||
public DataSourceAdapter(ObjectListView olv) {
|
||||
if (olv == null) throw new ArgumentNullException("olv");
|
||||
|
||||
this.ListView = olv;
|
||||
// ReSharper disable once DoNotCallOverridableMethodsInConstructor
|
||||
this.BindListView(this.ListView);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalize this object
|
||||
/// </summary>
|
||||
~DataSourceAdapter() {
|
||||
this.Dispose(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Release all the resources used by this instance
|
||||
/// </summary>
|
||||
public void Dispose() {
|
||||
this.Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Release all the resources used by this instance
|
||||
/// </summary>
|
||||
public virtual void Dispose(bool fromUser) {
|
||||
this.UnbindListView(this.ListView);
|
||||
this.UnbindDataSource();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether or not columns will be automatically generated to show the
|
||||
/// columns when the DataSource is set.
|
||||
/// </summary>
|
||||
/// <remarks>This must be set before the DataSource is set. It has no effect afterwards.</remarks>
|
||||
public bool AutoGenerateColumns {
|
||||
get { return this.autoGenerateColumns; }
|
||||
set { this.autoGenerateColumns = value; }
|
||||
}
|
||||
private bool autoGenerateColumns = true;
|
||||
|
||||
/// <summary>
|
||||
/// Get or set the DataSource that will be displayed in this list view.
|
||||
/// </summary>
|
||||
public virtual Object DataSource {
|
||||
get { return dataSource; }
|
||||
set {
|
||||
dataSource = value;
|
||||
this.RebindDataSource(true);
|
||||
}
|
||||
}
|
||||
private Object dataSource;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the list or table in the data source for which the DataListView is displaying data.
|
||||
/// </summary>
|
||||
/// <remarks>If the data source is not a DataSet or DataViewManager, this property has no effect</remarks>
|
||||
public virtual string DataMember {
|
||||
get { return dataMember; }
|
||||
set {
|
||||
if (dataMember != value) {
|
||||
dataMember = value;
|
||||
RebindDataSource();
|
||||
}
|
||||
}
|
||||
}
|
||||
private string dataMember = "";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ObjectListView upon which this adaptor will operate
|
||||
/// </summary>
|
||||
public ObjectListView ListView {
|
||||
get { return listView; }
|
||||
internal set { listView = value; }
|
||||
}
|
||||
private ObjectListView listView;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the currency manager which is handling our binding context
|
||||
/// </summary>
|
||||
protected CurrencyManager CurrencyManager {
|
||||
get { return currencyManager; }
|
||||
set { currencyManager = value; }
|
||||
}
|
||||
private CurrencyManager currencyManager;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Binding and unbinding
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="olv"></param>
|
||||
protected virtual void BindListView(ObjectListView olv) {
|
||||
if (olv == null)
|
||||
return;
|
||||
|
||||
olv.Freezing += new EventHandler<FreezeEventArgs>(HandleListViewFreezing);
|
||||
olv.SelectionChanged += new EventHandler(HandleListViewSelectionChanged);
|
||||
olv.BindingContextChanged += new EventHandler(HandleListViewBindingContextChanged);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="olv"></param>
|
||||
protected virtual void UnbindListView(ObjectListView olv) {
|
||||
if (olv == null)
|
||||
return;
|
||||
|
||||
olv.Freezing -= new EventHandler<FreezeEventArgs>(HandleListViewFreezing);
|
||||
olv.SelectionChanged -= new EventHandler(HandleListViewSelectionChanged);
|
||||
olv.BindingContextChanged -= new EventHandler(HandleListViewBindingContextChanged);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
protected virtual void BindDataSource() {
|
||||
if (this.CurrencyManager == null)
|
||||
return;
|
||||
|
||||
this.CurrencyManager.MetaDataChanged += new EventHandler(HandleCurrencyManagerMetaDataChanged);
|
||||
this.CurrencyManager.PositionChanged += new EventHandler(HandleCurrencyManagerPositionChanged);
|
||||
this.CurrencyManager.ListChanged += new ListChangedEventHandler(CurrencyManagerListChanged);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
protected virtual void UnbindDataSource() {
|
||||
if (this.CurrencyManager == null)
|
||||
return;
|
||||
|
||||
this.CurrencyManager.MetaDataChanged -= new EventHandler(HandleCurrencyManagerMetaDataChanged);
|
||||
this.CurrencyManager.PositionChanged -= new EventHandler(HandleCurrencyManagerPositionChanged);
|
||||
this.CurrencyManager.ListChanged -= new ListChangedEventHandler(CurrencyManagerListChanged);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Initialization
|
||||
|
||||
/// <summary>
|
||||
/// Our data source has changed. Figure out how to handle the new source
|
||||
/// </summary>
|
||||
protected virtual void RebindDataSource() {
|
||||
RebindDataSource(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Our data source has changed. Figure out how to handle the new source
|
||||
/// </summary>
|
||||
protected virtual void RebindDataSource(bool forceDataInitialization) {
|
||||
|
||||
CurrencyManager tempCurrencyManager = null;
|
||||
if (this.ListView != null && this.ListView.BindingContext != null && this.DataSource != null) {
|
||||
tempCurrencyManager = this.ListView.BindingContext[this.DataSource, this.DataMember] as CurrencyManager;
|
||||
}
|
||||
|
||||
// Has our currency manager changed?
|
||||
if (this.CurrencyManager != tempCurrencyManager) {
|
||||
this.UnbindDataSource();
|
||||
this.CurrencyManager = tempCurrencyManager;
|
||||
this.BindDataSource();
|
||||
|
||||
// Our currency manager has changed so we have to initialize a new data source
|
||||
forceDataInitialization = true;
|
||||
}
|
||||
|
||||
if (forceDataInitialization)
|
||||
InitializeDataSource();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The data source for this control has changed. Reconfigure the control for the new source
|
||||
/// </summary>
|
||||
protected virtual void InitializeDataSource() {
|
||||
if (this.ListView.Frozen || this.CurrencyManager == null)
|
||||
return;
|
||||
|
||||
this.CreateColumnsFromSource();
|
||||
this.CreateMissingAspectGettersAndPutters();
|
||||
this.SetListContents();
|
||||
this.ListView.AutoSizeColumns();
|
||||
|
||||
// Fake a position change event so that the control matches any initial Position
|
||||
this.HandleCurrencyManagerPositionChanged(null, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Take the contents of the currently bound list and put them into the control
|
||||
/// </summary>
|
||||
protected virtual void SetListContents() {
|
||||
this.ListView.Objects = this.CurrencyManager.List;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create columns for the listview based on what properties are available in the data source
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>This method will create columns if there is not already a column displaying that property.</para>
|
||||
/// </remarks>
|
||||
protected virtual void CreateColumnsFromSource() {
|
||||
if (this.CurrencyManager == null)
|
||||
return;
|
||||
|
||||
// Don't generate any columns in design mode. If we do, the user will see them,
|
||||
// but the Designer won't know about them and won't persist them, which is very confusing
|
||||
if (this.ListView.IsDesignMode)
|
||||
return;
|
||||
|
||||
// Don't create columns if we've been told not to
|
||||
if (!this.AutoGenerateColumns)
|
||||
return;
|
||||
|
||||
// Use a Generator to create columns
|
||||
Generator generator = Generator.Instance as Generator ?? new Generator();
|
||||
|
||||
PropertyDescriptorCollection properties = this.CurrencyManager.GetItemProperties();
|
||||
if (properties.Count == 0)
|
||||
return;
|
||||
|
||||
bool wereColumnsAdded = false;
|
||||
foreach (PropertyDescriptor property in properties) {
|
||||
|
||||
if (!this.ShouldCreateColumn(property))
|
||||
continue;
|
||||
|
||||
// Create a column
|
||||
OLVColumn column = generator.MakeColumnFromPropertyDescriptor(property);
|
||||
this.ConfigureColumn(column, property);
|
||||
|
||||
// Add it to our list
|
||||
this.ListView.AllColumns.Add(column);
|
||||
wereColumnsAdded = true;
|
||||
}
|
||||
|
||||
if (wereColumnsAdded)
|
||||
generator.PostCreateColumns(this.ListView);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decide if a new column should be added to the control to display
|
||||
/// the given property
|
||||
/// </summary>
|
||||
/// <param name="property"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual bool ShouldCreateColumn(PropertyDescriptor property) {
|
||||
|
||||
// Is there a column that already shows this property? If so, we don't show it again
|
||||
if (this.ListView.AllColumns.Exists(delegate(OLVColumn x) { return x.AspectName == property.Name; }))
|
||||
return false;
|
||||
|
||||
// Relationships to other tables turn up as IBindibleLists. Don't make columns to show them.
|
||||
// CHECK: Is this always true? What other things could be here? Constraints? Triggers?
|
||||
if (property.PropertyType == typeof(IBindingList))
|
||||
return false;
|
||||
|
||||
// Ignore anything marked with [OLVIgnore]
|
||||
return property.Attributes[typeof(OLVIgnoreAttribute)] == null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configure the given column to show the given property.
|
||||
/// The title and aspect name of the column are already filled in.
|
||||
/// </summary>
|
||||
/// <param name="column"></param>
|
||||
/// <param name="property"></param>
|
||||
protected virtual void ConfigureColumn(OLVColumn column, PropertyDescriptor property) {
|
||||
|
||||
column.LastDisplayIndex = this.ListView.AllColumns.Count;
|
||||
|
||||
// If our column is a BLOB, it could be an image, so assign a renderer to draw it.
|
||||
// CONSIDER: Is this a common enough case to warrant this code?
|
||||
if (property.PropertyType == typeof(System.Byte[]))
|
||||
column.Renderer = new ImageRenderer();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate aspect getters and putters for any columns that are missing them (and for which we have
|
||||
/// enough information to actually generate a getter)
|
||||
/// </summary>
|
||||
protected virtual void CreateMissingAspectGettersAndPutters() {
|
||||
foreach (OLVColumn x in this.ListView.AllColumns) {
|
||||
OLVColumn column = x; // stack based variable accessible from closures
|
||||
if (column.AspectGetter == null && !String.IsNullOrEmpty(column.AspectName)) {
|
||||
column.AspectGetter = delegate(object row) {
|
||||
// In most cases, rows will be DataRowView objects
|
||||
DataRowView drv = row as DataRowView;
|
||||
if (drv == null)
|
||||
return column.GetAspectByName(row);
|
||||
return (drv.Row.RowState == DataRowState.Detached) ? null : drv[column.AspectName];
|
||||
};
|
||||
}
|
||||
if (column.IsEditable && column.AspectPutter == null && !String.IsNullOrEmpty(column.AspectName)) {
|
||||
column.AspectPutter = delegate(object row, object newValue) {
|
||||
// In most cases, rows will be DataRowView objects
|
||||
DataRowView drv = row as DataRowView;
|
||||
if (drv == null)
|
||||
column.PutAspectByName(row, newValue);
|
||||
else {
|
||||
if (drv.Row.RowState != DataRowState.Detached)
|
||||
drv[column.AspectName] = newValue;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
/// <summary>
|
||||
/// CurrencyManager ListChanged event handler.
|
||||
/// Deals with fine-grained changes to list items.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// It's actually difficult to deal with these changes in a fine-grained manner.
|
||||
/// If our listview is grouped, then any change may make a new group appear or
|
||||
/// an old group disappear. It is rarely enough to simply update the affected row.
|
||||
/// </remarks>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
protected virtual void CurrencyManagerListChanged(object sender, ListChangedEventArgs e) {
|
||||
Debug.Assert(sender == this.CurrencyManager);
|
||||
|
||||
// Ignore changes make while frozen, since we will do a complete rebuild when we unfreeze
|
||||
if (this.ListView.Frozen)
|
||||
return;
|
||||
|
||||
//System.Diagnostics.Debug.WriteLine(e.ListChangedType);
|
||||
Stopwatch sw = Stopwatch.StartNew();
|
||||
switch (e.ListChangedType) {
|
||||
|
||||
case ListChangedType.Reset:
|
||||
this.HandleListChangedReset(e);
|
||||
break;
|
||||
|
||||
case ListChangedType.ItemChanged:
|
||||
this.HandleListChangedItemChanged(e);
|
||||
break;
|
||||
|
||||
case ListChangedType.ItemAdded:
|
||||
this.HandleListChangedItemAdded(e);
|
||||
break;
|
||||
|
||||
// An item has gone away.
|
||||
case ListChangedType.ItemDeleted:
|
||||
this.HandleListChangedItemDeleted(e);
|
||||
break;
|
||||
|
||||
// An item has changed its index.
|
||||
case ListChangedType.ItemMoved:
|
||||
this.HandleListChangedItemMoved(e);
|
||||
break;
|
||||
|
||||
// Something has changed in the metadata.
|
||||
// CHECK: When are these events actually fired?
|
||||
case ListChangedType.PropertyDescriptorAdded:
|
||||
case ListChangedType.PropertyDescriptorChanged:
|
||||
case ListChangedType.PropertyDescriptorDeleted:
|
||||
this.HandleListChangedMetadataChanged(e);
|
||||
break;
|
||||
}
|
||||
sw.Stop();
|
||||
System.Diagnostics.Debug.WriteLine(String.Format("PERF - Processing {0} event on {1} rows took {2}ms", e.ListChangedType, this.ListView.GetItemCount(), sw.ElapsedMilliseconds));
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle PropertyDescriptor* events
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
protected virtual void HandleListChangedMetadataChanged(ListChangedEventArgs e) {
|
||||
this.InitializeDataSource();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle ItemMoved event
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
protected virtual void HandleListChangedItemMoved(ListChangedEventArgs e) {
|
||||
// When is this actually triggered?
|
||||
this.InitializeDataSource();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle the ItemDeleted event
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
protected virtual void HandleListChangedItemDeleted(ListChangedEventArgs e) {
|
||||
this.InitializeDataSource();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle an ItemAdded event.
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
protected virtual void HandleListChangedItemAdded(ListChangedEventArgs e) {
|
||||
// We get this event twice if certain grid controls are used to add a new row to a
|
||||
// datatable: once when the editing of a new row begins, and once again when that
|
||||
// editing commits. (If the user cancels the creation of the new row, we never see
|
||||
// the second creation.) We detect this by seeing if this is a view on a row in a
|
||||
// DataTable, and if it is, testing to see if it's a new row under creation.
|
||||
|
||||
Object newRow = this.CurrencyManager.List[e.NewIndex];
|
||||
DataRowView drv = newRow as DataRowView;
|
||||
if (drv == null || !drv.IsNew) {
|
||||
// Either we're not dealing with a view on a data table, or this is the commit
|
||||
// notification. Either way, this is the final notification, so we want to
|
||||
// handle the new row now!
|
||||
this.InitializeDataSource();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle the Reset event
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
protected virtual void HandleListChangedReset(ListChangedEventArgs e) {
|
||||
// The whole list has changed utterly, so reload it.
|
||||
this.InitializeDataSource();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle ItemChanged event. This is triggered when a single item
|
||||
/// has changed, so just refresh that one item.
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
/// <remarks>Even in this simple case, we should probably rebuild the list.
|
||||
/// For example, the change could put the item into its own new group.</remarks>
|
||||
protected virtual void HandleListChangedItemChanged(ListChangedEventArgs e) {
|
||||
// A single item has changed, so just refresh that.
|
||||
//System.Diagnostics.Debug.WriteLine(String.Format("HandleListChangedItemChanged: {0}, {1}", e.NewIndex, e.PropertyDescriptor.Name));
|
||||
|
||||
Object changedRow = this.CurrencyManager.List[e.NewIndex];
|
||||
this.ListView.RefreshObject(changedRow);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The CurrencyManager calls this if the data source looks
|
||||
/// different. We just reload everything.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
/// <remarks>
|
||||
/// CHECK: Do we need this if we are handle ListChanged metadata events?
|
||||
/// </remarks>
|
||||
protected virtual void HandleCurrencyManagerMetaDataChanged(object sender, EventArgs e) {
|
||||
this.InitializeDataSource();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by the CurrencyManager when the currently selected item
|
||||
/// changes. We update the ListView selection so that we stay in sync
|
||||
/// with any other controls bound to the same source.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
protected virtual void HandleCurrencyManagerPositionChanged(object sender, EventArgs e) {
|
||||
int index = this.CurrencyManager.Position;
|
||||
|
||||
// Make sure the index is sane (-1 pops up from time to time)
|
||||
if (index < 0 || index >= this.ListView.GetItemCount())
|
||||
return;
|
||||
|
||||
// Avoid recursion. If we are currently changing the index, don't
|
||||
// start the process again.
|
||||
if (this.isChangingIndex)
|
||||
return;
|
||||
|
||||
try {
|
||||
this.isChangingIndex = true;
|
||||
this.ChangePosition(index);
|
||||
}
|
||||
finally {
|
||||
this.isChangingIndex = false;
|
||||
}
|
||||
}
|
||||
private bool isChangingIndex = false;
|
||||
|
||||
/// <summary>
|
||||
/// Change the control's position (which is it's currently selected row)
|
||||
/// to the nth row in the dataset
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the row to be selected</param>
|
||||
protected virtual void ChangePosition(int index) {
|
||||
// We can't use the index directly, since our listview may be sorted
|
||||
// Only assign if not null
|
||||
if (this.ListView.SelectedObject != null)
|
||||
{
|
||||
this.ListView.SelectedObject = this.CurrencyManager.List[index];
|
||||
}
|
||||
|
||||
// THINK: Do we always want to bring it into view?
|
||||
if (this.ListView.SelectedIndices.Count > 0)
|
||||
this.ListView.EnsureVisible(this.ListView.SelectedIndices[0]);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ObjectListView event handlers
|
||||
|
||||
/// <summary>
|
||||
/// Handle the selection changing in our ListView.
|
||||
/// We need to tell our currency manager about the new position.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
protected virtual void HandleListViewSelectionChanged(object sender, EventArgs e) {
|
||||
// Prevent recursion
|
||||
if (this.isChangingIndex)
|
||||
return;
|
||||
|
||||
// Sanity
|
||||
if (this.CurrencyManager == null)
|
||||
return;
|
||||
|
||||
// If only one item is selected, tell the currency manager which item is selected.
|
||||
// CurrencyManager can't handle multiple selection so there's nothing we can do
|
||||
// if more than one row is selected.
|
||||
if (this.ListView.SelectedIndices.Count != 1)
|
||||
return;
|
||||
|
||||
try {
|
||||
this.isChangingIndex = true;
|
||||
|
||||
// We can't use the selectedIndex directly, since our listview may be sorted and/or filtered
|
||||
// So we have to find the index of the selected object within the original list.
|
||||
this.CurrencyManager.Position = this.CurrencyManager.List.IndexOf(this.ListView.SelectedObject);
|
||||
} finally {
|
||||
this.isChangingIndex = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle the frozenness of our ListView changing.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
protected virtual void HandleListViewFreezing(object sender, FreezeEventArgs e) {
|
||||
if (!alreadyFreezing && e.FreezeLevel == 0) {
|
||||
try {
|
||||
alreadyFreezing = true;
|
||||
this.RebindDataSource(true);
|
||||
} finally {
|
||||
alreadyFreezing = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
private bool alreadyFreezing = false;
|
||||
|
||||
/// <summary>
|
||||
/// Handle a change to the BindingContext of our ListView.
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
protected virtual void HandleListViewBindingContextChanged(object sender, EventArgs e) {
|
||||
this.RebindDataSource(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
168
ObjectListView/Implementation/Delegates.cs
Normal file
168
ObjectListView/Implementation/Delegates.cs
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Delegates - All delegate definitions used in ObjectListView
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 31-March-2011 5:53 pm
|
||||
*
|
||||
* Change log:
|
||||
* v2.10
|
||||
* 2015-12-30 JPP - Added CellRendererGetterDelegate
|
||||
* v2.?
|
||||
* 2011-03-31 JPP - Split into its own file
|
||||
*
|
||||
* Copyright (C) 2011-2015 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using System.Drawing;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
#region Delegate declarations
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to extract an aspect from a row object
|
||||
/// </summary>
|
||||
public delegate Object AspectGetterDelegate(Object rowObject);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to put a changed value back into a model object
|
||||
/// </summary>
|
||||
public delegate void AspectPutterDelegate(Object rowObject, Object newValue);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates can be used to convert an aspect value to a display string,
|
||||
/// instead of using the default ToString()
|
||||
/// </summary>
|
||||
public delegate string AspectToStringConverterDelegate(Object value);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to get the tooltip for a cell
|
||||
/// </summary>
|
||||
public delegate String CellToolTipGetterDelegate(OLVColumn column, Object modelObject);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to the state of the checkbox for a row object.
|
||||
/// </summary>
|
||||
/// <remarks><para>
|
||||
/// For reasons known only to someone in Microsoft, we can only set
|
||||
/// a boolean on the ListViewItem to indicate it's "checked-ness", but when
|
||||
/// we receive update events, we have to use a tristate CheckState. So we can
|
||||
/// be told about an indeterminate state, but we can't set it ourselves.
|
||||
/// </para>
|
||||
/// <para>As of version 2.0, we can now return indeterminate state.</para>
|
||||
/// </remarks>
|
||||
public delegate CheckState CheckStateGetterDelegate(Object rowObject);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to get the state of the checkbox for a row object.
|
||||
/// </summary>
|
||||
/// <param name="rowObject"></param>
|
||||
/// <returns></returns>
|
||||
public delegate bool BooleanCheckStateGetterDelegate(Object rowObject);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to put a changed check state back into a model object
|
||||
/// </summary>
|
||||
public delegate CheckState CheckStatePutterDelegate(Object rowObject, CheckState newValue);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to put a changed check state back into a model object
|
||||
/// </summary>
|
||||
/// <param name="rowObject"></param>
|
||||
/// <param name="newValue"></param>
|
||||
/// <returns></returns>
|
||||
public delegate bool BooleanCheckStatePutterDelegate(Object rowObject, bool newValue);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to get the renderer for a particular cell
|
||||
/// </summary>
|
||||
public delegate IRenderer CellRendererGetterDelegate(Object rowObject, OLVColumn column);
|
||||
|
||||
/// <summary>
|
||||
/// The callbacks for RightColumnClick events
|
||||
/// </summary>
|
||||
public delegate void ColumnRightClickEventHandler(object sender, ColumnClickEventArgs e);
|
||||
|
||||
/// <summary>
|
||||
/// This delegate will be used to own draw header column.
|
||||
/// </summary>
|
||||
public delegate bool HeaderDrawingDelegate(Graphics g, Rectangle r, int columnIndex, OLVColumn column, bool isPressed, HeaderStateStyle stateStyle);
|
||||
|
||||
/// <summary>
|
||||
/// This delegate is called when a group has been created but not yet made
|
||||
/// into a real ListViewGroup. The user can take this opportunity to fill
|
||||
/// in lots of other details about the group.
|
||||
/// </summary>
|
||||
public delegate void GroupFormatterDelegate(OLVGroup group, GroupingParameters parms);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to retrieve the object that is the key of the group to which the given row belongs.
|
||||
/// </summary>
|
||||
public delegate Object GroupKeyGetterDelegate(Object rowObject);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to convert a group key into a title for the group
|
||||
/// </summary>
|
||||
public delegate string GroupKeyToTitleConverterDelegate(Object groupKey);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to get the tooltip for a column header
|
||||
/// </summary>
|
||||
public delegate String HeaderToolTipGetterDelegate(OLVColumn column);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to fetch the image selector that should be used
|
||||
/// to choose an image for this column.
|
||||
/// </summary>
|
||||
public delegate Object ImageGetterDelegate(Object rowObject);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to draw a cell
|
||||
/// </summary>
|
||||
public delegate bool RenderDelegate(EventArgs e, Graphics g, Rectangle r, Object rowObject);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to fetch a row object for virtual lists
|
||||
/// </summary>
|
||||
public delegate Object RowGetterDelegate(int rowIndex);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to format a listviewitem before it is added to the control.
|
||||
/// </summary>
|
||||
public delegate void RowFormatterDelegate(OLVListItem olvItem);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates can be used to return the array of texts that should be searched for text filtering
|
||||
/// </summary>
|
||||
public delegate string[] SearchValueGetterDelegate(Object value);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to sort the listview in some custom fashion
|
||||
/// </summary>
|
||||
public delegate void SortDelegate(OLVColumn column, SortOrder sortOrder);
|
||||
|
||||
/// <summary>
|
||||
/// These delegates are used to order two strings.
|
||||
/// x cannot be null. y can be null.
|
||||
/// </summary>
|
||||
public delegate int StringCompareDelegate(string x, string y);
|
||||
|
||||
#endregion
|
||||
}
|
||||
407
ObjectListView/Implementation/DragSource.cs
Normal file
407
ObjectListView/Implementation/DragSource.cs
Normal file
@@ -0,0 +1,407 @@
|
||||
///*
|
||||
// * DragSource.cs - Add drag source functionality to an ObjectListView
|
||||
// *
|
||||
// * UNFINISHED
|
||||
// *
|
||||
// * Author: Phillip Piper
|
||||
// * Date: 2009-03-17 5:15 PM
|
||||
// *
|
||||
// * Change log:
|
||||
// * v2.3
|
||||
// * 2009-07-06 JPP - Make sure Link is acceptable as an drop effect by default
|
||||
// * (since MS didn't make it part of the 'All' value)
|
||||
// * v2.2
|
||||
// * 2009-04-15 JPP - Separated DragSource.cs into DropSink.cs
|
||||
// * 2009-03-17 JPP - Initial version
|
||||
// *
|
||||
// * Copyright (C) 2009 Phillip Piper
|
||||
// *
|
||||
// * This program is free software: you can redistribute it and/or modify
|
||||
// * it under the terms of the GNU General Public License as published by
|
||||
// * the Free Software Foundation, either version 3 of the License, or
|
||||
// * (at your option) any later version.
|
||||
// *
|
||||
// * This program is distributed in the hope that it will be useful,
|
||||
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// * GNU General Public License for more details.
|
||||
// *
|
||||
// * You should have received a copy of the GNU General Public License
|
||||
// * along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
// *
|
||||
// * If you wish to use this code in a closed source application, please contact phillip_piper@bigfoot.com.
|
||||
// */
|
||||
|
||||
//using System;
|
||||
//using System.Collections;
|
||||
//using System.Collections.Generic;
|
||||
//using System.Text;
|
||||
//using System.Windows.Forms;
|
||||
//using System.Drawing;
|
||||
//using System.Drawing.Drawing2D;
|
||||
|
||||
//namespace BrightIdeasSoftware
|
||||
//{
|
||||
// /// <summary>
|
||||
// /// An IDragSource controls how drag out from the ObjectListView will behave
|
||||
// /// </summary>
|
||||
// public interface IDragSource
|
||||
// {
|
||||
// /// <summary>
|
||||
// /// A drag operation is beginning. Return the data object that will be used
|
||||
// /// for data transfer. Return null to prevent the drag from starting.
|
||||
// /// </summary>
|
||||
// /// <remarks>
|
||||
// /// The returned object is later passed to the GetAllowedEffect() and EndDrag()
|
||||
// /// methods.
|
||||
// /// </remarks>
|
||||
// /// <param name="olv">What ObjectListView is being dragged from.</param>
|
||||
// /// <param name="button">Which mouse button is down?</param>
|
||||
// /// <param name="item">What item was directly dragged by the user? There may be more than just this
|
||||
// /// item selected.</param>
|
||||
// /// <returns>The data object that will be used for data transfer. This will often be a subclass
|
||||
// /// of DataObject, but does not need to be.</returns>
|
||||
// Object StartDrag(ObjectListView olv, MouseButtons button, OLVListItem item);
|
||||
|
||||
// /// <summary>
|
||||
// /// What operations are possible for this drag? This controls the icon shown during the drag
|
||||
// /// </summary>
|
||||
// /// <param name="dragObject">The data object returned by StartDrag()</param>
|
||||
// /// <returns>A combination of DragDropEffects flags</returns>
|
||||
// DragDropEffects GetAllowedEffects(Object dragObject);
|
||||
|
||||
// /// <summary>
|
||||
// /// The drag operation is complete. Do whatever is necessary to complete the action.
|
||||
// /// </summary>
|
||||
// /// <param name="dragObject">The data object returned by StartDrag()</param>
|
||||
// /// <param name="effect">The value returned from GetAllowedEffects()</param>
|
||||
// void EndDrag(Object dragObject, DragDropEffects effect);
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// A do-nothing implementation of IDragSource that can be safely subclassed.
|
||||
// /// </summary>
|
||||
// public class AbstractDragSource : IDragSource
|
||||
// {
|
||||
// #region IDragSource Members
|
||||
|
||||
// /// <summary>
|
||||
// /// See IDragSource documentation
|
||||
// /// </summary>
|
||||
// /// <param name="olv"></param>
|
||||
// /// <param name="button"></param>
|
||||
// /// <param name="item"></param>
|
||||
// /// <returns></returns>
|
||||
// public virtual Object StartDrag(ObjectListView olv, MouseButtons button, OLVListItem item) {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// See IDragSource documentation
|
||||
// /// </summary>
|
||||
// /// <param name="data"></param>
|
||||
// /// <returns></returns>
|
||||
// public virtual DragDropEffects GetAllowedEffects(Object data) {
|
||||
// return DragDropEffects.None;
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// See IDragSource documentation
|
||||
// /// </summary>
|
||||
// /// <param name="dragObject"></param>
|
||||
// /// <param name="effect"></param>
|
||||
// public virtual void EndDrag(Object dragObject, DragDropEffects effect) {
|
||||
// }
|
||||
|
||||
// #endregion
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// A reasonable implementation of IDragSource that provides normal
|
||||
// /// drag source functionality. It creates a data object that supports
|
||||
// /// inter-application dragging of text and HTML representation of
|
||||
// /// the dragged rows. It can optionally force a refresh of all dragged
|
||||
// /// rows when the drag is complete.
|
||||
// /// </summary>
|
||||
// /// <remarks>Subclasses can override GetDataObject() to add new
|
||||
// /// data formats to the data transfer object.</remarks>
|
||||
// public class SimpleDragSource : IDragSource
|
||||
// {
|
||||
// #region Constructors
|
||||
|
||||
// /// <summary>
|
||||
// /// Construct a SimpleDragSource
|
||||
// /// </summary>
|
||||
// public SimpleDragSource() {
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Construct a SimpleDragSource that refreshes the dragged rows when
|
||||
// /// the drag is complete
|
||||
// /// </summary>
|
||||
// /// <param name="refreshAfterDrop"></param>
|
||||
// public SimpleDragSource(bool refreshAfterDrop) {
|
||||
// this.RefreshAfterDrop = refreshAfterDrop;
|
||||
// }
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Public properties
|
||||
|
||||
// /// <summary>
|
||||
// /// Gets or sets whether the dragged rows should be refreshed when the
|
||||
// /// drag operation is complete.
|
||||
// /// </summary>
|
||||
// public bool RefreshAfterDrop {
|
||||
// get { return refreshAfterDrop; }
|
||||
// set { refreshAfterDrop = value; }
|
||||
// }
|
||||
// private bool refreshAfterDrop;
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region IDragSource Members
|
||||
|
||||
// /// <summary>
|
||||
// /// Create a DataObject when the user does a left mouse drag operation.
|
||||
// /// See IDragSource for further information.
|
||||
// /// </summary>
|
||||
// /// <param name="olv"></param>
|
||||
// /// <param name="button"></param>
|
||||
// /// <param name="item"></param>
|
||||
// /// <returns></returns>
|
||||
// public virtual Object StartDrag(ObjectListView olv, MouseButtons button, OLVListItem item) {
|
||||
// // We only drag on left mouse
|
||||
// if (button != MouseButtons.Left)
|
||||
// return null;
|
||||
|
||||
// return this.CreateDataObject(olv);
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Which operations are allowed in the operation? By default, all operations are supported.
|
||||
// /// </summary>
|
||||
// /// <param name="data"></param>
|
||||
// /// <returns>All opertions are supported</returns>
|
||||
// public virtual DragDropEffects GetAllowedEffects(Object data) {
|
||||
// return DragDropEffects.All | DragDropEffects.Link; // why didn't MS include 'Link' in 'All'??
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// The drag operation is finished. Refreshe the dragged rows if so configured.
|
||||
// /// </summary>
|
||||
// /// <param name="dragObject"></param>
|
||||
// /// <param name="effect"></param>
|
||||
// public virtual void EndDrag(Object dragObject, DragDropEffects effect) {
|
||||
// OLVDataObject data = dragObject as OLVDataObject;
|
||||
// if (data == null)
|
||||
// return;
|
||||
|
||||
// if (this.RefreshAfterDrop)
|
||||
// data.ListView.RefreshObjects(data.ModelObjects);
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Create a data object that will be used to as the data object
|
||||
// /// for the drag operation.
|
||||
// /// </summary>
|
||||
// /// <remarks>
|
||||
// /// Subclasses can override this method add new formats to the data object.
|
||||
// /// </remarks>
|
||||
// /// <param name="olv">The ObjectListView that is the source of the drag</param>
|
||||
// /// <returns>A data object for the drag</returns>
|
||||
// protected virtual object CreateDataObject(ObjectListView olv) {
|
||||
// OLVDataObject data = new OLVDataObject(olv);
|
||||
// data.CreateTextFormats();
|
||||
// return data;
|
||||
// }
|
||||
|
||||
// #endregion
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// A data transfer object that knows how to transform a list of model
|
||||
// /// objects into a text and HTML representation.
|
||||
// /// </summary>
|
||||
// public class OLVDataObject : DataObject
|
||||
// {
|
||||
// #region Life and death
|
||||
|
||||
// /// <summary>
|
||||
// /// Create a data object from the selected objects in the given ObjectListView
|
||||
// /// </summary>
|
||||
// /// <param name="olv">The source of the data object</param>
|
||||
// public OLVDataObject(ObjectListView olv) : this(olv, olv.SelectedObjects) {
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Create a data object which operates on the given model objects
|
||||
// /// in the given ObjectListView
|
||||
// /// </summary>
|
||||
// /// <param name="olv">The source of the data object</param>
|
||||
// /// <param name="modelObjects">The model objects to be put into the data object</param>
|
||||
// public OLVDataObject(ObjectListView olv, IList modelObjects) {
|
||||
// this.objectListView = olv;
|
||||
// this.modelObjects = modelObjects;
|
||||
// this.includeHiddenColumns = olv.IncludeHiddenColumnsInDataTransfer;
|
||||
// this.includeColumnHeaders = olv.IncludeColumnHeadersInCopy;
|
||||
// }
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Properties
|
||||
|
||||
// /// <summary>
|
||||
// /// Gets or sets whether hidden columns will also be included in the text
|
||||
// /// and HTML representation. If this is false, only visible columns will
|
||||
// /// be included.
|
||||
// /// </summary>
|
||||
// public bool IncludeHiddenColumns {
|
||||
// get { return includeHiddenColumns; }
|
||||
// }
|
||||
// private bool includeHiddenColumns;
|
||||
|
||||
// /// <summary>
|
||||
// /// Gets or sets whether column headers will also be included in the text
|
||||
// /// and HTML representation.
|
||||
// /// </summary>
|
||||
// public bool IncludeColumnHeaders
|
||||
// {
|
||||
// get { return includeColumnHeaders; }
|
||||
// }
|
||||
// private bool includeColumnHeaders;
|
||||
|
||||
// /// <summary>
|
||||
// /// Gets the ObjectListView that is being used as the source of the data
|
||||
// /// </summary>
|
||||
// public ObjectListView ListView {
|
||||
// get { return objectListView; }
|
||||
// }
|
||||
// private ObjectListView objectListView;
|
||||
|
||||
// /// <summary>
|
||||
// /// Gets the model objects that are to be placed in the data object
|
||||
// /// </summary>
|
||||
// public IList ModelObjects {
|
||||
// get { return modelObjects; }
|
||||
// }
|
||||
// private IList modelObjects = new ArrayList();
|
||||
|
||||
// #endregion
|
||||
|
||||
// /// <summary>
|
||||
// /// Put a text and HTML representation of our model objects
|
||||
// /// into the data object.
|
||||
// /// </summary>
|
||||
// public void CreateTextFormats() {
|
||||
// IList<OLVColumn> columns = this.IncludeHiddenColumns ? this.ListView.AllColumns : this.ListView.ColumnsInDisplayOrder;
|
||||
|
||||
// // Build text and html versions of the selection
|
||||
// StringBuilder sbText = new StringBuilder();
|
||||
// StringBuilder sbHtml = new StringBuilder("<table>");
|
||||
|
||||
// // Include column headers
|
||||
// if (includeColumnHeaders)
|
||||
// {
|
||||
// sbHtml.Append("<tr><td>");
|
||||
// foreach (OLVColumn col in columns)
|
||||
// {
|
||||
// if (col != columns[0])
|
||||
// {
|
||||
// sbText.Append("\t");
|
||||
// sbHtml.Append("</td><td>");
|
||||
// }
|
||||
// string strValue = col.Text;
|
||||
// sbText.Append(strValue);
|
||||
// sbHtml.Append(strValue); //TODO: Should encode the string value
|
||||
// }
|
||||
// sbText.AppendLine();
|
||||
// sbHtml.AppendLine("</td></tr>");
|
||||
// }
|
||||
|
||||
// foreach (object modelObject in this.ModelObjects)
|
||||
// {
|
||||
// sbHtml.Append("<tr><td>");
|
||||
// foreach (OLVColumn col in columns) {
|
||||
// if (col != columns[0]) {
|
||||
// sbText.Append("\t");
|
||||
// sbHtml.Append("</td><td>");
|
||||
// }
|
||||
// string strValue = col.GetStringValue(modelObject);
|
||||
// sbText.Append(strValue);
|
||||
// sbHtml.Append(strValue); //TODO: Should encode the string value
|
||||
// }
|
||||
// sbText.AppendLine();
|
||||
// sbHtml.AppendLine("</td></tr>");
|
||||
// }
|
||||
// sbHtml.AppendLine("</table>");
|
||||
|
||||
// // Put both the text and html versions onto the clipboard.
|
||||
// // For some reason, SetText() with UnicodeText doesn't set the basic CF_TEXT format,
|
||||
// // but using SetData() does.
|
||||
// //this.SetText(sbText.ToString(), TextDataFormat.UnicodeText);
|
||||
// this.SetData(sbText.ToString());
|
||||
// this.SetText(ConvertToHtmlFragment(sbHtml.ToString()), TextDataFormat.Html);
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Make a HTML representation of our model objects
|
||||
// /// </summary>
|
||||
// public string CreateHtml() {
|
||||
// IList<OLVColumn> columns = this.ListView.ColumnsInDisplayOrder;
|
||||
|
||||
// // Build html version of the selection
|
||||
// StringBuilder sbHtml = new StringBuilder("<table>");
|
||||
|
||||
// foreach (object modelObject in this.ModelObjects) {
|
||||
// sbHtml.Append("<tr><td>");
|
||||
// foreach (OLVColumn col in columns) {
|
||||
// if (col != columns[0]) {
|
||||
// sbHtml.Append("</td><td>");
|
||||
// }
|
||||
// string strValue = col.GetStringValue(modelObject);
|
||||
// sbHtml.Append(strValue); //TODO: Should encode the string value
|
||||
// }
|
||||
// sbHtml.AppendLine("</td></tr>");
|
||||
// }
|
||||
// sbHtml.AppendLine("</table>");
|
||||
|
||||
// return sbHtml.ToString();
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// Convert the fragment of HTML into the Clipboards HTML format.
|
||||
// /// </summary>
|
||||
// /// <remarks>The HTML format is found here http://msdn2.microsoft.com/en-us/library/aa767917.aspx
|
||||
// /// </remarks>
|
||||
// /// <param name="fragment">The HTML to put onto the clipboard. It must be valid HTML!</param>
|
||||
// /// <returns>A string that can be put onto the clipboard and will be recognized as HTML</returns>
|
||||
// private string ConvertToHtmlFragment(string fragment) {
|
||||
// // Minimal implementation of HTML clipboard format
|
||||
// string source = "http://www.codeproject.com/KB/list/ObjectListView.aspx";
|
||||
|
||||
// const String MARKER_BLOCK =
|
||||
// "Version:1.0\r\n" +
|
||||
// "StartHTML:{0,8}\r\n" +
|
||||
// "EndHTML:{1,8}\r\n" +
|
||||
// "StartFragment:{2,8}\r\n" +
|
||||
// "EndFragment:{3,8}\r\n" +
|
||||
// "StartSelection:{2,8}\r\n" +
|
||||
// "EndSelection:{3,8}\r\n" +
|
||||
// "SourceURL:{4}\r\n" +
|
||||
// "{5}";
|
||||
|
||||
// int prefixLength = String.Format(MARKER_BLOCK, 0, 0, 0, 0, source, "").Length;
|
||||
|
||||
// const String DEFAULT_HTML_BODY =
|
||||
// "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">" +
|
||||
// "<HTML><HEAD></HEAD><BODY><!--StartFragment-->{0}<!--EndFragment--></BODY></HTML>";
|
||||
|
||||
// string html = String.Format(DEFAULT_HTML_BODY, fragment);
|
||||
// int startFragment = prefixLength + html.IndexOf(fragment);
|
||||
// int endFragment = startFragment + fragment.Length;
|
||||
|
||||
// return String.Format(MARKER_BLOCK, prefixLength, prefixLength + html.Length, startFragment, endFragment, source, html);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
1398
ObjectListView/Implementation/DropSink.cs
Normal file
1398
ObjectListView/Implementation/DropSink.cs
Normal file
File diff suppressed because it is too large
Load Diff
104
ObjectListView/Implementation/Enums.cs
Normal file
104
ObjectListView/Implementation/Enums.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Enums - All enum definitions used in ObjectListView
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 31-March-2011 5:53 pm
|
||||
*
|
||||
* Change log:
|
||||
* 2011-03-31 JPP - Split into its own file
|
||||
*
|
||||
* Copyright (C) 2011-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
public partial class ObjectListView {
|
||||
/// <summary>
|
||||
/// How does a user indicate that they want to edit cells?
|
||||
/// </summary>
|
||||
public enum CellEditActivateMode {
|
||||
/// <summary>
|
||||
/// This list cannot be edited. F2 does nothing.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// A single click on a <strong>subitem</strong> will edit the value. Single clicking the primary column,
|
||||
/// selects the row just like normal. The user must press F2 to edit the primary column.
|
||||
/// </summary>
|
||||
SingleClick = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Double clicking a subitem or the primary column will edit that cell.
|
||||
/// F2 will edit the primary column.
|
||||
/// </summary>
|
||||
DoubleClick = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Pressing F2 is the only way to edit the cells. Once the primary column is being edited,
|
||||
/// the other cells in the row can be edited by pressing Tab.
|
||||
/// </summary>
|
||||
F2Only = 3,
|
||||
|
||||
/// <summary>
|
||||
/// A single click on a <strong>any</strong> cell will edit the value, even the primary column.
|
||||
/// </summary>
|
||||
SingleClickAlways = 4,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// These values specify how column selection will be presented to the user
|
||||
/// </summary>
|
||||
public enum ColumnSelectBehaviour {
|
||||
/// <summary>
|
||||
/// No column selection will be presented
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// The columns will be show in the main menu
|
||||
/// </summary>
|
||||
InlineMenu,
|
||||
|
||||
/// <summary>
|
||||
/// The columns will be shown in a submenu
|
||||
/// </summary>
|
||||
Submenu,
|
||||
|
||||
/// <summary>
|
||||
/// A model dialog will be presented to allow the user to choose columns
|
||||
/// </summary>
|
||||
ModelDialog,
|
||||
|
||||
/*
|
||||
* NonModelDialog is just a little bit tricky since the OLV can change views while the dialog is showing
|
||||
* So, just comment this out for the time being.
|
||||
|
||||
/// <summary>
|
||||
/// A non-model dialog will be presented to allow the user to choose columns
|
||||
/// </summary>
|
||||
NonModelDialog
|
||||
*
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
2471
ObjectListView/Implementation/Events.cs
Normal file
2471
ObjectListView/Implementation/Events.cs
Normal file
File diff suppressed because it is too large
Load Diff
175
ObjectListView/Implementation/GroupingParameters.cs
Normal file
175
ObjectListView/Implementation/GroupingParameters.cs
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* GroupingParameters - All the data that is used to create groups in an ObjectListView
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 31-March-2011 5:53 pm
|
||||
*
|
||||
* Change log:
|
||||
* 2011-03-31 JPP - Split into its own file
|
||||
*
|
||||
* Copyright (C) 2011-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
/// <summary>
|
||||
/// This class contains all the settings used when groups are created
|
||||
/// </summary>
|
||||
public class GroupingParameters {
|
||||
/// <summary>
|
||||
/// Create a GroupingParameters
|
||||
/// </summary>
|
||||
/// <param name="olv"></param>
|
||||
/// <param name="groupByColumn"></param>
|
||||
/// <param name="groupByOrder"></param>
|
||||
/// <param name="column"></param>
|
||||
/// <param name="order"></param>
|
||||
/// <param name="secondaryColumn"></param>
|
||||
/// <param name="secondaryOrder"></param>
|
||||
/// <param name="titleFormat"></param>
|
||||
/// <param name="titleSingularFormat"></param>
|
||||
/// <param name="sortItemsByPrimaryColumn"></param>
|
||||
public GroupingParameters(ObjectListView olv, OLVColumn groupByColumn, SortOrder groupByOrder,
|
||||
OLVColumn column, SortOrder order, OLVColumn secondaryColumn, SortOrder secondaryOrder,
|
||||
string titleFormat, string titleSingularFormat, bool sortItemsByPrimaryColumn) {
|
||||
this.ListView = olv;
|
||||
this.GroupByColumn = groupByColumn;
|
||||
this.GroupByOrder = groupByOrder;
|
||||
this.PrimarySort = column;
|
||||
this.PrimarySortOrder = order;
|
||||
this.SecondarySort = secondaryColumn;
|
||||
this.SecondarySortOrder = secondaryOrder;
|
||||
this.SortItemsByPrimaryColumn = sortItemsByPrimaryColumn;
|
||||
this.TitleFormat = titleFormat;
|
||||
this.TitleSingularFormat = titleSingularFormat;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ObjectListView being grouped
|
||||
/// </summary>
|
||||
public ObjectListView ListView {
|
||||
get { return this.listView; }
|
||||
set { this.listView = value; }
|
||||
}
|
||||
private ObjectListView listView;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the column used to create groups
|
||||
/// </summary>
|
||||
public OLVColumn GroupByColumn {
|
||||
get { return this.groupByColumn; }
|
||||
set { this.groupByColumn = value; }
|
||||
}
|
||||
private OLVColumn groupByColumn;
|
||||
|
||||
/// <summary>
|
||||
/// In what order will the groups themselves be sorted?
|
||||
/// </summary>
|
||||
public SortOrder GroupByOrder {
|
||||
get { return this.groupByOrder; }
|
||||
set { this.groupByOrder = value; }
|
||||
}
|
||||
private SortOrder groupByOrder;
|
||||
|
||||
/// <summary>
|
||||
/// If this is set, this comparer will be used to order the groups
|
||||
/// </summary>
|
||||
public IComparer<OLVGroup> GroupComparer {
|
||||
get { return this.groupComparer; }
|
||||
set { this.groupComparer = value; }
|
||||
}
|
||||
private IComparer<OLVGroup> groupComparer;
|
||||
|
||||
/// <summary>
|
||||
/// If this is set, this comparer will be used to order items within each group
|
||||
/// </summary>
|
||||
public IComparer<OLVListItem> ItemComparer {
|
||||
get { return this.itemComparer; }
|
||||
set { this.itemComparer = value; }
|
||||
}
|
||||
private IComparer<OLVListItem> itemComparer;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the column that will be the primary sort
|
||||
/// </summary>
|
||||
public OLVColumn PrimarySort {
|
||||
get { return this.primarySort; }
|
||||
set { this.primarySort = value; }
|
||||
}
|
||||
private OLVColumn primarySort;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ordering for the primary sort
|
||||
/// </summary>
|
||||
public SortOrder PrimarySortOrder {
|
||||
get { return this.primarySortOrder; }
|
||||
set { this.primarySortOrder = value; }
|
||||
}
|
||||
private SortOrder primarySortOrder;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the column used for secondary sorting
|
||||
/// </summary>
|
||||
public OLVColumn SecondarySort {
|
||||
get { return this.secondarySort; }
|
||||
set { this.secondarySort = value; }
|
||||
}
|
||||
private OLVColumn secondarySort;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ordering for the secondary sort
|
||||
/// </summary>
|
||||
public SortOrder SecondarySortOrder {
|
||||
get { return this.secondarySortOrder; }
|
||||
set { this.secondarySortOrder = value; }
|
||||
}
|
||||
private SortOrder secondarySortOrder;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the title format used for groups with zero or more than one element
|
||||
/// </summary>
|
||||
public string TitleFormat {
|
||||
get { return this.titleFormat; }
|
||||
set { this.titleFormat = value; }
|
||||
}
|
||||
private string titleFormat;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the title format used for groups with only one element
|
||||
/// </summary>
|
||||
public string TitleSingularFormat {
|
||||
get { return this.titleSingularFormat; }
|
||||
set { this.titleSingularFormat = value; }
|
||||
}
|
||||
private string titleSingularFormat;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the items should be sorted by the primary column
|
||||
/// </summary>
|
||||
public bool SortItemsByPrimaryColumn {
|
||||
get { return this.sortItemsByPrimaryColumn; }
|
||||
set { this.sortItemsByPrimaryColumn = value; }
|
||||
}
|
||||
private bool sortItemsByPrimaryColumn;
|
||||
}
|
||||
}
|
||||
747
ObjectListView/Implementation/Groups.cs
Normal file
747
ObjectListView/Implementation/Groups.cs
Normal file
@@ -0,0 +1,747 @@
|
||||
/*
|
||||
* Groups - Enhancements to the normal ListViewGroup
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 22/08/2009 6:03PM
|
||||
*
|
||||
* Change log:
|
||||
* v2.3
|
||||
* 2009-09-09 JPP - Added Collapsed and Collapsible properties
|
||||
* 2009-09-01 JPP - Cleaned up code, added more docs
|
||||
* - Works under VS2005 again
|
||||
* 2009-08-22 JPP - Initial version
|
||||
*
|
||||
* To do:
|
||||
* - Implement subseting
|
||||
* - Implement footer items
|
||||
*
|
||||
* Copyright (C) 2009-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Windows.Forms;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace BrightIdeasSoftware
|
||||
{
|
||||
/// <summary>
|
||||
/// These values indicate what is the state of the group. These values
|
||||
/// are taken directly from the SDK and many are not used by ObjectListView.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum GroupState
|
||||
{
|
||||
/// <summary>
|
||||
/// Normal
|
||||
/// </summary>
|
||||
LVGS_NORMAL = 0x0,
|
||||
|
||||
/// <summary>
|
||||
/// Collapsed
|
||||
/// </summary>
|
||||
LVGS_COLLAPSED = 0x1,
|
||||
|
||||
/// <summary>
|
||||
/// Hidden
|
||||
/// </summary>
|
||||
LVGS_HIDDEN = 0x2,
|
||||
|
||||
/// <summary>
|
||||
/// NoHeader
|
||||
/// </summary>
|
||||
LVGS_NOHEADER = 0x4,
|
||||
|
||||
/// <summary>
|
||||
/// Can be collapsed
|
||||
/// </summary>
|
||||
LVGS_COLLAPSIBLE = 0x8,
|
||||
|
||||
/// <summary>
|
||||
/// Has focus
|
||||
/// </summary>
|
||||
LVGS_FOCUSED = 0x10,
|
||||
|
||||
/// <summary>
|
||||
/// Is Selected
|
||||
/// </summary>
|
||||
LVGS_SELECTED = 0x20,
|
||||
|
||||
/// <summary>
|
||||
/// Is subsetted
|
||||
/// </summary>
|
||||
LVGS_SUBSETED = 0x40,
|
||||
|
||||
/// <summary>
|
||||
/// Subset link has focus
|
||||
/// </summary>
|
||||
LVGS_SUBSETLINKFOCUSED = 0x80,
|
||||
|
||||
/// <summary>
|
||||
/// All styles
|
||||
/// </summary>
|
||||
LVGS_ALL = 0xFFFF
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This mask indicates which members of a LVGROUP have valid data. These values
|
||||
/// are taken directly from the SDK and many are not used by ObjectListView.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum GroupMask
|
||||
{
|
||||
/// <summary>
|
||||
/// No mask
|
||||
/// </summary>
|
||||
LVGF_NONE = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Group has header
|
||||
/// </summary>
|
||||
LVGF_HEADER = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Group has footer
|
||||
/// </summary>
|
||||
LVGF_FOOTER = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Group has state
|
||||
/// </summary>
|
||||
LVGF_STATE = 4,
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
LVGF_ALIGN = 8,
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
LVGF_GROUPID = 0x10,
|
||||
|
||||
/// <summary>
|
||||
/// pszSubtitle is valid
|
||||
/// </summary>
|
||||
LVGF_SUBTITLE = 0x00100,
|
||||
|
||||
/// <summary>
|
||||
/// pszTask is valid
|
||||
/// </summary>
|
||||
LVGF_TASK = 0x00200,
|
||||
|
||||
/// <summary>
|
||||
/// pszDescriptionTop is valid
|
||||
/// </summary>
|
||||
LVGF_DESCRIPTIONTOP = 0x00400,
|
||||
|
||||
/// <summary>
|
||||
/// pszDescriptionBottom is valid
|
||||
/// </summary>
|
||||
LVGF_DESCRIPTIONBOTTOM = 0x00800,
|
||||
|
||||
/// <summary>
|
||||
/// iTitleImage is valid
|
||||
/// </summary>
|
||||
LVGF_TITLEIMAGE = 0x01000,
|
||||
|
||||
/// <summary>
|
||||
/// iExtendedImage is valid
|
||||
/// </summary>
|
||||
LVGF_EXTENDEDIMAGE = 0x02000,
|
||||
|
||||
/// <summary>
|
||||
/// iFirstItem and cItems are valid
|
||||
/// </summary>
|
||||
LVGF_ITEMS = 0x04000,
|
||||
|
||||
/// <summary>
|
||||
/// pszSubsetTitle is valid
|
||||
/// </summary>
|
||||
LVGF_SUBSET = 0x08000,
|
||||
|
||||
/// <summary>
|
||||
/// readonly, cItems holds count of items in visible subset, iFirstItem is valid
|
||||
/// </summary>
|
||||
LVGF_SUBSETITEMS = 0x10000
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This mask indicates which members of a GROUPMETRICS structure are valid
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum GroupMetricsMask
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
LVGMF_NONE = 0,
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
LVGMF_BORDERSIZE = 1,
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
LVGMF_BORDERCOLOR = 2,
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
LVGMF_TEXTCOLOR = 4
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instances of this class enhance the capabilities of a normal ListViewGroup,
|
||||
/// enabling the functionality that was released in v6 of the common controls.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// In this implementation (2009-09), these objects are essentially passive.
|
||||
/// Setting properties does not automatically change the associated group in
|
||||
/// the listview. Collapsed and Collapsible are two exceptions to this and
|
||||
/// give immediate results.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This really should be a subclass of ListViewGroup, but that class is
|
||||
/// sealed (why is that?). So this class provides the same interface as a
|
||||
/// ListViewGroup, plus many other new properties.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class OLVGroup
|
||||
{
|
||||
#region Creation
|
||||
|
||||
/// <summary>
|
||||
/// Create an OLVGroup
|
||||
/// </summary>
|
||||
public OLVGroup() : this("Default group header") {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a group with the given title
|
||||
/// </summary>
|
||||
/// <param name="header">Title of the group</param>
|
||||
public OLVGroup(string header) {
|
||||
this.Header = header;
|
||||
this.Id = OLVGroup.nextId++;
|
||||
this.TitleImage = -1;
|
||||
this.ExtendedImage = -1;
|
||||
}
|
||||
private static int nextId;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the bottom description of the group
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Descriptions only appear when group is centered and there is a title image
|
||||
/// </remarks>
|
||||
public string BottomDescription {
|
||||
get { return this.bottomDescription; }
|
||||
set { this.bottomDescription = value; }
|
||||
}
|
||||
private string bottomDescription;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether or not this group is collapsed
|
||||
/// </summary>
|
||||
public bool Collapsed {
|
||||
get { return this.GetOneState(GroupState.LVGS_COLLAPSED); }
|
||||
set { this.SetOneState(value, GroupState.LVGS_COLLAPSED); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether or not this group can be collapsed
|
||||
/// </summary>
|
||||
public bool Collapsible {
|
||||
get { return this.GetOneState(GroupState.LVGS_COLLAPSIBLE); }
|
||||
set { this.SetOneState(value, GroupState.LVGS_COLLAPSIBLE); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets some representation of the contents of this group
|
||||
/// </summary>
|
||||
/// <remarks>This is user defined (like Tag)</remarks>
|
||||
public IList Contents {
|
||||
get { return this.contents; }
|
||||
set { this.contents = value; }
|
||||
}
|
||||
private IList contents;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether this group has been created.
|
||||
/// </summary>
|
||||
public bool Created {
|
||||
get { return this.ListView != null; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the int or string that will select the extended image to be shown against the title
|
||||
/// </summary>
|
||||
public object ExtendedImage {
|
||||
get { return this.extendedImage; }
|
||||
set { this.extendedImage = value; }
|
||||
}
|
||||
private object extendedImage;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the footer of the group
|
||||
/// </summary>
|
||||
public string Footer {
|
||||
get { return this.footer; }
|
||||
set { this.footer = value; }
|
||||
}
|
||||
private string footer;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the internal id of our associated ListViewGroup.
|
||||
/// </summary>
|
||||
public int GroupId {
|
||||
get {
|
||||
if (this.ListViewGroup == null)
|
||||
return this.Id;
|
||||
|
||||
// Use reflection to get around the access control on the ID property
|
||||
if (OLVGroup.groupIdPropInfo == null) {
|
||||
OLVGroup.groupIdPropInfo = typeof(ListViewGroup).GetProperty("ID",
|
||||
BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
System.Diagnostics.Debug.Assert(OLVGroup.groupIdPropInfo != null);
|
||||
}
|
||||
|
||||
int? groupId = OLVGroup.groupIdPropInfo.GetValue(this.ListViewGroup, null) as int?;
|
||||
return groupId.HasValue ? groupId.Value : -1;
|
||||
}
|
||||
}
|
||||
private static PropertyInfo groupIdPropInfo;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the header of the group
|
||||
/// </summary>
|
||||
public string Header {
|
||||
get { return this.header; }
|
||||
set { this.header = value; }
|
||||
}
|
||||
private string header;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the horizontal alignment of the group header
|
||||
/// </summary>
|
||||
public HorizontalAlignment HeaderAlignment {
|
||||
get { return this.headerAlignment; }
|
||||
set { this.headerAlignment = value; }
|
||||
}
|
||||
private HorizontalAlignment headerAlignment;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the internally created id of the group
|
||||
/// </summary>
|
||||
public int Id {
|
||||
get { return this.id; }
|
||||
set { this.id = value; }
|
||||
}
|
||||
private int id;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets ListViewItems that are members of this group
|
||||
/// </summary>
|
||||
/// <remarks>Listener of the BeforeCreatingGroups event can populate this collection.
|
||||
/// It is only used on non-virtual lists.</remarks>
|
||||
public IList<OLVListItem> Items {
|
||||
get { return this.items; }
|
||||
set { this.items = value; }
|
||||
}
|
||||
private IList<OLVListItem> items = new List<OLVListItem>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the key that was used to partition objects into this group
|
||||
/// </summary>
|
||||
/// <remarks>This is user defined (like Tag)</remarks>
|
||||
public object Key {
|
||||
get { return this.key; }
|
||||
set { this.key = value; }
|
||||
}
|
||||
private object key;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ObjectListView that this group belongs to
|
||||
/// </summary>
|
||||
/// <remarks>If this is null, the group has not yet been created.</remarks>
|
||||
public ObjectListView ListView {
|
||||
get { return this.listView; }
|
||||
protected set { this.listView = value; }
|
||||
}
|
||||
private ObjectListView listView;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the group
|
||||
/// </summary>
|
||||
/// <remarks>As of 2009-09-01, this property is not used.</remarks>
|
||||
public string Name {
|
||||
get { return this.name; }
|
||||
set { this.name = value; }
|
||||
}
|
||||
private string name;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether this group is focused
|
||||
/// </summary>
|
||||
public bool Focused
|
||||
{
|
||||
get { return this.GetOneState(GroupState.LVGS_FOCUSED); }
|
||||
set { this.SetOneState(value, GroupState.LVGS_FOCUSED); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether this group is selected
|
||||
/// </summary>
|
||||
public bool Selected
|
||||
{
|
||||
get { return this.GetOneState(GroupState.LVGS_SELECTED); }
|
||||
set { this.SetOneState(value, GroupState.LVGS_SELECTED); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the text that will show that this group is subsetted
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// As of WinSDK v7.0, subsetting of group is officially unimplemented.
|
||||
/// We can get around this using undocumented interfaces and may do so.
|
||||
/// </remarks>
|
||||
public string SubsetTitle {
|
||||
get { return this.subsetTitle; }
|
||||
set { this.subsetTitle = value; }
|
||||
}
|
||||
private string subsetTitle;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or set the subtitleof the task
|
||||
/// </summary>
|
||||
public string Subtitle {
|
||||
get { return this.subtitle; }
|
||||
set { this.subtitle = value; }
|
||||
}
|
||||
private string subtitle;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value by which this group will be sorted.
|
||||
/// </summary>
|
||||
public IComparable SortValue {
|
||||
get { return this.sortValue; }
|
||||
set { this.sortValue = value; }
|
||||
}
|
||||
private IComparable sortValue;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the state of the group
|
||||
/// </summary>
|
||||
public GroupState State {
|
||||
get { return this.state; }
|
||||
set { this.state = value; }
|
||||
}
|
||||
private GroupState state;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets which bits of State are valid
|
||||
/// </summary>
|
||||
public GroupState StateMask {
|
||||
get { return this.stateMask; }
|
||||
set { this.stateMask = value; }
|
||||
}
|
||||
private GroupState stateMask;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether this group is showing only a subset of its elements
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// As of WinSDK v7.0, this property officially does nothing.
|
||||
/// </remarks>
|
||||
public bool Subseted {
|
||||
get { return this.GetOneState(GroupState.LVGS_SUBSETED); }
|
||||
set { this.SetOneState(value, GroupState.LVGS_SUBSETED); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the user-defined data attached to this group
|
||||
/// </summary>
|
||||
public object Tag {
|
||||
get { return this.tag; }
|
||||
set { this.tag = value; }
|
||||
}
|
||||
private object tag;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the task of this group
|
||||
/// </summary>
|
||||
/// <remarks>This task is the clickable text that appears on the right margin
|
||||
/// of the group header.</remarks>
|
||||
public string Task {
|
||||
get { return this.task; }
|
||||
set { this.task = value; }
|
||||
}
|
||||
private string task;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the int or string that will select the image to be shown against the title
|
||||
/// </summary>
|
||||
public object TitleImage {
|
||||
get { return this.titleImage; }
|
||||
set { this.titleImage = value; }
|
||||
}
|
||||
private object titleImage;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the top description of the group
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Descriptions only appear when group is centered and there is a title image
|
||||
/// </remarks>
|
||||
public string TopDescription {
|
||||
get { return this.topDescription; }
|
||||
set { this.topDescription = value; }
|
||||
}
|
||||
private string topDescription;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of items that are within this group.
|
||||
/// </summary>
|
||||
/// <remarks>This should only be used for virtual groups.</remarks>
|
||||
public int VirtualItemCount {
|
||||
get { return this.virtualItemCount; }
|
||||
set { this.virtualItemCount = value; }
|
||||
}
|
||||
private int virtualItemCount;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ListViewGroup that is shadowed by this group.
|
||||
/// </summary>
|
||||
/// <remarks>For virtual groups, this will always be null.</remarks>
|
||||
protected ListViewGroup ListViewGroup {
|
||||
get { return this.listViewGroup; }
|
||||
set { this.listViewGroup = value; }
|
||||
}
|
||||
private ListViewGroup listViewGroup;
|
||||
#endregion
|
||||
|
||||
#region Calculations/Conversions
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the index into the group image list of the given image selector
|
||||
/// </summary>
|
||||
/// <param name="imageSelector"></param>
|
||||
/// <returns></returns>
|
||||
public int GetImageIndex(object imageSelector) {
|
||||
if (imageSelector == null || this.ListView == null || this.ListView.GroupImageList == null)
|
||||
return -1;
|
||||
|
||||
if (imageSelector is Int32)
|
||||
return (int)imageSelector;
|
||||
|
||||
String imageSelectorAsString = imageSelector as String;
|
||||
if (imageSelectorAsString != null)
|
||||
return this.ListView.GroupImageList.Images.IndexOfKey(imageSelectorAsString);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert this object to a string representation
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override string ToString() {
|
||||
return this.Header;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Commands
|
||||
|
||||
/// <summary>
|
||||
/// Insert a native group into the underlying Windows control,
|
||||
/// *without* using a ListViewGroup
|
||||
/// </summary>
|
||||
/// <param name="olv"></param>
|
||||
/// <remarks>This is used when creating virtual groups</remarks>
|
||||
public void InsertGroupNewStyle(ObjectListView olv) {
|
||||
this.ListView = olv;
|
||||
NativeMethods.InsertGroup(olv, this.AsNativeGroup(true));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Insert a native group into the underlying control via a ListViewGroup
|
||||
/// </summary>
|
||||
/// <param name="olv"></param>
|
||||
public void InsertGroupOldStyle(ObjectListView olv) {
|
||||
this.ListView = olv;
|
||||
|
||||
// Create/update the associated ListViewGroup
|
||||
if (this.ListViewGroup == null)
|
||||
this.ListViewGroup = new ListViewGroup();
|
||||
this.ListViewGroup.Header = this.Header;
|
||||
this.ListViewGroup.HeaderAlignment = this.HeaderAlignment;
|
||||
this.ListViewGroup.Name = this.Name;
|
||||
|
||||
// Remember which OLVGroup created the ListViewGroup
|
||||
this.ListViewGroup.Tag = this;
|
||||
|
||||
// Add the group to the control
|
||||
olv.Groups.Add(this.ListViewGroup);
|
||||
|
||||
// Add any extra information
|
||||
NativeMethods.SetGroupInfo(olv, this.GroupId, this.AsNativeGroup(false));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Change the members of the group to match the current contents of Items,
|
||||
/// using a ListViewGroup
|
||||
/// </summary>
|
||||
public void SetItemsOldStyle() {
|
||||
List<OLVListItem> list = this.Items as List<OLVListItem>;
|
||||
if (list == null) {
|
||||
foreach (OLVListItem item in this.Items) {
|
||||
this.ListViewGroup.Items.Add(item);
|
||||
}
|
||||
} else {
|
||||
this.ListViewGroup.Items.AddRange(list.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation
|
||||
|
||||
/// <summary>
|
||||
/// Create a native LVGROUP structure that matches this group
|
||||
/// </summary>
|
||||
internal NativeMethods.LVGROUP2 AsNativeGroup(bool withId) {
|
||||
|
||||
NativeMethods.LVGROUP2 group = new NativeMethods.LVGROUP2();
|
||||
group.cbSize = (uint)Marshal.SizeOf(typeof(NativeMethods.LVGROUP2));
|
||||
group.mask = (uint)(GroupMask.LVGF_HEADER ^ GroupMask.LVGF_ALIGN ^ GroupMask.LVGF_STATE);
|
||||
group.pszHeader = this.Header;
|
||||
group.uAlign = (uint)this.HeaderAlignment;
|
||||
group.stateMask = (uint)this.StateMask;
|
||||
group.state = (uint)this.State;
|
||||
|
||||
if (withId) {
|
||||
group.iGroupId = this.GroupId;
|
||||
group.mask ^= (uint)GroupMask.LVGF_GROUPID;
|
||||
}
|
||||
|
||||
if (!String.IsNullOrEmpty(this.Footer)) {
|
||||
group.pszFooter = this.Footer;
|
||||
group.mask ^= (uint)GroupMask.LVGF_FOOTER;
|
||||
}
|
||||
|
||||
if (!String.IsNullOrEmpty(this.Subtitle)) {
|
||||
group.pszSubtitle = this.Subtitle;
|
||||
group.mask ^= (uint)GroupMask.LVGF_SUBTITLE;
|
||||
}
|
||||
|
||||
if (!String.IsNullOrEmpty(this.Task)) {
|
||||
group.pszTask = this.Task;
|
||||
group.mask ^= (uint)GroupMask.LVGF_TASK;
|
||||
}
|
||||
|
||||
if (!String.IsNullOrEmpty(this.TopDescription)) {
|
||||
group.pszDescriptionTop = this.TopDescription;
|
||||
group.mask ^= (uint)GroupMask.LVGF_DESCRIPTIONTOP;
|
||||
}
|
||||
|
||||
if (!String.IsNullOrEmpty(this.BottomDescription)) {
|
||||
group.pszDescriptionBottom = this.BottomDescription;
|
||||
group.mask ^= (uint)GroupMask.LVGF_DESCRIPTIONBOTTOM;
|
||||
}
|
||||
|
||||
int imageIndex = this.GetImageIndex(this.TitleImage);
|
||||
if (imageIndex >= 0) {
|
||||
group.iTitleImage = imageIndex;
|
||||
group.mask ^= (uint)GroupMask.LVGF_TITLEIMAGE;
|
||||
}
|
||||
|
||||
imageIndex = this.GetImageIndex(this.ExtendedImage);
|
||||
if (imageIndex >= 0) {
|
||||
group.iExtendedImage = imageIndex;
|
||||
group.mask ^= (uint)GroupMask.LVGF_EXTENDEDIMAGE;
|
||||
}
|
||||
|
||||
if (!String.IsNullOrEmpty(this.SubsetTitle)) {
|
||||
group.pszSubsetTitle = this.SubsetTitle;
|
||||
group.mask ^= (uint)GroupMask.LVGF_SUBSET;
|
||||
}
|
||||
|
||||
if (this.VirtualItemCount > 0) {
|
||||
group.cItems = this.VirtualItemCount;
|
||||
group.mask ^= (uint)GroupMask.LVGF_ITEMS;
|
||||
}
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
private bool GetOneState(GroupState mask) {
|
||||
if (this.Created)
|
||||
this.State = this.GetState();
|
||||
return (this.State & mask) == mask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the current state of this group from the underlying control
|
||||
/// </summary>
|
||||
protected GroupState GetState() {
|
||||
return NativeMethods.GetGroupState(this.ListView, this.GroupId, GroupState.LVGS_ALL);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the current state of this group from the underlying control
|
||||
/// </summary>
|
||||
protected int SetState(GroupState newState, GroupState mask) {
|
||||
NativeMethods.LVGROUP2 group = new NativeMethods.LVGROUP2();
|
||||
group.cbSize = ((uint)Marshal.SizeOf(typeof(NativeMethods.LVGROUP2)));
|
||||
group.mask = (uint)GroupMask.LVGF_STATE;
|
||||
group.state = (uint)newState;
|
||||
group.stateMask = (uint)mask;
|
||||
return NativeMethods.SetGroupInfo(this.ListView, this.GroupId, group);
|
||||
}
|
||||
|
||||
private void SetOneState(bool value, GroupState mask)
|
||||
{
|
||||
this.StateMask ^= mask;
|
||||
if (value)
|
||||
this.State ^= mask;
|
||||
else
|
||||
this.State &= ~mask;
|
||||
|
||||
if (this.Created)
|
||||
this.SetState(this.State, mask);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
568
ObjectListView/Implementation/Munger.cs
Normal file
568
ObjectListView/Implementation/Munger.cs
Normal file
@@ -0,0 +1,568 @@
|
||||
/*
|
||||
* Munger - An Interface pattern on getting and setting values from object through Reflection
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 28/11/2008 17:15
|
||||
*
|
||||
* Change log:
|
||||
* v2.5.1
|
||||
* 2012-05-01 JPP - Added IgnoreMissingAspects property
|
||||
* v2.5
|
||||
* 2011-05-20 JPP - Accessing through an indexer when the target had both a integer and
|
||||
* a string indexer didn't work reliably.
|
||||
* v2.4.1
|
||||
* 2010-08-10 JPP - Refactored into Munger/SimpleMunger. 3x faster!
|
||||
* v2.3
|
||||
* 2009-02-15 JPP - Made Munger a public class
|
||||
* 2009-01-20 JPP - Made the Munger capable of handling indexed access.
|
||||
* Incidentally, this removed the ugliness that the last change introduced.
|
||||
* 2009-01-18 JPP - Handle target objects from a DataListView (normally DataRowViews)
|
||||
* v2.0
|
||||
* 2008-11-28 JPP Initial version
|
||||
*
|
||||
* TO DO:
|
||||
*
|
||||
* Copyright (C) 2006-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace BrightIdeasSoftware
|
||||
{
|
||||
/// <summary>
|
||||
/// An instance of Munger gets a value from or puts a value into a target object. The property
|
||||
/// to be peeked (or poked) is determined from a string. The peeking or poking is done using reflection.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Name of the aspect to be peeked can be a field, property or parameterless method. The name of an
|
||||
/// aspect to poke can be a field, writable property or single parameter method.
|
||||
/// <para>
|
||||
/// Aspect names can be dotted to chain a series of references.
|
||||
/// </para>
|
||||
/// <example>Order.Customer.HomeAddress.State</example>
|
||||
/// </remarks>
|
||||
public class Munger
|
||||
{
|
||||
#region Life and death
|
||||
|
||||
/// <summary>
|
||||
/// Create a do nothing Munger
|
||||
/// </summary>
|
||||
public Munger()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a Munger that works on the given aspect name
|
||||
/// </summary>
|
||||
/// <param name="aspectName">The name of the </param>
|
||||
public Munger(String aspectName)
|
||||
{
|
||||
this.AspectName = aspectName;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Static utility methods
|
||||
|
||||
/// <summary>
|
||||
/// A helper method to put the given value into the given aspect of the given object.
|
||||
/// </summary>
|
||||
/// <remarks>This method catches and silently ignores any errors that occur
|
||||
/// while modifying the target object</remarks>
|
||||
/// <param name="target">The object to be modified</param>
|
||||
/// <param name="propertyName">The name of the property/field to be modified</param>
|
||||
/// <param name="value">The value to be assigned</param>
|
||||
/// <returns>Did the modification work?</returns>
|
||||
public static bool PutProperty(object target, string propertyName, object value) {
|
||||
try {
|
||||
Munger munger = new Munger(propertyName);
|
||||
return munger.PutValue(target, value);
|
||||
}
|
||||
catch (MungerException) {
|
||||
// Not a lot we can do about this. Something went wrong in the bowels
|
||||
// of the property. Let's take the ostrich approach and just ignore it :-)
|
||||
|
||||
// Normally, we would never just silently ignore an exception.
|
||||
// However, in this case, this is a utility method that explicitly
|
||||
// contracts to catch and ignore errors. If this is not acceptible,
|
||||
// the programmer should not use this method.
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether Mungers will silently ignore missing aspect errors.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// By default, if a Munger is asked to fetch a field/property/method
|
||||
/// that does not exist from a model, it returns an error message, since that
|
||||
/// condition is normally a programming error. There are some use cases where
|
||||
/// this is not an error, and the munger should simply keep quiet.
|
||||
/// </para>
|
||||
/// <para>By default this is true during release builds.</para>
|
||||
/// </remarks>
|
||||
public static bool IgnoreMissingAspects {
|
||||
get { return ignoreMissingAspects; }
|
||||
set { ignoreMissingAspects = value; }
|
||||
}
|
||||
private static bool ignoreMissingAspects
|
||||
#if !DEBUG
|
||||
= true
|
||||
#endif
|
||||
;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public properties
|
||||
|
||||
/// <summary>
|
||||
/// The name of the aspect that is to be peeked or poked.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This name can be a field, property or parameter-less method.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The name can be dotted, which chains references. If any link in the chain returns
|
||||
/// null, the entire chain is considered to return null.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <example>"DateOfBirth"</example>
|
||||
/// <example>"Owner.HomeAddress.Postcode"</example>
|
||||
public string AspectName
|
||||
{
|
||||
get { return aspectName; }
|
||||
set {
|
||||
aspectName = value;
|
||||
|
||||
// Clear any cache
|
||||
aspectParts = null;
|
||||
}
|
||||
}
|
||||
private string aspectName;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Public interface
|
||||
|
||||
/// <summary>
|
||||
/// Extract the value indicated by our AspectName from the given target.
|
||||
/// </summary>
|
||||
/// <remarks>If the aspect name is null or empty, this will return null.</remarks>
|
||||
/// <param name="target">The object that will be peeked</param>
|
||||
/// <returns>The value read from the target</returns>
|
||||
public Object GetValue(Object target) {
|
||||
if (this.Parts.Count == 0)
|
||||
return null;
|
||||
|
||||
try {
|
||||
return this.EvaluateParts(target, this.Parts);
|
||||
} catch (MungerException ex) {
|
||||
if (Munger.IgnoreMissingAspects)
|
||||
return null;
|
||||
|
||||
return String.Format("'{0}' is not a parameter-less method, property or field of type '{1}'",
|
||||
ex.Munger.AspectName, ex.Target.GetType());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract the value indicated by our AspectName from the given target, raising exceptions
|
||||
/// if the munger fails.
|
||||
/// </summary>
|
||||
/// <remarks>If the aspect name is null or empty, this will return null.</remarks>
|
||||
/// <param name="target">The object that will be peeked</param>
|
||||
/// <returns>The value read from the target</returns>
|
||||
public Object GetValueEx(Object target) {
|
||||
if (this.Parts.Count == 0)
|
||||
return null;
|
||||
|
||||
return this.EvaluateParts(target, this.Parts);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Poke the given value into the given target indicated by our AspectName.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// If the AspectName is a dotted path, all the selectors bar the last
|
||||
/// are used to find the object that should be updated, and the last
|
||||
/// selector is used as the property to update on that object.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// So, if 'target' is a Person and the AspectName is "HomeAddress.Postcode",
|
||||
/// this method will first fetch "HomeAddress" property, and then try to set the
|
||||
/// "Postcode" property on the home address object.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="target">The object that will be poked</param>
|
||||
/// <param name="value">The value that will be poked into the target</param>
|
||||
/// <returns>bool indicating whether the put worked</returns>
|
||||
public bool PutValue(Object target, Object value)
|
||||
{
|
||||
if (this.Parts.Count == 0)
|
||||
return false;
|
||||
|
||||
SimpleMunger lastPart = this.Parts[this.Parts.Count - 1];
|
||||
|
||||
if (this.Parts.Count > 1) {
|
||||
List<SimpleMunger> parts = new List<SimpleMunger>(this.Parts);
|
||||
parts.RemoveAt(parts.Count - 1);
|
||||
try {
|
||||
target = this.EvaluateParts(target, parts);
|
||||
} catch (MungerException ex) {
|
||||
this.ReportPutValueException(ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (target != null) {
|
||||
try {
|
||||
return lastPart.PutValue(target, value);
|
||||
} catch (MungerException ex) {
|
||||
this.ReportPutValueException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of SimpleMungers that match our AspectName
|
||||
/// </summary>
|
||||
private IList<SimpleMunger> Parts {
|
||||
get {
|
||||
if (aspectParts == null)
|
||||
aspectParts = BuildParts(this.AspectName);
|
||||
return aspectParts;
|
||||
}
|
||||
}
|
||||
private IList<SimpleMunger> aspectParts;
|
||||
|
||||
/// <summary>
|
||||
/// Convert a possibly dotted AspectName into a list of SimpleMungers
|
||||
/// </summary>
|
||||
/// <param name="aspect"></param>
|
||||
/// <returns></returns>
|
||||
private IList<SimpleMunger> BuildParts(string aspect) {
|
||||
List<SimpleMunger> parts = new List<SimpleMunger>();
|
||||
if (!String.IsNullOrEmpty(aspect)) {
|
||||
foreach (string part in aspect.Split('.')) {
|
||||
parts.Add(new SimpleMunger(part.Trim()));
|
||||
}
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluate the given chain of SimpleMungers against an initial target.
|
||||
/// </summary>
|
||||
/// <param name="target"></param>
|
||||
/// <param name="parts"></param>
|
||||
/// <returns></returns>
|
||||
private object EvaluateParts(object target, IList<SimpleMunger> parts) {
|
||||
foreach (SimpleMunger part in parts) {
|
||||
if (target == null)
|
||||
break;
|
||||
target = part.GetValue(target);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
private void ReportPutValueException(MungerException ex) {
|
||||
//TODO: How should we report this error?
|
||||
System.Diagnostics.Debug.WriteLine("PutValue failed");
|
||||
System.Diagnostics.Debug.WriteLine(String.Format("- Culprit aspect: {0}", ex.Munger.AspectName));
|
||||
System.Diagnostics.Debug.WriteLine(String.Format("- Target: {0} of type {1}", ex.Target, ex.Target.GetType()));
|
||||
System.Diagnostics.Debug.WriteLine(String.Format("- Inner exception: {0}", ex.InnerException));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A SimpleMunger deals with a single property/field/method on its target.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Munger uses a chain of these resolve a dotted aspect name.
|
||||
/// </remarks>
|
||||
public class SimpleMunger
|
||||
{
|
||||
#region Life and death
|
||||
|
||||
/// <summary>
|
||||
/// Create a SimpleMunger
|
||||
/// </summary>
|
||||
/// <param name="aspectName"></param>
|
||||
public SimpleMunger(String aspectName)
|
||||
{
|
||||
this.aspectName = aspectName;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public properties
|
||||
|
||||
/// <summary>
|
||||
/// The name of the aspect that is to be peeked or poked.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This name can be a field, property or method.
|
||||
/// When using a method to get a value, the method must be parameter-less.
|
||||
/// When using a method to set a value, the method must accept 1 parameter.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// It cannot be a dotted name.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public string AspectName {
|
||||
get { return aspectName; }
|
||||
}
|
||||
private readonly string aspectName;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public interface
|
||||
|
||||
/// <summary>
|
||||
/// Get a value from the given target
|
||||
/// </summary>
|
||||
/// <param name="target"></param>
|
||||
/// <returns></returns>
|
||||
public Object GetValue(Object target) {
|
||||
if (target == null)
|
||||
return null;
|
||||
|
||||
this.ResolveName(target, this.AspectName, 0);
|
||||
|
||||
try {
|
||||
if (this.resolvedPropertyInfo != null)
|
||||
return this.resolvedPropertyInfo.GetValue(target, null);
|
||||
|
||||
if (this.resolvedMethodInfo != null)
|
||||
return this.resolvedMethodInfo.Invoke(target, null);
|
||||
|
||||
if (this.resolvedFieldInfo != null)
|
||||
return this.resolvedFieldInfo.GetValue(target);
|
||||
|
||||
// If that didn't work, try to use the indexer property.
|
||||
// This covers things like dictionaries and DataRows.
|
||||
if (this.indexerPropertyInfo != null)
|
||||
return this.indexerPropertyInfo.GetValue(target, new object[] { this.AspectName });
|
||||
} catch (Exception ex) {
|
||||
// Lots of things can do wrong in these invocations
|
||||
throw new MungerException(this, target, ex);
|
||||
}
|
||||
|
||||
// If we get to here, we couldn't find a match for the aspect
|
||||
throw new MungerException(this, target, new MissingMethodException());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Poke the given value into the given target indicated by our AspectName.
|
||||
/// </summary>
|
||||
/// <param name="target">The object that will be poked</param>
|
||||
/// <param name="value">The value that will be poked into the target</param>
|
||||
/// <returns>bool indicating if the put worked</returns>
|
||||
public bool PutValue(object target, object value) {
|
||||
if (target == null)
|
||||
return false;
|
||||
|
||||
this.ResolveName(target, this.AspectName, 1);
|
||||
|
||||
try {
|
||||
if (this.resolvedPropertyInfo != null) {
|
||||
this.resolvedPropertyInfo.SetValue(target, value, null);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.resolvedMethodInfo != null) {
|
||||
this.resolvedMethodInfo.Invoke(target, new object[] { value });
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.resolvedFieldInfo != null) {
|
||||
this.resolvedFieldInfo.SetValue(target, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
// If that didn't work, try to use the indexer property.
|
||||
// This covers things like dictionaries and DataRows.
|
||||
if (this.indexerPropertyInfo != null) {
|
||||
this.indexerPropertyInfo.SetValue(target, value, new object[] { this.AspectName });
|
||||
return true;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
// Lots of things can do wrong in these invocations
|
||||
throw new MungerException(this, target, ex);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation
|
||||
|
||||
private void ResolveName(object target, string name, int numberMethodParameters) {
|
||||
|
||||
if (cachedTargetType == target.GetType() && cachedName == name && cachedNumberParameters == numberMethodParameters)
|
||||
return;
|
||||
|
||||
cachedTargetType = target.GetType();
|
||||
cachedName = name;
|
||||
cachedNumberParameters = numberMethodParameters;
|
||||
|
||||
resolvedFieldInfo = null;
|
||||
resolvedPropertyInfo = null;
|
||||
resolvedMethodInfo = null;
|
||||
indexerPropertyInfo = null;
|
||||
|
||||
const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance /*| BindingFlags.NonPublic*/;
|
||||
|
||||
foreach (PropertyInfo pinfo in target.GetType().GetProperties(flags)) {
|
||||
if (pinfo.Name == name) {
|
||||
resolvedPropertyInfo = pinfo;
|
||||
return;
|
||||
}
|
||||
|
||||
// See if we can find an string indexer property while we are here.
|
||||
// We also need to allow for old style <object> keyed collections.
|
||||
if (indexerPropertyInfo == null && pinfo.Name == "Item") {
|
||||
ParameterInfo[] par = pinfo.GetGetMethod().GetParameters();
|
||||
if (par.Length > 0) {
|
||||
Type parameterType = par[0].ParameterType;
|
||||
if (parameterType == typeof(string) || parameterType == typeof(object))
|
||||
indexerPropertyInfo = pinfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (FieldInfo info in target.GetType().GetFields(flags)) {
|
||||
if (info.Name == name) {
|
||||
resolvedFieldInfo = info;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (MethodInfo info in target.GetType().GetMethods(flags)) {
|
||||
if (info.Name == name && info.GetParameters().Length == numberMethodParameters) {
|
||||
resolvedMethodInfo = info;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Type cachedTargetType;
|
||||
private string cachedName;
|
||||
private int cachedNumberParameters;
|
||||
|
||||
private FieldInfo resolvedFieldInfo;
|
||||
private PropertyInfo resolvedPropertyInfo;
|
||||
private MethodInfo resolvedMethodInfo;
|
||||
private PropertyInfo indexerPropertyInfo;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// These exceptions are raised when a munger finds something it cannot process
|
||||
/// </summary>
|
||||
public class MungerException : ApplicationException
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a MungerException
|
||||
/// </summary>
|
||||
/// <param name="munger"></param>
|
||||
/// <param name="target"></param>
|
||||
/// <param name="ex"></param>
|
||||
public MungerException(SimpleMunger munger, object target, Exception ex)
|
||||
: base("Munger failed", ex) {
|
||||
this.munger = munger;
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the munger that raised the exception
|
||||
/// </summary>
|
||||
public SimpleMunger Munger {
|
||||
get { return munger; }
|
||||
}
|
||||
private readonly SimpleMunger munger;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the target that threw the exception
|
||||
/// </summary>
|
||||
public object Target {
|
||||
get { return target; }
|
||||
}
|
||||
private readonly object target;
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't currently need this
|
||||
* 2010-08-06
|
||||
*
|
||||
|
||||
internal class SimpleBinder : Binder
|
||||
{
|
||||
public override FieldInfo BindToField(BindingFlags bindingAttr, FieldInfo[] match, object value, System.Globalization.CultureInfo culture) {
|
||||
//return Type.DefaultBinder.BindToField(
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override object ChangeType(object value, Type type, System.Globalization.CultureInfo culture) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override MethodBase BindToMethod(BindingFlags bindingAttr, MethodBase[] match, ref object[] args, ParameterModifier[] modifiers, System.Globalization.CultureInfo culture, string[] names, out object state) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void ReorderArgumentArray(ref object[] args, object state) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override MethodBase SelectMethod(BindingFlags bindingAttr, MethodBase[] match, Type[] types, ParameterModifier[] modifiers) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override PropertyInfo SelectProperty(BindingFlags bindingAttr, PropertyInfo[] match, Type returnType, Type[] indexes, ParameterModifier[] modifiers) {
|
||||
if (match == null)
|
||||
throw new ArgumentNullException("match");
|
||||
|
||||
if (match.Length == 0)
|
||||
return null;
|
||||
|
||||
return match[0];
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
1226
ObjectListView/Implementation/NativeMethods.cs
Normal file
1226
ObjectListView/Implementation/NativeMethods.cs
Normal file
File diff suppressed because it is too large
Load Diff
87
ObjectListView/Implementation/NullableDictionary.cs
Normal file
87
ObjectListView/Implementation/NullableDictionary.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* NullableDictionary - A simple Dictionary that can handle null as a key
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 31-March-2011 5:53 pm
|
||||
*
|
||||
* Change log:
|
||||
* 2011-03-31 JPP - Split into its own file
|
||||
*
|
||||
* Copyright (C) 2011-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Collections;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
/// <summary>
|
||||
/// A simple-minded implementation of a Dictionary that can handle null as a key.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey">The type of the dictionary key</typeparam>
|
||||
/// <typeparam name="TValue">The type of the values to be stored</typeparam>
|
||||
/// <remarks>This is not a full implementation and is only meant to handle
|
||||
/// collecting groups by their keys, since groups can have null as a key value.</remarks>
|
||||
internal class NullableDictionary<TKey, TValue> : Dictionary<TKey, TValue> {
|
||||
private bool hasNullKey;
|
||||
private TValue nullValue;
|
||||
|
||||
new public TValue this[TKey key] {
|
||||
get {
|
||||
if (key != null)
|
||||
return base[key];
|
||||
|
||||
if (this.hasNullKey)
|
||||
return this.nullValue;
|
||||
|
||||
throw new KeyNotFoundException();
|
||||
}
|
||||
set {
|
||||
if (key == null) {
|
||||
this.hasNullKey = true;
|
||||
this.nullValue = value;
|
||||
} else
|
||||
base[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
new public bool ContainsKey(TKey key) {
|
||||
return key == null ? this.hasNullKey : base.ContainsKey(key);
|
||||
}
|
||||
|
||||
new public IList Keys {
|
||||
get {
|
||||
ArrayList list = new ArrayList(base.Keys);
|
||||
if (this.hasNullKey)
|
||||
list.Add(null);
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
new public IList<TValue> Values {
|
||||
get {
|
||||
List<TValue> list = new List<TValue>(base.Values);
|
||||
if (this.hasNullKey)
|
||||
list.Add(this.nullValue);
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
321
ObjectListView/Implementation/OLVListItem.cs
Normal file
321
ObjectListView/Implementation/OLVListItem.cs
Normal file
@@ -0,0 +1,321 @@
|
||||
/*
|
||||
* OLVListItem - A row in an ObjectListView
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 31-March-2011 5:53 pm
|
||||
*
|
||||
* Change log:
|
||||
* 2015-08-22 JPP - Added OLVListItem.SelectedBackColor and SelectedForeColor
|
||||
* 2015-06-09 JPP - Added HasAnyHyperlinks property
|
||||
* v2.8
|
||||
* 2014-09-27 JPP - Remove faulty caching of CheckState
|
||||
* 2014-05-06 JPP - Added OLVListItem.Enabled flag
|
||||
* vOld
|
||||
* 2011-03-31 JPP - Split into its own file
|
||||
*
|
||||
* Copyright (C) 2011-2015 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using System.Drawing;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
/// <summary>
|
||||
/// OLVListItems are specialized ListViewItems that know which row object they came from,
|
||||
/// and the row index at which they are displayed, even when in group view mode. They
|
||||
/// also know the image they should draw against themselves
|
||||
/// </summary>
|
||||
public class OLVListItem : ListViewItem {
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Create a OLVListItem for the given row object
|
||||
/// </summary>
|
||||
public OLVListItem(object rowObject) {
|
||||
this.rowObject = rowObject;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a OLVListItem for the given row object, represented by the given string and image
|
||||
/// </summary>
|
||||
public OLVListItem(object rowObject, string text, Object image)
|
||||
: base(text, -1) {
|
||||
this.rowObject = rowObject;
|
||||
this.imageSelector = image;
|
||||
}
|
||||
|
||||
#endregion.
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bounding rectangle of the item, including all subitems
|
||||
/// </summary>
|
||||
new public Rectangle Bounds {
|
||||
get {
|
||||
try {
|
||||
return base.Bounds;
|
||||
}
|
||||
catch (System.ArgumentException) {
|
||||
// If the item is part of a collapsed group, Bounds will throw an exception
|
||||
return Rectangle.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets how many pixels will be left blank around each cell of this item
|
||||
/// </summary>
|
||||
/// <remarks>This setting only takes effect when the control is owner drawn.</remarks>
|
||||
public Rectangle? CellPadding {
|
||||
get { return this.cellPadding; }
|
||||
set { this.cellPadding = value; }
|
||||
}
|
||||
private Rectangle? cellPadding;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets how the cells of this item will be vertically aligned
|
||||
/// </summary>
|
||||
/// <remarks>This setting only takes effect when the control is owner drawn.</remarks>
|
||||
public StringAlignment? CellVerticalAlignment {
|
||||
get { return this.cellVerticalAlignment; }
|
||||
set { this.cellVerticalAlignment = value; }
|
||||
}
|
||||
private StringAlignment? cellVerticalAlignment;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the checkedness of this item.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Virtual lists don't handle checkboxes well, so we have to intercept attempts to change them
|
||||
/// through the items, and change them into something that will work.
|
||||
/// Unfortunately, this won't work if this property is set through the base class, since
|
||||
/// the property is not declared as virtual.
|
||||
/// </remarks>
|
||||
new public bool Checked {
|
||||
get {
|
||||
return base.Checked;
|
||||
}
|
||||
set {
|
||||
if (this.Checked != value) {
|
||||
if (value)
|
||||
((ObjectListView)this.ListView).CheckObject(this.RowObject);
|
||||
else
|
||||
((ObjectListView)this.ListView).UncheckObject(this.RowObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable tri-state checkbox.
|
||||
/// </summary>
|
||||
/// <remarks>.NET's Checked property was not built to handle tri-state checkboxes,
|
||||
/// and will return True for both Checked and Indeterminate states.</remarks>
|
||||
public CheckState CheckState {
|
||||
get {
|
||||
switch (this.StateImageIndex) {
|
||||
case 0:
|
||||
return System.Windows.Forms.CheckState.Unchecked;
|
||||
case 1:
|
||||
return System.Windows.Forms.CheckState.Checked;
|
||||
case 2:
|
||||
return System.Windows.Forms.CheckState.Indeterminate;
|
||||
default:
|
||||
return System.Windows.Forms.CheckState.Unchecked;
|
||||
}
|
||||
}
|
||||
set {
|
||||
switch (value) {
|
||||
case System.Windows.Forms.CheckState.Unchecked:
|
||||
this.StateImageIndex = 0;
|
||||
break;
|
||||
case System.Windows.Forms.CheckState.Checked:
|
||||
this.StateImageIndex = 1;
|
||||
break;
|
||||
case System.Windows.Forms.CheckState.Indeterminate:
|
||||
this.StateImageIndex = 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets if this item has any decorations set for it.
|
||||
/// </summary>
|
||||
public bool HasDecoration {
|
||||
get {
|
||||
return this.decorations != null && this.decorations.Count > 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the decoration that will be drawn over this item
|
||||
/// </summary>
|
||||
/// <remarks>Setting this replaces all other decorations</remarks>
|
||||
public IDecoration Decoration {
|
||||
get {
|
||||
if (this.HasDecoration)
|
||||
return this.Decorations[0];
|
||||
else
|
||||
return null;
|
||||
}
|
||||
set {
|
||||
this.Decorations.Clear();
|
||||
if (value != null)
|
||||
this.Decorations.Add(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of decorations that will be drawn over this item
|
||||
/// </summary>
|
||||
public IList<IDecoration> Decorations {
|
||||
get {
|
||||
if (this.decorations == null)
|
||||
this.decorations = new List<IDecoration>();
|
||||
return this.decorations;
|
||||
}
|
||||
}
|
||||
private IList<IDecoration> decorations;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether or not this row can be selected and activated
|
||||
/// </summary>
|
||||
public bool Enabled
|
||||
{
|
||||
get { return this.enabled; }
|
||||
internal set { this.enabled = value; }
|
||||
}
|
||||
private bool enabled;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether any cell on this item is showing a hyperlink
|
||||
/// </summary>
|
||||
public bool HasAnyHyperlinks {
|
||||
get {
|
||||
foreach (OLVListSubItem subItem in this.SubItems) {
|
||||
if (!String.IsNullOrEmpty(subItem.Url))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get or set the image that should be shown against this item
|
||||
/// </summary>
|
||||
/// <remarks><para>This can be an Image, a string or an int. A string or an int will
|
||||
/// be used as an index into the small image list.</para></remarks>
|
||||
public Object ImageSelector {
|
||||
get { return imageSelector; }
|
||||
set {
|
||||
imageSelector = value;
|
||||
if (value is Int32)
|
||||
this.ImageIndex = (Int32)value;
|
||||
else if (value is String)
|
||||
this.ImageKey = (String)value;
|
||||
else
|
||||
this.ImageIndex = -1;
|
||||
}
|
||||
}
|
||||
private Object imageSelector;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the the model object that is source of the data for this list item.
|
||||
/// </summary>
|
||||
public object RowObject {
|
||||
get { return rowObject; }
|
||||
set { rowObject = value; }
|
||||
}
|
||||
private object rowObject;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color that will be used for this row's background when it is selected and
|
||||
/// the control is focused.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>To work reliably, this property must be set during a FormatRow event.</para>
|
||||
/// <para>
|
||||
/// If this is not set, the normal selection BackColor will be used.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public Color? SelectedBackColor {
|
||||
get { return this.selectedBackColor; }
|
||||
set { this.selectedBackColor = value; }
|
||||
}
|
||||
private Color? selectedBackColor;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color that will be used for this row's foreground when it is selected and
|
||||
/// the control is focused.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>To work reliably, this property must be set during a FormatRow event.</para>
|
||||
/// <para>
|
||||
/// If this is not set, the normal selection ForeColor will be used.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public Color? SelectedForeColor
|
||||
{
|
||||
get { return this.selectedForeColor; }
|
||||
set { this.selectedForeColor = value; }
|
||||
}
|
||||
private Color? selectedForeColor;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Accessing
|
||||
|
||||
/// <summary>
|
||||
/// Return the sub item at the given index
|
||||
/// </summary>
|
||||
/// <param name="index">Index of the subitem to be returned</param>
|
||||
/// <returns>An OLVListSubItem</returns>
|
||||
public virtual OLVListSubItem GetSubItem(int index) {
|
||||
if (index >= 0 && index < this.SubItems.Count)
|
||||
return (OLVListSubItem)this.SubItems[index];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Return bounds of the given subitem
|
||||
/// </summary>
|
||||
/// <remarks>This correctly calculates the bounds even for column 0.</remarks>
|
||||
public virtual Rectangle GetSubItemBounds(int subItemIndex) {
|
||||
if (subItemIndex == 0) {
|
||||
Rectangle r = this.Bounds;
|
||||
Point sides = NativeMethods.GetScrolledColumnSides(this.ListView, subItemIndex);
|
||||
r.X = sides.X + 1;
|
||||
r.Width = sides.Y - sides.X;
|
||||
return r;
|
||||
}
|
||||
|
||||
OLVListSubItem subItem = this.GetSubItem(subItemIndex);
|
||||
return subItem == null ? new Rectangle() : subItem.Bounds;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
173
ObjectListView/Implementation/OLVListSubItem.cs
Normal file
173
ObjectListView/Implementation/OLVListSubItem.cs
Normal file
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* OLVListSubItem - A single cell in an ObjectListView
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 31-March-2011 5:53 pm
|
||||
*
|
||||
* Change log:
|
||||
* 2011-03-31 JPP - Split into its own file
|
||||
*
|
||||
* Copyright (C) 2011-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
/// <summary>
|
||||
/// A ListViewSubItem that knows which image should be drawn against it.
|
||||
/// </summary>
|
||||
[Browsable(false)]
|
||||
public class OLVListSubItem : ListViewItem.ListViewSubItem {
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Create a OLVListSubItem
|
||||
/// </summary>
|
||||
public OLVListSubItem() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a OLVListSubItem that shows the given string and image
|
||||
/// </summary>
|
||||
public OLVListSubItem(object modelValue, string text, Object image) {
|
||||
this.ModelValue = modelValue;
|
||||
this.Text = text;
|
||||
this.ImageSelector = image;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets how many pixels will be left blank around this cell
|
||||
/// </summary>
|
||||
/// <remarks>This setting only takes effect when the control is owner drawn.</remarks>
|
||||
public Rectangle? CellPadding {
|
||||
get { return this.cellPadding; }
|
||||
set { this.cellPadding = value; }
|
||||
}
|
||||
private Rectangle? cellPadding;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets how this cell will be vertically aligned
|
||||
/// </summary>
|
||||
/// <remarks>This setting only takes effect when the control is owner drawn.</remarks>
|
||||
public StringAlignment? CellVerticalAlignment {
|
||||
get { return this.cellVerticalAlignment; }
|
||||
set { this.cellVerticalAlignment = value; }
|
||||
}
|
||||
private StringAlignment? cellVerticalAlignment;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the model value is being displayed by this subitem.
|
||||
/// </summary>
|
||||
public object ModelValue
|
||||
{
|
||||
get { return modelValue; }
|
||||
private set { modelValue = value; }
|
||||
}
|
||||
private object modelValue;
|
||||
|
||||
/// <summary>
|
||||
/// Gets if this subitem has any decorations set for it.
|
||||
/// </summary>
|
||||
public bool HasDecoration {
|
||||
get {
|
||||
return this.decorations != null && this.decorations.Count > 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the decoration that will be drawn over this item
|
||||
/// </summary>
|
||||
/// <remarks>Setting this replaces all other decorations</remarks>
|
||||
public IDecoration Decoration {
|
||||
get {
|
||||
return this.HasDecoration ? this.Decorations[0] : null;
|
||||
}
|
||||
set {
|
||||
this.Decorations.Clear();
|
||||
if (value != null)
|
||||
this.Decorations.Add(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of decorations that will be drawn over this item
|
||||
/// </summary>
|
||||
public IList<IDecoration> Decorations {
|
||||
get {
|
||||
if (this.decorations == null)
|
||||
this.decorations = new List<IDecoration>();
|
||||
return this.decorations;
|
||||
}
|
||||
}
|
||||
private IList<IDecoration> decorations;
|
||||
|
||||
/// <summary>
|
||||
/// Get or set the image that should be shown against this item
|
||||
/// </summary>
|
||||
/// <remarks><para>This can be an Image, a string or an int. A string or an int will
|
||||
/// be used as an index into the small image list.</para></remarks>
|
||||
public Object ImageSelector {
|
||||
get { return imageSelector; }
|
||||
set { imageSelector = value; }
|
||||
}
|
||||
private Object imageSelector;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the url that should be invoked when this subitem is clicked
|
||||
/// </summary>
|
||||
public string Url
|
||||
{
|
||||
get { return this.url; }
|
||||
set { this.url = value; }
|
||||
}
|
||||
private string url;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether this cell is selected
|
||||
/// </summary>
|
||||
public bool Selected
|
||||
{
|
||||
get { return this.selected; }
|
||||
set { this.selected = value; }
|
||||
}
|
||||
private bool selected;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation Properties
|
||||
|
||||
/// <summary>
|
||||
/// Return the state of the animatation of the image on this subitem.
|
||||
/// Null means there is either no image, or it is not an animation
|
||||
/// </summary>
|
||||
internal ImageRenderer.AnimationState AnimationState;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
||||
388
ObjectListView/Implementation/OlvListViewHitTestInfo.cs
Normal file
388
ObjectListView/Implementation/OlvListViewHitTestInfo.cs
Normal file
@@ -0,0 +1,388 @@
|
||||
/*
|
||||
* OlvListViewHitTestInfo - All information gathered during a OlvHitTest() operation
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 31-March-2011 5:53 pm
|
||||
*
|
||||
* Change log:
|
||||
* 2011-03-31 JPP - Split into its own file
|
||||
*
|
||||
* Copyright (C) 2011-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace BrightIdeasSoftware {
|
||||
|
||||
/// <summary>
|
||||
/// An indication of where a hit was within ObjectListView cell
|
||||
/// </summary>
|
||||
public enum HitTestLocation {
|
||||
/// <summary>
|
||||
/// Nowhere
|
||||
/// </summary>
|
||||
Nothing,
|
||||
|
||||
/// <summary>
|
||||
/// On the text
|
||||
/// </summary>
|
||||
Text,
|
||||
|
||||
/// <summary>
|
||||
/// On the image
|
||||
/// </summary>
|
||||
Image,
|
||||
|
||||
/// <summary>
|
||||
/// On the checkbox
|
||||
/// </summary>
|
||||
CheckBox,
|
||||
|
||||
/// <summary>
|
||||
/// On the expand button (TreeListView)
|
||||
/// </summary>
|
||||
ExpandButton,
|
||||
|
||||
/// <summary>
|
||||
/// in a button (cell must have ButtonRenderer)
|
||||
/// </summary>
|
||||
Button,
|
||||
|
||||
/// <summary>
|
||||
/// in the cell but not in any more specific location
|
||||
/// </summary>
|
||||
InCell,
|
||||
|
||||
/// <summary>
|
||||
/// UserDefined location1 (used for custom renderers)
|
||||
/// </summary>
|
||||
UserDefined,
|
||||
|
||||
/// <summary>
|
||||
/// On the expand/collapse widget of the group
|
||||
/// </summary>
|
||||
GroupExpander,
|
||||
|
||||
/// <summary>
|
||||
/// Somewhere on a group
|
||||
/// </summary>
|
||||
Group,
|
||||
|
||||
/// <summary>
|
||||
/// Somewhere in a column header
|
||||
/// </summary>
|
||||
Header,
|
||||
|
||||
/// <summary>
|
||||
/// Somewhere in a column header checkbox
|
||||
/// </summary>
|
||||
HeaderCheckBox,
|
||||
|
||||
/// <summary>
|
||||
/// Somewhere in a header divider
|
||||
/// </summary>
|
||||
HeaderDivider,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A collection of ListViewHitTest constants
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum HitTestLocationEx {
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
LVHT_NOWHERE = 0x00000001,
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
LVHT_ONITEMICON = 0x00000002,
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
LVHT_ONITEMLABEL = 0x00000004,
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
LVHT_ONITEMSTATEICON = 0x00000008,
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
LVHT_ONITEM = (LVHT_ONITEMICON | LVHT_ONITEMLABEL | LVHT_ONITEMSTATEICON),
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
LVHT_ABOVE = 0x00000008,
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
LVHT_BELOW = 0x00000010,
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
LVHT_TORIGHT = 0x00000020,
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
LVHT_TOLEFT = 0x00000040,
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
LVHT_EX_GROUP_HEADER = 0x10000000,
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
LVHT_EX_GROUP_FOOTER = 0x20000000,
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
LVHT_EX_GROUP_COLLAPSE = 0x40000000,
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
LVHT_EX_GROUP_BACKGROUND = -2147483648, // 0x80000000
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
LVHT_EX_GROUP_STATEICON = 0x01000000,
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
LVHT_EX_GROUP_SUBSETLINK = 0x02000000,
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
LVHT_EX_GROUP = (LVHT_EX_GROUP_BACKGROUND | LVHT_EX_GROUP_COLLAPSE | LVHT_EX_GROUP_FOOTER | LVHT_EX_GROUP_HEADER | LVHT_EX_GROUP_STATEICON | LVHT_EX_GROUP_SUBSETLINK),
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
LVHT_EX_GROUP_MINUS_FOOTER_AND_BKGRD = (LVHT_EX_GROUP_COLLAPSE | LVHT_EX_GROUP_HEADER | LVHT_EX_GROUP_STATEICON | LVHT_EX_GROUP_SUBSETLINK),
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
LVHT_EX_ONCONTENTS = 0x04000000, // On item AND not on the background
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
LVHT_EX_FOOTER = 0x08000000,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instances of this class encapsulate the information gathered during a OlvHitTest()
|
||||
/// operation.
|
||||
/// </summary>
|
||||
/// <remarks>Custom renderers can use HitTestLocation.UserDefined and the UserData
|
||||
/// object to store more specific locations for use during event handlers.</remarks>
|
||||
public class OlvListViewHitTestInfo {
|
||||
|
||||
/// <summary>
|
||||
/// Create a OlvListViewHitTestInfo
|
||||
/// </summary>
|
||||
public OlvListViewHitTestInfo(OLVListItem olvListItem, OLVListSubItem subItem, int flags, OLVGroup group, int iColumn)
|
||||
{
|
||||
this.item = olvListItem;
|
||||
this.subItem = subItem;
|
||||
this.location = ConvertNativeFlagsToDotNetLocation(olvListItem, flags);
|
||||
this.HitTestLocationEx = (HitTestLocationEx)flags;
|
||||
this.Group = group;
|
||||
this.ColumnIndex = iColumn;
|
||||
this.ListView = olvListItem == null ? null : (ObjectListView)olvListItem.ListView;
|
||||
|
||||
switch (location) {
|
||||
case ListViewHitTestLocations.StateImage:
|
||||
this.HitTestLocation = HitTestLocation.CheckBox;
|
||||
break;
|
||||
case ListViewHitTestLocations.Image:
|
||||
this.HitTestLocation = HitTestLocation.Image;
|
||||
break;
|
||||
case ListViewHitTestLocations.Label:
|
||||
this.HitTestLocation = HitTestLocation.Text;
|
||||
break;
|
||||
default:
|
||||
if ((this.HitTestLocationEx & HitTestLocationEx.LVHT_EX_GROUP_COLLAPSE) == HitTestLocationEx.LVHT_EX_GROUP_COLLAPSE)
|
||||
this.HitTestLocation = HitTestLocation.GroupExpander;
|
||||
else if ((this.HitTestLocationEx & HitTestLocationEx.LVHT_EX_GROUP_MINUS_FOOTER_AND_BKGRD) != 0)
|
||||
this.HitTestLocation = HitTestLocation.Group;
|
||||
else
|
||||
this.HitTestLocation = HitTestLocation.Nothing;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a OlvListViewHitTestInfo when the header was hit
|
||||
/// </summary>
|
||||
public OlvListViewHitTestInfo(ObjectListView olv, int iColumn, bool isOverCheckBox, int iDivider) {
|
||||
this.ListView = olv;
|
||||
this.ColumnIndex = iColumn;
|
||||
this.HeaderDividerIndex = iDivider;
|
||||
this.HitTestLocation = isOverCheckBox ? HitTestLocation.HeaderCheckBox : (iDivider < 0 ? HitTestLocation.Header : HitTestLocation.HeaderDivider);
|
||||
}
|
||||
|
||||
private static ListViewHitTestLocations ConvertNativeFlagsToDotNetLocation(OLVListItem hitItem, int flags)
|
||||
{
|
||||
// Untangle base .NET behaviour.
|
||||
|
||||
// In Windows SDK, the value 8 can have two meanings here: LVHT_ONITEMSTATEICON or LVHT_ABOVE.
|
||||
// .NET changes these to be:
|
||||
// - LVHT_ABOVE becomes ListViewHitTestLocations.AboveClientArea (which is 0x100).
|
||||
// - LVHT_ONITEMSTATEICON becomes ListViewHitTestLocations.StateImage (which is 0x200).
|
||||
// So, if we see the 8 bit set in flags, we change that to either a state image hit
|
||||
// (if we hit an item) or to AboveClientAream if nothing was hit.
|
||||
|
||||
if ((8 & flags) == 8)
|
||||
return (ListViewHitTestLocations)(0xf7 & flags | (hitItem == null ? 0x100 : 0x200));
|
||||
|
||||
// Mask off the LVHT_EX_XXXX values since ListViewHitTestLocations doesn't have them
|
||||
return (ListViewHitTestLocations)(flags & 0xffff);
|
||||
}
|
||||
|
||||
#region Public fields
|
||||
|
||||
/// <summary>
|
||||
/// Where is the hit location?
|
||||
/// </summary>
|
||||
public HitTestLocation HitTestLocation;
|
||||
|
||||
/// <summary>
|
||||
/// Where is the hit location?
|
||||
/// </summary>
|
||||
public HitTestLocationEx HitTestLocationEx;
|
||||
|
||||
/// <summary>
|
||||
/// Which group was hit?
|
||||
/// </summary>
|
||||
public OLVGroup Group;
|
||||
|
||||
/// <summary>
|
||||
/// Custom renderers can use this information to supply more details about the hit location
|
||||
/// </summary>
|
||||
public Object UserData;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public read-only properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the item that was hit
|
||||
/// </summary>
|
||||
public OLVListItem Item {
|
||||
get { return item; }
|
||||
internal set { item = value; }
|
||||
}
|
||||
private OLVListItem item;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the subitem that was hit
|
||||
/// </summary>
|
||||
public OLVListSubItem SubItem {
|
||||
get { return subItem; }
|
||||
internal set { subItem = value; }
|
||||
}
|
||||
private OLVListSubItem subItem;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the part of the subitem that was hit
|
||||
/// </summary>
|
||||
public ListViewHitTestLocations Location {
|
||||
get { return location; }
|
||||
internal set { location = value; }
|
||||
}
|
||||
private ListViewHitTestLocations location;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ObjectListView that was tested
|
||||
/// </summary>
|
||||
public ObjectListView ListView {
|
||||
get { return listView; }
|
||||
internal set { listView = value; }
|
||||
}
|
||||
private ObjectListView listView;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the model object that was hit
|
||||
/// </summary>
|
||||
public Object RowObject {
|
||||
get {
|
||||
return this.Item == null ? null : this.Item.RowObject;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the row under the hit point or -1
|
||||
/// </summary>
|
||||
public int RowIndex {
|
||||
get { return this.Item == null ? -1 : this.Item.Index; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the column under the hit point
|
||||
/// </summary>
|
||||
public int ColumnIndex {
|
||||
get { return columnIndex; }
|
||||
internal set { columnIndex = value; }
|
||||
}
|
||||
private int columnIndex;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the header divider
|
||||
/// </summary>
|
||||
public int HeaderDividerIndex {
|
||||
get { return headerDividerIndex; }
|
||||
internal set { headerDividerIndex = value; }
|
||||
}
|
||||
private int headerDividerIndex = -1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the column that was hit
|
||||
/// </summary>
|
||||
public OLVColumn Column {
|
||||
get {
|
||||
int index = this.ColumnIndex;
|
||||
return index < 0 || this.ListView == null ? null : this.ListView.GetColumn(index);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string that represents the current object.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A string that represents the current object.
|
||||
/// </returns>
|
||||
/// <filterpriority>2</filterpriority>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("HitTestLocation: {0}, HitTestLocationEx: {1}, Item: {2}, SubItem: {3}, Location: {4}, Group: {5}, ColumnIndex: {6}",
|
||||
this.HitTestLocation, this.HitTestLocationEx, this.item, this.subItem, this.location, this.Group, this.ColumnIndex);
|
||||
}
|
||||
|
||||
internal class HeaderHitTestInfo
|
||||
{
|
||||
public int ColumnIndex;
|
||||
public bool IsOverCheckBox;
|
||||
public int OverDividerIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
262
ObjectListView/Implementation/TreeDataSourceAdapter.cs
Normal file
262
ObjectListView/Implementation/TreeDataSourceAdapter.cs
Normal file
@@ -0,0 +1,262 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace BrightIdeasSoftware
|
||||
{
|
||||
/// <summary>
|
||||
/// A TreeDataSourceAdapter knows how to build a tree structure from a binding list.
|
||||
/// </summary>
|
||||
/// <remarks>To build a tree</remarks>
|
||||
public class TreeDataSourceAdapter : DataSourceAdapter
|
||||
{
|
||||
#region Life and death
|
||||
|
||||
/// <summary>
|
||||
/// Create a data source adaptor that knows how to build a tree structure
|
||||
/// </summary>
|
||||
/// <param name="tlv"></param>
|
||||
public TreeDataSourceAdapter(DataTreeListView tlv)
|
||||
: base(tlv) {
|
||||
this.treeListView = tlv;
|
||||
this.treeListView.CanExpandGetter = delegate(object model) { return this.CalculateHasChildren(model); };
|
||||
this.treeListView.ChildrenGetter = delegate(object model) { return this.CalculateChildren(model); };
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the property/column that uniquely identifies each row.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The value contained by this column must be unique across all rows
|
||||
/// in the data source. Odd and unpredictable things will happen if two
|
||||
/// rows have the same id.
|
||||
/// </para>
|
||||
/// <para>Null cannot be a valid key value.</para>
|
||||
/// </remarks>
|
||||
public virtual string KeyAspectName {
|
||||
get { return keyAspectName; }
|
||||
set {
|
||||
if (keyAspectName == value)
|
||||
return;
|
||||
keyAspectName = value;
|
||||
this.keyMunger = new Munger(this.KeyAspectName);
|
||||
this.InitializeDataSource();
|
||||
}
|
||||
}
|
||||
private string keyAspectName;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the property/column that contains the key of
|
||||
/// the parent of a row.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The test condition for deciding if one row is the parent of another is functionally
|
||||
/// equivilent to this:
|
||||
/// <code>
|
||||
/// Object.Equals(candidateParentRow[this.KeyAspectName], row[this.ParentKeyAspectName])
|
||||
/// </code>
|
||||
/// </para>
|
||||
/// <para>Unlike key value, parent keys can be null but a null parent key can only be used
|
||||
/// to identify root objects.</para>
|
||||
/// </remarks>
|
||||
public virtual string ParentKeyAspectName {
|
||||
get { return parentKeyAspectName; }
|
||||
set {
|
||||
if (parentKeyAspectName == value)
|
||||
return;
|
||||
parentKeyAspectName = value;
|
||||
this.parentKeyMunger = new Munger(this.ParentKeyAspectName);
|
||||
this.InitializeDataSource();
|
||||
}
|
||||
}
|
||||
private string parentKeyAspectName;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value that identifies a row as a root object.
|
||||
/// When the ParentKey of a row equals the RootKeyValue, that row will
|
||||
/// be treated as root of the TreeListView.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The test condition for deciding a root object is functionally
|
||||
/// equivilent to this:
|
||||
/// <code>
|
||||
/// Object.Equals(candidateRow[this.ParentKeyAspectName], this.RootKeyValue)
|
||||
/// </code>
|
||||
/// </para>
|
||||
/// <para>The RootKeyValue can be null.</para>
|
||||
/// </remarks>
|
||||
public virtual object RootKeyValue {
|
||||
get { return rootKeyValue; }
|
||||
set {
|
||||
if (Equals(rootKeyValue, value))
|
||||
return;
|
||||
rootKeyValue = value;
|
||||
this.InitializeDataSource();
|
||||
}
|
||||
}
|
||||
private object rootKeyValue;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether or not the key columns (id and parent id) should
|
||||
/// be shown to the user.
|
||||
/// </summary>
|
||||
/// <remarks>This must be set before the DataSource is set. It has no effect
|
||||
/// afterwards.</remarks>
|
||||
public virtual bool ShowKeyColumns {
|
||||
get { return showKeyColumns; }
|
||||
set { showKeyColumns = value; }
|
||||
}
|
||||
private bool showKeyColumns = true;
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the DataTreeListView that is being managed
|
||||
/// </summary>
|
||||
protected DataTreeListView TreeListView {
|
||||
get { return treeListView; }
|
||||
}
|
||||
private readonly DataTreeListView treeListView;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
protected override void InitializeDataSource() {
|
||||
base.InitializeDataSource();
|
||||
this.TreeListView.RebuildAll(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
protected override void SetListContents() {
|
||||
this.TreeListView.Roots = this.CalculateRoots();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="property"></param>
|
||||
/// <returns></returns>
|
||||
protected override bool ShouldCreateColumn(PropertyDescriptor property) {
|
||||
// If the property is a key column, and we aren't supposed to show keys, don't show it
|
||||
if (!this.ShowKeyColumns && (property.Name == this.KeyAspectName || property.Name == this.ParentKeyAspectName))
|
||||
return false;
|
||||
|
||||
return base.ShouldCreateColumn(property);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="e"></param>
|
||||
protected override void HandleListChangedItemChanged(System.ComponentModel.ListChangedEventArgs e) {
|
||||
// If the id or the parent id of a row changes, we just rebuild everything.
|
||||
// We can't do anything more specific. We don't know what the previous values, so we can't
|
||||
// tell the previous parent to refresh itself. If the id itself has changed, things that used
|
||||
// to be children will no longer be children. Just rebuild everything.
|
||||
// It seems PropertyDescriptor is only filled in .NET 4 :(
|
||||
if (e.PropertyDescriptor != null &&
|
||||
(e.PropertyDescriptor.Name == this.KeyAspectName ||
|
||||
e.PropertyDescriptor.Name == this.ParentKeyAspectName))
|
||||
this.InitializeDataSource();
|
||||
else
|
||||
base.HandleListChangedItemChanged(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
protected override void ChangePosition(int index) {
|
||||
// We can't use our base method directly, since the normal position management
|
||||
// doesn't know about our tree structure. They treat our dataset as a flat list
|
||||
// but we have a collapsable structure. This means that the 5'th row to them
|
||||
// may not even be visible to us
|
||||
|
||||
// To display the n'th row, we have to make sure that all its ancestors
|
||||
// are expanded. Then we will be able to select it.
|
||||
object model = this.CurrencyManager.List[index];
|
||||
object parent = this.CalculateParent(model);
|
||||
while (parent != null && !this.TreeListView.IsExpanded(parent)) {
|
||||
this.TreeListView.Expand(parent);
|
||||
parent = this.CalculateParent(parent);
|
||||
}
|
||||
|
||||
base.ChangePosition(index);
|
||||
}
|
||||
|
||||
private IEnumerable CalculateRoots() {
|
||||
foreach (object x in this.CurrencyManager.List) {
|
||||
object parentKey = this.GetParentValue(x);
|
||||
if (Object.Equals(this.RootKeyValue, parentKey))
|
||||
yield return x;
|
||||
}
|
||||
}
|
||||
|
||||
private bool CalculateHasChildren(object model) {
|
||||
object keyValue = this.GetKeyValue(model);
|
||||
if (keyValue == null)
|
||||
return false;
|
||||
|
||||
foreach (object x in this.CurrencyManager.List) {
|
||||
object parentKey = this.GetParentValue(x);
|
||||
if (Object.Equals(keyValue, parentKey))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private IEnumerable CalculateChildren(object model) {
|
||||
object keyValue = this.GetKeyValue(model);
|
||||
if (keyValue != null) {
|
||||
foreach (object x in this.CurrencyManager.List) {
|
||||
object parentKey = this.GetParentValue(x);
|
||||
if (Object.Equals(keyValue, parentKey))
|
||||
yield return x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private object CalculateParent(object model) {
|
||||
object parentValue = this.GetParentValue(model);
|
||||
if (parentValue == null)
|
||||
return null;
|
||||
|
||||
foreach (object x in this.CurrencyManager.List) {
|
||||
object key = this.GetKeyValue(x);
|
||||
if (Object.Equals(parentValue, key))
|
||||
return x;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private object GetKeyValue(object model) {
|
||||
return this.keyMunger == null ? null : this.keyMunger.GetValue(model);
|
||||
}
|
||||
|
||||
private object GetParentValue(object model) {
|
||||
return this.parentKeyMunger == null ? null : this.parentKeyMunger.GetValue(model);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private Munger keyMunger;
|
||||
private Munger parentKeyMunger;
|
||||
}
|
||||
}
|
||||
355
ObjectListView/Implementation/VirtualGroups.cs
Normal file
355
ObjectListView/Implementation/VirtualGroups.cs
Normal file
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
* Virtual groups - Classes and interfaces needed to implement virtual groups
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 28/08/2009 11:10am
|
||||
*
|
||||
* Change log:
|
||||
* 2011-02-21 JPP - Correctly honor group comparer and collapsible groups settings
|
||||
* v2.3
|
||||
* 2009-08-28 JPP - Initial version
|
||||
*
|
||||
* To do:
|
||||
*
|
||||
* Copyright (C) 2009-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Forms;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace BrightIdeasSoftware
|
||||
{
|
||||
/// <summary>
|
||||
/// A IVirtualGroups is the interface that a virtual list must implement to support virtual groups
|
||||
/// </summary>
|
||||
public interface IVirtualGroups
|
||||
{
|
||||
/// <summary>
|
||||
/// Return the list of groups that should be shown according to the given parameters
|
||||
/// </summary>
|
||||
/// <param name="parameters"></param>
|
||||
/// <returns></returns>
|
||||
IList<OLVGroup> GetGroups(GroupingParameters parameters);
|
||||
|
||||
/// <summary>
|
||||
/// Return the index of the item that appears at the given position within the given group.
|
||||
/// </summary>
|
||||
/// <param name="group"></param>
|
||||
/// <param name="indexWithinGroup"></param>
|
||||
/// <returns></returns>
|
||||
int GetGroupMember(OLVGroup group, int indexWithinGroup);
|
||||
|
||||
/// <summary>
|
||||
/// Return the index of the group to which the given item belongs
|
||||
/// </summary>
|
||||
/// <param name="itemIndex"></param>
|
||||
/// <returns></returns>
|
||||
int GetGroup(int itemIndex);
|
||||
|
||||
/// <summary>
|
||||
/// Return the index at which the given item is shown in the given group
|
||||
/// </summary>
|
||||
/// <param name="group"></param>
|
||||
/// <param name="itemIndex"></param>
|
||||
/// <returns></returns>
|
||||
int GetIndexWithinGroup(OLVGroup group, int itemIndex);
|
||||
|
||||
/// <summary>
|
||||
/// A hint that the given range of items are going to be required
|
||||
/// </summary>
|
||||
/// <param name="fromGroupIndex"></param>
|
||||
/// <param name="fromIndex"></param>
|
||||
/// <param name="toGroupIndex"></param>
|
||||
/// <param name="toIndex"></param>
|
||||
void CacheHint(int fromGroupIndex, int fromIndex, int toGroupIndex, int toIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is a safe, do nothing implementation of a grouping strategy
|
||||
/// </summary>
|
||||
public class AbstractVirtualGroups : IVirtualGroups
|
||||
{
|
||||
/// <summary>
|
||||
/// Return the list of groups that should be shown according to the given parameters
|
||||
/// </summary>
|
||||
/// <param name="parameters"></param>
|
||||
/// <returns></returns>
|
||||
public virtual IList<OLVGroup> GetGroups(GroupingParameters parameters) {
|
||||
return new List<OLVGroup>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the index of the item that appears at the given position within the given group.
|
||||
/// </summary>
|
||||
/// <param name="group"></param>
|
||||
/// <param name="indexWithinGroup"></param>
|
||||
/// <returns></returns>
|
||||
public virtual int GetGroupMember(OLVGroup group, int indexWithinGroup) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the index of the group to which the given item belongs
|
||||
/// </summary>
|
||||
/// <param name="itemIndex"></param>
|
||||
/// <returns></returns>
|
||||
public virtual int GetGroup(int itemIndex) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the index at which the given item is shown in the given group
|
||||
/// </summary>
|
||||
/// <param name="group"></param>
|
||||
/// <param name="itemIndex"></param>
|
||||
/// <returns></returns>
|
||||
public virtual int GetIndexWithinGroup(OLVGroup group, int itemIndex) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A hint that the given range of items are going to be required
|
||||
/// </summary>
|
||||
/// <param name="fromGroupIndex"></param>
|
||||
/// <param name="fromIndex"></param>
|
||||
/// <param name="toGroupIndex"></param>
|
||||
/// <param name="toIndex"></param>
|
||||
public virtual void CacheHint(int fromGroupIndex, int fromIndex, int toGroupIndex, int toIndex) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Provides grouping functionality to a FastObjectListView
|
||||
/// </summary>
|
||||
public class FastListGroupingStrategy : AbstractVirtualGroups
|
||||
{
|
||||
/// <summary>
|
||||
/// Create groups for FastListView
|
||||
/// </summary>
|
||||
/// <param name="parmameters"></param>
|
||||
/// <returns></returns>
|
||||
public override IList<OLVGroup> GetGroups(GroupingParameters parmameters) {
|
||||
|
||||
// There is a lot of overlap between this method and ObjectListView.MakeGroups()
|
||||
// Any changes made here may need to be reflected there
|
||||
|
||||
// This strategy can only be used on FastObjectListViews
|
||||
FastObjectListView folv = (FastObjectListView)parmameters.ListView;
|
||||
|
||||
// Separate the list view items into groups, using the group key as the descrimanent
|
||||
int objectCount = 0;
|
||||
NullableDictionary<object, List<object>> map = new NullableDictionary<object, List<object>>();
|
||||
foreach (object model in folv.FilteredObjects) {
|
||||
object key = parmameters.GroupByColumn.GetGroupKey(model);
|
||||
if (!map.ContainsKey(key))
|
||||
map[key] = new List<object>();
|
||||
map[key].Add(model);
|
||||
objectCount++;
|
||||
}
|
||||
|
||||
// Sort the items within each group
|
||||
// TODO: Give parameters a ModelComparer property
|
||||
OLVColumn primarySortColumn = parmameters.SortItemsByPrimaryColumn ? parmameters.ListView.GetColumn(0) : parmameters.PrimarySort;
|
||||
ModelObjectComparer sorter = new ModelObjectComparer(primarySortColumn, parmameters.PrimarySortOrder,
|
||||
parmameters.SecondarySort, parmameters.SecondarySortOrder);
|
||||
foreach (object key in map.Keys) {
|
||||
map[key].Sort(sorter);
|
||||
}
|
||||
|
||||
// Make a list of the required groups
|
||||
List<OLVGroup> groups = new List<OLVGroup>();
|
||||
foreach (object key in map.Keys) {
|
||||
string title = parmameters.GroupByColumn.ConvertGroupKeyToTitle(key);
|
||||
if (!String.IsNullOrEmpty(parmameters.TitleFormat)) {
|
||||
int count = map[key].Count;
|
||||
string format = (count == 1 ? parmameters.TitleSingularFormat : parmameters.TitleFormat);
|
||||
try {
|
||||
title = String.Format(format, title, count);
|
||||
} catch (FormatException) {
|
||||
title = "Invalid group format: " + format;
|
||||
}
|
||||
}
|
||||
OLVGroup lvg = new OLVGroup(title);
|
||||
lvg.Collapsible = folv.HasCollapsibleGroups;
|
||||
lvg.Key = key;
|
||||
lvg.SortValue = key as IComparable;
|
||||
lvg.Contents = map[key].ConvertAll<int>(delegate(object x) { return folv.IndexOf(x); });
|
||||
lvg.VirtualItemCount = map[key].Count;
|
||||
if (parmameters.GroupByColumn.GroupFormatter != null)
|
||||
parmameters.GroupByColumn.GroupFormatter(lvg, parmameters);
|
||||
groups.Add(lvg);
|
||||
}
|
||||
|
||||
// Sort the groups
|
||||
if (parmameters.GroupByOrder != SortOrder.None)
|
||||
groups.Sort(parmameters.GroupComparer ?? new OLVGroupComparer(parmameters.GroupByOrder));
|
||||
|
||||
// Build an array that remembers which group each item belongs to.
|
||||
this.indexToGroupMap = new List<int>(objectCount);
|
||||
this.indexToGroupMap.AddRange(new int[objectCount]);
|
||||
|
||||
for (int i = 0; i < groups.Count; i++) {
|
||||
OLVGroup group = groups[i];
|
||||
List<int> members = (List<int>)group.Contents;
|
||||
foreach (int j in members)
|
||||
this.indexToGroupMap[j] = i;
|
||||
}
|
||||
|
||||
return groups;
|
||||
}
|
||||
private List<int> indexToGroupMap;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="group"></param>
|
||||
/// <param name="indexWithinGroup"></param>
|
||||
/// <returns></returns>
|
||||
public override int GetGroupMember(OLVGroup group, int indexWithinGroup) {
|
||||
return (int)group.Contents[indexWithinGroup];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="itemIndex"></param>
|
||||
/// <returns></returns>
|
||||
public override int GetGroup(int itemIndex) {
|
||||
return this.indexToGroupMap[itemIndex];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="group"></param>
|
||||
/// <param name="itemIndex"></param>
|
||||
/// <returns></returns>
|
||||
public override int GetIndexWithinGroup(OLVGroup group, int itemIndex) {
|
||||
return group.Contents.IndexOf(itemIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This is the COM interface that a ListView must be given in order for groups in virtual lists to work.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This interface is NOT documented by MS. It was found on Greg Chapell's site. This means that there is
|
||||
/// no guarantee that it will work on future versions of Windows, nor continue to work on current ones.
|
||||
/// </remarks>
|
||||
[ComImport(),
|
||||
InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
|
||||
Guid("44C09D56-8D3B-419D-A462-7B956B105B47")]
|
||||
internal interface IOwnerDataCallback
|
||||
{
|
||||
/// <summary>
|
||||
/// Not sure what this does
|
||||
/// </summary>
|
||||
/// <param name="i"></param>
|
||||
/// <param name="pt"></param>
|
||||
void GetItemPosition(int i, out NativeMethods.POINT pt);
|
||||
|
||||
/// <summary>
|
||||
/// Not sure what this does
|
||||
/// </summary>
|
||||
/// <param name="t"></param>
|
||||
/// <param name="pt"></param>
|
||||
void SetItemPosition(int t, NativeMethods.POINT pt);
|
||||
|
||||
/// <summary>
|
||||
/// Get the index of the item that occurs at the n'th position of the indicated group.
|
||||
/// </summary>
|
||||
/// <param name="groupIndex">Index of the group</param>
|
||||
/// <param name="n">Index within the group</param>
|
||||
/// <param name="itemIndex">Index of the item within the whole list</param>
|
||||
void GetItemInGroup(int groupIndex, int n, out int itemIndex);
|
||||
|
||||
/// <summary>
|
||||
/// Get the index of the group to which the given item belongs
|
||||
/// </summary>
|
||||
/// <param name="itemIndex">Index of the item within the whole list</param>
|
||||
/// <param name="occurrenceCount">Which occurences of the item is wanted</param>
|
||||
/// <param name="groupIndex">Index of the group</param>
|
||||
void GetItemGroup(int itemIndex, int occurrenceCount, out int groupIndex);
|
||||
|
||||
/// <summary>
|
||||
/// Get the number of groups that contain the given item
|
||||
/// </summary>
|
||||
/// <param name="itemIndex">Index of the item within the whole list</param>
|
||||
/// <param name="occurrenceCount">How many groups does it occur within</param>
|
||||
void GetItemGroupCount(int itemIndex, out int occurrenceCount);
|
||||
|
||||
/// <summary>
|
||||
/// A hint to prepare any cache for the given range of requests
|
||||
/// </summary>
|
||||
/// <param name="i"></param>
|
||||
/// <param name="j"></param>
|
||||
void OnCacheHint(NativeMethods.LVITEMINDEX i, NativeMethods.LVITEMINDEX j);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A default implementation of the IOwnerDataCallback interface
|
||||
/// </summary>
|
||||
[Guid("6FC61F50-80E8-49b4-B200-3F38D3865ABD")]
|
||||
internal class OwnerDataCallbackImpl : IOwnerDataCallback
|
||||
{
|
||||
public OwnerDataCallbackImpl(VirtualObjectListView olv) {
|
||||
this.olv = olv;
|
||||
}
|
||||
VirtualObjectListView olv;
|
||||
|
||||
#region IOwnerDataCallback Members
|
||||
|
||||
public void GetItemPosition(int i, out NativeMethods.POINT pt) {
|
||||
//System.Diagnostics.Debug.WriteLine("GetItemPosition");
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void SetItemPosition(int t, NativeMethods.POINT pt) {
|
||||
//System.Diagnostics.Debug.WriteLine("SetItemPosition");
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void GetItemInGroup(int groupIndex, int n, out int itemIndex) {
|
||||
//System.Diagnostics.Debug.WriteLine(String.Format("-> GetItemInGroup({0}, {1})", groupIndex, n));
|
||||
itemIndex = this.olv.GroupingStrategy.GetGroupMember(this.olv.OLVGroups[groupIndex], n);
|
||||
//System.Diagnostics.Debug.WriteLine(String.Format("<- {0}", itemIndex));
|
||||
}
|
||||
|
||||
public void GetItemGroup(int itemIndex, int occurrenceCount, out int groupIndex) {
|
||||
//System.Diagnostics.Debug.WriteLine(String.Format("GetItemGroup({0}, {1})", itemIndex, occurrenceCount));
|
||||
groupIndex = this.olv.GroupingStrategy.GetGroup(itemIndex);
|
||||
//System.Diagnostics.Debug.WriteLine(String.Format("<- {0}", groupIndex));
|
||||
}
|
||||
|
||||
public void GetItemGroupCount(int itemIndex, out int occurrenceCount) {
|
||||
//System.Diagnostics.Debug.WriteLine(String.Format("GetItemGroupCount({0})", itemIndex));
|
||||
occurrenceCount = 1;
|
||||
}
|
||||
|
||||
public void OnCacheHint(NativeMethods.LVITEMINDEX from, NativeMethods.LVITEMINDEX to) {
|
||||
//System.Diagnostics.Debug.WriteLine(String.Format("OnCacheHint({0}, {1}, {2}, {3})", from.iGroup, from.iItem, to.iGroup, to.iItem));
|
||||
this.olv.GroupingStrategy.CacheHint(from.iGroup, from.iItem, to.iGroup, to.iItem);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
349
ObjectListView/Implementation/VirtualListDataSource.cs
Normal file
349
ObjectListView/Implementation/VirtualListDataSource.cs
Normal file
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
* VirtualListDataSource - Encapsulate how data is provided to a virtual list
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 28/08/2009 11:10am
|
||||
*
|
||||
* Change log:
|
||||
* v2.4
|
||||
* 2010-04-01 JPP - Added IFilterableDataSource
|
||||
* v2.3
|
||||
* 2009-08-28 JPP - Initial version (Separated from VirtualObjectListView.cs)
|
||||
*
|
||||
* To do:
|
||||
*
|
||||
* Copyright (C) 2009-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace BrightIdeasSoftware
|
||||
{
|
||||
/// <summary>
|
||||
/// A VirtualListDataSource is a complete manner to provide functionality to a virtual list.
|
||||
/// An object that implements this interface provides a VirtualObjectListView with all the
|
||||
/// information it needs to be fully functional.
|
||||
/// </summary>
|
||||
/// <remarks>Implementors must provide functioning implementations of at least GetObjectCount()
|
||||
/// and GetNthObject(), otherwise nothing will appear in the list.</remarks>
|
||||
public interface IVirtualListDataSource
|
||||
{
|
||||
/// <summary>
|
||||
/// Return the object that should be displayed at the n'th row.
|
||||
/// </summary>
|
||||
/// <param name="n">The index of the row whose object is to be returned.</param>
|
||||
/// <returns>The model object at the n'th row, or null if the fetching was unsuccessful.</returns>
|
||||
Object GetNthObject(int n);
|
||||
|
||||
/// <summary>
|
||||
/// Return the number of rows that should be visible in the virtual list
|
||||
/// </summary>
|
||||
/// <returns>The number of rows the list view should have.</returns>
|
||||
int GetObjectCount();
|
||||
|
||||
/// <summary>
|
||||
/// Get the index of the row that is showing the given model object
|
||||
/// </summary>
|
||||
/// <param name="model">The model object sought</param>
|
||||
/// <returns>The index of the row showing the model, or -1 if the object could not be found.</returns>
|
||||
int GetObjectIndex(Object model);
|
||||
|
||||
/// <summary>
|
||||
/// The ListView is about to request the given range of items. Do
|
||||
/// whatever caching seems appropriate.
|
||||
/// </summary>
|
||||
/// <param name="first"></param>
|
||||
/// <param name="last"></param>
|
||||
void PrepareCache(int first, int last);
|
||||
|
||||
/// <summary>
|
||||
/// Find the first row that "matches" the given text in the given range.
|
||||
/// </summary>
|
||||
/// <param name="value">The text typed by the user</param>
|
||||
/// <param name="first">Start searching from this index. This may be greater than the 'to' parameter,
|
||||
/// in which case the search should descend</param>
|
||||
/// <param name="last">Do not search beyond this index. This may be less than the 'from' parameter.</param>
|
||||
/// <param name="column">The column that should be considered when looking for a match.</param>
|
||||
/// <returns>Return the index of row that was matched, or -1 if no match was found</returns>
|
||||
int SearchText(string value, int first, int last, OLVColumn column);
|
||||
|
||||
/// <summary>
|
||||
/// Sort the model objects in the data source.
|
||||
/// </summary>
|
||||
/// <param name="column"></param>
|
||||
/// <param name="order"></param>
|
||||
void Sort(OLVColumn column, SortOrder order);
|
||||
|
||||
//-----------------------------------------------------------------------------------
|
||||
// Modification commands
|
||||
// THINK: Should we split these four into a separate interface?
|
||||
|
||||
/// <summary>
|
||||
/// Add the given collection of model objects to this control.
|
||||
/// </summary>
|
||||
/// <param name="modelObjects">A collection of model objects</param>
|
||||
void AddObjects(ICollection modelObjects);
|
||||
|
||||
/// <summary>
|
||||
/// Insert the given collection of model objects to this control at the position
|
||||
/// </summary>
|
||||
/// <param name="index">Index where the collection will be added</param>
|
||||
/// <param name="modelObjects">A collection of model objects</param>
|
||||
void InsertObjects(int index, ICollection modelObjects);
|
||||
|
||||
/// <summary>
|
||||
/// Remove all of the given objects from the control
|
||||
/// </summary>
|
||||
/// <param name="modelObjects">Collection of objects to be removed</param>
|
||||
void RemoveObjects(ICollection modelObjects);
|
||||
|
||||
/// <summary>
|
||||
/// Set the collection of objects that this control will show.
|
||||
/// </summary>
|
||||
/// <param name="collection"></param>
|
||||
void SetObjects(IEnumerable collection);
|
||||
|
||||
/// <summary>
|
||||
/// Update/replace the nth object with the given object
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="modelObject"></param>
|
||||
void UpdateObject(int index, object modelObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This extension allow virtual lists to filter their contents
|
||||
/// </summary>
|
||||
public interface IFilterableDataSource
|
||||
{
|
||||
/// <summary>
|
||||
/// All subsequent retrievals on this data source should be filtered
|
||||
/// through the given filters. null means no filtering of that kind.
|
||||
/// </summary>
|
||||
/// <param name="modelFilter"></param>
|
||||
/// <param name="listFilter"></param>
|
||||
void ApplyFilters(IModelFilter modelFilter, IListFilter listFilter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A do-nothing implementation of the VirtualListDataSource interface.
|
||||
/// </summary>
|
||||
public class AbstractVirtualListDataSource : IVirtualListDataSource, IFilterableDataSource
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an AbstractVirtualListDataSource
|
||||
/// </summary>
|
||||
/// <param name="listView"></param>
|
||||
public AbstractVirtualListDataSource(VirtualObjectListView listView) {
|
||||
this.listView = listView;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The list view that this data source is giving information to.
|
||||
/// </summary>
|
||||
protected VirtualObjectListView listView;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="n"></param>
|
||||
/// <returns></returns>
|
||||
public virtual object GetNthObject(int n) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual int GetObjectCount() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="model"></param>
|
||||
/// <returns></returns>
|
||||
public virtual int GetObjectIndex(object model) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="from"></param>
|
||||
/// <param name="to"></param>
|
||||
public virtual void PrepareCache(int from, int to) {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="first"></param>
|
||||
/// <param name="last"></param>
|
||||
/// <param name="column"></param>
|
||||
/// <returns></returns>
|
||||
public virtual int SearchText(string value, int first, int last, OLVColumn column) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="column"></param>
|
||||
/// <param name="order"></param>
|
||||
public virtual void Sort(OLVColumn column, SortOrder order) {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="modelObjects"></param>
|
||||
public virtual void AddObjects(ICollection modelObjects) {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="modelObjects"></param>
|
||||
public virtual void InsertObjects(int index, ICollection modelObjects) {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="modelObjects"></param>
|
||||
public virtual void RemoveObjects(ICollection modelObjects) {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="collection"></param>
|
||||
public virtual void SetObjects(IEnumerable collection) {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update/replace the nth object with the given object
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="modelObject"></param>
|
||||
public virtual void UpdateObject(int index, object modelObject) {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is a useful default implementation of SearchText method, intended to be called
|
||||
/// by implementors of IVirtualListDataSource.
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="first"></param>
|
||||
/// <param name="last"></param>
|
||||
/// <param name="column"></param>
|
||||
/// <param name="source"></param>
|
||||
/// <returns></returns>
|
||||
static public int DefaultSearchText(string value, int first, int last, OLVColumn column, IVirtualListDataSource source) {
|
||||
if (first <= last) {
|
||||
for (int i = first; i <= last; i++) {
|
||||
string data = column.GetStringValue(source.GetNthObject(i));
|
||||
if (data.StartsWith(value, StringComparison.CurrentCultureIgnoreCase))
|
||||
return i;
|
||||
}
|
||||
} else {
|
||||
for (int i = first; i >= last; i--) {
|
||||
string data = column.GetStringValue(source.GetNthObject(i));
|
||||
if (data.StartsWith(value, StringComparison.CurrentCultureIgnoreCase))
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
#region IFilterableDataSource Members
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="modelFilter"></param>
|
||||
/// <param name="listFilter"></param>
|
||||
virtual public void ApplyFilters(IModelFilter modelFilter, IListFilter listFilter) {
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This class mimics the behavior of VirtualObjectListView v1.x.
|
||||
/// </summary>
|
||||
public class VirtualListVersion1DataSource : AbstractVirtualListDataSource
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a VirtualListVersion1DataSource
|
||||
/// </summary>
|
||||
/// <param name="listView"></param>
|
||||
public VirtualListVersion1DataSource(VirtualObjectListView listView)
|
||||
: base(listView) {
|
||||
}
|
||||
|
||||
#region Public properties
|
||||
|
||||
/// <summary>
|
||||
/// How will the n'th object of the data source be fetched?
|
||||
/// </summary>
|
||||
public RowGetterDelegate RowGetter {
|
||||
get { return rowGetter; }
|
||||
set { rowGetter = value; }
|
||||
}
|
||||
private RowGetterDelegate rowGetter;
|
||||
|
||||
#endregion
|
||||
|
||||
#region IVirtualListDataSource implementation
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="n"></param>
|
||||
/// <returns></returns>
|
||||
public override object GetNthObject(int n) {
|
||||
if (this.RowGetter == null)
|
||||
return null;
|
||||
else
|
||||
return this.RowGetter(n);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="first"></param>
|
||||
/// <param name="last"></param>
|
||||
/// <param name="column"></param>
|
||||
/// <returns></returns>
|
||||
public override int SearchText(string value, int first, int last, OLVColumn column) {
|
||||
return DefaultSearchText(value, first, last, column, this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
1911
ObjectListView/OLVColumn.cs
Normal file
1911
ObjectListView/OLVColumn.cs
Normal file
File diff suppressed because it is too large
Load Diff
550
ObjectListView/ObjectListView.DesignTime.cs
Normal file
550
ObjectListView/ObjectListView.DesignTime.cs
Normal file
@@ -0,0 +1,550 @@
|
||||
/*
|
||||
* DesignSupport - Design time support for the various classes within ObjectListView
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 12/08/2009 8:36 PM
|
||||
*
|
||||
* Change log:
|
||||
* 2012-08-27 JPP - Fall back to more specific type name for the ListViewDesigner if
|
||||
* the first GetType() fails.
|
||||
* v2.5.1
|
||||
* 2012-04-26 JPP - Filter group events from TreeListView since it can't have groups
|
||||
* 2011-06-06 JPP - Vastly improved ObjectListViewDesigner, based off information in
|
||||
* "'Inheriting' from an Internal WinForms Designer" on CodeProject.
|
||||
* v2.3
|
||||
* 2009-08-12 JPP - Initial version
|
||||
*
|
||||
* To do:
|
||||
*
|
||||
* Copyright (C) 2009-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.Design;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Reflection;
|
||||
using System.Windows.Forms;
|
||||
using System.Windows.Forms.Design;
|
||||
|
||||
namespace BrightIdeasSoftware.Design
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Designer for <see cref="ObjectListView"/> and its subclasses.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This designer removes properties and events that are available on ListView but that are not
|
||||
/// useful on ObjectListView.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// We can't inherit from System.Windows.Forms.Design.ListViewDesigner, since it is marked internal.
|
||||
/// So, this class uses reflection to create a ListViewDesigner and then forwards messages to that designer.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class ObjectListViewDesigner : ControlDesigner
|
||||
{
|
||||
|
||||
#region Initialize & Dispose
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the designer with the specified component.
|
||||
/// </summary>
|
||||
/// <param name="component">The <see cref="T:System.ComponentModel.IComponent"/> to associate the designer with. This component must always be an instance of, or derive from, <see cref="T:System.Windows.Forms.Control"/>. </param>
|
||||
public override void Initialize(IComponent component) {
|
||||
// Debug.WriteLine("ObjectListViewDesigner.Initialize");
|
||||
|
||||
// Use reflection to bypass the "internal" marker on ListViewDesigner
|
||||
// If we can't get the unversioned designer, look specifically for .NET 4.0 version of it.
|
||||
Type tListViewDesigner = Type.GetType("System.Windows.Forms.Design.ListViewDesigner, System.Design") ??
|
||||
Type.GetType("System.Windows.Forms.Design.ListViewDesigner, System.Design, " +
|
||||
"Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
|
||||
if (tListViewDesigner == null) throw new ArgumentException("Could not load ListViewDesigner");
|
||||
|
||||
this.listViewDesigner = (ControlDesigner)Activator.CreateInstance(tListViewDesigner, BindingFlags.Instance | BindingFlags.Public, null, null, null);
|
||||
this.designerFilter = this.listViewDesigner;
|
||||
|
||||
// Fetch the methods from the ListViewDesigner that we know we want to use
|
||||
this.listViewDesignGetHitTest = tListViewDesigner.GetMethod("GetHitTest", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
this.listViewDesignWndProc = tListViewDesigner.GetMethod("WndProc", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
Debug.Assert(this.listViewDesignGetHitTest != null, "Required method (GetHitTest) not found on ListViewDesigner");
|
||||
Debug.Assert(this.listViewDesignWndProc != null, "Required method (WndProc) not found on ListViewDesigner");
|
||||
|
||||
// Tell the Designer to use properties of default designer as well as the properties of this class (do before base.Initialize)
|
||||
TypeDescriptor.CreateAssociation(component, this.listViewDesigner);
|
||||
|
||||
IServiceContainer site = (IServiceContainer)component.Site;
|
||||
if (site != null && GetService(typeof(DesignerCommandSet)) == null) {
|
||||
site.AddService(typeof(DesignerCommandSet), new CDDesignerCommandSet(this));
|
||||
} else {
|
||||
Debug.Fail("site != null && GetService(typeof (DesignerCommandSet)) == null");
|
||||
}
|
||||
|
||||
this.listViewDesigner.Initialize(component);
|
||||
base.Initialize(component);
|
||||
|
||||
RemoveDuplicateDockingActionList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a newly created component.
|
||||
/// </summary>
|
||||
/// <param name="defaultValues">A name/value dictionary of default values to apply to properties. May be null if no default values are specified.</param>
|
||||
public override void InitializeNewComponent(IDictionary defaultValues) {
|
||||
// Debug.WriteLine("ObjectListViewDesigner.InitializeNewComponent");
|
||||
base.InitializeNewComponent(defaultValues);
|
||||
this.listViewDesigner.InitializeNewComponent(defaultValues);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the unmanaged resources used by the <see cref="T:System.Windows.Forms.Design.ControlDesigner"/> and optionally releases the managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources. </param>
|
||||
protected override void Dispose(bool disposing) {
|
||||
// Debug.WriteLine("ObjectListViewDesigner.Dispose");
|
||||
if (disposing) {
|
||||
if (this.listViewDesigner != null) {
|
||||
this.listViewDesigner.Dispose();
|
||||
// Normally we would now null out the designer, but this designer
|
||||
// still has methods called AFTER it is disposed.
|
||||
}
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the duplicate DockingActionList added by this designer to the <see cref="DesignerActionService"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <see cref="ControlDesigner.Initialize"/> adds an internal DockingActionList : 'Dock/Undock in Parent Container'.
|
||||
/// But the default designer has already added that action list. So we need to remove one.
|
||||
/// </remarks>
|
||||
private void RemoveDuplicateDockingActionList() {
|
||||
// This is a true hack -- in a class that is basically a huge hack itself.
|
||||
// Reach into the bowel of our base class, get a private field, and use that fields value to
|
||||
// remove an action from the designer.
|
||||
// In ControlDesigner, there is "private DockingActionList dockingAction;"
|
||||
// Don't you just love Reflector?!
|
||||
FieldInfo fi = typeof(ControlDesigner).GetField("dockingAction", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
if (fi != null) {
|
||||
DesignerActionList dockingAction = (DesignerActionList)fi.GetValue(this);
|
||||
if (dockingAction != null) {
|
||||
DesignerActionService service = (DesignerActionService)GetService(typeof(DesignerActionService));
|
||||
if (service != null) {
|
||||
service.Remove(this.Control, dockingAction);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDesignerFilter overrides
|
||||
|
||||
/// <summary>
|
||||
/// Adjusts the set of properties the component exposes through a <see cref="T:System.ComponentModel.TypeDescriptor"/>.
|
||||
/// </summary>
|
||||
/// <param name="properties">An <see cref="T:System.Collections.IDictionary"/> containing the properties for the class of the component. </param>
|
||||
protected override void PreFilterProperties(IDictionary properties) {
|
||||
// Debug.WriteLine("ObjectListViewDesigner.PreFilterProperties");
|
||||
|
||||
// Always call the base PreFilterProperties implementation
|
||||
// before you modify the properties collection.
|
||||
base.PreFilterProperties(properties);
|
||||
|
||||
// Give the listviewdesigner a chance to filter the properties
|
||||
// (though we already know it's not going to do anything)
|
||||
this.designerFilter.PreFilterProperties(properties);
|
||||
|
||||
// I'd like to just remove the redundant properties, but that would
|
||||
// break backward compatibility. The deserialiser that handles the XXX.Designer.cs file
|
||||
// works off the designer, so even if the property exists in the class, the deserialiser will
|
||||
// throw an error if the associated designer actually removes that property.
|
||||
// So we shadow the unwanted properties, and give the replacement properties
|
||||
// non-browsable attributes so that they are hidden from the user
|
||||
|
||||
List<string> unwantedProperties = new List<string>(new string[] {
|
||||
"BackgroundImage", "BackgroundImageTiled", "HotTracking", "HoverSelection",
|
||||
"LabelEdit", "VirtualListSize", "VirtualMode" });
|
||||
|
||||
// Also hid Tooltip properties, since giving a tooltip to the control through the IDE
|
||||
// messes up the tooltip handling
|
||||
foreach (string propertyName in properties.Keys) {
|
||||
if (propertyName.StartsWith("ToolTip")) {
|
||||
unwantedProperties.Add(propertyName);
|
||||
}
|
||||
}
|
||||
|
||||
// If we are looking at a TreeListView, remove group related properties
|
||||
// since TreeListViews can't show groups
|
||||
if (this.Control is TreeListView) {
|
||||
unwantedProperties.AddRange(new string[] {
|
||||
"GroupImageList", "GroupWithItemCountFormat", "GroupWithItemCountSingularFormat", "HasCollapsibleGroups",
|
||||
"SpaceBetweenGroups", "ShowGroups", "SortGroupItemsByPrimaryColumn", "ShowItemCountOnGroups"
|
||||
});
|
||||
}
|
||||
|
||||
// Shadow the unwanted properties, and give the replacement properties
|
||||
// non-browsable attributes so that they are hidden from the user
|
||||
foreach (string unwantedProperty in unwantedProperties) {
|
||||
PropertyDescriptor propertyDesc = TypeDescriptor.CreateProperty(
|
||||
typeof(ObjectListView),
|
||||
(PropertyDescriptor)properties[unwantedProperty],
|
||||
new BrowsableAttribute(false));
|
||||
properties[unwantedProperty] = propertyDesc;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows a designer to add to the set of events that it exposes through a <see cref="T:System.ComponentModel.TypeDescriptor"/>.
|
||||
/// </summary>
|
||||
/// <param name="events">The events for the class of the component. </param>
|
||||
protected override void PreFilterEvents(IDictionary events) {
|
||||
// Debug.WriteLine("ObjectListViewDesigner.PreFilterEvents");
|
||||
base.PreFilterEvents(events);
|
||||
this.designerFilter.PreFilterEvents(events);
|
||||
|
||||
// Remove the events that don't make sense for an ObjectListView.
|
||||
// See PreFilterProperties() for why we do this dance rather than just remove the event.
|
||||
List<string> unwanted = new List<string>(new string[] {
|
||||
"AfterLabelEdit",
|
||||
"BeforeLabelEdit",
|
||||
"DrawColumnHeader",
|
||||
"DrawItem",
|
||||
"DrawSubItem",
|
||||
"RetrieveVirtualItem",
|
||||
"SearchForVirtualItem",
|
||||
"VirtualItemsSelectionRangeChanged"
|
||||
});
|
||||
|
||||
// If we are looking at a TreeListView, remove group related events
|
||||
// since TreeListViews can't show groups
|
||||
if (this.Control is TreeListView) {
|
||||
unwanted.AddRange(new string[] {
|
||||
"AboutToCreateGroups",
|
||||
"AfterCreatingGroups",
|
||||
"BeforeCreatingGroups",
|
||||
"GroupTaskClicked",
|
||||
"GroupExpandingCollapsing",
|
||||
"GroupStateChanged"
|
||||
});
|
||||
}
|
||||
|
||||
foreach (string unwantedEvent in unwanted) {
|
||||
EventDescriptor eventDesc = TypeDescriptor.CreateEvent(
|
||||
typeof(ObjectListView),
|
||||
(EventDescriptor)events[unwantedEvent],
|
||||
new BrowsableAttribute(false));
|
||||
events[unwantedEvent] = eventDesc;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows a designer to change or remove items from the set of attributes that it exposes through a <see cref="T:System.ComponentModel.TypeDescriptor"/>.
|
||||
/// </summary>
|
||||
/// <param name="attributes">The attributes for the class of the component. </param>
|
||||
protected override void PostFilterAttributes(IDictionary attributes) {
|
||||
// Debug.WriteLine("ObjectListViewDesigner.PostFilterAttributes");
|
||||
this.designerFilter.PostFilterAttributes(attributes);
|
||||
base.PostFilterAttributes(attributes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows a designer to change or remove items from the set of events that it exposes through a <see cref="T:System.ComponentModel.TypeDescriptor"/>.
|
||||
/// </summary>
|
||||
/// <param name="events">The events for the class of the component. </param>
|
||||
protected override void PostFilterEvents(IDictionary events) {
|
||||
// Debug.WriteLine("ObjectListViewDesigner.PostFilterEvents");
|
||||
this.designerFilter.PostFilterEvents(events);
|
||||
base.PostFilterEvents(events);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Overrides
|
||||
|
||||
/// <summary>
|
||||
/// Gets the design-time action lists supported by the component associated with the designer.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The design-time action lists supported by the component associated with the designer.
|
||||
/// </returns>
|
||||
public override DesignerActionListCollection ActionLists {
|
||||
get {
|
||||
// We want to change the first action list so it only has the commands we want
|
||||
DesignerActionListCollection actionLists = this.listViewDesigner.ActionLists;
|
||||
if (actionLists.Count > 0 && !(actionLists[0] is ListViewActionListAdapter)) {
|
||||
actionLists[0] = new ListViewActionListAdapter(this, actionLists[0]);
|
||||
}
|
||||
return actionLists;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of components associated with the component managed by the designer.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The components that are associated with the component managed by the designer.
|
||||
/// </returns>
|
||||
public override ICollection AssociatedComponents {
|
||||
get {
|
||||
ArrayList components = new ArrayList(base.AssociatedComponents);
|
||||
components.AddRange(this.listViewDesigner.AssociatedComponents);
|
||||
return components;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether a mouse click at the specified point should be handled by the control.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// true if a click at the specified point is to be handled by the control; otherwise, false.
|
||||
/// </returns>
|
||||
/// <param name="point">A <see cref="T:System.Drawing.Point"/> indicating the position at which the mouse was clicked, in screen coordinates. </param>
|
||||
protected override bool GetHitTest(Point point) {
|
||||
// The ListViewDesigner wants to allow column dividers to be resized
|
||||
return (bool)this.listViewDesignGetHitTest.Invoke(listViewDesigner, new object[] { point });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes Windows messages and optionally routes them to the control.
|
||||
/// </summary>
|
||||
/// <param name="m">The <see cref="T:System.Windows.Forms.Message"/> to process. </param>
|
||||
protected override void WndProc(ref Message m) {
|
||||
switch (m.Msg) {
|
||||
case 0x4e:
|
||||
case 0x204e:
|
||||
// The listview designer is interested in HDN_ENDTRACK notifications
|
||||
this.listViewDesignWndProc.Invoke(listViewDesigner, new object[] { m });
|
||||
break;
|
||||
default:
|
||||
base.WndProc(ref m);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation variables
|
||||
|
||||
private ControlDesigner listViewDesigner;
|
||||
private IDesignerFilter designerFilter;
|
||||
private MethodInfo listViewDesignGetHitTest;
|
||||
private MethodInfo listViewDesignWndProc;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Custom action list
|
||||
|
||||
/// <summary>
|
||||
/// This class modifies a ListViewActionList, by removing the "Edit Items" and "Edit Groups" actions.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// That class is internal, so we cannot simply subclass it, which would be simplier.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Action lists use reflection to determine if that action can be executed, so we not
|
||||
/// only have to modify the returned collection of actions, but we have to implement
|
||||
/// the properties and commands that the returned actions use. </para>
|
||||
/// </remarks>
|
||||
private class ListViewActionListAdapter : DesignerActionList
|
||||
{
|
||||
public ListViewActionListAdapter(ObjectListViewDesigner designer, DesignerActionList wrappedList)
|
||||
: base(wrappedList.Component) {
|
||||
this.designer = designer;
|
||||
this.wrappedList = wrappedList;
|
||||
}
|
||||
|
||||
public override DesignerActionItemCollection GetSortedActionItems() {
|
||||
DesignerActionItemCollection items = wrappedList.GetSortedActionItems();
|
||||
items.RemoveAt(2); // remove Edit Groups
|
||||
items.RemoveAt(0); // remove Edit Items
|
||||
return items;
|
||||
}
|
||||
|
||||
private void EditValue(ComponentDesigner componentDesigner, IComponent iComponent, string propertyName) {
|
||||
// One more complication. The ListViewActionList classes uses an internal class, EditorServiceContext, to
|
||||
// edit the items/columns/groups collections. So, we use reflection to bypass the data hiding.
|
||||
Type tEditorServiceContext = Type.GetType("System.Windows.Forms.Design.EditorServiceContext, System.Design");
|
||||
tEditorServiceContext.InvokeMember("EditValue", BindingFlags.InvokeMethod | BindingFlags.Static, null, null, new object[] { componentDesigner, iComponent, propertyName });
|
||||
}
|
||||
|
||||
private void SetValue(object target, string propertyName, object value) {
|
||||
TypeDescriptor.GetProperties(target)[propertyName].SetValue(target, value);
|
||||
}
|
||||
|
||||
public void InvokeColumnsDialog() {
|
||||
EditValue(this.designer, base.Component, "Columns");
|
||||
}
|
||||
|
||||
// Don't need these since we removed their corresponding actions from the list.
|
||||
// Keep the methods just in case.
|
||||
|
||||
//public void InvokeGroupsDialog() {
|
||||
// EditValue(this.designer, base.Component, "Groups");
|
||||
//}
|
||||
|
||||
//public void InvokeItemsDialog() {
|
||||
// EditValue(this.designer, base.Component, "Items");
|
||||
//}
|
||||
|
||||
public ImageList LargeImageList {
|
||||
get { return ((ListView)base.Component).LargeImageList; }
|
||||
set { SetValue(base.Component, "LargeImageList", value); }
|
||||
}
|
||||
|
||||
public ImageList SmallImageList {
|
||||
get { return ((ListView)base.Component).SmallImageList; }
|
||||
set { SetValue(base.Component, "SmallImageList", value); }
|
||||
}
|
||||
|
||||
public View View {
|
||||
get { return ((ListView)base.Component).View; }
|
||||
set { SetValue(base.Component, "View", value); }
|
||||
}
|
||||
|
||||
ObjectListViewDesigner designer;
|
||||
DesignerActionList wrappedList;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region DesignerCommandSet
|
||||
|
||||
private class CDDesignerCommandSet : DesignerCommandSet
|
||||
{
|
||||
|
||||
public CDDesignerCommandSet(ComponentDesigner componentDesigner) {
|
||||
this.componentDesigner = componentDesigner;
|
||||
}
|
||||
|
||||
public override ICollection GetCommands(string name) {
|
||||
// Debug.WriteLine("CDDesignerCommandSet.GetCommands:" + name);
|
||||
if (componentDesigner != null) {
|
||||
if (name.Equals("Verbs")) {
|
||||
return componentDesigner.Verbs;
|
||||
}
|
||||
if (name.Equals("ActionLists")) {
|
||||
return componentDesigner.ActionLists;
|
||||
}
|
||||
}
|
||||
return base.GetCommands(name);
|
||||
}
|
||||
|
||||
private readonly ComponentDesigner componentDesigner;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This class works in conjunction with the OLVColumns property to allow OLVColumns
|
||||
/// to be added to the ObjectListView.
|
||||
/// </summary>
|
||||
public class OLVColumnCollectionEditor : System.ComponentModel.Design.CollectionEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a OLVColumnCollectionEditor
|
||||
/// </summary>
|
||||
/// <param name="t"></param>
|
||||
public OLVColumnCollectionEditor(Type t)
|
||||
: base(t) {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// What type of object does this editor create?
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected override Type CreateCollectionItemType() {
|
||||
return typeof(OLVColumn);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Edit a given value
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="provider"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) {
|
||||
if (context == null)
|
||||
throw new ArgumentNullException("context");
|
||||
if (provider == null)
|
||||
throw new ArgumentNullException("provider");
|
||||
|
||||
// Figure out which ObjectListView we are working on. This should be the Instance of the context.
|
||||
ObjectListView olv = context.Instance as ObjectListView;
|
||||
Debug.Assert(olv != null, "Instance must be an ObjectListView");
|
||||
|
||||
// Edit all the columns, not just the ones that are visible
|
||||
base.EditValue(context, provider, olv.AllColumns);
|
||||
|
||||
// Set the columns on the ListView to just the visible columns
|
||||
List<OLVColumn> newColumns = olv.GetFilteredColumns(View.Details);
|
||||
olv.Columns.Clear();
|
||||
olv.Columns.AddRange(newColumns.ToArray());
|
||||
|
||||
return olv.Columns;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// What text should be shown in the list for the given object?
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
protected override string GetDisplayText(object value) {
|
||||
OLVColumn col = value as OLVColumn;
|
||||
if (col == null || String.IsNullOrEmpty(col.AspectName))
|
||||
return base.GetDisplayText(value);
|
||||
|
||||
return String.Format("{0} ({1})", base.GetDisplayText(value), col.AspectName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Control how the overlay is presented in the IDE
|
||||
/// </summary>
|
||||
internal class OverlayConverter : ExpandableObjectConverter
|
||||
{
|
||||
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) {
|
||||
return destinationType == typeof(string) || base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) {
|
||||
if (destinationType == typeof(string)) {
|
||||
ImageOverlay imageOverlay = value as ImageOverlay;
|
||||
if (imageOverlay != null) {
|
||||
return imageOverlay.Image == null ? "(none)" : "(set)";
|
||||
}
|
||||
TextOverlay textOverlay = value as TextOverlay;
|
||||
if (textOverlay != null) {
|
||||
return String.IsNullOrEmpty(textOverlay.Text) ? "(none)" : "(set)";
|
||||
}
|
||||
}
|
||||
|
||||
return base.ConvertTo(context, culture, value, destinationType);
|
||||
}
|
||||
}
|
||||
}
|
||||
38
ObjectListView/ObjectListView.NetCore.csproj
Normal file
38
ObjectListView/ObjectListView.NetCore.csproj
Normal file
@@ -0,0 +1,38 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0-windows7.0</TargetFramework>
|
||||
<Deterministic>false</Deterministic>
|
||||
<RootNamespace>BrightIdeasSoftware</RootNamespace>
|
||||
<AssemblyName>ObjectListView</AssemblyName>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="CustomDictionary.xml" Link="CustomDictionary.xml" />
|
||||
<Content Include="Resources\clear-filter.png" Link="Resources\clear-filter.png" />
|
||||
<Content Include="Resources\coffee.jpg" Link="Resources\coffee.jpg" />
|
||||
<Content Include="Resources\filter-icons3.png" Link="Resources\filter-icons3.png" />
|
||||
<Content Include="Resources\filter.png" Link="Resources\filter.png" />
|
||||
<Content Include="Resources\sort-ascending.png" Link="Resources\sort-ascending.png" />
|
||||
<Content Include="Resources\sort-descending.png" Link="Resources\sort-descending.png" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="CellEditing\" />
|
||||
<Folder Include="DragDrop\" />
|
||||
<Folder Include="Filtering\" />
|
||||
<Folder Include="Implementation\" />
|
||||
<Folder Include="Utilities\" />
|
||||
<Folder Include="SubControls\" />
|
||||
<Folder Include="Resources\" />
|
||||
<Folder Include="Rendering\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Drawing.Common" Version="9.0.1" />
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.1.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
10789
ObjectListView/ObjectListView.cs
Normal file
10789
ObjectListView/ObjectListView.cs
Normal file
File diff suppressed because it is too large
Load Diff
37
ObjectListView/Package.nuspec
Normal file
37
ObjectListView/Package.nuspec
Normal file
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2013/01/nuspec.xsd">
|
||||
<metadata minClientVersion="2.12">
|
||||
<id>ObjectListView.Updated</id>
|
||||
<title>ObjectListView (Updated)</title>
|
||||
<version>$version$</version>
|
||||
<authors>Phillip Piper</authors>
|
||||
<owners>$author$</owners>
|
||||
<license type="file">LICENSE</license>
|
||||
<icon>.editoricon.png</icon>
|
||||
<projectUrl>https://github.com/ennerperez/ObjectListView</projectUrl>
|
||||
<description>
|
||||
ObjectListView is a .NET ListView wired on caffeine, guarana and steroids.
|
||||
More calmly, it is a C# wrapper around a .NET ListView, which makes the ListView much easier to use and teaches it lots of neat new tricks.
|
||||
</description>
|
||||
<summary>$description$</summary>
|
||||
<copyright>$copyright$</copyright>
|
||||
<tags>.Net WinForms ListView Controls</tags>
|
||||
<repository type="git" url="https://github.com/ennerperez/ObjectListView" />
|
||||
<dependencies>
|
||||
<group targetFramework=".NETFramework4.0" />
|
||||
<group targetFramework=".NETStandard2.0">
|
||||
<dependency id="System.Drawing.Common" version="4.7.0" />
|
||||
</group>
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="..\.editoricon.png" target=".editoricon.png" />
|
||||
<file src="..\..\README.md" target="README.md" />
|
||||
<file src="..\..\CHANGELOG.md" target="CHANGELOG.md" />
|
||||
<file src="..\..\LICENSE" target="LICENSE" />
|
||||
<!-- NETFX -->
|
||||
<file src="..\ObjectListView\bin\release\ObjectListView.dll" target="lib\net40\ObjectListView.dll" />
|
||||
<!-- NETCORE -->
|
||||
<file src="..\ObjectListView.NetCore\bin\release\netcoreapp3.1\ObjectListView.dll" target="lib\netstandard2.0\ObjectListView.dll" />
|
||||
</files>
|
||||
</package>
|
||||
22
ObjectListView/Properties/AssemblyInfo.cs
Normal file
22
ObjectListView/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("ObjectListView")]
|
||||
[assembly: AssemblyDescription("A much easier to use ListView and friends")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Bright Ideas Software")]
|
||||
[assembly: AssemblyProduct("ObjectListView")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2006-2020")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("ef28c7a8-77ae-442d-abc3-bb023fa31e57")]
|
||||
113
ObjectListView/Properties/Resources.Designer.cs
generated
Normal file
113
ObjectListView/Properties/Resources.Designer.cs
generated
Normal file
@@ -0,0 +1,113 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.18444
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace BrightIdeasSoftware.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.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>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </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("BrightIdeasSoftware.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap ClearFiltering {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("ClearFiltering", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap ColumnFilterIndicator {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("ColumnFilterIndicator", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap Filtering {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("Filtering", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap SortAscending {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("SortAscending", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap SortDescending {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("SortDescending", resourceCulture);
|
||||
return ((System.Drawing.Bitmap)(obj));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
137
ObjectListView/Properties/Resources.resx
Normal file
137
ObjectListView/Properties/Resources.resx
Normal file
@@ -0,0 +1,137 @@
|
||||
<?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="ClearFiltering" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\clear-filter.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
<data name="ColumnFilterIndicator" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\filter-icons3.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="Filtering" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\filter.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="SortAscending" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\sort-ascending.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="SortDescending" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\sort-descending.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
</root>
|
||||
743
ObjectListView/Rendering/Adornments.cs
Normal file
743
ObjectListView/Rendering/Adornments.cs
Normal file
@@ -0,0 +1,743 @@
|
||||
/*
|
||||
* Adornments - Adornments are the basis for overlays and decorations -- things that can be rendered over the top of a ListView
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 16/08/2009 1:02 AM
|
||||
*
|
||||
* Change log:
|
||||
* v2.6
|
||||
* 2012-08-18 JPP - Correctly dispose of brush and pen resources
|
||||
* v2.3
|
||||
* 2009-09-22 JPP - Added Wrap property to TextAdornment, to allow text wrapping to be disabled
|
||||
* - Added ShrinkToWidth property to ImageAdornment
|
||||
* 2009-08-17 JPP - Initial version
|
||||
*
|
||||
* To do:
|
||||
* - Use IPointLocator rather than Corners
|
||||
* - Add RotationCenter property ratherr than always using middle center
|
||||
*
|
||||
* Copyright (C) 2009-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Drawing.Imaging;
|
||||
|
||||
namespace BrightIdeasSoftware
|
||||
{
|
||||
/// <summary>
|
||||
/// An adorment is the common base for overlays and decorations.
|
||||
/// </summary>
|
||||
public class GraphicAdornment
|
||||
{
|
||||
#region Public properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the corner of the adornment that will be positioned at the reference corner
|
||||
/// </summary>
|
||||
[Browsable(false),
|
||||
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
public System.Drawing.ContentAlignment AdornmentCorner {
|
||||
get { return this.adornmentCorner; }
|
||||
set { this.adornmentCorner = value; }
|
||||
}
|
||||
private System.Drawing.ContentAlignment adornmentCorner = System.Drawing.ContentAlignment.MiddleCenter;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets location within the reference rectange where the adornment will be drawn
|
||||
/// </summary>
|
||||
/// <remarks>This is a simplied interface to ReferenceCorner and AdornmentCorner </remarks>
|
||||
[Category("ObjectListView"),
|
||||
Description("How will the adornment be aligned"),
|
||||
DefaultValue(System.Drawing.ContentAlignment.BottomRight),
|
||||
NotifyParentProperty(true)]
|
||||
public System.Drawing.ContentAlignment Alignment {
|
||||
get { return this.alignment; }
|
||||
set {
|
||||
this.alignment = value;
|
||||
this.ReferenceCorner = value;
|
||||
this.AdornmentCorner = value;
|
||||
}
|
||||
}
|
||||
private System.Drawing.ContentAlignment alignment = System.Drawing.ContentAlignment.BottomRight;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the offset by which the position of the adornment will be adjusted
|
||||
/// </summary>
|
||||
[Category("ObjectListView"),
|
||||
Description("The offset by which the position of the adornment will be adjusted"),
|
||||
DefaultValue(typeof(Size), "0,0")]
|
||||
public Size Offset {
|
||||
get { return this.offset; }
|
||||
set { this.offset = value; }
|
||||
}
|
||||
private Size offset = new Size();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the point of the reference rectangle to which the adornment will be aligned.
|
||||
/// </summary>
|
||||
[Browsable(false),
|
||||
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
public System.Drawing.ContentAlignment ReferenceCorner {
|
||||
get { return this.referenceCorner; }
|
||||
set { this.referenceCorner = value; }
|
||||
}
|
||||
private System.Drawing.ContentAlignment referenceCorner = System.Drawing.ContentAlignment.MiddleCenter;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the degree of rotation by which the adornment will be transformed.
|
||||
/// The centre of rotation will be the center point of the adornment.
|
||||
/// </summary>
|
||||
[Category("ObjectListView"),
|
||||
Description("The degree of rotation that will be applied to the adornment."),
|
||||
DefaultValue(0),
|
||||
NotifyParentProperty(true)]
|
||||
public int Rotation {
|
||||
get { return this.rotation; }
|
||||
set { this.rotation = value; }
|
||||
}
|
||||
private int rotation;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the transparency of the overlay.
|
||||
/// 0 is completely transparent, 255 is completely opaque.
|
||||
/// </summary>
|
||||
[Category("ObjectListView"),
|
||||
Description("The transparency of this adornment. 0 is completely transparent, 255 is completely opaque."),
|
||||
DefaultValue(128)]
|
||||
public int Transparency {
|
||||
get { return this.transparency; }
|
||||
set { this.transparency = Math.Min(255, Math.Max(0, value)); }
|
||||
}
|
||||
private int transparency = 128;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Calculations
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the location of rectangle of the given size,
|
||||
/// so that it's indicated corner would be at the given point.
|
||||
/// </summary>
|
||||
/// <param name="pt">The point</param>
|
||||
/// <param name="size"></param>
|
||||
/// <param name="corner">Which corner will be positioned at the reference point</param>
|
||||
/// <returns></returns>
|
||||
/// <example>CalculateAlignedPosition(new Point(50, 100), new Size(10, 20), System.Drawing.ContentAlignment.TopLeft) -> Point(50, 100)</example>
|
||||
/// <example>CalculateAlignedPosition(new Point(50, 100), new Size(10, 20), System.Drawing.ContentAlignment.MiddleCenter) -> Point(45, 90)</example>
|
||||
/// <example>CalculateAlignedPosition(new Point(50, 100), new Size(10, 20), System.Drawing.ContentAlignment.BottomRight) -> Point(40, 80)</example>
|
||||
public virtual Point CalculateAlignedPosition(Point pt, Size size, System.Drawing.ContentAlignment corner) {
|
||||
switch (corner) {
|
||||
case System.Drawing.ContentAlignment.TopLeft:
|
||||
return pt;
|
||||
case System.Drawing.ContentAlignment.TopCenter:
|
||||
return new Point(pt.X - (size.Width / 2), pt.Y);
|
||||
case System.Drawing.ContentAlignment.TopRight:
|
||||
return new Point(pt.X - size.Width, pt.Y);
|
||||
case System.Drawing.ContentAlignment.MiddleLeft:
|
||||
return new Point(pt.X, pt.Y - (size.Height / 2));
|
||||
case System.Drawing.ContentAlignment.MiddleCenter:
|
||||
return new Point(pt.X - (size.Width / 2), pt.Y - (size.Height / 2));
|
||||
case System.Drawing.ContentAlignment.MiddleRight:
|
||||
return new Point(pt.X - size.Width, pt.Y - (size.Height / 2));
|
||||
case System.Drawing.ContentAlignment.BottomLeft:
|
||||
return new Point(pt.X, pt.Y - size.Height);
|
||||
case System.Drawing.ContentAlignment.BottomCenter:
|
||||
return new Point(pt.X - (size.Width / 2), pt.Y - size.Height);
|
||||
case System.Drawing.ContentAlignment.BottomRight:
|
||||
return new Point(pt.X - size.Width, pt.Y - size.Height);
|
||||
}
|
||||
|
||||
// Should never reach here
|
||||
return pt;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate a rectangle that has the given size which is positioned so that
|
||||
/// its alignment point is at the reference location of the given rect.
|
||||
/// </summary>
|
||||
/// <param name="r"></param>
|
||||
/// <param name="sz"></param>
|
||||
/// <returns></returns>
|
||||
public virtual Rectangle CreateAlignedRectangle(Rectangle r, Size sz) {
|
||||
return this.CreateAlignedRectangle(r, sz, this.ReferenceCorner, this.AdornmentCorner, this.Offset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a rectangle of the given size which is positioned so that
|
||||
/// its indicated corner is at the indicated corner of the reference rect.
|
||||
/// </summary>
|
||||
/// <param name="r"></param>
|
||||
/// <param name="sz"></param>
|
||||
/// <param name="corner"></param>
|
||||
/// <param name="referenceCorner"></param>
|
||||
/// <param name="offset"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// <para>Creates a rectangle so that its bottom left is at the centre of the reference:
|
||||
/// corner=BottomLeft, referenceCorner=MiddleCenter</para>
|
||||
/// <para>This is a powerful concept that takes some getting used to, but is
|
||||
/// very neat once you understand it.</para>
|
||||
/// </remarks>
|
||||
public virtual Rectangle CreateAlignedRectangle(Rectangle r, Size sz,
|
||||
System.Drawing.ContentAlignment corner, System.Drawing.ContentAlignment referenceCorner, Size offset) {
|
||||
Point referencePt = this.CalculateCorner(r, referenceCorner);
|
||||
Point topLeft = this.CalculateAlignedPosition(referencePt, sz, corner);
|
||||
return new Rectangle(topLeft + offset, sz);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the point at the indicated corner of the given rectangle (it doesn't
|
||||
/// have to be a corner, but a named location)
|
||||
/// </summary>
|
||||
/// <param name="r">The reference rectangle</param>
|
||||
/// <param name="corner">Which point of the rectangle should be returned?</param>
|
||||
/// <returns>A point</returns>
|
||||
/// <example>CalculateReferenceLocation(new Rectangle(0, 0, 50, 100), System.Drawing.ContentAlignment.TopLeft) -> Point(0, 0)</example>
|
||||
/// <example>CalculateReferenceLocation(new Rectangle(0, 0, 50, 100), System.Drawing.ContentAlignment.MiddleCenter) -> Point(25, 50)</example>
|
||||
/// <example>CalculateReferenceLocation(new Rectangle(0, 0, 50, 100), System.Drawing.ContentAlignment.BottomRight) -> Point(50, 100)</example>
|
||||
public virtual Point CalculateCorner(Rectangle r, System.Drawing.ContentAlignment corner) {
|
||||
switch (corner) {
|
||||
case System.Drawing.ContentAlignment.TopLeft:
|
||||
return new Point(r.Left, r.Top);
|
||||
case System.Drawing.ContentAlignment.TopCenter:
|
||||
return new Point(r.X + (r.Width / 2), r.Top);
|
||||
case System.Drawing.ContentAlignment.TopRight:
|
||||
return new Point(r.Right, r.Top);
|
||||
case System.Drawing.ContentAlignment.MiddleLeft:
|
||||
return new Point(r.Left, r.Top + (r.Height / 2));
|
||||
case System.Drawing.ContentAlignment.MiddleCenter:
|
||||
return new Point(r.X + (r.Width / 2), r.Top + (r.Height / 2));
|
||||
case System.Drawing.ContentAlignment.MiddleRight:
|
||||
return new Point(r.Right, r.Top + (r.Height / 2));
|
||||
case System.Drawing.ContentAlignment.BottomLeft:
|
||||
return new Point(r.Left, r.Bottom);
|
||||
case System.Drawing.ContentAlignment.BottomCenter:
|
||||
return new Point(r.X + (r.Width / 2), r.Bottom);
|
||||
case System.Drawing.ContentAlignment.BottomRight:
|
||||
return new Point(r.Right, r.Bottom);
|
||||
}
|
||||
|
||||
// Should never reach here
|
||||
return r.Location;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given the item and the subitem, calculate its bounds.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="subItem"></param>
|
||||
/// <returns></returns>
|
||||
public virtual Rectangle CalculateItemBounds(OLVListItem item, OLVListSubItem subItem) {
|
||||
if (item == null)
|
||||
return Rectangle.Empty;
|
||||
|
||||
if (subItem == null)
|
||||
return item.Bounds;
|
||||
|
||||
return item.GetSubItemBounds(item.SubItems.IndexOf(subItem));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Commands
|
||||
|
||||
/// <summary>
|
||||
/// Apply any specified rotation to the Graphic content.
|
||||
/// </summary>
|
||||
/// <param name="g">The Graphics to be transformed</param>
|
||||
/// <param name="r">The rotation will be around the centre of this rect</param>
|
||||
protected virtual void ApplyRotation(Graphics g, Rectangle r) {
|
||||
if (this.Rotation == 0)
|
||||
return;
|
||||
|
||||
// THINK: Do we want to reset the transform? I think we want to push a new transform
|
||||
g.ResetTransform();
|
||||
Matrix m = new Matrix();
|
||||
m.RotateAt(this.Rotation, new Point(r.Left + r.Width / 2, r.Top + r.Height / 2));
|
||||
g.Transform = m;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reverse the rotation created by ApplyRotation()
|
||||
/// </summary>
|
||||
/// <param name="g"></param>
|
||||
protected virtual void UnapplyRotation(Graphics g) {
|
||||
if (this.Rotation != 0)
|
||||
g.ResetTransform();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An overlay that will draw an image over the top of the ObjectListView
|
||||
/// </summary>
|
||||
public class ImageAdornment : GraphicAdornment
|
||||
{
|
||||
#region Public properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the image that will be drawn
|
||||
/// </summary>
|
||||
[Category("ObjectListView"),
|
||||
Description("The image that will be drawn"),
|
||||
DefaultValue(null),
|
||||
NotifyParentProperty(true)]
|
||||
public Image Image {
|
||||
get { return this.image; }
|
||||
set { this.image = value; }
|
||||
}
|
||||
private Image image;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if the image will be shrunk to fit with its horizontal bounds
|
||||
/// </summary>
|
||||
[Category("ObjectListView"),
|
||||
Description("Will the image be shrunk to fit within its width?"),
|
||||
DefaultValue(false)]
|
||||
public bool ShrinkToWidth {
|
||||
get { return this.shrinkToWidth; }
|
||||
set { this.shrinkToWidth = value; }
|
||||
}
|
||||
private bool shrinkToWidth;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Commands
|
||||
|
||||
/// <summary>
|
||||
/// Draw the image in its specified location
|
||||
/// </summary>
|
||||
/// <param name="g">The Graphics used for drawing</param>
|
||||
/// <param name="r">The bounds of the rendering</param>
|
||||
public virtual void DrawImage(Graphics g, Rectangle r) {
|
||||
if (this.ShrinkToWidth)
|
||||
this.DrawScaledImage(g, r, this.Image, this.Transparency);
|
||||
else
|
||||
this.DrawImage(g, r, this.Image, this.Transparency);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw the image in its specified location
|
||||
/// </summary>
|
||||
/// <param name="image">The image to be drawn</param>
|
||||
/// <param name="g">The Graphics used for drawing</param>
|
||||
/// <param name="r">The bounds of the rendering</param>
|
||||
/// <param name="transparency">How transparent should the image be (0 is completely transparent, 255 is opaque)</param>
|
||||
public virtual void DrawImage(Graphics g, Rectangle r, Image image, int transparency) {
|
||||
if (image != null)
|
||||
this.DrawImage(g, r, image, image.Size, transparency);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw the image in its specified location
|
||||
/// </summary>
|
||||
/// <param name="image">The image to be drawn</param>
|
||||
/// <param name="g">The Graphics used for drawing</param>
|
||||
/// <param name="r">The bounds of the rendering</param>
|
||||
/// <param name="sz">How big should the image be?</param>
|
||||
/// <param name="transparency">How transparent should the image be (0 is completely transparent, 255 is opaque)</param>
|
||||
public virtual void DrawImage(Graphics g, Rectangle r, Image image, Size sz, int transparency) {
|
||||
if (image == null)
|
||||
return;
|
||||
|
||||
Rectangle adornmentBounds = this.CreateAlignedRectangle(r, sz);
|
||||
try {
|
||||
this.ApplyRotation(g, adornmentBounds);
|
||||
this.DrawTransparentBitmap(g, adornmentBounds, image, transparency);
|
||||
}
|
||||
finally {
|
||||
this.UnapplyRotation(g);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw the image in its specified location, scaled so that it is not wider
|
||||
/// than the given rectangle. Height is scaled proportional to the width.
|
||||
/// </summary>
|
||||
/// <param name="image">The image to be drawn</param>
|
||||
/// <param name="g">The Graphics used for drawing</param>
|
||||
/// <param name="r">The bounds of the rendering</param>
|
||||
/// <param name="transparency">How transparent should the image be (0 is completely transparent, 255 is opaque)</param>
|
||||
public virtual void DrawScaledImage(Graphics g, Rectangle r, Image image, int transparency) {
|
||||
if (image == null)
|
||||
return;
|
||||
|
||||
// If the image is too wide to be drawn in the space provided, proportionally scale it down.
|
||||
// Too tall images are not scaled.
|
||||
Size size = image.Size;
|
||||
if (image.Width > r.Width) {
|
||||
float scaleRatio = (float)r.Width / (float)image.Width;
|
||||
size.Height = (int)((float)image.Height * scaleRatio);
|
||||
size.Width = r.Width - 1;
|
||||
}
|
||||
|
||||
this.DrawImage(g, r, image, size, transparency);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Utility to draw a bitmap transparenly.
|
||||
/// </summary>
|
||||
/// <param name="g"></param>
|
||||
/// <param name="r"></param>
|
||||
/// <param name="image"></param>
|
||||
/// <param name="transparency"></param>
|
||||
protected virtual void DrawTransparentBitmap(Graphics g, Rectangle r, Image image, int transparency) {
|
||||
ImageAttributes imageAttributes = null;
|
||||
if (transparency != 255) {
|
||||
imageAttributes = new ImageAttributes();
|
||||
float a = (float)transparency / 255.0f;
|
||||
float[][] colorMatrixElements = {
|
||||
new float[] {1, 0, 0, 0, 0},
|
||||
new float[] {0, 1, 0, 0, 0},
|
||||
new float[] {0, 0, 1, 0, 0},
|
||||
new float[] {0, 0, 0, a, 0},
|
||||
new float[] {0, 0, 0, 0, 1}};
|
||||
|
||||
imageAttributes.SetColorMatrix(new ColorMatrix(colorMatrixElements));
|
||||
}
|
||||
|
||||
g.DrawImage(image,
|
||||
r, // destination rectangle
|
||||
0, 0, image.Size.Width, image.Size.Height, // source rectangle
|
||||
GraphicsUnit.Pixel,
|
||||
imageAttributes);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An adornment that will draw text
|
||||
/// </summary>
|
||||
public class TextAdornment : GraphicAdornment
|
||||
{
|
||||
#region Public properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the background color of the text
|
||||
/// Set this to Color.Empty to not draw a background
|
||||
/// </summary>
|
||||
[Category("ObjectListView"),
|
||||
Description("The background color of the text"),
|
||||
DefaultValue(typeof(Color), "")]
|
||||
public Color BackColor {
|
||||
get { return this.backColor; }
|
||||
set { this.backColor = value; }
|
||||
}
|
||||
private Color backColor = Color.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the brush that will be used to paint the text
|
||||
/// </summary>
|
||||
[Browsable(false)]
|
||||
public Brush BackgroundBrush {
|
||||
get {
|
||||
return new SolidBrush(Color.FromArgb(this.workingTransparency, this.BackColor));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color of the border around the billboard.
|
||||
/// Set this to Color.Empty to remove the border
|
||||
/// </summary>
|
||||
[Category("ObjectListView"),
|
||||
Description("The color of the border around the text"),
|
||||
DefaultValue(typeof(Color), "")]
|
||||
public Color BorderColor {
|
||||
get { return this.borderColor; }
|
||||
set { this.borderColor = value; }
|
||||
}
|
||||
private Color borderColor = Color.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the brush that will be used to paint the text
|
||||
/// </summary>
|
||||
[Browsable(false)]
|
||||
public Pen BorderPen {
|
||||
get {
|
||||
return new Pen(Color.FromArgb(this.workingTransparency, this.BorderColor), this.BorderWidth);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the width of the border around the text
|
||||
/// </summary>
|
||||
[Category("ObjectListView"),
|
||||
Description("The width of the border around the text"),
|
||||
DefaultValue(0.0f)]
|
||||
public float BorderWidth {
|
||||
get { return this.borderWidth; }
|
||||
set { this.borderWidth = value; }
|
||||
}
|
||||
private float borderWidth;
|
||||
|
||||
/// <summary>
|
||||
/// How rounded should the corners of the border be? 0 means no rounding.
|
||||
/// </summary>
|
||||
/// <remarks>If this value is too large, the edges of the border will appear odd.</remarks>
|
||||
[Category("ObjectListView"),
|
||||
Description("How rounded should the corners of the border be? 0 means no rounding."),
|
||||
DefaultValue(16.0f),
|
||||
NotifyParentProperty(true)]
|
||||
public float CornerRounding {
|
||||
get { return this.cornerRounding; }
|
||||
set { this.cornerRounding = value; }
|
||||
}
|
||||
private float cornerRounding = 16.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the font that will be used to draw the text
|
||||
/// </summary>
|
||||
[Category("ObjectListView"),
|
||||
Description("The font that will be used to draw the text"),
|
||||
DefaultValue(null),
|
||||
NotifyParentProperty(true)]
|
||||
public Font Font {
|
||||
get { return this.font; }
|
||||
set { this.font = value; }
|
||||
}
|
||||
private Font font;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the font that will be used to draw the text or a reasonable default
|
||||
/// </summary>
|
||||
[Browsable(false)]
|
||||
public Font FontOrDefault {
|
||||
get {
|
||||
return this.Font ?? new Font("Tahoma", 16);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does this text have a background?
|
||||
/// </summary>
|
||||
[Browsable(false)]
|
||||
public bool HasBackground {
|
||||
get {
|
||||
return this.BackColor != Color.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does this overlay have a border?
|
||||
/// </summary>
|
||||
[Browsable(false)]
|
||||
public bool HasBorder {
|
||||
get {
|
||||
return this.BorderColor != Color.Empty && this.BorderWidth > 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum width of the text. Text longer than this will wrap.
|
||||
/// 0 means no maximum.
|
||||
/// </summary>
|
||||
[Category("ObjectListView"),
|
||||
Description("The maximum width the text (0 means no maximum). Text longer than this will wrap"),
|
||||
DefaultValue(0)]
|
||||
public int MaximumTextWidth {
|
||||
get { return this.maximumTextWidth; }
|
||||
set { this.maximumTextWidth = value; }
|
||||
}
|
||||
private int maximumTextWidth;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the formatting that should be used on the text
|
||||
/// </summary>
|
||||
[Browsable(false),
|
||||
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
public virtual StringFormat StringFormat {
|
||||
get {
|
||||
if (this.stringFormat == null) {
|
||||
this.stringFormat = new StringFormat();
|
||||
this.stringFormat.Alignment = StringAlignment.Center;
|
||||
this.stringFormat.LineAlignment = StringAlignment.Center;
|
||||
this.stringFormat.Trimming = StringTrimming.EllipsisCharacter;
|
||||
if (!this.Wrap)
|
||||
this.stringFormat.FormatFlags = StringFormatFlags.NoWrap;
|
||||
}
|
||||
return this.stringFormat;
|
||||
}
|
||||
set { this.stringFormat = value; }
|
||||
}
|
||||
private StringFormat stringFormat;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the text that will be drawn
|
||||
/// </summary>
|
||||
[Category("ObjectListView"),
|
||||
Description("The text that will be drawn over the top of the ListView"),
|
||||
DefaultValue(null),
|
||||
NotifyParentProperty(true),
|
||||
Localizable(true)]
|
||||
public string Text {
|
||||
get { return this.text; }
|
||||
set { this.text = value; }
|
||||
}
|
||||
private string text;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the brush that will be used to paint the text
|
||||
/// </summary>
|
||||
[Browsable(false)]
|
||||
public Brush TextBrush {
|
||||
get {
|
||||
return new SolidBrush(Color.FromArgb(this.workingTransparency, this.TextColor));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color of the text
|
||||
/// </summary>
|
||||
[Category("ObjectListView"),
|
||||
Description("The color of the text"),
|
||||
DefaultValue(typeof(Color), "DarkBlue"),
|
||||
NotifyParentProperty(true)]
|
||||
public Color TextColor {
|
||||
get { return this.textColor; }
|
||||
set { this.textColor = value; }
|
||||
}
|
||||
private Color textColor = Color.DarkBlue;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the text will wrap when it exceeds its bounds
|
||||
/// </summary>
|
||||
[Category("ObjectListView"),
|
||||
Description("Will the text wrap?"),
|
||||
DefaultValue(true)]
|
||||
public bool Wrap {
|
||||
get { return this.wrap; }
|
||||
set { this.wrap = value; }
|
||||
}
|
||||
private bool wrap = true;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation
|
||||
|
||||
/// <summary>
|
||||
/// Draw our text with our stored configuration in relation to the given
|
||||
/// reference rectangle
|
||||
/// </summary>
|
||||
/// <param name="g">The Graphics used for drawing</param>
|
||||
/// <param name="r">The reference rectangle in relation to which the text will be drawn</param>
|
||||
public virtual void DrawText(Graphics g, Rectangle r) {
|
||||
this.DrawText(g, r, this.Text, this.Transparency);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw the given text with our stored configuration
|
||||
/// </summary>
|
||||
/// <param name="g">The Graphics used for drawing</param>
|
||||
/// <param name="r">The reference rectangle in relation to which the text will be drawn</param>
|
||||
/// <param name="s">The text to draw</param>
|
||||
/// <param name="transparency">How opaque should be text be</param>
|
||||
public virtual void DrawText(Graphics g, Rectangle r, string s, int transparency) {
|
||||
if (String.IsNullOrEmpty(s))
|
||||
return;
|
||||
|
||||
Rectangle textRect = this.CalculateTextBounds(g, r, s);
|
||||
this.DrawBorderedText(g, textRect, s, transparency);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw the text with a border
|
||||
/// </summary>
|
||||
/// <param name="g">The Graphics used for drawing</param>
|
||||
/// <param name="textRect">The bounds within which the text should be drawn</param>
|
||||
/// <param name="text">The text to draw</param>
|
||||
/// <param name="transparency">How opaque should be text be</param>
|
||||
protected virtual void DrawBorderedText(Graphics g, Rectangle textRect, string text, int transparency) {
|
||||
Rectangle borderRect = textRect;
|
||||
borderRect.Inflate((int)this.BorderWidth / 2, (int)this.BorderWidth / 2);
|
||||
borderRect.Y -= 1; // Looker better a little higher
|
||||
|
||||
try {
|
||||
this.ApplyRotation(g, textRect);
|
||||
using (GraphicsPath path = this.GetRoundedRect(borderRect, this.CornerRounding)) {
|
||||
this.workingTransparency = transparency;
|
||||
if (this.HasBackground) {
|
||||
using (Brush b = this.BackgroundBrush)
|
||||
g.FillPath(b, path);
|
||||
}
|
||||
|
||||
using (Brush b = this.TextBrush)
|
||||
g.DrawString(text, this.FontOrDefault, b, textRect, this.StringFormat);
|
||||
|
||||
if (this.HasBorder) {
|
||||
using (Pen p = this.BorderPen)
|
||||
g.DrawPath(p, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
this.UnapplyRotation(g);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the rectangle that will be the precise bounds of the displayed text
|
||||
/// </summary>
|
||||
/// <param name="g"></param>
|
||||
/// <param name="r"></param>
|
||||
/// <param name="s"></param>
|
||||
/// <returns>The bounds of the text</returns>
|
||||
protected virtual Rectangle CalculateTextBounds(Graphics g, Rectangle r, string s) {
|
||||
int maxWidth = this.MaximumTextWidth <= 0 ? r.Width : this.MaximumTextWidth;
|
||||
SizeF sizeF = g.MeasureString(s, this.FontOrDefault, maxWidth, this.StringFormat);
|
||||
Size size = new Size(1 + (int)sizeF.Width, 1 + (int)sizeF.Height);
|
||||
return this.CreateAlignedRectangle(r, size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a GraphicPath that is a round cornered rectangle
|
||||
/// </summary>
|
||||
/// <param name="rect">The rectangle</param>
|
||||
/// <param name="diameter">The diameter of the corners</param>
|
||||
/// <returns>A round cornered rectagle path</returns>
|
||||
/// <remarks>If I could rely on people using C# 3.0+, this should be
|
||||
/// an extension method of GraphicsPath.</remarks>
|
||||
protected virtual GraphicsPath GetRoundedRect(Rectangle rect, float diameter) {
|
||||
GraphicsPath path = new GraphicsPath();
|
||||
|
||||
if (diameter > 0) {
|
||||
RectangleF arc = new RectangleF(rect.X, rect.Y, diameter, diameter);
|
||||
path.AddArc(arc, 180, 90);
|
||||
arc.X = rect.Right - diameter;
|
||||
path.AddArc(arc, 270, 90);
|
||||
arc.Y = rect.Bottom - diameter;
|
||||
path.AddArc(arc, 0, 90);
|
||||
arc.X = rect.Left;
|
||||
path.AddArc(arc, 90, 90);
|
||||
path.CloseFigure();
|
||||
} else {
|
||||
path.AddRectangle(rect);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private int workingTransparency;
|
||||
}
|
||||
}
|
||||
820
ObjectListView/Rendering/Decorations.cs
Normal file
820
ObjectListView/Rendering/Decorations.cs
Normal file
@@ -0,0 +1,820 @@
|
||||
/*
|
||||
* Decorations - Images, text or other things that can be rendered onto an ObjectListView
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 19/08/2009 10:56 PM
|
||||
*
|
||||
* Change log:
|
||||
* 2011-04-04 JPP - Added ability to have a gradient background on BorderDecoration
|
||||
* v2.4
|
||||
* 2010-04-15 JPP - Tweaked LightBoxDecoration a little
|
||||
* v2.3
|
||||
* 2009-09-23 JPP - Added LeftColumn and RightColumn to RowBorderDecoration
|
||||
* 2009-08-23 JPP - Added LightBoxDecoration
|
||||
* 2009-08-19 JPP - Initial version. Separated from Overlays.cs
|
||||
*
|
||||
* To do:
|
||||
*
|
||||
* Copyright (C) 2009-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace BrightIdeasSoftware
|
||||
{
|
||||
/// <summary>
|
||||
/// A decoration is an overlay that draws itself in relation to a given row or cell.
|
||||
/// Decorations scroll when the listview scrolls.
|
||||
/// </summary>
|
||||
public interface IDecoration : IOverlay
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the row that is to be decorated
|
||||
/// </summary>
|
||||
OLVListItem ListItem { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the subitem that is to be decorated
|
||||
/// </summary>
|
||||
OLVListSubItem SubItem { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An AbstractDecoration is a safe do-nothing implementation of the IDecoration interface
|
||||
/// </summary>
|
||||
public class AbstractDecoration : IDecoration
|
||||
{
|
||||
#region IDecoration Members
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the row that is to be decorated
|
||||
/// </summary>
|
||||
public OLVListItem ListItem {
|
||||
get { return listItem; }
|
||||
set { listItem = value; }
|
||||
}
|
||||
private OLVListItem listItem;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the subitem that is to be decorated
|
||||
/// </summary>
|
||||
public OLVListSubItem SubItem {
|
||||
get { return subItem; }
|
||||
set { subItem = value; }
|
||||
}
|
||||
private OLVListSubItem subItem;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bounds of the decorations row
|
||||
/// </summary>
|
||||
public Rectangle RowBounds {
|
||||
get {
|
||||
if (this.ListItem == null)
|
||||
return Rectangle.Empty;
|
||||
else
|
||||
return this.ListItem.Bounds;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the bounds of the decorations cell
|
||||
/// </summary>
|
||||
public Rectangle CellBounds {
|
||||
get {
|
||||
if (this.ListItem == null || this.SubItem == null)
|
||||
return Rectangle.Empty;
|
||||
else
|
||||
return this.ListItem.GetSubItemBounds(this.ListItem.SubItems.IndexOf(this.SubItem));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IOverlay Members
|
||||
|
||||
/// <summary>
|
||||
/// Draw the decoration
|
||||
/// </summary>
|
||||
/// <param name="olv"></param>
|
||||
/// <param name="g"></param>
|
||||
/// <param name="r"></param>
|
||||
public virtual void Draw(ObjectListView olv, Graphics g, Rectangle r) {
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This decoration draws a slight tint over a column of the
|
||||
/// owning listview. If no column is explicitly set, the selected
|
||||
/// column in the listview will be used.
|
||||
/// The selected column is normally the sort column, but does not have to be.
|
||||
/// </summary>
|
||||
public class TintedColumnDecoration : AbstractDecoration
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Create a TintedColumnDecoration
|
||||
/// </summary>
|
||||
public TintedColumnDecoration() {
|
||||
this.Tint = Color.FromArgb(15, Color.Blue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a TintedColumnDecoration
|
||||
/// </summary>
|
||||
/// <param name="column"></param>
|
||||
public TintedColumnDecoration(OLVColumn column)
|
||||
: this() {
|
||||
this.ColumnToTint = column;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the column that will be tinted
|
||||
/// </summary>
|
||||
public OLVColumn ColumnToTint {
|
||||
get { return this.columnToTint; }
|
||||
set { this.columnToTint = value; }
|
||||
}
|
||||
private OLVColumn columnToTint;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color that will be 'tinted' over the selected column
|
||||
/// </summary>
|
||||
public Color Tint {
|
||||
get { return this.tint; }
|
||||
set {
|
||||
if (this.tint == value)
|
||||
return;
|
||||
|
||||
if (this.tintBrush != null) {
|
||||
this.tintBrush.Dispose();
|
||||
this.tintBrush = null;
|
||||
}
|
||||
|
||||
this.tint = value;
|
||||
this.tintBrush = new SolidBrush(this.tint);
|
||||
}
|
||||
}
|
||||
private Color tint;
|
||||
private SolidBrush tintBrush;
|
||||
|
||||
#endregion
|
||||
|
||||
#region IOverlay Members
|
||||
|
||||
/// <summary>
|
||||
/// Draw a slight colouring over our tinted column
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This overlay only works when:
|
||||
/// - the list is in Details view
|
||||
/// - there is at least one row
|
||||
/// - there is a selected column (or a specified tint column)
|
||||
/// </remarks>
|
||||
/// <param name="olv"></param>
|
||||
/// <param name="g"></param>
|
||||
/// <param name="r"></param>
|
||||
public override void Draw(ObjectListView olv, Graphics g, Rectangle r) {
|
||||
|
||||
if (olv.View != System.Windows.Forms.View.Details)
|
||||
return;
|
||||
|
||||
if (olv.GetItemCount() == 0)
|
||||
return;
|
||||
|
||||
OLVColumn column = this.ColumnToTint ?? olv.SelectedColumn;
|
||||
if (column == null)
|
||||
return;
|
||||
|
||||
Point sides = NativeMethods.GetScrolledColumnSides(olv, column.Index);
|
||||
if (sides.X == -1)
|
||||
return;
|
||||
|
||||
Rectangle columnBounds = new Rectangle(sides.X, r.Top, sides.Y - sides.X, r.Bottom);
|
||||
|
||||
// Find the bottom of the last item. The tinting should extend only to there.
|
||||
OLVListItem lastItem = olv.GetLastItemInDisplayOrder();
|
||||
if (lastItem != null) {
|
||||
Rectangle lastItemBounds = lastItem.Bounds;
|
||||
if (!lastItemBounds.IsEmpty && lastItemBounds.Bottom < columnBounds.Bottom)
|
||||
columnBounds.Height = lastItemBounds.Bottom - columnBounds.Top;
|
||||
}
|
||||
g.FillRectangle(this.tintBrush, columnBounds);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This decoration draws an optionally filled border around a rectangle.
|
||||
/// Subclasses must override CalculateBounds().
|
||||
/// </summary>
|
||||
public class BorderDecoration : AbstractDecoration
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Create a BorderDecoration
|
||||
/// </summary>
|
||||
public BorderDecoration()
|
||||
: this(new Pen(Color.FromArgb(64, Color.Blue), 1)) {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a BorderDecoration
|
||||
/// </summary>
|
||||
/// <param name="borderPen">The pen used to draw the border</param>
|
||||
public BorderDecoration(Pen borderPen) {
|
||||
this.BorderPen = borderPen;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a BorderDecoration
|
||||
/// </summary>
|
||||
/// <param name="borderPen">The pen used to draw the border</param>
|
||||
/// <param name="fill">The brush used to fill the rectangle</param>
|
||||
public BorderDecoration(Pen borderPen, Brush fill) {
|
||||
this.BorderPen = borderPen;
|
||||
this.FillBrush = fill;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the pen that will be used to draw the border
|
||||
/// </summary>
|
||||
public Pen BorderPen {
|
||||
get { return this.borderPen; }
|
||||
set { this.borderPen = value; }
|
||||
}
|
||||
private Pen borderPen;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the padding that will be added to the bounds of the item
|
||||
/// before drawing the border and fill.
|
||||
/// </summary>
|
||||
public Size BoundsPadding {
|
||||
get { return this.boundsPadding; }
|
||||
set { this.boundsPadding = value; }
|
||||
}
|
||||
private Size boundsPadding = new Size(-1, 2);
|
||||
|
||||
/// <summary>
|
||||
/// How rounded should the corners of the border be? 0 means no rounding.
|
||||
/// </summary>
|
||||
/// <remarks>If this value is too large, the edges of the border will appear odd.</remarks>
|
||||
public float CornerRounding {
|
||||
get { return this.cornerRounding; }
|
||||
set { this.cornerRounding = value; }
|
||||
}
|
||||
private float cornerRounding = 16.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the brush that will be used to fill the border
|
||||
/// </summary>
|
||||
/// <remarks>This value is ignored when using gradient brush</remarks>
|
||||
public Brush FillBrush {
|
||||
get { return this.fillBrush; }
|
||||
set { this.fillBrush = value; }
|
||||
}
|
||||
private Brush fillBrush = new SolidBrush(Color.FromArgb(64, Color.Blue));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color that will be used as the start of a gradient fill.
|
||||
/// </summary>
|
||||
/// <remarks>This and FillGradientTo must be given value to show a gradient</remarks>
|
||||
public Color? FillGradientFrom {
|
||||
get { return this.fillGradientFrom; }
|
||||
set { this.fillGradientFrom = value; }
|
||||
}
|
||||
private Color? fillGradientFrom;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color that will be used as the end of a gradient fill.
|
||||
/// </summary>
|
||||
/// <remarks>This and FillGradientFrom must be given value to show a gradient</remarks>
|
||||
public Color? FillGradientTo {
|
||||
get { return this.fillGradientTo; }
|
||||
set { this.fillGradientTo = value; }
|
||||
}
|
||||
private Color? fillGradientTo;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the fill mode that will be used for the gradient.
|
||||
/// </summary>
|
||||
public LinearGradientMode FillGradientMode {
|
||||
get { return this.fillGradientMode; }
|
||||
set { this.fillGradientMode = value; }
|
||||
}
|
||||
private LinearGradientMode fillGradientMode = LinearGradientMode.Vertical;
|
||||
|
||||
#endregion
|
||||
|
||||
#region IOverlay Members
|
||||
|
||||
/// <summary>
|
||||
/// Draw a filled border
|
||||
/// </summary>
|
||||
/// <param name="olv"></param>
|
||||
/// <param name="g"></param>
|
||||
/// <param name="r"></param>
|
||||
public override void Draw(ObjectListView olv, Graphics g, Rectangle r) {
|
||||
Rectangle bounds = this.CalculateBounds();
|
||||
if (!bounds.IsEmpty)
|
||||
this.DrawFilledBorder(g, bounds);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Subclass responsibility
|
||||
|
||||
/// <summary>
|
||||
/// Subclasses should override this to say where the border should be drawn
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual Rectangle CalculateBounds() {
|
||||
return Rectangle.Empty;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation utlities
|
||||
|
||||
/// <summary>
|
||||
/// Do the actual work of drawing the filled border
|
||||
/// </summary>
|
||||
/// <param name="g"></param>
|
||||
/// <param name="bounds"></param>
|
||||
protected void DrawFilledBorder(Graphics g, Rectangle bounds) {
|
||||
bounds.Inflate(this.BoundsPadding);
|
||||
GraphicsPath path = this.GetRoundedRect(bounds, this.CornerRounding);
|
||||
if (this.FillGradientFrom != null && this.FillGradientTo != null) {
|
||||
if (this.FillBrush != null)
|
||||
this.FillBrush.Dispose();
|
||||
this.FillBrush = new LinearGradientBrush(bounds, this.FillGradientFrom.Value, this.FillGradientTo.Value, this.FillGradientMode);
|
||||
}
|
||||
if (this.FillBrush != null)
|
||||
g.FillPath(this.FillBrush, path);
|
||||
if (this.BorderPen != null)
|
||||
g.DrawPath(this.BorderPen, path);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a GraphicsPath that represents a round cornered rectangle.
|
||||
/// </summary>
|
||||
/// <param name="rect"></param>
|
||||
/// <param name="diameter">If this is 0 or less, the rectangle will not be rounded.</param>
|
||||
/// <returns></returns>
|
||||
protected GraphicsPath GetRoundedRect(RectangleF rect, float diameter) {
|
||||
GraphicsPath path = new GraphicsPath();
|
||||
|
||||
if (diameter <= 0.0f) {
|
||||
path.AddRectangle(rect);
|
||||
} else {
|
||||
RectangleF arc = new RectangleF(rect.X, rect.Y, diameter, diameter);
|
||||
path.AddArc(arc, 180, 90);
|
||||
arc.X = rect.Right - diameter;
|
||||
path.AddArc(arc, 270, 90);
|
||||
arc.Y = rect.Bottom - diameter;
|
||||
path.AddArc(arc, 0, 90);
|
||||
arc.X = rect.Left;
|
||||
path.AddArc(arc, 90, 90);
|
||||
path.CloseFigure();
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instances of this class draw a border around the decorated row
|
||||
/// </summary>
|
||||
public class RowBorderDecoration : BorderDecoration
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the index of the left most column to be used for the border
|
||||
/// </summary>
|
||||
public int LeftColumn {
|
||||
get { return leftColumn; }
|
||||
set { leftColumn = value; }
|
||||
}
|
||||
private int leftColumn = -1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the index of the right most column to be used for the border
|
||||
/// </summary>
|
||||
public int RightColumn {
|
||||
get { return rightColumn; }
|
||||
set { rightColumn = value; }
|
||||
}
|
||||
private int rightColumn = -1;
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the boundaries of the border
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected override Rectangle CalculateBounds() {
|
||||
Rectangle bounds = this.RowBounds;
|
||||
if (this.ListItem == null)
|
||||
return bounds;
|
||||
|
||||
if (this.LeftColumn >= 0) {
|
||||
Rectangle leftCellBounds = this.ListItem.GetSubItemBounds(this.LeftColumn);
|
||||
if (!leftCellBounds.IsEmpty) {
|
||||
bounds.Width = bounds.Right - leftCellBounds.Left;
|
||||
bounds.X = leftCellBounds.Left;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.RightColumn >= 0) {
|
||||
Rectangle rightCellBounds = this.ListItem.GetSubItemBounds(this.RightColumn);
|
||||
if (!rightCellBounds.IsEmpty) {
|
||||
bounds.Width = rightCellBounds.Right - bounds.Left;
|
||||
}
|
||||
}
|
||||
|
||||
return bounds;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instances of this class draw a border around the decorated subitem.
|
||||
/// </summary>
|
||||
public class CellBorderDecoration : BorderDecoration
|
||||
{
|
||||
/// <summary>
|
||||
/// Calculate the boundaries of the border
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected override Rectangle CalculateBounds() {
|
||||
return this.CellBounds;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This decoration puts a border around the cell being edited and
|
||||
/// optionally "lightboxes" the cell (makes the rest of the control dark).
|
||||
/// </summary>
|
||||
public class EditingCellBorderDecoration : BorderDecoration
|
||||
{
|
||||
#region Life and death
|
||||
|
||||
/// <summary>
|
||||
/// Create a EditingCellBorderDecoration
|
||||
/// </summary>
|
||||
public EditingCellBorderDecoration() {
|
||||
this.FillBrush = null;
|
||||
this.BorderPen = new Pen(Color.DarkBlue, 2);
|
||||
this.CornerRounding = 8;
|
||||
this.BoundsPadding = new Size(10, 8);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a EditingCellBorderDecoration
|
||||
/// </summary>
|
||||
/// <param name="useLightBox">Should the decoration use a lighbox display style?</param>
|
||||
public EditingCellBorderDecoration(bool useLightBox) : this()
|
||||
{
|
||||
this.UseLightbox = useLightbox;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Configuration properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or set whether the decoration should make the rest of
|
||||
/// the control dark when a cell is being edited
|
||||
/// </summary>
|
||||
/// <remarks>If this is true, FillBrush is used to overpaint
|
||||
/// the control.</remarks>
|
||||
public bool UseLightbox {
|
||||
get { return this.useLightbox; }
|
||||
set {
|
||||
if (this.useLightbox == value)
|
||||
return;
|
||||
this.useLightbox = value;
|
||||
if (this.useLightbox) {
|
||||
if (this.FillBrush == null)
|
||||
this.FillBrush = new SolidBrush(Color.FromArgb(64, Color.Black));
|
||||
}
|
||||
}
|
||||
}
|
||||
private bool useLightbox;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Implementation
|
||||
|
||||
/// <summary>
|
||||
/// Draw the decoration
|
||||
/// </summary>
|
||||
/// <param name="olv"></param>
|
||||
/// <param name="g"></param>
|
||||
/// <param name="r"></param>
|
||||
public override void Draw(ObjectListView olv, Graphics g, Rectangle r) {
|
||||
if (!olv.IsCellEditing)
|
||||
return;
|
||||
|
||||
Rectangle bounds = olv.CellEditor.Bounds;
|
||||
if (bounds.IsEmpty)
|
||||
return;
|
||||
|
||||
bounds.Inflate(this.BoundsPadding);
|
||||
GraphicsPath path = this.GetRoundedRect(bounds, this.CornerRounding);
|
||||
if (this.FillBrush != null) {
|
||||
if (this.UseLightbox) {
|
||||
using (Region newClip = new Region(r)) {
|
||||
newClip.Exclude(path);
|
||||
Region originalClip = g.Clip;
|
||||
g.Clip = newClip;
|
||||
g.FillRectangle(this.FillBrush, r);
|
||||
g.Clip = originalClip;
|
||||
}
|
||||
} else {
|
||||
g.FillPath(this.FillBrush, path);
|
||||
}
|
||||
}
|
||||
if (this.BorderPen != null)
|
||||
g.DrawPath(this.BorderPen, path);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This decoration causes everything *except* the row under the mouse to be overpainted
|
||||
/// with a tint, making the row under the mouse stand out in comparison.
|
||||
/// The darker and more opaque the fill color, the more obvious the
|
||||
/// decorated row becomes.
|
||||
/// </summary>
|
||||
public class LightBoxDecoration : BorderDecoration
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a LightBoxDecoration
|
||||
/// </summary>
|
||||
public LightBoxDecoration() {
|
||||
this.BoundsPadding = new Size(-1, 4);
|
||||
this.CornerRounding = 8.0f;
|
||||
this.FillBrush = new SolidBrush(Color.FromArgb(72, Color.Black));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draw a tint over everything in the ObjectListView except the
|
||||
/// row under the mouse.
|
||||
/// </summary>
|
||||
/// <param name="olv"></param>
|
||||
/// <param name="g"></param>
|
||||
/// <param name="r"></param>
|
||||
public override void Draw(ObjectListView olv, Graphics g, Rectangle r) {
|
||||
if (!r.Contains(olv.PointToClient(Cursor.Position)))
|
||||
return;
|
||||
|
||||
Rectangle bounds = this.RowBounds;
|
||||
if (bounds.IsEmpty) {
|
||||
if (olv.View == View.Tile)
|
||||
g.FillRectangle(this.FillBrush, r);
|
||||
return;
|
||||
}
|
||||
|
||||
using (Region newClip = new Region(r)) {
|
||||
bounds.Inflate(this.BoundsPadding);
|
||||
newClip.Exclude(this.GetRoundedRect(bounds, this.CornerRounding));
|
||||
Region originalClip = g.Clip;
|
||||
g.Clip = newClip;
|
||||
g.FillRectangle(this.FillBrush, r);
|
||||
g.Clip = originalClip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instances of this class put an Image over the row/cell that it is decorating
|
||||
/// </summary>
|
||||
public class ImageDecoration : ImageAdornment, IDecoration
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Create an image decoration
|
||||
/// </summary>
|
||||
public ImageDecoration() {
|
||||
this.Alignment = ContentAlignment.MiddleRight;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an image decoration
|
||||
/// </summary>
|
||||
/// <param name="image"></param>
|
||||
public ImageDecoration(Image image)
|
||||
: this() {
|
||||
this.Image = image;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an image decoration
|
||||
/// </summary>
|
||||
/// <param name="image"></param>
|
||||
/// <param name="transparency"></param>
|
||||
public ImageDecoration(Image image, int transparency)
|
||||
: this() {
|
||||
this.Image = image;
|
||||
this.Transparency = transparency;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an image decoration
|
||||
/// </summary>
|
||||
/// <param name="image"></param>
|
||||
/// <param name="alignment"></param>
|
||||
public ImageDecoration(Image image, ContentAlignment alignment)
|
||||
: this() {
|
||||
this.Image = image;
|
||||
this.Alignment = alignment;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an image decoration
|
||||
/// </summary>
|
||||
/// <param name="image"></param>
|
||||
/// <param name="transparency"></param>
|
||||
/// <param name="alignment"></param>
|
||||
public ImageDecoration(Image image, int transparency, ContentAlignment alignment)
|
||||
: this() {
|
||||
this.Image = image;
|
||||
this.Transparency = transparency;
|
||||
this.Alignment = alignment;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDecoration Members
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the item being decorated
|
||||
/// </summary>
|
||||
public OLVListItem ListItem {
|
||||
get { return listItem; }
|
||||
set { listItem = value; }
|
||||
}
|
||||
private OLVListItem listItem;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the sub item being decorated
|
||||
/// </summary>
|
||||
public OLVListSubItem SubItem {
|
||||
get { return subItem; }
|
||||
set { subItem = value; }
|
||||
}
|
||||
private OLVListSubItem subItem;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Commands
|
||||
|
||||
/// <summary>
|
||||
/// Draw this decoration
|
||||
/// </summary>
|
||||
/// <param name="olv">The ObjectListView being decorated</param>
|
||||
/// <param name="g">The Graphics used for drawing</param>
|
||||
/// <param name="r">The bounds of the rendering</param>
|
||||
public virtual void Draw(ObjectListView olv, Graphics g, Rectangle r) {
|
||||
this.DrawImage(g, this.CalculateItemBounds(this.ListItem, this.SubItem));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instances of this class draw some text over the row/cell that they are decorating
|
||||
/// </summary>
|
||||
public class TextDecoration : TextAdornment, IDecoration
|
||||
{
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Create a TextDecoration
|
||||
/// </summary>
|
||||
public TextDecoration() {
|
||||
this.Alignment = ContentAlignment.MiddleRight;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a TextDecoration
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
public TextDecoration(string text)
|
||||
: this() {
|
||||
this.Text = text;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a TextDecoration
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <param name="transparency"></param>
|
||||
public TextDecoration(string text, int transparency)
|
||||
: this() {
|
||||
this.Text = text;
|
||||
this.Transparency = transparency;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a TextDecoration
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <param name="alignment"></param>
|
||||
public TextDecoration(string text, ContentAlignment alignment)
|
||||
: this() {
|
||||
this.Text = text;
|
||||
this.Alignment = alignment;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a TextDecoration
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// <param name="transparency"></param>
|
||||
/// <param name="alignment"></param>
|
||||
public TextDecoration(string text, int transparency, ContentAlignment alignment)
|
||||
: this() {
|
||||
this.Text = text;
|
||||
this.Transparency = transparency;
|
||||
this.Alignment = alignment;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDecoration Members
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the item being decorated
|
||||
/// </summary>
|
||||
public OLVListItem ListItem {
|
||||
get { return listItem; }
|
||||
set { listItem = value; }
|
||||
}
|
||||
private OLVListItem listItem;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the sub item being decorated
|
||||
/// </summary>
|
||||
public OLVListSubItem SubItem {
|
||||
get { return subItem; }
|
||||
set { subItem = value; }
|
||||
}
|
||||
private OLVListSubItem subItem;
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Commands
|
||||
|
||||
/// <summary>
|
||||
/// Draw this decoration
|
||||
/// </summary>
|
||||
/// <param name="olv">The ObjectListView being decorated</param>
|
||||
/// <param name="g">The Graphics used for drawing</param>
|
||||
/// <param name="r">The bounds of the rendering</param>
|
||||
public virtual void Draw(ObjectListView olv, Graphics g, Rectangle r) {
|
||||
this.DrawText(g, this.CalculateItemBounds(this.ListItem, this.SubItem));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
302
ObjectListView/Rendering/Overlays.cs
Normal file
302
ObjectListView/Rendering/Overlays.cs
Normal file
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
* Overlays - Images, text or other things that can be rendered over the top of a ListView
|
||||
*
|
||||
* Author: Phillip Piper
|
||||
* Date: 14/04/2009 4:36 PM
|
||||
*
|
||||
* Change log:
|
||||
* v2.3
|
||||
* 2009-08-17 JPP - Overlays now use Adornments
|
||||
* - Added ITransparentOverlay interface. Overlays can now have separate transparency levels
|
||||
* 2009-08-10 JPP - Moved decoration related code to new file
|
||||
* v2.2.1
|
||||
* 200-07-24 JPP - TintedColumnDecoration now works when last item is a member of a collapsed
|
||||
* group (well, it no longer crashes).
|
||||
* v2.2
|
||||
* 2009-06-01 JPP - Make sure that TintedColumnDecoration reaches to the last item in group view
|
||||
* 2009-05-05 JPP - Unified BillboardOverlay text rendering with that of TextOverlay
|
||||
* 2009-04-30 JPP - Added TintedColumnDecoration
|
||||
* 2009-04-14 JPP - Initial version
|
||||
*
|
||||
* To do:
|
||||
*
|
||||
* Copyright (C) 2009-2014 Phillip Piper
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* If you wish to use this code in a closed source application, please contact phillip.piper@gmail.com.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Drawing.Imaging;
|
||||
|
||||
namespace BrightIdeasSoftware
|
||||
{
|
||||
/// <summary>
|
||||
/// The interface for an object which can draw itself over the top of
|
||||
/// an ObjectListView.
|
||||
/// </summary>
|
||||
public interface IOverlay
|
||||
{
|
||||
/// <summary>
|
||||
/// Draw this overlay
|
||||
/// </summary>
|
||||
/// <param name="olv">The ObjectListView that is being overlaid</param>
|
||||
/// <param name="g">The Graphics onto the given OLV</param>
|
||||
/// <param name="r">The content area of the OLV</param>
|
||||
void Draw(ObjectListView olv, Graphics g, Rectangle r);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An interface for an overlay that supports variable levels of transparency
|
||||
/// </summary>
|
||||
public interface ITransparentOverlay : IOverlay
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the transparency of the overlay.
|
||||
/// 0 is completely transparent, 255 is completely opaque.
|
||||
/// </summary>
|
||||
int Transparency { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A null implementation of the IOverlay interface
|
||||
/// </summary>
|
||||
public class AbstractOverlay : ITransparentOverlay
|
||||
{
|
||||
#region IOverlay Members
|
||||
|
||||
/// <summary>
|
||||
/// Draw this overlay
|
||||
/// </summary>
|
||||
/// <param name="olv">The ObjectListView that is being overlaid</param>
|
||||
/// <param name="g">The Graphics onto the given OLV</param>
|
||||
/// <param name="r">The content area of the OLV</param>
|
||||
public virtual void Draw(ObjectListView olv, Graphics g, Rectangle r) {
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ITransparentOverlay Members
|
||||
|
||||
/// <summary>
|
||||
/// How transparent should this overlay be?
|
||||
/// </summary>
|
||||
[Category("ObjectListView"),
|
||||
Description("How transparent should this overlay be"),
|
||||
DefaultValue(128),
|
||||
NotifyParentProperty(true)]
|
||||
public int Transparency {
|
||||
get { return this.transparency; }
|
||||
set { this.transparency = Math.Min(255, Math.Max(0, value)); }
|
||||
}
|
||||
private int transparency = 128;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An overlay that will draw an image over the top of the ObjectListView
|
||||
/// </summary>
|
||||
[TypeConverter("BrightIdeasSoftware.Design.OverlayConverter")]
|
||||
public class ImageOverlay : ImageAdornment, ITransparentOverlay
|
||||
{
|
||||
/// <summary>
|
||||
/// Create an ImageOverlay
|
||||
/// </summary>
|
||||
public ImageOverlay() {
|
||||
this.Alignment = System.Drawing.ContentAlignment.BottomRight;
|
||||
}
|
||||
|
||||
#region Public properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the horizontal inset by which the position of the overlay will be adjusted
|
||||
/// </summary>
|
||||
[Category("ObjectListView"),
|
||||
Description("The horizontal inset by which the position of the overlay will be adjusted"),
|
||||
DefaultValue(20),
|
||||
NotifyParentProperty(true)]
|
||||
public int InsetX {
|
||||
get { return this.insetX; }
|
||||
set { this.insetX = Math.Max(0, value); }
|
||||
}
|
||||
private int insetX = 20;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the vertical inset by which the position of the overlay will be adjusted
|
||||
/// </summary>
|
||||
[Category("ObjectListView"),
|
||||
Description("Gets or sets the vertical inset by which the position of the overlay will be adjusted"),
|
||||
DefaultValue(20),
|
||||
NotifyParentProperty(true)]
|
||||
public int InsetY {
|
||||
get { return this.insetY; }
|
||||
set { this.insetY = Math.Max(0, value); }
|
||||
}
|
||||
private int insetY = 20;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Commands
|
||||
|
||||
/// <summary>
|
||||
/// Draw this overlay
|
||||
/// </summary>
|
||||
/// <param name="olv">The ObjectListView being decorated</param>
|
||||
/// <param name="g">The Graphics used for drawing</param>
|
||||
/// <param name="r">The bounds of the rendering</param>
|
||||
public virtual void Draw(ObjectListView olv, Graphics g, Rectangle r) {
|
||||
Rectangle insetRect = r;
|
||||
insetRect.Inflate(-this.InsetX, -this.InsetY);
|
||||
|
||||
// We hard code a transparency of 255 here since transparency is handled by the glass panel
|
||||
this.DrawImage(g, insetRect, this.Image, 255);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An overlay that will draw text over the top of the ObjectListView
|
||||
/// </summary>
|
||||
[TypeConverter("BrightIdeasSoftware.Design.OverlayConverter")]
|
||||
public class TextOverlay : TextAdornment, ITransparentOverlay
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a TextOverlay
|
||||
/// </summary>
|
||||
public TextOverlay() {
|
||||
this.Alignment = System.Drawing.ContentAlignment.BottomRight;
|
||||
}
|
||||
|
||||
#region Public properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the horizontal inset by which the position of the overlay will be adjusted
|
||||
/// </summary>
|
||||
[Category("ObjectListView"),
|
||||
Description("The horizontal inset by which the position of the overlay will be adjusted"),
|
||||
DefaultValue(20),
|
||||
NotifyParentProperty(true)]
|
||||
public int InsetX {
|
||||
get { return this.insetX; }
|
||||
set { this.insetX = Math.Max(0, value); }
|
||||
}
|
||||
private int insetX = 20;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the vertical inset by which the position of the overlay will be adjusted
|
||||
/// </summary>
|
||||
[Category("ObjectListView"),
|
||||
Description("Gets or sets the vertical inset by which the position of the overlay will be adjusted"),
|
||||
DefaultValue(20),
|
||||
NotifyParentProperty(true)]
|
||||
public int InsetY {
|
||||
get { return this.insetY; }
|
||||
set { this.insetY = Math.Max(0, value); }
|
||||
}
|
||||
private int insetY = 20;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the border will be drawn with rounded corners
|
||||
/// </summary>
|
||||
[Browsable(false),
|
||||
Obsolete("Use CornerRounding instead", false),
|
||||
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||||
public bool RoundCorneredBorder {
|
||||
get { return this.CornerRounding > 0; }
|
||||
set {
|
||||
if (value)
|
||||
this.CornerRounding = 16.0f;
|
||||
else
|
||||
this.CornerRounding = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Commands
|
||||
|
||||
/// <summary>
|
||||
/// Draw this overlay
|
||||
/// </summary>
|
||||
/// <param name="olv">The ObjectListView being decorated</param>
|
||||
/// <param name="g">The Graphics used for drawing</param>
|
||||
/// <param name="r">The bounds of the rendering</param>
|
||||
public virtual void Draw(ObjectListView olv, Graphics g, Rectangle r) {
|
||||
if (String.IsNullOrEmpty(this.Text))
|
||||
return;
|
||||
|
||||
Rectangle insetRect = r;
|
||||
insetRect.Inflate(-this.InsetX, -this.InsetY);
|
||||
// We hard code a transparency of 255 here since transparency is handled by the glass panel
|
||||
this.DrawText(g, insetRect, this.Text, 255);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A Billboard overlay is a TextOverlay positioned at an absolute point
|
||||
/// </summary>
|
||||
public class BillboardOverlay : TextOverlay
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a BillboardOverlay
|
||||
/// </summary>
|
||||
public BillboardOverlay() {
|
||||
this.Transparency = 255;
|
||||
this.BackColor = Color.PeachPuff;
|
||||
this.TextColor = Color.Black;
|
||||
this.BorderColor = Color.Empty;
|
||||
this.Font = new Font("Tahoma", 10);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets where should the top left of the billboard be placed
|
||||
/// </summary>
|
||||
public Point Location {
|
||||
get { return this.location; }
|
||||
set { this.location = value; }
|
||||
}
|
||||
private Point location;
|
||||
|
||||
/// <summary>
|
||||
/// Draw this overlay
|
||||
/// </summary>
|
||||
/// <param name="olv">The ObjectListView being decorated</param>
|
||||
/// <param name="g">The Graphics used for drawing</param>
|
||||
/// <param name="r">The bounds of the rendering</param>
|
||||
public override void Draw(ObjectListView olv, Graphics g, Rectangle r) {
|
||||
if (String.IsNullOrEmpty(this.Text))
|
||||
return;
|
||||
|
||||
// Calculate the bounds of the text, and then move it to where it should be
|
||||
Rectangle textRect = this.CalculateTextBounds(g, r, this.Text);
|
||||
textRect.Location = this.Location;
|
||||
|
||||
// Make sure the billboard is within the bounds of the List, as far as is possible
|
||||
if (textRect.Right > r.Width)
|
||||
textRect.X = Math.Max(r.Left, r.Width - textRect.Width);
|
||||
if (textRect.Bottom > r.Height)
|
||||
textRect.Y = Math.Max(r.Top, r.Height - textRect.Height);
|
||||
|
||||
this.DrawBorderedText(g, textRect, this.Text, 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user