| ofs | hex dump | ascii |
|---|
| 0000 | 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 00 00 00 40 00 00 00 40 08 06 00 00 00 aa 69 71 | .PNG........IHDR...@...@......iq |
| 0020 | de 00 00 00 04 73 42 49 54 08 08 08 08 7c 08 64 88 00 00 00 09 70 48 59 73 00 00 01 bb 00 00 01 | .....sBIT....|.d.....pHYs....... |
| 0040 | bb 01 3a ec e3 e2 00 00 00 19 74 45 58 74 53 6f 66 74 77 61 72 65 00 77 77 77 2e 69 6e 6b 73 63 | ..:.......tEXtSoftware.www.inksc |
| 0060 | 61 70 65 2e 6f 72 67 9b ee 3c 1a 00 00 12 30 49 44 41 54 78 9c e5 9b 7f 90 54 d5 95 c7 3f f7 be | ape.org..<....0IDATx.....T...?.. |
| 0080 | d7 af 7f 4e cf 0f 60 98 19 18 c7 c0 68 22 20 30 80 41 94 a0 19 65 09 1a 74 d7 38 a2 b5 a9 98 35 | ...N..`.....h".0.A...e..t.8....5 |
| 00a0 | 89 89 b2 c1 25 9a ca d6 66 53 94 55 ba 15 8d d1 e8 46 dc 8a 89 c6 24 26 22 9a c4 a0 68 56 07 13 | ....%...fS.U.....F....$&"...hV.. |
| 00c0 | 8d bf 19 19 31 62 04 04 04 99 19 06 98 e9 99 ee 9e e9 1f ef bd bb 7f bc ee 9e f7 7a 7e f5 08 24 | ....1b.....................z~..$ |
| 00e0 | 5b 95 6f 55 17 cc 7d f7 d7 39 f7 dc 73 cf 39 f7 5c a1 94 e2 64 62 c3 ce 9d 91 6c 3a dd 2c 95 9a | [.oU..}..9..s.9.\...db....l:.,.. |
| 0100 | 61 43 9d 80 3a bc 3f 80 8e fc 4f 41 87 84 0e 5b 88 bd 3e bf 7f eb f5 b3 66 25 4e e6 fc c4 c9 60 | aC..:.?...OA...[..>.....f%N....` |
| 0120 | c0 5d 6f bd 35 4d b3 ed 55 0a 2e 11 d0 0c f8 3f 62 57 69 05 5b 05 fc ce 92 72 f3 ba 79 f3 0e 9d | .]o.5M..U......?bWi.[....r..y... |
| 0140 | c8 79 c2 09 64 c0 8f da da 42 29 4d bb 5e 29 b5 5a c0 42 40 9c 90 8e 87 a0 14 b4 09 21 36 06 2c | .y..d....B)M.^).Z.B@........!6., |
| 0160 | 6b c3 b5 0b 17 0e 9c 88 4e 8f 9b 01 9b 36 6d d2 3a 4f 3b ed 5f 50 ea 66 86 44 fa 64 a3 03 21 d6 | k.......N....6m.:O;._P.f.D.d..!. |
| 0180 | d7 ee de fd 60 4b 4b 8b 75 3c 1d 1d 17 03 ee 7e f3 cd cf 0a 21 6e 03 66 8d 57 57 c3 24 22 62 84 | ....`KK.u<.....~....!n.f.WW.$"b. |
| 01a0 | e9 27 22 fa 08 d3 4f 58 f4 01 90 54 e5 24 89 92 28 fc 5b 81 85 5e ca 14 76 2a a5 be 75 c3 82 05 | .'"...OX...T.$..(.[..^..v*..u... |
| 01c0 | 4f 7e 54 1a 3e 12 03 ee dd be bd c1 82 87 80 f3 c6 aa 57 26 62 cc 10 6f 33 43 fe 99 69 62 0f 12 | O~T.>.............W&b..o3C..ib.. |
| 01e0 | bb a4 fe 6d 24 87 54 23 7b ed 39 ec 55 67 12 57 15 e3 35 f9 a3 06 57 af 69 6a fa a0 34 0a 86 30 | ...m$.T#{.9.Ug.W..5...W.ij..4..0 |
| 0200 | 61 06 dc d3 d6 b6 14 29 7f 0d 4c 19 e9 bb 9f 01 e6 ca 97 99 29 77 50 2d 0e 8e da 8f ca 42 fa 0d | a......)..L.........)wP-.....B.. |
| 0220 | 47 7a fd 67 69 08 df e8 63 76 ab 7a de b7 e7 b2 c3 3e 87 34 a1 d1 aa 1d c1 b6 2f 5b bb 70 e1 9f | Gz.gi...cv.z.....>.4....../[.p.. |
| 0240 | 4a 24 05 98 20 03 fe 7b fb f6 6b 14 dc 07 18 c5 df 34 4c e6 cb 17 58 a4 b5 e2 67 48 3f 99 07 6d | J$.....{..k......4L...X...gH?..m |
| 0260 | d2 af 5b 88 88 20 b4 62 48 ac ed 3e c5 b1 9b 52 00 4c ba 23 80 2c 1f d2 99 83 cf 99 d8 09 85 7f | ..[....bH..>...R.L.#.,.......... |
| 0280 | 91 86 3e 5d 16 ca d3 84 d8 66 5d 40 bb bd 6c b4 2d 92 11 70 dd d7 9b 9a 1e 28 95 a6 92 36 5a 4e | ..>].....f]@..l.-..p.....(...6ZN |
| 02a0 | d1 7d 0f 58 57 fc 4d a0 38 43 be c1 d9 f2 69 22 22 36 ac 6d 76 97 cd c0 33 26 5a 8d 97 01 63 61 | .}.XW.M.8C....i""6.mv...3&Z...ca |
| 02c0 | f0 05 13 ab 53 21 cb 84 87 01 7e 06 38 57 db cc 3c f9 22 af da 2b 79 d7 3e 0b e5 3d 6c 0c 05 3f | ....S!....~.8W..<."..+y.>..=l..? |
| 02e0 | b9 a7 bd 7d 4e ed ee dd df 2c 45 41 ca f1 2a 6c da b4 49 eb 6c 6c 7c 1c a5 86 11 1f a6 9f 16 fd | ...}N....,EA..*l..I.ll|......... |
| 0300 | 6e 2e d4 7e 45 44 c4 b0 8e 29 cc fd de 7d ee 3b c3 19 c2 ea 52 d8 b1 f1 a5 cd 8e 29 ac 4e e5 69 | n..~ED...)...}.;....R......).N.i |
| 0320 | 9b 87 b9 df c6 3a a6 88 88 18 17 6a bf a2 45 bf 9b 30 fd c3 3b 51 6a 5d 67 63 e3 e3 9b 36 6d d2 | .....:.....j..E..0..;Qj]gc...6m. |
| 0340 | c6 1b 6f 5c 06 e4 56 fe d2 e2 f2 6a 71 90 d5 fa 9d d4 08 47 ef a4 5f b3 e8 fd 4e 8a fe 1f 67 c0 | ..o\..V....jq......G.._...N...g. |
| 0360 | c5 77 bd 4e a2 37 48 02 4b 34 54 76 bc d1 1c dd 10 58 a2 a1 37 48 f4 3a d7 f4 2c e8 ff 71 86 de | .w.N.7H.K4Tv.....X..7H.:..,..q.. |
| 0380 | ef a4 48 bf e6 0c 50 23 3e 60 b5 7e e7 68 ba e6 d2 dc dc c7 c4 98 3a 20 b7 e7 7f 52 5c 7e 9a 6c | ..H...P#>`.~.h........:....R\~.l |
| 03a0 | 67 b9 f6 4b 74 86 28 b2 ba 15 3d eb 53 60 42 e4 0a 1f c1 e5 e3 8b 7b 9e 21 63 29 c0 3c 06 9f 35 | g..Kt.(...=.S`B.......{.!c).<..5 |
| 03c0 | 49 3c 9a 05 1d aa 6e 0e a0 55 0f 89 be 89 8f ad e6 15 fc 45 2d 1a d6 4e c0 97 c6 d2 09 a3 4a c0 | I<....n..U.........E-..N......J. |
| 03e0 | 3d 6d 6d 4b 73 0a cf 83 05 72 2b 2b b5 9f 79 88 07 d0 aa 05 a1 0b 1d a2 93 9b 4d ec f8 f8 e2 2e | =mmKs....r++..y...........M..... |
| 0400 | 7c a5 11 6f c7 15 c9 cd 26 00 a1 0b 75 0f f1 00 3a 59 fe 41 fb 25 cd e9 47 87 b5 55 70 df 3d 6d | |..o....&...u...:Y.A.%..G..Up.=m |
| 0420 | 6d 4b 47 eb 7b c4 65 ba 77 fb f6 86 dc 51 e7 d1 f6 a7 c9 76 96 6a 4f 02 8a f4 6b 16 7a 83 44 ab | mKG.{.e.w....Q.....v.jO...k.z.D. |
| 0440 | 19 9a 4c e8 62 1f a9 97 2d 44 04 ec 7e 47 89 b9 d1 71 54 e3 ed f7 7d bc b3 d7 47 5f 52 92 cd 0a | ..L.b...-D..~G...qT...}...G_R... |
| 0460 | b2 16 e8 1a cc a8 33 99 39 cd 64 e6 74 93 86 1a d3 cb 80 7e 85 ac 04 e5 13 84 2e f6 72 cc ea 52 | ......3.9.d.t......~........r..R |
| 0480 | 98 1f d8 f8 17 6b cc 31 5e 45 25 04 cf 47 5a dc 55 0c a4 fc f5 bd db b7 9f 35 92 9d 30 22 03 72 | .....k.1^E%..GZ.U........5..0".r |
| 04a0 | 46 8e e7 9c af 16 07 59 ae fd b2 40 7c ff 4f 32 c8 a8 a0 e2 26 7f 81 09 22 00 e5 37 1a e8 35 b2 | F......Y...@|.O2....&..."..7..5. |
| 04c0 | 20 5b 87 7b 34 36 6d 0d f1 cc ab 01 8e f5 8d ae 72 fe f0 e6 90 bf 34 bd da 62 e5 92 41 56 2e 49 | .[.{46m.........r.....4..b..AV.I |
| 04e0 | 51 3b c9 42 9f 26 a9 5a 1f c0 ec b2 11 01 2f f1 b1 3b d2 d8 fd 8a 28 06 fe c5 1a b3 07 5f a3 3b | Q;.B.&.Z....../..;....(......_.; |
| 0500 | 5d cf 3b fe b3 dd dd 4f c9 d1 74 7e f1 b8 c3 74 40 ce bc dd ec 2e 0b d3 cf 6a fd 4e 22 39 d3 b5 | ].;....O..t~...t@........j.N"9.. |
| 0520 | 30 70 9f 42 56 0a aa 6e 0d 0c 13 e5 bd 1d 3a 3f db 12 e6 b9 37 02 58 ae 83 61 4a 85 cd dc c6 0c | 0p.BV..n......:?....7.X..aJ..... |
| 0540 | 95 65 36 41 bf 22 18 50 f4 f6 4b 0e 1c d6 39 d0 a5 d1 d5 a3 91 9f 92 10 b0 6c 7e 9a 75 57 c6 99 | .e6A.".P..K...9..........l~.uW.. |
| 0560 | 5a e5 3d d1 54 16 7a be 9d c2 ee 55 c8 72 ef 42 64 bb 0d 1e 29 bf 91 5e 59 ed 6d a3 d4 aa 62 b3 | Z.=.T.z....U.r.Bd...)..^Y.m...b. |
| 0580 | d9 c3 80 dc 91 b7 03 97 6d 2f 50 b4 e8 77 17 b4 7d 1e 56 97 22 76 67 9a c8 6a 1f fe 85 de d3 66 | ........m/P..w..}.V."vg..j.....f |
| 05a0 | 63 6b 88 7b 1f 8b 90 35 9d 09 9d 32 d5 a2 a5 79 80 c5 73 d2 d4 57 8f 7d 34 1f ee d1 f8 fd 6b 01 | ck.{...5...2...y..s..W.}4.....k. |
| 05c0 | 9e 7e 25 c0 fe 4e 47 40 83 7e c5 b5 97 26 b8 e2 82 01 a4 4b 88 d2 6d 16 89 8d 59 2a be e1 f7 6c | .~%..NG@.~...&.....K..m...Y*...l |
| 05e0 | 45 4c 48 75 87 b8 7f ca 2d c5 76 c2 ce da 3d 7b e6 ba ed 03 0f 03 ee 69 6f ff 32 4a dd ef 6e 31 | ELHu....-.v...={.......io.2J..n1 |
| 0600 | 4b be ce 85 da af 46 9c ac ca 7a 95 58 2c 2e b9 e5 a7 51 5e da e1 88 73 e3 74 93 6b 56 25 38 af | K.....F...z.X,....Q^...s.t.kV%8. |
| 0620 | 29 8d fc 08 ce f1 eb 3b 0d ee 78 38 ca c1 6e 87 c1 8b ce c8 70 fb 9a 18 41 ff d0 9c 8b e7 50 40 | )......;..x8..n.....p...A.....P@ |
| 0640 | a7 e2 dd c0 62 9e 8d 5c e5 2d 17 e2 2b 6b e7 cf ff 71 e1 cf 3c 03 7e d4 d6 16 4a 49 b9 1b 97 4b | ....b..\.-..+k...q..<.~...JI...K |
| 0660 | ab 61 72 b5 7e 2b 11 11 73 94 de c7 e4 30 0d ec 26 fe ab b7 57 72 a0 4b 47 08 b8 6a f9 00 d7 5d | .ar.~+..s....0..&...Wr.KG..j...] |
| 0680 | 16 47 1f d7 14 19 1b 99 ac e0 a1 2d 61 1e da 12 c6 b2 e1 8c 53 b3 dc 79 43 8c 8a c8 c8 8e 95 d5 | .G.........-a.......S..yC....... |
| 06a0 | ad 30 f7 d9 f8 e7 69 a8 83 f0 60 fd 7a 12 9a c7 99 ea 08 d8 f6 69 f9 78 42 41 a0 52 9a 76 3d 45 | .0....i...`.z........i.xBA.R.v=E |
| 06c0 | fe fc 7c f9 42 c1 c2 8b 3f 94 a1 67 7d 8a e4 e3 59 54 ca 3b 68 72 50 70 c3 0f 1c e2 43 01 c5 f7 | ..|.B...?..g}...YT.;hrPp....C... |
| 06e0 | d7 c6 f8 7a cb f1 13 0f 60 f8 14 5f b9 34 c1 6d 6b 62 18 3e c5 bb fb 7d 7c f5 b6 4a 62 71 af 42 | ...z....`.._.4.mkb.>...}|..Jbq.B |
| 0700 | 55 29 48 3e 9e a5 67 7d 8a f8 43 19 ac a4 42 04 14 cb e3 c3 a4 b7 2e 47 2b 1e 06 28 a5 56 bb 6b | U)H>..g}..C...B........G+..(.V.k |
| 0720 | f9 19 60 91 d6 ea 7c 8b 2b 64 95 00 13 06 9e 31 e9 f9 76 0a b3 c3 59 01 d3 82 9b 7e 58 c1 ae 03 | ..`...|.+d.....1..v...Y....~X... |
| 0740 | 3a 86 4f 71 c7 d7 63 2c 99 93 3e 7e ca 8b 70 ee dc 34 3f f8 b7 18 a1 80 e2 40 97 ce 2d 3f 8d 16 | :.Oq..c,..>~..p..4?......@..-?.. |
| 0760 | be 99 1d 36 3d df 4e 31 f0 8c 09 26 c8 2a 81 8a 2b a8 14 4c ef db 45 58 f5 79 fa 72 d3 2a c1 89 | ...6=.N1...&.*..+..L..EX.y.r.*.. |
| 0780 | e1 e5 c2 58 05 cc 95 2f 17 bc 3a fd 54 49 d5 cd 01 22 57 f8 10 41 81 88 e0 1c 75 c0 23 cf 86 69 | ...X.../..:.TI..."W..A....u.#..i |
| 07a0 | df 65 a0 49 f8 af af f5 d1 74 7a e6 84 13 9f 47 d3 e9 19 6e f9 6a 1f 42 c0 4b 3b fc 6c 6c 75 5c | .e.I.....tz....G...n.j.B.K;.llu\ |
| 07c0 | 63 bd 46 22 22 20 82 82 c8 15 3e aa 6e 0e a0 9f 2a 21 0c c2 56 34 27 36 79 fa 11 b0 f0 ae b7 de | c.F"".....>.n...*!..V4'6y....... |
| 07e0 | 9a 56 60 80 66 db ab 28 8a e1 cd 94 3b bc a3 6b 10 5c ae 53 75 ab 9f e8 b5 06 48 e8 ee d5 78 e0 | .V`.f..(....;..k.\.Su.....H...x. |
| 0800 | c9 30 00 5f 58 99 e4 dc b9 27 7e e5 8b b1 64 4e 9a ab 96 3b 0b 73 ef 63 11 f6 76 e8 20 21 7a ad | .0._X....'~...dN...;.s.c..v..!z. |
| 0820 | 41 d5 ad 7e c7 04 cf 6f 3d 01 04 e1 94 d8 5f 10 78 8e 7b 91 a3 d9 61 80 82 4b dc 5f cb 44 6c d4 | A..~...o=....._.x.{...a..K._.Dl. |
| 0840 | 60 86 2c 13 e8 d3 9c d5 bf fb d1 08 83 69 41 7d b5 c5 17 2f 4e 9e 40 32 c7 c6 75 97 c5 69 9c 6e | `.,..........iA}.../N.@2..u..i.n |
| 0860 | 92 35 05 3f db e2 2c 80 3e 4d 0e b3 3c 01 08 82 96 35 39 2d d5 ee 29 ce d3 2c 37 ec dc 19 c9 85 | .5.?..,.>M..<....59-..)..,7..... |
| 0880 | ae 0b 98 21 de 06 9c 60 c6 60 ab 59 d8 ef 6e 74 1d d3 78 be cd 31 cb 6e fa e7 7e 0c df c9 bd 5f | ...!...`.`.Y..nt..x..1.n..~...._ |
| 08a0 | 70 43 d7 e0 9a 55 ce 75 41 eb b6 00 dd bd c3 b5 ad d9 91 9b 7b 8f 33 af 4f a4 b6 79 be 0b 68 de | pC...U.uA...........{.3.O..y..h. |
| 08c0 | b0 73 67 44 cf a6 d3 cd a2 28 6e 3f 43 fe 19 80 f4 eb 96 a3 58 00 bd 41 52 f9 9f 43 d5 9e 7a 39 | .sgD.....(n?C.......X..AR..C..z9 |
| 08e0 | 80 52 70 6a ad c9 27 67 9d bc 7d 3f 1a ce 6b 4a 73 ca 54 8b 03 87 35 1e 6d 0d f2 af 97 0f dd 9f | .Rpj..'g..}?..kJs.T...5.m....... |
| 0900 | f4 de 92 c6 fc c0 59 b4 d0 0a 1d fd 4c 9d 9a f4 fe e2 2e fc d9 74 ba 59 4a a5 66 b8 4b 35 4c a6 | ......Y.....L........t.YJ.f.K5L. |
| 0920 | 89 3d 00 88 88 28 58 58 7a dd 90 78 29 05 5b 5e 0e 02 b0 72 49 d1 99 f8 57 82 14 d0 d2 ec e8 82 | .=...(XXz..x).[^...rI...W....... |
| 0940 | d6 6d 01 cf b7 fc 5c b5 1a 81 28 13 a0 43 60 30 39 cc 83 95 4a cd d0 73 d7 55 05 44 44 ac 10 bd | .m....\...(..C`09...J..s.U.DD... |
| 0960 | 0d ad d0 09 ad d0 b1 63 ca 13 cc 78 ff 90 4e c7 51 0d 21 60 c5 e2 bf 0d 03 00 16 cf 49 03 65 74 | .......c...x..N.Q.!`........I.et |
| 0980 | 1d d3 38 dc a3 15 fc 85 d0 2a 1f e1 cb 7c c8 8a 1c 65 fb 14 98 30 2d bb 87 0f 7c 67 14 da db 50 | ..8......*...|...e...0-...|g...P |
| 09a0 | 27 45 91 f1 33 52 88 49 56 08 b4 29 43 6c da d7 e1 d8 e8 b5 93 ac 61 4e 4a a9 c8 9a 82 77 f6 f9 | 'E..3R.IV..)Cl........aNJ....w.. |
| 09c0 | 78 67 9f af e0 33 4c 14 f5 d5 16 53 2a 9c c5 da be 6b c8 1e d6 a6 88 21 e2 a1 70 be d5 a5 f7 79 | pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Media.TextFormatting;
using System.Windows.Threading;
using Tango.Scripting.Editors.Document;
using Tango.Scripting.Editors.Rendering;
using Tango.Scripting.Editors.Utils;
namespace Tango.Scripting.Editors.Editing
{
/// <summary>
/// Helper class with caret-related methods.
/// </summary>
public sealed class Caret
{
readonly TextArea textArea;
readonly TextView textView;
readonly CaretLayer caretAdorner;
bool visible;
internal Caret(TextArea textArea)
{
this.textArea = textArea;
this.textView = textArea.TextView;
position = new TextViewPosition(1, 1, 0);
caretAdorner = new CaretLayer(textView);
textView.InsertLayer(caretAdorner, KnownLayer.Caret, LayerInsertionPosition.Replace);
textView.VisualLinesChanged += TextView_VisualLinesChanged;
textView.ScrollOffsetChanged += TextView_ScrollOffsetChanged;
}
void TextView_VisualLinesChanged(object sender, EventArgs e)
{
if (visible) {
Show();
}
// required because the visual columns might have changed if the
// element generators did something differently than on the last run
// (e.g. a FoldingSection was collapsed)
InvalidateVisualColumn();
}
void TextView_ScrollOffsetChanged(object sender, EventArgs e)
{
if (caretAdorner != null) {
caretAdorner.InvalidateVisual();
}
}
double desiredXPos = double.NaN;
TextViewPosition position;
/// <summary>
/// Gets/Sets the position of the caret.
/// Retrieving this property will validate the visual column (which can be expensive).
/// Use the <see cref="Location"/> property instead if you don't need the visual column.
/// </summary>
public TextViewPosition Position {
get {
ValidateVisualColumn();
return position;
}
set {
if (position != value) {
position = value;
storedCaretOffset = -1;
//Debug.WriteLine("Caret position changing to " + value);
ValidatePosition();
InvalidateVisualColumn();
RaisePositionChanged();
Log("Caret position changed to " + value);
if (visible)
Show();
}
}
}
/// <summary>
/// Gets the caret position without validating it.
/// </summary>
internal TextViewPosition NonValidatedPosition {
get {
return position;
}
}
/// <summary>
/// Gets/Sets the location of the caret.
/// The getter of this property is faster than <see cref="Position"/> because it doesn't have
/// to validate the visual column.
/// </summary>
public TextLocation Location {
get {
return position.Location;
}
set {
this.Position = new TextViewPosition(value);
}
}
/// <summary>
/// Gets/Sets the caret line.
/// </summary>
public int Line {
get { return position.Line; }
set {
this.Position = new TextViewPosition(value, position.Column);
}
}
/// <summary>
/// Gets/Sets the caret column.
/// </summary>
public int Column {
get { return position.Column; }
set {
this.Position = new TextViewPosition(position.Line, value);
}
}
/// <summary>
/// Gets/Sets the caret visual column.
/// </summary>
public int VisualColumn {
get {
ValidateVisualColumn();
return position.VisualColumn;
}
set {
this.Position = new TextViewPosition(position.Line, position.Column, value);
}
}
bool isInVirtualSpace;
/// <summary>
/// Gets whether the caret is in virtual space.
/// </summary>
public bool IsInVirtualSpace {
get {
ValidateVisualColumn();
return isInVirtualSpace;
}
}
int storedCaretOffset;
internal void OnDocumentChanging()
{
storedCaretOffset = this.Offset;
InvalidateVisualColumn();
}
internal void OnDocumentChanged(DocumentChangeEventArgs e)
{
InvalidateVisualColumn();
if (storedCaretOffset >= 0) {
int newCaretOffset = e.GetNewOffset(storedCaretOffset, AnchorMovementType.Default);
TextDocument document = textArea.Document;
if (document != null) {
// keep visual column
this.Position = new TextViewPosition(document.GetLocation(newCaretOffset), position.VisualColumn);
}
}
storedCaretOffset = -1;
}
/// <summary>
/// Gets/Sets the caret offset.
/// Setting the caret offset has the side effect of setting the <see cref="DesiredXPos"/> to NaN.
/// </summary>
public int Offset {
get {
TextDocument document = textArea.Document;
if (document == null) {
return 0;
} else {
return document.GetOffset(position.Location);
}
}
set {
TextDocument document = textArea.Document;
if (document != null) {
this.Position = new TextViewPosition(document.GetLocation(value));
this.DesiredXPos = double.NaN;
}
}
}
/// <summary>
/// Gets/Sets the desired x-position of the caret, in device-independent pixels.
/// This property is NaN if the caret has no desired position.
/// </summary>
public double DesiredXPos {
get { return desiredXPos; }
set { desiredXPos = value; }
}
void ValidatePosition()
{
if (position.Line < 1)
position.Line = 1;
if (position.Column < 1)
position.Column = 1;
if (position.VisualColumn < -1)
position.VisualColumn = -1;
TextDocument document = textArea.Document;
if (document != null) {
if (position.Line > document.LineCount) {
position.Line = document.LineCount;
position.Column = document.GetLineByNumber(position.Line).Length + 1;
position.VisualColumn = -1;
} else {
DocumentLine line = document.GetLineByNumber(position.Line);
if (position.Column > line.Length + 1) {
position.Column = line.Length + 1;
position.VisualColumn = -1;
}
}
}
}
/// <summary>
/// Event raised when the caret position has changed.
/// If the caret position is changed inside a document update (between BeginUpdate/EndUpdate calls),
/// the PositionChanged event is raised only once at the end of the document update.
/// </summary>
public event EventHandler PositionChanged;
bool raisePositionChangedOnUpdateFinished;
void RaisePositionChanged()
{
if (textArea.Document != null && textArea.Document.IsInUpdate) {
raisePositionChangedOnUpdateFinished = true;
} else {
if (PositionChanged != null) {
PositionChanged(this, EventArgs.Empty);
}
}
}
internal void OnDocumentUpdateFinished()
{
if (raisePositionChangedOnUpdateFinished) {
if (PositionChanged != null) {
PositionChanged(this, EventArgs.Empty);
}
}
}
bool visualColumnValid;
void ValidateVisualColumn()
{
if (!visualColumnValid) {
TextDocument document = textArea.Document;
if (document != null) {
//Debug.WriteLine("Explicit validation of caret column");
var documentLine = document.GetLineByNumber(position.Line);
RevalidateVisualColumn(textView.GetOrConstructVisualLine(documentLine));
}
}
}
void InvalidateVisualColumn()
{
visualColumnValid = false;
}
/// <summary>
/// Validates the visual column of the caret using the specified visual line.
/// The visual line must contain the caret offset.
/// </summary>
void RevalidateVisualColumn(VisualLine visualLine)
{
if (visualLine == null)
throw new ArgumentNullException("visualLine");
// mark column as validated
visualColumnValid = true;
int caretOffset = textView.Document.GetOffset(position.Location);
int firstDocumentLineOffset = visualLine.FirstDocumentLine.Offset;
position.VisualColumn = visualLine.ValidateVisualColumn(position, textArea.Selection.EnableVirtualSpace);
// search possible caret positions
int newVisualColumnForwards = visualLine.GetNextCaretPosition(position.VisualColumn - 1, LogicalDirection.Forward, CaretPositioningMode.Normal, textArea.Selection.EnableVirtualSpace);
// If position.VisualColumn was valid, we're done with validation.
if (newVisualColumnForwards != position.VisualColumn) {
// also search backwards so that we can pick the better match
int newVisualColumnBackwards = visualLine.GetNextCaretPosition(position.VisualColumn + 1, LogicalDirection.Backward, CaretPositioningMode.Normal, textArea.Selection.EnableVirtualSpace);
if (newVisualColumnForwards < 0 && newVisualColumnBackwards < 0)
throw ThrowUtil.NoValidCaretPosition();
// determine offsets for new visual column positions
int newOffsetForwards;
if (newVisualColumnForwards >= 0)
newOffsetForwards = visualLine.GetRelativeOffset(newVisualColumnForwards) + firstDocumentLineOffset;
else
newOffsetForwards = -1;
int newOffsetBackwards;
if (newVisualColumnBackwards >= 0)
newOffsetBackwards = visualLine.GetRelativeOffset(newVisualColumnBackwards) + firstDocumentLineOffset;
else
newOffsetBackwards = -1;
int newVisualColumn, newOffset;
// if there's only one valid position, use it
if (newVisualColumnForwards < 0) {
newVisualColumn = newVisualColumnBackwards;
newOffset = newOffsetBackwards;
} else if (newVisualColumnBackwards < 0) {
newVisualColumn = newVisualColumnForwards;
newOffset = newOffsetForwards;
} else {
// two valid positions: find the better match
if (Math.Abs(newOffsetBackwards - caretOffset) < Math.Abs(newOffsetForwards - caretOffset)) {
// backwards is better
newVisualColumn = newVisualColumnBackwards;
newOffset = newOffsetBackwards;
} else {
// forwards is better
newVisualColumn = newVisualColumnForwards;
newOffset = newOffsetForwards;
}
}
this.Position = new TextViewPosition(textView.Document.GetLocation(newOffset), newVisualColumn);
}
isInVirtualSpace = (position.VisualColumn > visualLine.VisualLength);
}
Rect CalcCaretRectangle(VisualLine visualLine)
{
if (!visualColumnValid) {
RevalidateVisualColumn(visualLine);
}
TextLine textLine = visualLine.GetTextLine(position.VisualColumn);
double xPos = visualLine.GetTextLineVisualXPosition(textLine, position.VisualColumn);
double lineTop = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.TextTop);
double lineBottom = visualLine.GetTextLineVisualYPosition(textLine, VisualYPosition.TextBottom);
return new Rect(xPos,
lineTop,
SystemParameters.CaretWidth,
lineBottom - lineTop);
}
/// <summary>
/// Returns the caret rectangle. The coordinate system is in device-independent pixels from the top of the document.
/// </summary>
public Rect CalculateCaretRectangle()
{
if (textView != null && textView.Document != null) {
VisualLine visualLine = textView.GetOrConstructVisualLine(textView.Document.GetLineByNumber(position.Line));
return CalcCaretRectangle(visualLine);
} else {
return Rect.Empty;
}
}
/// <summary>
/// Minimum distance of the caret to the view border.
/// </summary>
internal const double MinimumDistanceToViewBorder = 30;
/// <summary>
/// Scrolls the text view so that the caret is visible.
/// </summary>
public void BringCaretToView()
{
BringCaretToView(MinimumDistanceToViewBorder);
}
internal void BringCaretToView(double border)
{
Rect caretRectangle = CalculateCaretRectangle();
if (!caretRectangle.IsEmpty) {
caretRectangle.Inflate(border, border);
textView.MakeVisible(caretRectangle);
}
}
/// <summary>
/// Makes the caret visible and updates its on-screen position.
/// </summary>
public void Show()
{
Log("Caret.Show()");
visible = true;
if (!showScheduled) {
showScheduled = true;
textArea.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(ShowInternal));
}
}
bool showScheduled;
bool hasWin32Caret;
void ShowInternal()
{
showScheduled = false;
// if show was scheduled but caret hidden in the meantime
if (!visible)
return;
if (caretAdorner != null && textView != null) {
VisualLine visualLine = textView.GetVisualLine(position.Line);
if (visualLine != null) {
Rect caretRect = CalcCaretRectangle(visualLine);
// Create Win32 caret so that Windows knows where our managed caret is. This is necessary for
// features like 'Follow text editing' in the Windows Magnifier.
if (!hasWin32Caret) {
hasWin32Caret = Win32.CreateCaret(textView, caretRect.Size);
}
if (hasWin32Caret) {
Win32.SetCaretPosition(textView, caretRect.Location - textView.ScrollOffset);
}
caretAdorner.Show(caretRect);
textArea.ime.UpdateCompositionWindow();
} else {
caretAdorner.Hide();
}
}
}
/// <summary>
/// Makes the caret invisible.
/// </summary>
public void Hide()
{
Log("Caret.Hide()");
visible = false;
if (hasWin32Caret) {
Win32.DestroyCaret();
hasWin32Caret = false;
}
if (caretAdorner != null) {
caretAdorner.Hide();
}
}
[Conditional("DEBUG")]
static void Log(string text)
{
// commented out to make debug output less noisy - add back if there are any problems with the caret
//Debug.WriteLine(text);
}
/// <summary>
/// Gets/Sets the color of the caret.
/// </summary>
public Brush CaretBrush {
get { return caretAdorner.CaretBrush; }
set { caretAdorner.CaretBrush = value; }
}
}
}
|