aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Graphics/speed.png
blob: 709f699fcc557bd0ddc65e7301fdd1045e851a26 (plain)
ofshex dumpascii
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; }
		}
	}
}