X'.Q.| | Q j.t v v b X* o.m.K.'.[.A.q.C.W f.(.Q (.Q (.XXE Q E XX(.(.(.*XsXv y.7X0 j t 7XoXt.[.m.| | Q.O.m m oXd m t 7XoX7Xa o.[.Q.-.d.R.W [ R [ f.f.f.A.Q {.| 9.9.C.C.D.T R.A.C.Z.Q.9.q.d.9.{ Y p.{ =.9.R | | 9.f.q.`.m.W A.q.f.R R 8.E (.(.^.} *. ",
+" X .'.'.'.^ ]./ C.p.).Q.C.o.p ^ ]./ p p.0.Y Y Y u.wXb.D.[ 9.W A.A.W W p.T W =.9.8.d.| Q.R.9.C.Q.{ R { p.} } p.} 9.{ s.W W 9.9.[ W 9.9.Q I.d.f.O.uXe e m.Q Q q.f.V.gXj w l.$Xx [.H.9 K.oX].m.f.E Q Q I.I.XXE Q E Q q.q.W E | Q ;.uXe iXsXyXe ..;.t.q.| T.| D oXm m 7XoX7X8 q iXd Z.C.Q.Z.A.q.Q.C.f.9.W Q d.A.A.W.d.D.e.L.p.n.L.Z.d.C.C.f.W 9.f.9.[ [ 9.W W [ 8.[ R [ R [ W | A.V.o.q.m.I.8.~.(.(.XXXX(.A ",
+" W.M.~ u ^ L.C.C.V.A.C.o.o.Q.o.^ L.C.{ Y =.R 8.C.A.a C.n.R [ [ [ W W =.8.~.9.9.9.W [ [ W d.n.C.Q.m.m.{.R.} 9.R.XXQ.d.W [ 9.9.E 9.R.[ d.d.W T t.G e e '.R.=.R W E t.m 0 w XOX[.X.].m.^ '.d.d.| f.W.I.(.XXI.| E [ q.d.[ I.I.Q U.y 8 t J 0Xf f 7Xd o.| XX[ /.oX7XO.oXt L j q q /.f.[ T A.R.{ [ W W Y [ W W E E -.].L.D.n.[ p.Q.Q.8.9.} } R [ } 8.} } R } 8.8.8.W R R ~.} [ E T [ V.d.d.| T.(.| {.| {.I.Z ",
+" Z.^ m.^ L.^ n.f.d.X.o.A.W Q.C.D.W R [ 8.p.[ W W V.o.L.a b.b.).A.| C.[ (.| | | 9.d.[ d.C.W 9.A.X.H.m. .X.R.W q.m.d.C.[ [ f.W [ | E f.W W W =.aXv w e O.9.[ [ E 8.s v v b 3Xu Z.d.Q.q.Q.j.m.Q D f.| XX(.XXXXXX| [ [ E {.(.XXI.;.kX..e k e G uX7X[.E T.9.9.Q.t.t oX7Xt 8 v 0 m W 9.T.8.~.[ R [ =.R [ ~.[ | f.9.d.[.L.f.C.[ R [ | 9.R 8.8.9.8.T.T.^.^.^.T.(.T.| 8.8.9.E | 8.[ { [ { ).f.| T.8.E m.q.W.`.X ",
+" !./.Q.Q.Q.].C.9.9.9.W T C.oXL.Z.W =.q.j.Q.^ ].n.b.( 5.< Y e.Y C.A.d.d.C.8.T.| E f.d.d.9.[ 9.W Z.Z.A.C.R.f.Z.A.Q.A.f.f.d.q.{.E `.| 8.[ T =.E :.sXe l.9 R.T.T.E ~.o.t y w l F.Z.| 8.f.).[ f.Q | E f.XX(.^.^.(.^.8.[ ~.I.XX*X`.F G y j q t 7Xt gXD | T.| (.| 0Xm 0X0X7Xv q q [.f.| (.(.XX| T.W [ 9.8.} [ 9.[ } } W [ E | | 9.T.| | f.Q I.T.I.{.-.XXXXI.T.(.| 9.| [ d.E q.I.8.[ 9.8.T.} [ =.c.f.A.A.b.Y ",
+" X f.| | d.Q.d.W 8.8.[ Y T [.o.C.q.m.M.x.;.>.;XB.B.x - L.C.9.9.[ T f.A.Q.9.9.E | `.-.Q.| 9.| W R.[ W [ f.f.W d.| q.Q.m.o.Q Q A.W.| ).W W d.W.:.L y l ~ d.9.| d.XX*X..j j l.&Xp | 8.9.8.[ [ [ W R R 9.8.8.(.T.8.XXf.E | {.{.H.O.uXq w k e ..gXoXq.Q (.T.| d.7Xd f t.t y 0 t o.I.*XI.d.| I.C.8.8.R } T.T.E 8.8.[ [ f.| ~.8.T.8.(.f.d.`.`.8.*X`.-.I.-.*XXXT.XX| R.| q.f.q.Q d.9.8.[ 8.W [ 0.Z.Y b.Z.b.wX ",
+" R E 9.W 8.| 8.T.9.[ Z.A.).A.A.j.m.m /.o.N N / u / p o.A.R.C.W [ [ f.R T W [ T [ (.T.| T.8.8.| W R W W W 8.| | :.H.:.A.d.E E E E [ W f.E I.:...k e r m.Q.Q.d.Q A.t.v y y 3Xp Q (.(.T.E E T p.B.p.W 9.[ Q `.{.:.Q :.f.Q.d.X.m.q w w q j j sXgXf.E | W E Q m gX0Xt uX8 uXO.V.E | {.9.W W W R } [ E | | XXT.| T.| d.9.| | E ^.E f.=.C.o.X.-.{.{.`.*XI.{.| | I.R.A.R.W f.W =.[ [ E [ R 9.W } { [ T [ X ",
+" A | E f.| {.| } } R 8.R =.[ f.V.=.A.C.L.N L.[.A.[ R [ p.W [ Q.W W d.f.9.f.E E E E 8.[ I.8.=.E E =.=.9.} 8.E E f.Q Q Q | Q ~.(.| 8.E E f.d.W.q y w M ^ [.X.o.Q.f.O.uXy l $X; E XX| E | f.f.L.].].f.W E | Q aX:.*X-.m.R.L.[.Q.H.j q j q 0 7X;.A.8.E E Q q.v J t.v 8 w j ].W [ [ | W W W W [ W [ E E E (.T.T.d.| | | d.| W R W W T j.q.o. . .F Y. .`.-.-.T.d.W C.C.C.D.p.T f.| ~.8.} 8.f.9.E [ R [ ",
+" C [ [ d.T A.d.C.[ f.9.} [ [ f.9.s.Y R p.Z.n.[ [ s.{ [ f.[ W f.q.| W V.W A.f.V.W {.9.XX*Xd.[ [ [ Y =.[ [ 8.~.~.E Q 8.f.E E Q XX*X{.| A.W q.:...e e rXC.C.L.o.o.o.[.uXq e l.N Z.[ E [ Y =.f.W f.m.Q.9.f.f.f.q.-.q.Q | f.q.H.m.t.q q G e e kX0XA.f.[ E W.d.gXd t t qX8 9 [.W E A.I.E | | 9.E E 8.8.~.~.~.^.(.| I.(.9.E Q | E 8.9.9.d.f.:.m.:.H._.H.m.Q.`.{.m.Q.A.'.N ].o.W R T.~.} ^.} T.d.q.8.T.+X ",
+" T D V.o.D )./.s ^ C.p.{ [ W R.7.R 8.[ [ W C.Z.Y =.f.A.f.q.f.Q E f.f.E E f.E f.q.q.A.Q f.~.[ W [ R ~.E (.I.T.~.~.| E Q D Q dX{.d.{.o.f.o.W q w w c D.Z.T f.d.Q j.9 v e =XOXY [ c.W =.V.E { R [ 9.{ W f.d.:.q.Q.f.f.E | d.Q A.! y j uXb uXL D } E E D q.oXgXt uXuXb d T Q [ Q {.d.I.| 9.[ } } 8.T.T.T.^.(.XXXXXXT.^.(.(.XX{.| (.| 9.T D f.m.M.m. .H.{.{.X.L.].'.m.X.W R T.XX(.(.| T.} T.9.R.f.' ",
+" I ].b.f.T _.t.[.q >.'.Q.R.A.A.W W 8.[ R T R b.=.a W W D W.q.d.T.T.T.T.T.E W f.q.A.E q.q.W./.f.f.E [ | ~.(.T.| } ~.E (.(.D dX`.-.m.[.b.T D q w l. Xp W T.8.| *XQ w.qXe l.u < E E 8.E [ [ =.[ [ W f.E E f.Q E f.E XXQ | XXT.| m uXkXL uX8 t o.9.9.E f.o.gX0Xj 0 v j * p.d.=.E {.{.*XXX9.{ [ W 9.| d.XXXXT.(.E XX| (.XX(.XXI.I.I.| | =.C.T ).f.D f.Q.d.R.D.C.C.{ W 8.T.9.I.I.XXXXXXXXT.XX| [ b.. ",
+" o / = b.).D O.:.'.L.o.'.R.X.d.W W ).f.Y Z.C.[ { S c.R 9.E d.| d.T.| | ~.[ R E Q D f.f.o.o.W.f.=.E ~.} ^.R R R =.[ E (.E (.q.Q A.f.q.V.[ D w e y c p Q | XXE *Xo.;.b y y $X< (.(.(.8.~.E [ f.8.[ W [ T f.| I.Q I.{.{.XX| T.Q :...qXw e w fX[.9.d.C.f.x.7Xv j 0 b y * V.f.} | XXI.{.d.R.A.^ A.Q.X.Q.{.I.I.| E (.~.(.~.E ~.(.XXE 9.W C.f.[ T b.Z.W W d.9.R.C.L.f.d.`.{.XXI.*XI.XX*XXX| d.| [ ,X ",
+" u.C./.o.D q.C.).W D.C.A.X.Q.W W /.A.C.W W A.R.W R [ R 9.W d.{.| XXd.Q c.=.R *.R =.=.=.[ R [ [ E T.^.~.} ~.~.~.[ E E ~.~.~.E f.[ f.W [ /...e l.4Xe.f.f.q.`.A.W v b w l.$X@.[ E (.8.} [ E I.8.[ W [ [ f.Q I.I.Q *Xf.Q | | | q.;.uXq v t 0 d C.A.L./.K.d ..8 y uX=X* a -.| | XX9.R.R.9.Q.X.X.Q.d.q.XXXXXXXX*XXXXX(.8.E ~.E | E E V.W [ C.W /.C.f.Q.`.A.f.W f.:.g.Y.:._.dXY.-.*X-.| d.o.o.C.. ",
+" 4 d.A.9.W E | f.W p.@.u.V.f.f.o.o.1 /./.A.f.R C.[ [ } { [ f.f.| T.E f.E [ *.S R =.E W } [ [ | ~.(.(.XXT.(.T.8.} [ } R } [ [ ~.[ =.[ =.t.j y e 3XI T E E A.Q m.L y y =XF.2Xu.| d.| [ E f.:.A.q.f.W j.A.f.f.E E | f.Q XX{.-.`.v j q iXe y t L.b.n./ oXm 8 j q j v g b.q.I.{.XXR.T.R.9.R.d.W Q.| I.{.I.T.(.(.I.{.I.q.W =.E f.[ [ | R.W 9.R.C.A.d.| Q.C.W V.f.D `.g.g.N.M.`.F Y. .o._.m.o.u. ",
+" c.=.[ [ R [ 9.[ W A S [ 9.9.[ { /.m.W.m.Y T C.[ { } S S E E E | 8.[ [ R c.R [ W [ E E (.XX(.| (.XX*XI.I.I.Q./ L.C.8.~.8.8.8.R ~.} } aXw qXqX=X5X- f.W W =.j.kXy y =XF.B b.Q.9.Q.C.)._.[.^ O._.[.:.f.f.E E Q D Q Q q.{.-.`.;.uXw j v j t L.a L.x.[.v t ..qXy c * @ q.d.`.-.{.9.d.R.W f.A.| Q Q `.I.XXXX| | d.f.j.Q.d.f.E E (.| | | T.[ E 9.Q.{.d.C.n.C.W W A.:. .F U.! ! H. .m.t._.o.4 ",
+" 3.T *.8.[ [ 8.d.R./ W 9.W d.d.f.f.T /.q.W D q.f.R f.R [ Q | } ~.E } R } R R =.E f.(.8.~.~.~.I.E [ I.{.m.K.N N N x.o.| E (.8.} (.} ~.U.e e e l.& a =.[ W Y ).v iXqXl.>.* U a Y m.m.q.]./ & ].]./.o.Z.f.q.Q Q Q *X*XQ Q `.M.O.y j w iXb v ].x.Z.Z.j.;.qXuXq j eXN p ).A.| | d.f.W T f.W E f.f.q.| `.-.{.{.| A.d.W W d.XX| | T.^.[ | d.[ f.R.R.Q.A.X.X./ b.[ V.V.f._.H.M.o.f.).W ).o.Z. ",
+" Y [ [ 9.8.9.q.m.'.L.L.f.m.q.q.f.f.E V.[ V.W E W E E E Q q.| | E f.~.E [ R ~.).W.q.W.{.*X-.*XE I.Q q.9 3XrXM %.K.W.I.(.| 9.E -.(.R aX..yXl iX%.p S R W T /.G e e iXr ;X; x.].o.j.[.oX].u ^ x.o.A.f.f.| Q | | | D f.f.q.:.t.zXw b v j v f Z.[ R.j.v j qXj q M ` ; C.C.[ [ 9.[ W | W E f.W R f.).R.m.W q.q.d.f.d.{.-.{.{.{.{.I.| 9.9.W W f.| | Q.C.A.C.p.W [ [ =.W T ).9.[ Y S =.c.C ",
+" 4 W | d.-.f.`.Q.X.oX/ f.q.Q A.[ E ~.V.f.f.d.f.E E E V.f.{.D Q A.A.W Z.T V.W ).m.;.w.{.(.XX{.XX| [ W.N.k e k rXL.A.d.E f.Q.H.{.T.=.V.N.e e l.* 1 E R 8.[ ).;.iXe e l.%.< u.A.q./.X.X.].].x.L./.V.A.f.W Q Q Q E V.E V.f.E j.q y w qXL gX7Xf.R f.D w.b y qXuXr r.= C.C.A.R.9.W R.R.9.T.E E 9.f.[ T.(.f.W d.Q Q q.q.`.q.{.`. .M.`.d.f.f.W ).f.[ A.A.n.p.W W [ p.[ { 8.9.T 9.T [ Z.c. ",
+" Z.f.A.f.p.o./ A.A.A.E I.I.I.Q d.A.E f.D q.q.A.f.E E E D f./.o.o.'.[.^ L.n 9 O.j.O.:.-.XXd.q.d.9.H.G.h.q S.k K.A.).W V.A.f.E 9.Y [ W.sXiXeX_ ,.V.~.[ | T [...y y y F.P < V.A.f.f.f.f.L.W W C.0.f.E Q W.`.Q q.A.f.D q.q.t...iXqXj b g f A.R.E /.g.iXb y y b N = p.W Q.A.p.Z.9.R.9.9.9.T.8.E Q T.E T.| I.d.d.d.f./.q.Q q.Y. .`.R.A.A.A.| [ T W T A.C.).R.C.[ 9.R.[ { { W C.d.C.C ",
+" X A.j.f.L.a C.C.W 9.=.Q Q {.E 9.[ [ [ f.D m.q.q.I.Q Q I.:.'.o.d ^ ;Xu OXN oX=Xy ! m ~ :.E [ q.A.O.z h.h.k q X.C.W W p [ b.T /.D o.Q.kXe yX3XP T f.E d.W V.q.;.j iXrX%.` x.d.| W V.d.C.p.T =.W W f.Q Q d.q.Q.d.A.).Q {._...l.e y b x - f [ E f.L w y y y j u i f.9.[ A.Q.T.9.| R.n.9.| f.[ R 8.f.I.I.d.f.f.A.).W ).f.W 9.f.W A.X.'.m.X.W W f.{ R W A.d.[ C.W R.C.W C.d.f.d.+X ",
+" u.A.a V.o.Q.q.[ E [ f.[ } f.Z.W p.[ R E f.).D q. .`.`.q.m.q.oX^ n u & * ^ t 9 ;.w.G m.D f.V.W j.J.w c [.[.X./.].X.f.C.V.W.H.m.K.m.m z k.k.>X% o.| d.W C.f./.kXe l.6X@X1.x.W f.E f.q.d.Z.[ 8.f.I.f.f.W Y Q.R.C.R.f.D ;.w w qXy q r.1 1 T =.~.;.q y y qXr & a d.9.R.Q.9.[ W p.C.C.d.d.d.E [ E E | | {.| q.d.d.d.f.| E | [ T [ X.'.! H.[.'.Q.b.=.Z.9.W W W A.f.f.W {.d.| 9.X ",
+" o ).].j.Q.q.q.| 8.9.T.[ W X./ L.A.R 8.E | E A.W.j.q.`.M.m.X.X.x.X.A.C.L.a ].[.[.j.g.:.A.f.E Q f.o.'.x 1 D o.]./.m./.A.D H.M.w...M.;.h.cXcXS.l.x.W.m.q.q.q.D g.k l eX@X% - +XQ Q Q Q Q I.f.E E E A.o.x.].K.m.Z.W c.).;.w iXk sXb $X= +.T W W aXq l.y e r n Q.Q.d.d.C.a Q.a L.C.W o.A.8.~.E f.E E f.Q f.d.j.f.| d.XXQ Q [ W =.).f.'.m.].X.X.o.Q.f.d.[ W { A.[ 9.E {.Q q.c. ",
+" wX].x.Z.f.-.H.A.Q.A.L.'.oX9 K.'.`.d.(.| Q q.W A.V.).[.o.f.Z.R W f.E W [ f.T f.Q.q.f.W q.Q Q f.~.D A.n.p V.W T Q.X./.:.xXH e k w.w h.S.k.yXqX>.x.W.H.:.H.m.O.z yX3X&X,.2.!.A.f.f.[ f.E Q {.d.f.D a / x.].m.Q.n.Y D ;.k iXw y qX6X_ = T E } Q ..y e l.w 9 A.{.q.d.Q.X.L.n.b.a { p.{ [ R.8.[ } ~.~.R T ).f.9.=.W | f.Q (.| [ ~.[ ).W ).a X.).C.[.d.[ [ [ p.T [ [ R E V.. ",
+" . p ].L.A.d.x.C.C.L.L.^ u ^ o.m.{.R.W 9.d.X.W f.[ T f.f.o.o.C.Z.W | Q f.Q.f.A.f.9.o.d.d.d.j.Q A.A.f.).W [ [.D j.[.F m M.U.G.e w H h.yXiXyXe l._. .! ! xXH.N.k w r M N :Xz.f.| D f.W =.V.:.A.Q.X.oXoXL.W C.9.C.n.Y gXw iXy iXy r * = o.[ E f.! y y e y ^ C.A.W.m.o.A.W C.b.n.[ b.C.f.m.| E [ } [ R =.[ } [ [ [ ~.~.Q | | E 8.8.[ [ A.o.o.f.).q.f.c.0.{ { c.R R [ =.' ",
+" 4 o.X.L.f.f.f.A.m./ / / oXA.Q.Q.R.p.p.C.R.W f.f.C.).:.w.w y oXo.o.q. . .m.X.Q.f.W.`.W.Q.Q.q.m.:.W.[.[.;.J.! hXJ.N.G G.N.G U.N.G k e w.lXk w L H.U.F H._.F U...y l.%X( , /.R.).A.A.b.Z.b.V.q.m.d ^ R.9.A.L./ L.L.0Xe e e iXy M _ i A.9.| d.m ~ r y r N o.C.a L.V.f.9.W n.A.o.W ).f.f.o.f.[ [ [ T { { W [ 8.T.[ E Q | W Y =.R R R W /.C.C.T V.T c.Y [ =.9.E [ 8.A ",
+" . u.C.f.f.E R.d.m.m ^ ].a V.R.R.L.D.p.n.A.C.q.x.hXO.L H z k e q ! M.N.G.G ..J.M.O.m.[.O.L ~ w.w.w.w H lXlXG U.aXN.U.;.xXxX_.g.xXN.U.N.w lXG m g.'.Q.f.W.q. .H.J.~ K./ i o.q.A.q.I.A.A.A.f.q.X.oXX.q. .~ d m.9 =Xl.e l iXw l.y F.L.| | q.{.m y y w M = L.p Y b.{ W W R { =.Y [ [ { b.V.Q.A.R.R.C.T d.Q.| T.^.^.T.8.E [ C.p.S S S R R.W W Y { p.Y T A.C.d.T.| f.C ",
+" C f.f.W R C.C.p /.A.f.C.W W C.A.Q.m.m._.H.w.e H e H H H H H H e G.H H vXH H k k H w lXz h.z z k vXz e G F H.-.W.q.D -.-.D W.D A.D W.xXM.O.j.H.m Q.q.W.W E f.d.Q {.f.f.A.d.{.`.Q I.| Q f.f.d.A.Q.q.A.t.w.~ w k k e H l l e e >.R.R.[ f.9.j.m v c u p b.[ Z.W [ R [ W [ [ 8.R =.9.f.E f.f.X.X.m.D.f.f.| T.8.8.| | (.8.[ f.T.} } R *.{ p.T { R p.T { [ 8.8.[ &. ",
+" wXA.[.o.[.s x./ j.[.:.q.f.q.m.:.M.U.G H z z H h.h.k H H H H G.H vXvXH z k z z z h.bXh.h.z z H z H G xXq.d.W.K.L.{.{.I.q.f.f.Q f.Q :.q.`.j.q.m.q.:.W.Q f.d.E f.Y R W [ E E f.(.(.E f.E f.f.[ A.d.A._.O.~ ! w.U.;.e e e e w u W f.D.W | o.t v w c p V.A.C.X.Q.9.[ } 9.d.f.9.f.Q.| d.{.Q.a A.Q.W [ 8.8.} } } } E (.(.| | T.8.T.T.T.[ ] Y W f.R.R.[ { 8.R c. ",
+" $ G G G e G ..G G w.'.Q.{.H.! G.H vXz h.h.k.S.S.S.h.H H vXz vXvXH H k k k H H z h.h.h.H w H k H g.`.{.d.:.~ Q.q.| *X{.XXQ A.[ E E f.).f.D q.q.q.d.| Q Q E f.[ W E 8.E ~.| E T.T.9.[ | R.9.R.f.A.T V.O.m.A.[.j.! w w e M C.C.o.o.d.Q [.q b M K 1 p.C.f.[ | d.R f.W d.X.L.L.[.Q.W A.W C.p.{ } 8.9.} *.^.^.^.^.^.T.T.9.[ 8.8.T.{.d.[ S p.S { R.R.Q.R b.X ",
+" X hXz bXh.z z H z H ..m O.! H h.h.S.S.cXcXS.S.S.k.H h.h.z z z h.h.h.h.h.h.H H H H H z z e 8 m '.o.m.W.m.X.m.Q.q.{.{.q.d.W f.f.V.j.A.W | Q | Q W E } R [ Y Y T f.f.8.8.8.T.| 8.8.[ [ R.C.f.W W [ { b.C.V.W [ f.:...w y m X.I.I.R.f.W A.oXb .. XL.W } W =.[ T W [ E Q.m.u * & / f.C.9.{ 9.} 9.(.T.T.8.9.} T.XX(.| 9.[ [ f.W E T.{.{.9.f.n.[ C.L.o._.C ",
+" + G bXh.z z z z z z k e z h.cXcXcXcXcXcXcXS.S.h.h.cXh.h.k.k.h.S.k.S.S.h.z H H H H G.H G N.O.].m.L./.oXL.A.A./.q.m.Q D q.f.Q.Q.q.q.| XX| f.| R.[ p.W W A./.'.o.C.=.8.8.[ 8.[ [ 9.W [ f.C.W R.W C.C.V.9.[ E | d.uXq y & | | | T.| d./.;.v b c i | R.[ [ 9.| 9.R W Q.q.X.N N / a f.d.| I.T.T.(.T.R.| | 9.8.T.d.[ f.{.d.E (.T.} W Q.C.f._.X.V.[.q.+X ",
+" 1XH z z H h.z h.h.bXS.cXcXcXcXcXS.cXcXcXcXS.h.bXS.h.S.h.k h.S.S.bXh.S.h.z vXH vXG.N.j.f.o.j.[.[.o.X.A.A.A.f.).A.q.D f.{.Y.X.Q.-.-.q.| f.f.-.C.].:.o.X.j.O.t.'.p p.Y *.8.{ { R 9.C.A.Q.W f.i C.Z.C.A.A.Q.{.Q.g.j b a W XXT.8.~.(.A.0Xc b q & d.m.X.A.d.m.Q.C.9.| Q.]./ N oXa f.Q E d.XXXX| | ^.| 9.} E | d.R f.| E | | f.R [ W. .j.m.[.C.A.T ",
+" $ H H H H z h.h.h.h.S.S.cXcXh.h.z S.S.cXS.S.cXS.h.S.h.h.h.S.S.h.bXbXz bXz H vXH g.j.O.q.f.Z./.o.q.q.A.j.Q.f.q.D W f.I.{.{.d.m.'.'.q.W.`.M.[.L ~ g.m._.;.`.[.o.W f.[ 9.9.C.9.R.R.R.C.Z.C.A.o.X.K.Q.C.q.m. .;.b r N T f.d.W R } W v ;.0 r a C.m._.H.A.Q.X.R.R.f.A.]./ N / N X.d.f.T.I.I.XXXX(.T.8.[ 8.8.W [ f.[ ~.E E E E =.d.:.q.f.D { b.X ",
+" X 6 H H H H h.bXbXcXh.h.h.z z z z z cXcXcXcXcXh.S.h.S.h.cXh.h.z h.h.h.h.z vXvXH G G M._.o.o.:.[.q._.'.H.o.:.W.o.q.-.{.I.m.Q._._.m.q.q. .xXH G U.m q.q.m.R.W =.f.W C.A.C.X.Q.m.d.f.9.9.f.f.f.f.m.d.W d.f.q.gX..y * ].O.m.d.A.d.o.f t b 3X* p C.x./ a L.A.C.W W | C.Q.X./ oXm.{.I.d.XX(.| { T.8.T.| (.I.(.| 8.[ | E E 8.[ [ E Q f.W W c.X ",
+" X gXvXH H H z h.h.h.z z H z h.z h.bXcXcXh.S.h.h.h.S.bXh.z z z h.cXbXbXz z z z vXvXH G G G G w.w.J.M.m.:.q.o.m.Q :.{.| W [ W T.f.f.V.W._.N.N.O.oXo.).W 9.9.W | E [ 9.W W f.{.| T [ 9.9.9.d.| f.9.R A.9.f.:...b & x._.R.R.A.W [.t v c r N & u ^ ].].o.L.L.n.f.d.XX| R.A.X.o.A.T.{.| T.T.T.T.XX| | ~.*XI.I.I.XX| 8.9.[ E 8.[ V.[ [ E C ",
+" 4 8 vXvXH z z z z h.h.bXh.bXh.h.cXh.cXcXcXS.k.z z z z H z z z bXcXh.z cXh.h.z z z z h.h.z z H H H G `.`.q.W.q.-.*Xf.| f.| | d.E T E q.D W.m.X.o./ s.T 9.| | | T.[ [ T [ D A.| f.[ d.d.q.I.{.{.Q {.d.| W.q b 1.b.d.d.R.f.[ o.v 8 y y ;Xx.'.m.X.A.^ ^ K.u i C.I.| R.W ).d.d.9.T.8.T.T.T.8.T.(.XX(.E Q XXQ Q Q R [ E [ [ E Q [ [ Z ",
+" 4 hXz vXvXvXz lXh.h.h.h.z bXcXbXS.cXS.cXz z h.lXz z z H z z z bXcXbXcXcXbXz z z bXbXz vXH z z H H G w.! xXF xX`.-.q.E f.E I.| W W 9.f.W T W m.K.x 1 A.q.f.| | 9.E f.E | | Q q.D m.`.`.{.`. .d.{.d.| W.j v u p f.d.XXd.f.[.8 b 3XM OX- ).R.f.R.Q./.`.{.X.q.| | | 9.W d.d.f.T.~.T.T.T.~.} ~.~.~.E T.Q ~.E f.[ V.~.f.D Q | E ,X ",
+" + G H H H z bXz vXz vXH z z z bXbXcXcXh.H z h.h.z z H H vXz bXbXbXbXbXbXbXh.z z z H vXH H z z H H H G.G.G.N.xXF :.A.Q {.Q d.d.d.Q.W A.].K.M. .^ n.Z.D Q d.| | q.Q | {.Q Q q. .o.H.m.q.d.Q.{.X.R.d.q.L r ;XT f.d.XX| q.! b eXqX3Xr.= b.E | | T f.q.| T.Q | T.^.E E Q Q | T.| | E ~.} } E ~.(.^.^.(.} 8.~.E =.[ Q W Q Q +X ",
+" 5 sXvXH H H vXvXvXvXvXH H vXh.bXbXh.vXz z h.h.k z lXz z vXz z vXz bXvXz z z z z H z z H vXz z H H H H H H G.G xXH. .`.-.Q.Q. . .q.`.W.L -.{.q.q.R.W A.E f.W E | | | | E | q.o._.m.X.q.Q.X.A.A.Q.f.y b N u.Q.I.T.9.| ;.q l.y l.%.` V.d.| (.| E 8.XXT.(.(.(.(.^.Q (.I.Q Q | XXXXE [ ~.(.^.(.XX^.^.} } ~.E [ [ ~.V.E c. ",
+" + fXH vXvXG.vXvXH H vXvXH vXz bXz z z bXbXz z vXbXvXz z z z vXz h.z H z z H z z z vXvXH vXH vXH G.H vXvXvXH G.N.g.Y.H.`.d.[ :.m.Q.T `. . .A.W.{.f.A.).f.W | I.T.f.d.Q.f.A.W V.A.q.Q.o.'.oXq.Q.t.uXM * 1 d.{.d.| Q :.w l.e iX=X& V.T.(.(.| f.Q XXXX(.XXI.(.XX*X(.(.Q | Q Q Q ~.E ~.T.^.^.XXI.T.T.~.~.(.~.=.~.(.,X ",
+" 4 fXvXH vXvXz z z bXz H z bXz bXbXbXh.bXh.z z bXz z bXvXbXz bXh.z h.z H z H vXz H z H G.vXvXvXz vXz z z z z G.G G G J.'.q. .Y.Q.W f.[ W f.A.W [ f.T W f.Q d.| A.A.d.d.R.W W W T A.o.X.'.X.Q.m q b OX,.E q.`.`.{.Y.g.w.y r uX` 1 Q | I.(.(.XX-.*XXX*X-.XXXX*X*XI.Q *X{.| E E I.(.(.XXXX(.XXXXXX(.T.XX8.} [ ,X ",
+" X fXvXz vXvXh.bXh.z z bXz z bXz bXbXcXbXbXz bXz bXbXvXbXz z bXz z z z H z vXvXH vXvXvXG.vXvXz z z z bXz z vXH vXH G N.F xXF d.q.d.d.d.[ Z.[ R { ).d.d.q.q.f.R.R.| {.A.A.A.9.{ =.C.p f.f.A.g.qXl.%X,.+XA.`.H.! U.H. .L b b r.I W | I.I.XXI.XX*X*X-.dX*XXXXX-.`.*XI.dXq.E E Q Q I.I.XX8.^.(.XXI.| E E [ ,X ",
+" X y.vXvXvXbXz bXz bXvXvXbXz z bXbXbXh.h.z bXz z z bXbXvXbXz bXbXbXh.z z z vXH H H z vXH vXvXvXvXvXbXz bXz H z z z H G.G.F I.{.I.{.{.f.W W 9.p.| | | f.9.f.d.f.f.W A.! Q.o.u.Y b.Z.f.9.).w.qXl.$XB < x.X.H.w.J.! M.~ j j %._ 2 ).(.-.I.I.| `.-.dXxXF xX-.Q -.-.Q :.o.j.q.q.:. .d.T.I.XX^.XXI.| T.[ Z ",
+" X jXvXvXvXz vXz vXz vXz z bXbXbXbXbXbXz bXbXbXbXbXbXbXbXbXbXbXbXbXz bXz z z h.h.h.z vXH vXG.H vXbXz bXh.z bXz bXz z G.dX*X`.*XXXXXW 9.R.f.| f.| d.| Q | Q m.{.q.Q.M.X./ ].o.V./.[ [ D q w iX3X` ; / N K.[.m :.U.;...y 0 r.g = ).dX`.*X`.W.W.`.g.U.G :.H.M.m.`.`.M.O.~ ~ w.H.A.f.8.(.(.I.-.I.~.' ",
+" jXG.H bXbXz bXz bXz vXz z bXbXcXcXz bXbXbXbXbXbXbXcXbXbXz z bXbXbXbXS.cXbXcXcXS.h.bXz H H H H vXH z z bXz z z vXG.F `.-.XXXX(.| [ | Q q.f.Q I.| d.9.| f.q.W.A.o.M.Q.[.o.o.f.b.V.).J.k yX=X1.= d u u L.[.o._.g.k yX6XJ % # = j.-.-.`.Y.M.U.aX_.aXt.aXG ..M.`.M.~ '.~ L 9 [.'.q.*X| (.{.E X ",
+" 5 fXbXbXbXbXz bXz z z z z bXcXcXbXz bXbXbXh.z bXbXbXz z z cXbXcXbXcXcXS.S.S.S.cXbXh.h.z vXH H H vXbXz bXbXz H J.F Y.F -.-.(.XX| | {.Q -.-.-.{.`.Q f.f.f.Z.Z.=.`.-.W =.q.A.W.).j.aXiXe b _ wX[.'.O._.o./.:.k h.l.b @Xr._ - p A.Q :.W._.xX;._.t.;.:.J.G M.H.m O.K.d 9 M M m m.d.XX~.A . ",
+" C gXz bXbXbXbXz bXbXz h.cXS.cXh.h.bXbXh.z z bXbXbXz bXcXcXcXcXcXbXcXh.S.h.h.h.cXcXh.z z z z bXbXbXbXbXbXz G.xXxXJ.xXY.-.I.d.XX| Q *XW.`.*X{.`.m.V.V.{ [ [ A.m.Q.Q. .M.M.g.! M.O.w.iX#XP I j. .M._.m.W.gXe lXk e qX4Xu B Y W Q W.f.D j.[.:.q.aXW. . .-.q.L M.'.v q ! ..m M.:.d.,X ",
+" X jXH bXbXbXbXbXbXbXbXh.bXh.bXbXbXh.bXz z vXvXvXz cXbXcXbXcXcXcXcXcXcXS.h.S.h.S.h.h.h.z z h.bXbXcXbXz z J.q.dXY.dX{.-.m.d.{.{.q.W. .`.A. .`.:.A.f.A./.]. .M.M.! N.G H G.G H N.q w #XU +.j.H.J.J. .! U.H k w w w r u N L.| XXQ f.A.V.E ).~.| E V.f.A.d.dX .m._.F H.M.q.A.T Z ",
+" 5 G z bXbXz z z bXz z h.h.bXz z z vXz vXvXvXvXbXbXbXcXcXcXcXcXcXcXcXh.S.h.h.h.h.h.h.z bXS.bXbXbXh.H U.U.F Y.M.H.O.L m.`.F F :.M._.H. .q.Q.f.j.[.a [.M...e G.H G.z k U.G.J.! sX@X_ I ).:.dX:.dXJ.U.G.H J.w.! ~ oX/ W E } ~.E ).[ V.V.f.f.E R [ ~.Q | Q V.D W.`.:.A.u.X ",
+" . jXN.z z bXz vXH H H vXh.h.z z H vXH H vXvXz bXcXcXcXcXcXcXS.h.z lXh.z z h.h.h.z z z bXh.h.bXh.N. .:.q.q.m.m.H.K.[.t.~ m ! N.G G g.m [.O.m ! w.N.G G.G.J.J.xXJ.H Y.`.-.N.r <.$ U #.D Q Q ~.I.-.dXdXY.`.{.{.X.Q.f.[ | E ~.E W Y b.D d.Q f.D :.Q f.E V.W D f.=.' ",
+" 5 hXvXvXH z z vXH vXvXbXvXH H vXH vXvXz bXbXbXcXcXcXcXcXh.z z H z z z z h.h.z lXlXz z vXz H U.H.Y.H.;.m oXr ~ 9 w.H lXH vXH vXH G w.N.U.N.N.U.N.J.N. .:.q._.F `.`. .M.t.& & g 2 @ q.| XXI.Q d.-.-.I.I.{.d.{.Q.A.o.Q.q.).H.D T [ Q D A.f.Q D E [ W f.W u.X ",
+" X jXG H z z z z H vXvXz H H vXvXlXvXvXvXbXbXbXcXcXcXcXbXz bXbXh.z h.h.z vXz vXvXH vXH H H .._._.g.w.r v k y w H k z z vXH G.G G.G.U.J.J.Y.W.dXdXY.`.q.f.Q W.`.-.`._.m.j.[.# ( < D Q XXd.| f.E {.{.XX{.q.Q./.a /.L _.o.q.V.=.S ).f.f.E =.R E E E V.,X ",
+" + fXH H H vXz z vXvXz bXvXz vXz vXvXz bXbXcXcXcXcXh.h.bXcXbXz bXz z z z vXlXvXG.vXvXe M.[.9 ~ '.m G z k h.z z z z z H G.U.xXvXF dXdX*X{.| Q *X*Xq.Q E E | | E Q -.q.m.f.x @.A.{.I.Q f.9.f.W f.d.W { D s e./.t.b.).W.[.f.T =.[ V.[ R V.| [ +X ",
+" X @ hXH H H vXvXvXz z vXvXvXz z vXvXvXbXz bXbXh.h.z z h.z H z vXH H z z vXvXvXH H H ..q ;.G oXd t lXz z z H bXvXz H J.F :.dX-.I.XX{.-.{.q.Q Q Q | {.`.-.| 9.{.I.{.d.9.9.v.[ Q I.E | W E =.Y ).W v././.[ Z.b.p V.o.q.q.).f.W f.A.E E V.Z ",
+" 4 jXN.H H vXH vXvXz z bXz z z vXvXvXvXlXlXH lXlXH z z vXH vXH H vXz H z H z vXH H z H w q q w H H z z H z G.N.J.W.q.Q Q Q (.I.-.-.*X{.{.d.f.Q.| Q.-.{.I.{.| I.I.XX9.8.T.| ~.E E =.[ Y V.[ W b.{ b.Z.p a L.a o.[.[.^ K._.O.g.j.,X. ",
+" 4 y.G.vXvXvXvXvXvXvXbXz vXvXvXz H z H lXe lXlXz z z z z vXH vXz vXbXz z bXbXz z h.z H H H H k h.z H H U.W.E E f.W.Q I.I.8.| *XY.`.d.A.d.Q.X.R.m.X.d.Q XX*XI.I.XXXX| | | f.f.b.Y D [ W 9.f.Z.p.=.v.o.L.L.C.o.K.K.9 ;.7X@ X ",
+" 5 y.G.vXvXvXvXvXz bXz bXbXcXbXh.k k z z h.h.cXbXh.bXh.bXz vXz z z bXz bXbXbXbXz z z z H H H H H G. .Q E Q I.Q W.`. .`.`.Y.-.{.m.M.'.Q.'.X.m.X.{.W.q.I.I.-.I.I.| d.f.V.T u.A.f.Y [ E Q f.p.b.W 9.d.C.C.Q.'.m d - X ",
+" . + 6 zXH z z bXbXbXh.cXcXS.h.S.h.S.h.S.h.z h.bXh.bXbXh.vXvXvXz vXbXz bXbXh.z z z z z H G.U.J.F q.{.I.| E D W.:. .Y.*XY.m.[.H.g.~ m.X.m.'.X.q.:.`.*X-.`.I.| [ ).Q A.A.T o.9.[ E [ W f.[ [ | R.d.R.9.C.).I X ",
+" + jXG.z bXcXbXh.bXcXcXS.cXcXcXS.S.z h.h.z z bXbXbXz vXz z bXvXbXvXz z vXH vXvXG.N.xXF `.d.W f.Q ).A.D q.q.W. .f.q.*XW.:.-.`.Q.{.m.m.H. ._.! :.F dX*X| Q q.Q | A.[ {.| T.d.8.| | Q.d.I.I.d.Q.{ ,X. ",
+" X @ fXvXz z H z h.bXcXcXcXcXS.h.h.h.z vXvXvXH bXbXbXvXvXvXvXG.G.z H vXvXU.`.`.-.*X| Q (.| | Q o.d.d.W f.8.XXd.~.Q W.q. .8.d.X.Q.X.H. .xX:.q.`.`.W.q.f.Q | E R } (.E f.q.W Q 9.8.[ f.c.Z ",
+" X 5 6 sXvXvXz vXz z lXz bXz h.bXvXz z z H vXz z vXvXvXH N.N.G.H G.U.xX-.Q Q Q E 9.9.d.| 9.[ [ [ R Q XXXX| | | D W.f.[ W Q.{.Q.{.Q.`.H.q.:.m.q.q.{.Q Q ~.[ | E =.E f.E [ f.+X,XX ",
+" 4 $ hXH H z lXz vXlXlXlXlXlXh.h.h.z G.H vXvXG.G.F F xXF F F `.W.Q E f.Q | W 8.| f.R [ ~.=.*.E | {.{.f.E Q D .C.R.R.X.-.d.d.d. .:.A.q.q. .q.Q I.XXE (.E 9.[ f.E u.' . ",
+" + $ kXlXlXz z vXz z lXbXbXz z z G.G.G.xXF `.*X-.*X*XXXD Q W.f.D Q I.Q {.E f.d.| W ~.[ R ~.T.[ c.E W | q.f.Q.R.8.d.| | W Q.d.A.Q.A.q.*XW.{.XXQ | Q | =.,XX ",
+" X 4 1XgXzXvXz vXlXvXvXz vXz H G.G Y.W.Q E (.~.E | f.Q V.~.E *X*XXX| d.8.E [ R =.R R 8.[ [ =.| T.| E A.m.A.A.W T f.d.W d.f.q.d.f.| {.{.| [ 8X,XX ",
+" . 4 8XjXfXN.lXlXH vXlXH G.g.{.| E f.[ c.T Q Q Q V.D XXQ Q *XI.Q Q f.d.[ R 8.[ ^.} R { E T.| | A.q.Q q.A.W | f.q.E T.{.Q f.T T A ' X ",
+" 4 5 jXgXzXH N.! `.I.| d.Q E [ D | f.I.E XX^.^.*X{.E W D `.M.q.f.d.| | 9.} T.XXI.| ~.[ A.| Q Q Q | V.E | V.=.A C . ",
+" X 4 5 u.V.C.| | | T.8.(.E | Q I.-.| XXXXI.f.A.Q.f.f.q.| W.-.-.d.XX| XXI.| 9.[ E V.~.~.).8XA Z X ",
+" X X ' Z A A Y =.V.=.T E Q (.(.`.q.q.j.:.f.f.| q.d.| E | 8.V.f.).,XwX,XZ X X X ",
+" X X X C ' ' + wXwXwXO Z Z 4 ,X' C X X X . "
+};
diff --git a/samples/richtext/makefile.bcc b/samples/richtext/makefile.bcc
new file mode 100644
index 0000000000..d82a82f2d6
--- /dev/null
+++ b/samples/richtext/makefile.bcc
@@ -0,0 +1,237 @@
+# =========================================================================
+# This makefile was generated by
+# Bakefile 0.1.9 (http://bakefile.sourceforge.net)
+# Do not modify, all changes will be overwritten!
+# =========================================================================
+
+.autodepend
+
+!ifndef BCCDIR
+!ifndef MAKEDIR
+!error Your Borland compiler does not define MAKEDIR. Please define the BCCDIR variable, e.g. BCCDIR=d:\bc4
+!endif
+BCCDIR = $(MAKEDIR)\..
+!endif
+
+!include ../../build/msw/config.bcc
+
+# -------------------------------------------------------------------------
+# Do not modify the rest of this file!
+# -------------------------------------------------------------------------
+
+### Variables: ###
+
+WX_RELEASE_NODOT = 27
+OBJS = \
+ bcc_$(PORTNAME)$(WXUNIVNAME)$(WXUNICODEFLAG)$(WXDEBUGFLAG)$(WXDLLFLAG)$(CFG)
+LIBDIRNAME = .\..\..\lib\bcc_$(LIBTYPE_SUFFIX)$(CFG)
+SETUPHDIR = \
+ $(LIBDIRNAME)\$(PORTNAME)$(WXUNIVNAME)$(WXUNICODEFLAG)$(WXDEBUGFLAG)
+RICHTEXT_CXXFLAGS = $(__RUNTIME_LIBS_7) -I$(BCCDIR)\include $(__DEBUGINFO) \
+ $(__OPTIMIZEFLAG_2) $(__THREADSFLAG_6) -D__WXMSW__ $(__WXUNIV_DEFINE_p) \
+ $(__DEBUG_DEFINE_p) $(__EXCEPTIONS_DEFINE_p) $(__RTTI_DEFINE_p) \
+ $(__THREAD_DEFINE_p) $(__UNICODE_DEFINE_p) $(__MSLU_DEFINE_p) \
+ -I.\..\..\include -I$(SETUPHDIR) -I. $(__DLLFLAG_p) -I.\..\..\samples -DNOPCH \
+ $(CPPFLAGS) $(CXXFLAGS)
+RICHTEXT_OBJECTS = \
+ $(OBJS)\richtext_richtext.obj
+
+### Conditionally set variables: ###
+
+!if "$(USE_GUI)" == "0"
+PORTNAME = base
+!endif
+!if "$(USE_GUI)" == "1"
+PORTNAME = msw
+!endif
+!if "$(BUILD)" == "debug" && "$(DEBUG_FLAG)" == "default"
+WXDEBUGFLAG = d
+!endif
+!if "$(DEBUG_FLAG)" == "1"
+WXDEBUGFLAG = d
+!endif
+!if "$(UNICODE)" == "1"
+WXUNICODEFLAG = u
+!endif
+!if "$(WXUNIV)" == "1"
+WXUNIVNAME = univ
+!endif
+!if "$(SHARED)" == "1"
+WXDLLFLAG = dll
+!endif
+!if "$(SHARED)" == "0"
+LIBTYPE_SUFFIX = lib
+!endif
+!if "$(SHARED)" == "1"
+LIBTYPE_SUFFIX = dll
+!endif
+!if "$(MONOLITHIC)" == "0"
+EXTRALIBS_FOR_BASE =
+!endif
+!if "$(MONOLITHIC)" == "1"
+EXTRALIBS_FOR_BASE =
+!endif
+!if "$(BUILD)" == "debug"
+__OPTIMIZEFLAG_2 = -Od
+!endif
+!if "$(BUILD)" == "release"
+__OPTIMIZEFLAG_2 = -O2
+!endif
+!if "$(USE_THREADS)" == "0"
+__THREADSFLAG_5 =
+!endif
+!if "$(USE_THREADS)" == "1"
+__THREADSFLAG_5 = mt
+!endif
+!if "$(USE_THREADS)" == "0"
+__THREADSFLAG_6 =
+!endif
+!if "$(USE_THREADS)" == "1"
+__THREADSFLAG_6 = -tWM
+!endif
+!if "$(RUNTIME_LIBS)" == "dynamic"
+__RUNTIME_LIBS_7 = -tWR
+!endif
+!if "$(RUNTIME_LIBS)" == "static"
+__RUNTIME_LIBS_7 =
+!endif
+!if "$(RUNTIME_LIBS)" == "dynamic"
+__RUNTIME_LIBS_8 = i
+!endif
+!if "$(RUNTIME_LIBS)" == "static"
+__RUNTIME_LIBS_8 =
+!endif
+!if "$(WXUNIV)" == "1"
+__WXUNIV_DEFINE_p = -D__WXUNIVERSAL__
+!endif
+!if "$(WXUNIV)" == "1"
+__WXUNIV_DEFINE_p_1 = -d__WXUNIVERSAL__
+!endif
+!if "$(BUILD)" == "debug" && "$(DEBUG_FLAG)" == "default"
+__DEBUG_DEFINE_p = -D__WXDEBUG__
+!endif
+!if "$(DEBUG_FLAG)" == "1"
+__DEBUG_DEFINE_p = -D__WXDEBUG__
+!endif
+!if "$(BUILD)" == "debug" && "$(DEBUG_FLAG)" == "default"
+__DEBUG_DEFINE_p_1 = -d__WXDEBUG__
+!endif
+!if "$(DEBUG_FLAG)" == "1"
+__DEBUG_DEFINE_p_1 = -d__WXDEBUG__
+!endif
+!if "$(USE_EXCEPTIONS)" == "0"
+__EXCEPTIONS_DEFINE_p = -DwxNO_EXCEPTIONS
+!endif
+!if "$(USE_EXCEPTIONS)" == "0"
+__EXCEPTIONS_DEFINE_p_1 = -dwxNO_EXCEPTIONS
+!endif
+!if "$(USE_RTTI)" == "0"
+__RTTI_DEFINE_p = -DwxNO_RTTI
+!endif
+!if "$(USE_RTTI)" == "0"
+__RTTI_DEFINE_p_1 = -dwxNO_RTTI
+!endif
+!if "$(USE_THREADS)" == "0"
+__THREAD_DEFINE_p = -DwxNO_THREADS
+!endif
+!if "$(USE_THREADS)" == "0"
+__THREAD_DEFINE_p_1 = -dwxNO_THREADS
+!endif
+!if "$(UNICODE)" == "1"
+__UNICODE_DEFINE_p = -D_UNICODE
+!endif
+!if "$(UNICODE)" == "1"
+__UNICODE_DEFINE_p_1 = -d_UNICODE
+!endif
+!if "$(MSLU)" == "1"
+__MSLU_DEFINE_p = -DwxUSE_UNICODE_MSLU=1
+!endif
+!if "$(MSLU)" == "1"
+__MSLU_DEFINE_p_1 = -dwxUSE_UNICODE_MSLU=1
+!endif
+!if "$(SHARED)" == "1"
+__DLLFLAG_p = -DWXUSINGDLL
+!endif
+!if "$(SHARED)" == "1"
+__DLLFLAG_p_1 = -dWXUSINGDLL
+!endif
+!if "$(MONOLITHIC)" == "0"
+__WXLIB_ADV_p = \
+ wx$(PORTNAME)$(WXUNIVNAME)$(WX_RELEASE_NODOT)$(WXUNICODEFLAG)$(WXDEBUGFLAG)$(WX_LIB_FLAVOUR)_adv.lib
+!endif
+!if "$(MONOLITHIC)" == "0"
+__WXLIB_XML_p = \
+ wxbase$(WX_RELEASE_NODOT)$(WXUNICODEFLAG)$(WXDEBUGFLAG)$(WX_LIB_FLAVOUR)_xml.lib
+!endif
+!if "$(MONOLITHIC)" == "0"
+__WXLIB_HTML_p = \
+ wx$(PORTNAME)$(WXUNIVNAME)$(WX_RELEASE_NODOT)$(WXUNICODEFLAG)$(WXDEBUGFLAG)$(WX_LIB_FLAVOUR)_html.lib
+!endif
+!if "$(MONOLITHIC)" == "0"
+__WXLIB_CORE_p = \
+ wx$(PORTNAME)$(WXUNIVNAME)$(WX_RELEASE_NODOT)$(WXUNICODEFLAG)$(WXDEBUGFLAG)$(WX_LIB_FLAVOUR)_core.lib
+!endif
+!if "$(MONOLITHIC)" == "0"
+__WXLIB_BASE_p = \
+ wxbase$(WX_RELEASE_NODOT)$(WXUNICODEFLAG)$(WXDEBUGFLAG)$(WX_LIB_FLAVOUR).lib
+!endif
+!if "$(MONOLITHIC)" == "1"
+__WXLIB_MONO_p = \
+ wx$(PORTNAME)$(WXUNIVNAME)$(WX_RELEASE_NODOT)$(WXUNICODEFLAG)$(WXDEBUGFLAG)$(WX_LIB_FLAVOUR).lib
+!endif
+!if "$(USE_GUI)" == "1"
+__LIB_TIFF_p = wxtiff$(WXDEBUGFLAG).lib
+!endif
+!if "$(USE_GUI)" == "1"
+__LIB_JPEG_p = wxjpeg$(WXDEBUGFLAG).lib
+!endif
+!if "$(USE_GUI)" == "1"
+__LIB_PNG_p = wxpng$(WXDEBUGFLAG).lib
+!endif
+!if "$(MSLU)" == "1"
+__UNICOWS_LIB_p = unicows.lib
+!endif
+!if "$(BUILD)" == "debug" && "$(DEBUG_INFO)" == "default"
+__DEBUGINFO = -v
+!endif
+!if "$(BUILD)" == "release" && "$(DEBUG_INFO)" == "default"
+__DEBUGINFO = -v-
+!endif
+!if "$(DEBUG_INFO)" == "0"
+__DEBUGINFO = -v-
+!endif
+!if "$(DEBUG_INFO)" == "1"
+__DEBUGINFO = -v
+!endif
+
+
+all: $(OBJS)
+$(OBJS):
+ -if not exist $(OBJS) mkdir $(OBJS)
+
+### Targets: ###
+
+all: $(OBJS)\richtext.exe
+
+clean:
+ -if exist $(OBJS)\*.obj del $(OBJS)\*.obj
+ -if exist $(OBJS)\*.res del $(OBJS)\*.res
+ -if exist $(OBJS)\*.csm del $(OBJS)\*.csm
+ -if exist $(OBJS)\richtext.exe del $(OBJS)\richtext.exe
+ -if exist $(OBJS)\richtext.tds del $(OBJS)\richtext.tds
+ -if exist $(OBJS)\richtext.ilc del $(OBJS)\richtext.ilc
+ -if exist $(OBJS)\richtext.ild del $(OBJS)\richtext.ild
+ -if exist $(OBJS)\richtext.ilf del $(OBJS)\richtext.ilf
+ -if exist $(OBJS)\richtext.ils del $(OBJS)\richtext.ils
+
+$(OBJS)\richtext.exe: $(RICHTEXT_OBJECTS) $(OBJS)\richtext_richtext.res
+ ilink32 -Tpe -q $(LDFLAGS) -L$(BCCDIR)\lib -L$(BCCDIR)\lib\psdk $(__DEBUGINFO) -L$(LIBDIRNAME) -aa @&&|
+ c0w32.obj $(RICHTEXT_OBJECTS),$@,, $(__WXLIB_ADV_p) $(__WXLIB_XML_p) $(__WXLIB_HTML_p) $(__WXLIB_CORE_p) $(__WXLIB_BASE_p) $(__WXLIB_MONO_p) $(__LIB_TIFF_p) $(__LIB_JPEG_p) $(__LIB_PNG_p) wxzlib$(WXDEBUGFLAG).lib wxregex$(WXUNICODEFLAG)$(WXDEBUGFLAG).lib wxexpat$(WXDEBUGFLAG).lib $(EXTRALIBS_FOR_BASE) $(__UNICOWS_LIB_p) ole2w32.lib oleacc.lib odbc32.lib import32.lib cw32$(__THREADSFLAG_5)$(__RUNTIME_LIBS_8).lib,, $(OBJS)\richtext_richtext.res
+|
+
+$(OBJS)\richtext_richtext.obj: .\richtext.cpp
+ $(CXX) -q -c -P -o$@ $(RICHTEXT_CXXFLAGS) $**
+
+$(OBJS)\richtext_richtext.res: .\richtext.rc
+ brcc32 -32 -r -fo$@ -i$(BCCDIR)\include -d__WXMSW__ $(__WXUNIV_DEFINE_p_1) $(__DEBUG_DEFINE_p_1) $(__EXCEPTIONS_DEFINE_p_1) $(__RTTI_DEFINE_p_1) $(__THREAD_DEFINE_p_1) $(__UNICODE_DEFINE_p_1) $(__MSLU_DEFINE_p_1) -i.\..\..\include -i$(SETUPHDIR) -i. $(__DLLFLAG_p_1) -i.\..\..\samples -dNOPCH $**
+
diff --git a/samples/richtext/makefile.dmc b/samples/richtext/makefile.dmc
new file mode 100644
index 0000000000..d476f91299
--- /dev/null
+++ b/samples/richtext/makefile.dmc
@@ -0,0 +1,45 @@
+# =========================================================================
+# This makefile was generated by
+# Bakefile 0.1.9 (http://bakefile.sourceforge.net)
+# Do not modify, all changes will be overwritten!
+# =========================================================================
+
+include ../../build/msw/config.dmc
+
+# -------------------------------------------------------------------------
+# Do not modify the rest of this file!
+# -------------------------------------------------------------------------
+
+### Variables: ###
+
+WX_RELEASE_NODOT = 27
+OBJS = dmc_mswd$(CFG)
+LIBTYPE_SUFFIX = lib
+LIBDIRNAME = .\..\..\lib\dmc_$(LIBTYPE_SUFFIX)$(CFG)
+SETUPHDIR = $(LIBDIRNAME)\mswd
+RICHTEXT_CXXFLAGS = -g -o+none -D_WIN32_WINNT=0x0400 -D__WXMSW__ -D__WXDEBUG__ \
+ -I.\..\..\include -I$(SETUPHDIR) -w- -I. -WA -I.\..\..\samples -DNOPCH -Ar -Ae \
+ $(CPPFLAGS) $(CXXFLAGS)
+RICHTEXT_OBJECTS = \
+ $(OBJS)\richtext_richtext.obj
+
+### Targets: ###
+
+all : $(OBJS)\richtext.exe
+
+clean :
+ -if exist $(OBJS)\*.obj del $(OBJS)\*.obj
+ -if exist $(OBJS)\*.res del $(OBJS)\*.res
+ -if exist $(OBJS)\*.sym del $(OBJS)\*.sym
+ -if exist $(OBJS)\richtext.exe del $(OBJS)\richtext.exe
+ -if exist $(OBJS)\richtext.map del $(OBJS)\richtext.map
+
+$(OBJS)\richtext.exe : $(RICHTEXT_OBJECTS) $(OBJS)\richtext_richtext.res
+ link /NOLOGO /SILENT /NOI /DELEXECUTABLE /EXETYPE:NT $(LDFLAGS) /DEBUG /CODEVIEW /su:windows:4.0 $(RICHTEXT_OBJECTS),$@,$(OBJS)\richtext.map, $(LIBDIRNAME)\ wxmsw$(WX_RELEASE_NODOT)d$(WX_LIB_FLAVOUR)_adv.lib wxbase$(WX_RELEASE_NODOT)d$(WX_LIB_FLAVOUR)_xml.lib wxmsw$(WX_RELEASE_NODOT)d$(WX_LIB_FLAVOUR)_html.lib wxmsw$(WX_RELEASE_NODOT)d$(WX_LIB_FLAVOUR)_core.lib wxbase$(WX_RELEASE_NODOT)d$(WX_LIB_FLAVOUR).lib wxtiffd.lib wxjpegd.lib wxpngd.lib wxzlibd.lib wxregexd.lib wxexpatd.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib odbc32.lib ,, $(OBJS)\richtext_richtext.res
+
+$(OBJS)\richtext_richtext.obj : .\richtext.cpp
+ $(CXX) -mn -c -cpp -o$@ $(RICHTEXT_CXXFLAGS) .\richtext.cpp
+
+$(OBJS)\richtext_richtext.res : .\richtext.rc
+ rcc $** -o$@ -D_WIN32_WINNT=0x0400 -D__WXMSW__ -D__WXDEBUG__ -I.\..\..\include -I$(SETUPHDIR) -I. -I.\..\..\samples -DNOPCH -32 -v-
+
diff --git a/samples/richtext/makefile.dms b/samples/richtext/makefile.dms
new file mode 100644
index 0000000000..56ef62b3cb
--- /dev/null
+++ b/samples/richtext/makefile.dms
@@ -0,0 +1,207 @@
+# =========================================================================
+# This makefile was generated by
+# Bakefile 0.1.9 (http://bakefile.sourceforge.net)
+# Do not modify, all changes will be overwritten!
+# =========================================================================
+
+.LONGCOMMANDLINE: dmc link lib smake
+
+!include ../../build/msw/config.dms
+
+# -------------------------------------------------------------------------
+# Do not modify the rest of this file!
+# -------------------------------------------------------------------------
+
+### Conditionally set variables: ###
+
+!if "$(USE_GUI)" == "0"
+PORTNAME = base
+!endif
+!if "$(USE_GUI)" == "1"
+PORTNAME = msw
+!endif
+!if "$(BUILD)" == "debug"
+!if "$(DEBUG_FLAG)" == "default"
+WXDEBUGFLAG = d
+!endif
+!endif
+!if "$(DEBUG_FLAG)" == "1"
+WXDEBUGFLAG = d
+!endif
+!if "$(UNICODE)" == "1"
+WXUNICODEFLAG = u
+!endif
+!if "$(WXUNIV)" == "1"
+WXUNIVNAME = univ
+!endif
+!if "$(SHARED)" == "1"
+WXDLLFLAG = dll
+!endif
+!if "$(SHARED)" == "0"
+LIBTYPE_SUFFIX = lib
+!endif
+!if "$(SHARED)" == "1"
+LIBTYPE_SUFFIX = dll
+!endif
+!if "$(MONOLITHIC)" == "0"
+EXTRALIBS_FOR_BASE =
+!endif
+!if "$(MONOLITHIC)" == "1"
+EXTRALIBS_FOR_BASE =
+!endif
+!if "$(BUILD)" == "debug"
+!if "$(DEBUG_INFO)" == "default"
+__DEBUGINFO_0 = -g
+!endif
+!endif
+!if "$(BUILD)" == "release"
+!if "$(DEBUG_INFO)" == "default"
+__DEBUGINFO_0 =
+!endif
+!endif
+!if "$(DEBUG_INFO)" == "0"
+__DEBUGINFO_0 =
+!endif
+!if "$(DEBUG_INFO)" == "1"
+__DEBUGINFO_0 = -g
+!endif
+!if "$(BUILD)" == "debug"
+!if "$(DEBUG_INFO)" == "default"
+__DEBUGINFO_1 = /DEBUG /CODEVIEW
+!endif
+!endif
+!if "$(BUILD)" == "release"
+!if "$(DEBUG_INFO)" == "default"
+__DEBUGINFO_1 =
+!endif
+!endif
+!if "$(DEBUG_INFO)" == "0"
+__DEBUGINFO_1 =
+!endif
+!if "$(DEBUG_INFO)" == "1"
+__DEBUGINFO_1 = /DEBUG /CODEVIEW
+!endif
+!if "$(BUILD)" == "debug"
+__OPTIMIZEFLAG_2 = -o+none
+!endif
+!if "$(BUILD)" == "release"
+__OPTIMIZEFLAG_2 = -o
+!endif
+!if "$(RUNTIME_LIBS)" == "dynamic"
+__RUNTIME_LIBS_5 = -ND
+!endif
+!if "$(RUNTIME_LIBS)" == "static"
+__RUNTIME_LIBS_5 =
+!endif
+!if "$(USE_RTTI)" == "0"
+__RTTIFLAG_6 =
+!endif
+!if "$(USE_RTTI)" == "1"
+__RTTIFLAG_6 = -Ar
+!endif
+!if "$(USE_EXCEPTIONS)" == "0"
+__EXCEPTIONSFLAG_7 =
+!endif
+!if "$(USE_EXCEPTIONS)" == "1"
+__EXCEPTIONSFLAG_7 = -Ae
+!endif
+!if "$(MONOLITHIC)" == "0"
+__WXLIB_ADV_p = \
+ wx$(PORTNAME)$(WXUNIVNAME)$(WX_RELEASE_NODOT)$(WXUNICODEFLAG)$(WXDEBUGFLAG)$(WX_LIB_FLAVOUR)_adv.lib
+!endif
+!if "$(MONOLITHIC)" == "0"
+__WXLIB_XML_p = \
+ wxbase$(WX_RELEASE_NODOT)$(WXUNICODEFLAG)$(WXDEBUGFLAG)$(WX_LIB_FLAVOUR)_xml.lib
+!endif
+!if "$(MONOLITHIC)" == "0"
+__WXLIB_HTML_p = \
+ wx$(PORTNAME)$(WXUNIVNAME)$(WX_RELEASE_NODOT)$(WXUNICODEFLAG)$(WXDEBUGFLAG)$(WX_LIB_FLAVOUR)_html.lib
+!endif
+!if "$(MONOLITHIC)" == "0"
+__WXLIB_CORE_p = \
+ wx$(PORTNAME)$(WXUNIVNAME)$(WX_RELEASE_NODOT)$(WXUNICODEFLAG)$(WXDEBUGFLAG)$(WX_LIB_FLAVOUR)_core.lib
+!endif
+!if "$(MONOLITHIC)" == "0"
+__WXLIB_BASE_p = \
+ wxbase$(WX_RELEASE_NODOT)$(WXUNICODEFLAG)$(WXDEBUGFLAG)$(WX_LIB_FLAVOUR).lib
+!endif
+!if "$(MONOLITHIC)" == "1"
+__WXLIB_MONO_p = \
+ wx$(PORTNAME)$(WXUNIVNAME)$(WX_RELEASE_NODOT)$(WXUNICODEFLAG)$(WXDEBUGFLAG)$(WX_LIB_FLAVOUR).lib
+!endif
+!if "$(USE_GUI)" == "1"
+__LIB_TIFF_p = wxtiff$(WXDEBUGFLAG).lib
+!endif
+!if "$(USE_GUI)" == "1"
+__LIB_JPEG_p = wxjpeg$(WXDEBUGFLAG).lib
+!endif
+!if "$(USE_GUI)" == "1"
+__LIB_PNG_p = wxpng$(WXDEBUGFLAG).lib
+!endif
+!if "$(MSLU)" == "1"
+__UNICOWS_LIB_p = unicows.lib
+!endif
+!if "$(WXUNIV)" == "1"
+__WXUNIV_DEFINE_p = -D__WXUNIVERSAL__
+!endif
+!if "$(BUILD)" == "debug"
+!if "$(DEBUG_FLAG)" == "default"
+__DEBUG_DEFINE_p = -D__WXDEBUG__
+!endif
+!endif
+!if "$(DEBUG_FLAG)" == "1"
+__DEBUG_DEFINE_p = -D__WXDEBUG__
+!endif
+!if "$(USE_EXCEPTIONS)" == "0"
+__EXCEPTIONS_DEFINE_p = -DwxNO_EXCEPTIONS
+!endif
+!if "$(USE_RTTI)" == "0"
+__RTTI_DEFINE_p = -DwxNO_RTTI
+!endif
+!if "$(USE_THREADS)" == "0"
+__THREAD_DEFINE_p = -DwxNO_THREADS
+!endif
+!if "$(UNICODE)" == "1"
+__UNICODE_DEFINE_p = -D_UNICODE
+!endif
+!if "$(MSLU)" == "1"
+__MSLU_DEFINE_p = -DwxUSE_UNICODE_MSLU=1
+!endif
+!if "$(SHARED)" == "1"
+__DLLFLAG_p = -DWXUSINGDLL
+!endif
+
+### Variables: ###
+
+WX_RELEASE_NODOT = 27
+OBJS = dmc_$(PORTNAME)$(WXUNIVNAME)$(WXUNICODEFLAG)$(WXDEBUGFLAG)$(WXDLLFLAG)$(CFG)
+LIBDIRNAME = .\..\..\lib\dmc_$(LIBTYPE_SUFFIX)$(CFG)
+SETUPHDIR = $(LIBDIRNAME)\$(PORTNAME)$(WXUNIVNAME)$(WXUNICODEFLAG)$(WXDEBUGFLAG)
+RICHTEXT_CXXFLAGS = $(__DEBUGINFO_0) $(__OPTIMIZEFLAG_2) $(__RUNTIME_LIBS_5) -D_WIN32_WINNT=0x0400 -D__WXMSW__ $(__WXUNIV_DEFINE_p) $(__DEBUG_DEFINE_p) $(__EXCEPTIONS_DEFINE_p) $(__RTTI_DEFINE_p) $(__THREAD_DEFINE_p) $(__UNICODE_DEFINE_p) $(__MSLU_DEFINE_p) -I.\..\..\include -I$(SETUPHDIR) -w- -I. $(__DLLFLAG_p) -WA -I.\..\..\samples -DNOPCH $(__RTTIFLAG_6) $(__EXCEPTIONSFLAG_7) $(CPPFLAGS) $(CXXFLAGS)
+RICHTEXT_OBJECTS = $(OBJS)\richtext_richtext.obj
+
+
+all : $(OBJS)
+$(OBJS) :
+ -if not exist $(OBJS) mkdir $(OBJS)
+
+### Targets: ###
+
+
+all : $(OBJS)\richtext.exe
+
+clean :
+ -if exist $(OBJS)\*.obj del $(OBJS)\*.obj
+ -if exist $(OBJS)\*.res del $(OBJS)\*.res
+ -if exist $(OBJS)\*.sym del $(OBJS)\*.sym
+ -if exist $(OBJS)\richtext.exe del $(OBJS)\richtext.exe
+ -if exist $(OBJS)\richtext.map del $(OBJS)\richtext.map
+
+$(OBJS)\richtext.exe : $(RICHTEXT_OBJECTS) $(OBJS)\richtext_richtext.res
+ link /NOLOGO /SILENT /NOI /DELEXECUTABLE /EXETYPE:NT $(LDFLAGS) $(__DEBUGINFO_1) /su:windows:4.0 $(RICHTEXT_OBJECTS),$@,$(OBJS)\richtext.map, $(LIBDIRNAME)\ $(__WXLIB_ADV_p) $(__WXLIB_XML_p) $(__WXLIB_HTML_p) $(__WXLIB_CORE_p) $(__WXLIB_BASE_p) $(__WXLIB_MONO_p) $(__LIB_TIFF_p) $(__LIB_JPEG_p) $(__LIB_PNG_p) wxzlib$(WXDEBUGFLAG).lib wxregex$(WXUNICODEFLAG)$(WXDEBUGFLAG).lib wxexpat$(WXDEBUGFLAG).lib $(EXTRALIBS_FOR_BASE) $(__UNICOWS_LIB_p) kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib odbc32.lib ,, $(OBJS)\richtext_richtext.res
+
+$(OBJS)\richtext_richtext.obj : .\richtext.cpp
+ $(CXX) -mn -c -cpp -o$@ $(RICHTEXT_CXXFLAGS) .\richtext.cpp
+
+$(OBJS)\richtext_richtext.res : .\richtext.rc
+ rcc $** -o$@ -D_WIN32_WINNT=0x0400 -D__WXMSW__ $(__WXUNIV_DEFINE_p) $(__DEBUG_DEFINE_p) $(__EXCEPTIONS_DEFINE_p) $(__RTTI_DEFINE_p) $(__THREAD_DEFINE_p) $(__UNICODE_DEFINE_p) $(__MSLU_DEFINE_p) -I.\..\..\include -I$(SETUPHDIR) -I. $(__DLLFLAG_p) -I.\..\..\samples -DNOPCH -32 -v-
diff --git a/samples/richtext/makefile.gcc b/samples/richtext/makefile.gcc
new file mode 100644
index 0000000000..a460ecb899
--- /dev/null
+++ b/samples/richtext/makefile.gcc
@@ -0,0 +1,235 @@
+# =========================================================================
+# This makefile was generated by
+# Bakefile 0.1.9 (http://bakefile.sourceforge.net)
+# Do not modify, all changes will be overwritten!
+# =========================================================================
+
+include ../../build/msw/config.gcc
+
+# -------------------------------------------------------------------------
+# Do not modify the rest of this file!
+# -------------------------------------------------------------------------
+
+### Variables: ###
+
+CPPDEPS = -MT$@ -MF$@.d -MD
+WX_RELEASE_NODOT = 27
+OBJS = \
+ gcc_$(PORTNAME)$(WXUNIVNAME)$(WXUNICODEFLAG)$(WXDEBUGFLAG)$(WXDLLFLAG)$(CFG)
+LIBDIRNAME = .\..\..\lib\gcc_$(LIBTYPE_SUFFIX)$(CFG)
+SETUPHDIR = \
+ $(LIBDIRNAME)\$(PORTNAME)$(WXUNIVNAME)$(WXUNICODEFLAG)$(WXDEBUGFLAG)
+RICHTEXT_CXXFLAGS = $(__DEBUGINFO) $(__OPTIMIZEFLAG_2) $(__THREADSFLAG) \
+ $(GCCFLAGS) -DHAVE_W32API_H -D__WXMSW__ $(__WXUNIV_DEFINE_p) \
+ $(__DEBUG_DEFINE_p) $(__EXCEPTIONS_DEFINE_p) $(__RTTI_DEFINE_p) \
+ $(__THREAD_DEFINE_p) $(__UNICODE_DEFINE_p) $(__MSLU_DEFINE_p) \
+ -I.\..\..\include -I$(SETUPHDIR) -W -Wall -I. $(__DLLFLAG_p) -I.\..\..\samples \
+ -DNOPCH $(__RTTIFLAG_5) $(__EXCEPTIONSFLAG_6) -Wno-ctor-dtor-privacy \
+ $(CPPFLAGS) $(CXXFLAGS)
+RICHTEXT_OBJECTS = \
+ $(OBJS)\richtext_richtext.o \
+ $(OBJS)\richtext_richtext_rc.o
+
+### Conditionally set variables: ###
+
+ifeq ($(GCC_VERSION),2.95)
+GCCFLAGS = -fvtable-thunks
+endif
+ifeq ($(USE_GUI),0)
+PORTNAME = base
+endif
+ifeq ($(USE_GUI),1)
+PORTNAME = msw
+endif
+ifeq ($(BUILD),debug)
+ifeq ($(DEBUG_FLAG),default)
+WXDEBUGFLAG = d
+endif
+endif
+ifeq ($(DEBUG_FLAG),1)
+WXDEBUGFLAG = d
+endif
+ifeq ($(UNICODE),1)
+WXUNICODEFLAG = u
+endif
+ifeq ($(WXUNIV),1)
+WXUNIVNAME = univ
+endif
+ifeq ($(SHARED),1)
+WXDLLFLAG = dll
+endif
+ifeq ($(SHARED),0)
+LIBTYPE_SUFFIX = lib
+endif
+ifeq ($(SHARED),1)
+LIBTYPE_SUFFIX = dll
+endif
+ifeq ($(MONOLITHIC),0)
+EXTRALIBS_FOR_BASE =
+endif
+ifeq ($(MONOLITHIC),1)
+EXTRALIBS_FOR_BASE =
+endif
+ifeq ($(BUILD),debug)
+__OPTIMIZEFLAG_2 = -O0
+endif
+ifeq ($(BUILD),release)
+__OPTIMIZEFLAG_2 = -O2
+endif
+ifeq ($(USE_RTTI),0)
+__RTTIFLAG_5 = -fno-rtti
+endif
+ifeq ($(USE_RTTI),1)
+__RTTIFLAG_5 =
+endif
+ifeq ($(USE_EXCEPTIONS),0)
+__EXCEPTIONSFLAG_6 = -fno-exceptions
+endif
+ifeq ($(USE_EXCEPTIONS),1)
+__EXCEPTIONSFLAG_6 =
+endif
+ifeq ($(WXUNIV),1)
+__WXUNIV_DEFINE_p = -D__WXUNIVERSAL__
+endif
+ifeq ($(WXUNIV),1)
+__WXUNIV_DEFINE_p_1 = --define __WXUNIVERSAL__
+endif
+ifeq ($(BUILD),debug)
+ifeq ($(DEBUG_FLAG),default)
+__DEBUG_DEFINE_p = -D__WXDEBUG__
+endif
+endif
+ifeq ($(DEBUG_FLAG),1)
+__DEBUG_DEFINE_p = -D__WXDEBUG__
+endif
+ifeq ($(BUILD),debug)
+ifeq ($(DEBUG_FLAG),default)
+__DEBUG_DEFINE_p_1 = --define __WXDEBUG__
+endif
+endif
+ifeq ($(DEBUG_FLAG),1)
+__DEBUG_DEFINE_p_1 = --define __WXDEBUG__
+endif
+ifeq ($(USE_EXCEPTIONS),0)
+__EXCEPTIONS_DEFINE_p = -DwxNO_EXCEPTIONS
+endif
+ifeq ($(USE_EXCEPTIONS),0)
+__EXCEPTIONS_DEFINE_p_1 = --define wxNO_EXCEPTIONS
+endif
+ifeq ($(USE_RTTI),0)
+__RTTI_DEFINE_p = -DwxNO_RTTI
+endif
+ifeq ($(USE_RTTI),0)
+__RTTI_DEFINE_p_1 = --define wxNO_RTTI
+endif
+ifeq ($(USE_THREADS),0)
+__THREAD_DEFINE_p = -DwxNO_THREADS
+endif
+ifeq ($(USE_THREADS),0)
+__THREAD_DEFINE_p_1 = --define wxNO_THREADS
+endif
+ifeq ($(UNICODE),1)
+__UNICODE_DEFINE_p = -D_UNICODE
+endif
+ifeq ($(UNICODE),1)
+__UNICODE_DEFINE_p_1 = --define _UNICODE
+endif
+ifeq ($(MSLU),1)
+__MSLU_DEFINE_p = -DwxUSE_UNICODE_MSLU=1
+endif
+ifeq ($(MSLU),1)
+__MSLU_DEFINE_p_1 = --define wxUSE_UNICODE_MSLU=1
+endif
+ifeq ($(SHARED),1)
+__DLLFLAG_p = -DWXUSINGDLL
+endif
+ifeq ($(SHARED),1)
+__DLLFLAG_p_1 = --define WXUSINGDLL
+endif
+ifeq ($(MONOLITHIC),0)
+__WXLIB_ADV_p = \
+ -lwx$(PORTNAME)$(WXUNIVNAME)$(WX_RELEASE_NODOT)$(WXUNICODEFLAG)$(WXDEBUGFLAG)$(WX_LIB_FLAVOUR)_adv
+endif
+ifeq ($(MONOLITHIC),0)
+__WXLIB_XML_p = \
+ -lwxbase$(WX_RELEASE_NODOT)$(WXUNICODEFLAG)$(WXDEBUGFLAG)$(WX_LIB_FLAVOUR)_xml
+endif
+ifeq ($(MONOLITHIC),0)
+__WXLIB_HTML_p = \
+ -lwx$(PORTNAME)$(WXUNIVNAME)$(WX_RELEASE_NODOT)$(WXUNICODEFLAG)$(WXDEBUGFLAG)$(WX_LIB_FLAVOUR)_html
+endif
+ifeq ($(MONOLITHIC),0)
+__WXLIB_CORE_p = \
+ -lwx$(PORTNAME)$(WXUNIVNAME)$(WX_RELEASE_NODOT)$(WXUNICODEFLAG)$(WXDEBUGFLAG)$(WX_LIB_FLAVOUR)_core
+endif
+ifeq ($(MONOLITHIC),0)
+__WXLIB_BASE_p = \
+ -lwxbase$(WX_RELEASE_NODOT)$(WXUNICODEFLAG)$(WXDEBUGFLAG)$(WX_LIB_FLAVOUR)
+endif
+ifeq ($(MONOLITHIC),1)
+__WXLIB_MONO_p = \
+ -lwx$(PORTNAME)$(WXUNIVNAME)$(WX_RELEASE_NODOT)$(WXUNICODEFLAG)$(WXDEBUGFLAG)$(WX_LIB_FLAVOUR)
+endif
+ifeq ($(USE_GUI),1)
+__LIB_TIFF_p = -lwxtiff$(WXDEBUGFLAG)
+endif
+ifeq ($(USE_GUI),1)
+__LIB_JPEG_p = -lwxjpeg$(WXDEBUGFLAG)
+endif
+ifeq ($(USE_GUI),1)
+__LIB_PNG_p = -lwxpng$(WXDEBUGFLAG)
+endif
+ifeq ($(MSLU),1)
+__UNICOWS_LIB_p = -lunicows
+endif
+ifeq ($(BUILD),debug)
+ifeq ($(DEBUG_INFO),default)
+__DEBUGINFO = -g
+endif
+endif
+ifeq ($(BUILD),release)
+ifeq ($(DEBUG_INFO),default)
+__DEBUGINFO =
+endif
+endif
+ifeq ($(DEBUG_INFO),0)
+__DEBUGINFO =
+endif
+ifeq ($(DEBUG_INFO),1)
+__DEBUGINFO = -g
+endif
+ifeq ($(USE_THREADS),0)
+__THREADSFLAG =
+endif
+ifeq ($(USE_THREADS),1)
+__THREADSFLAG = -mthreads
+endif
+
+
+all: $(OBJS)
+$(OBJS):
+ -if not exist $(OBJS) mkdir $(OBJS)
+
+### Targets: ###
+
+all: $(OBJS)\richtext.exe
+
+clean:
+ -if exist $(OBJS)\*.o del $(OBJS)\*.o
+ -if exist $(OBJS)\*.d del $(OBJS)\*.d
+ -if exist $(OBJS)\richtext.exe del $(OBJS)\richtext.exe
+
+$(OBJS)\richtext.exe: $(RICHTEXT_OBJECTS) $(OBJS)\richtext_richtext_rc.o
+ $(CXX) -o $@ $(RICHTEXT_OBJECTS) $(LDFLAGS) $(__DEBUGINFO) $(__THREADSFLAG) -L$(LIBDIRNAME) -Wl,--subsystem,windows -mwindows $(__WXLIB_ADV_p) $(__WXLIB_XML_p) $(__WXLIB_HTML_p) $(__WXLIB_CORE_p) $(__WXLIB_BASE_p) $(__WXLIB_MONO_p) $(__LIB_TIFF_p) $(__LIB_JPEG_p) $(__LIB_PNG_p) -lwxzlib$(WXDEBUGFLAG) -lwxregex$(WXUNICODEFLAG)$(WXDEBUGFLAG) -lwxexpat$(WXDEBUGFLAG) $(EXTRALIBS_FOR_BASE) $(__UNICOWS_LIB_p) -lkernel32 -luser32 -lgdi32 -lcomdlg32 -lwinspool -lwinmm -lshell32 -lcomctl32 -lole32 -loleaut32 -luuid -lrpcrt4 -ladvapi32 -lwsock32 -lodbc32
+
+$(OBJS)\richtext_richtext.o: ./richtext.cpp
+ $(CXX) -c -o $@ $(RICHTEXT_CXXFLAGS) $(CPPDEPS) $<
+
+$(OBJS)\richtext_richtext_rc.o: ./richtext.rc
+ windres --use-temp-file -i$< -o$@ --define __WXMSW__ $(__WXUNIV_DEFINE_p_1) $(__DEBUG_DEFINE_p_1) $(__EXCEPTIONS_DEFINE_p_1) $(__RTTI_DEFINE_p_1) $(__THREAD_DEFINE_p_1) $(__UNICODE_DEFINE_p_1) $(__MSLU_DEFINE_p_1) --include-dir ./../../include --include-dir $(SETUPHDIR) --include-dir . $(__DLLFLAG_p_1) --include-dir ./../../samples --define NOPCH
+
+.PHONY: all clean
+
+
+# Dependencies tracking:
+-include $(OBJS)/*.d
diff --git a/samples/richtext/readme.txt b/samples/richtext/readme.txt
new file mode 100644
index 0000000000..6282853661
--- /dev/null
+++ b/samples/richtext/readme.txt
@@ -0,0 +1,100 @@
+
+wxRichTextCtrl README
+=====================
+
+Welcome to wxRichTextCtrl. It includes the following functionality:
+
+* Text entry, paragraph wrapping
+
+* Scrolling, keyboard navigation
+
+* Application of character styles:
+
+ bold, italic, underlined, font face, text colour
+
+* Application of paragraph styles:
+
+ left/right indentation, sub-indentation (first-line indent),
+ paragraph spacing (before and after), line spacing,
+ left/centre/right alignment, numbered bullets
+
+* Insertion of images
+
+* Copy/paste
+
+* Undo/Redo with optional batching and undo history suppression
+
+* Named paragraph and character styles management and application
+
+* File handlers allow addition of file formats
+
+* Text saving and loading, XML saving and loading, HTML saving (unfinished)
+
+Sorry, this is a Windows-only demo for now but the code should
+compile on other platforms.
+
+Design
+======
+
+Data is represented by a hierarchy of objects, all derived from
+wxRichTextObject.
+
+The top of the hierarchy is the buffer, a kind of wxRichTextParagraphLayoutBox.
+These boxes will allow flexible placement of text boxes on a page, but
+for now there will be a single box representing the document,
+and this box will a wxRichTextParagraphLayoutBox which contains further
+wxRichTextParagraph objects, each of which can include text and images.
+
+Each object maintains a range (start and end position) measured
+from the start of the main parent box.
+A paragraph object knows its range, and a text fragment knows its range
+too. So, a character or image in a page has a position relative to the
+start of the document, and a character in an embedded text box has
+a position relative to that text box. For now, we will not be dealing with
+embedded objects but it's something to bear in mind for later.
+
+Before display, a changed buffer must have Layout() called on it,
+to do wrapping, alignment etc. Ranges representing wrapped lines are stored
+with each paragraph.
+
+Since wxRichTextBuffer is separate from wxRichTextCtrl, the storage
+and rendering facilities can be used by other controls.
+
+API
+===
+
+It's basically the wxTextCtrl with some additions. There is a new
+wxTextAttrEx class deriving from wxTextAttr, to accomodate new
+style attributes. This could be merged with wxTextAttr. There
+is also a wxRichTextAttr which is similar to wxTextAttrEx but
+doesn't store the font as a wxFont: this allows much more
+efficient operations, especially when querying styles in a
+UI update handler. We would not want to create several new wxFonts
+when querying for italics, bold, etc. every few milliseconds.
+
+See "Functionality specific to wxRichTextCtrl" section in richtextctrl.h.
+
+One addition is Set/GetBasicStyle, which is needed in addition to
+Set/GetDefaultStyle to get the overall style for the buffer
+from which content will inherit (after apply the default style).
+
+wxRichTextRange is a new class representing start and end positions.
+It's used in the implementation so that pieces of content
+know their range, and also in the API in preference to using
+two positions.
+
+What next?
+==========
+
+- Decision about where to put it: wxCode, wxWidgets
+- Makefiles/bakefiles
+- Refining the API
+- Documentation
+- Bug fixing/improvements
+
+
+See todo.txt for a list of bugs, improvements and features,
+and also TODO throughout the source.
+
+==
+Julian Smart, October 18th 2005
diff --git a/samples/richtext/richtext.cpp b/samples/richtext/richtext.cpp
new file mode 100644
index 0000000000..fb36a87852
--- /dev/null
+++ b/samples/richtext/richtext.cpp
@@ -0,0 +1,1013 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name: richedit.cpp
+// Purpose: wxWidgets rich text editor sample
+// Author: Julian Smart
+// Modified by:
+// Created: 2005-10-02
+// RCS-ID: $Id$
+// Copyright: (c) Julian Smart
+// Licence: wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+// ============================================================================
+// declarations
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// headers
+// ----------------------------------------------------------------------------
+
+// For compilers that support precompilation, includes "wx/wx.h".
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+ #pragma hdrstop
+#endif
+
+// for all others, include the necessary headers (this file is usually all you
+// need because it includes almost all "standard" wxWidgets headers)
+#ifndef WX_PRECOMP
+ #include "wx/wx.h"
+#endif
+
+#include "wx/fontdlg.h"
+#include "wx/splitter.h"
+#include "wx/sstream.h"
+#include "wx/html/htmlwin.h"
+
+#include "bitmaps/sample.xpm"
+#include "bitmaps/smiley.xpm"
+// #include "bitmaps/idea.xpm"
+#include "bitmaps/zebra.xpm"
+
+#include "bitmaps/open.xpm"
+#include "bitmaps/save.xpm"
+#include "bitmaps/copy.xpm"
+#include "bitmaps/cut.xpm"
+#include "bitmaps/paste.xpm"
+#include "bitmaps/undo.xpm"
+#include "bitmaps/redo.xpm"
+#include "bitmaps/bold.xpm"
+#include "bitmaps/italic.xpm"
+#include "bitmaps/underline.xpm"
+
+#include "bitmaps/alignleft.xpm"
+#include "bitmaps/alignright.xpm"
+#include "bitmaps/centre.xpm"
+#include "bitmaps/font.xpm"
+#include "bitmaps/indentless.xpm"
+#include "bitmaps/indentmore.xpm"
+
+#include "richtextctrl.h"
+#include "richtextstyles.h"
+#include "richtextxml.h"
+
+// ----------------------------------------------------------------------------
+// resources
+// ----------------------------------------------------------------------------
+
+// ----------------------------------------------------------------------------
+// private classes
+// ----------------------------------------------------------------------------
+
+// Define a new application type, each program should derive a class from wxApp
+class MyApp : public wxApp
+{
+public:
+ // override base class virtuals
+ // ----------------------------
+
+ // this one is called on application startup and is a good place for the app
+ // initialization (doing it here and not in the ctor allows to have an error
+ // return: if OnInit() returns false, the application terminates)
+ virtual bool OnInit();
+ virtual int OnExit();
+
+ void CreateStyles();
+
+ wxRichTextStyleSheet* GetStyleSheet() const { return m_styleSheet; }
+
+ wxRichTextStyleSheet* m_styleSheet;
+};
+
+// Define a new frame type: this is going to be our main frame
+class MyFrame : public wxFrame
+{
+public:
+ // ctor(s)
+ MyFrame(const wxString& title, wxWindowID id, const wxPoint& pos = wxDefaultPosition,
+ const wxSize& size = wxDefaultSize, long style = wxDEFAULT_FRAME_STYLE);
+
+ // event handlers (these functions should _not_ be virtual)
+ void OnQuit(wxCommandEvent& event);
+ void OnAbout(wxCommandEvent& event);
+
+ void OnOpen(wxCommandEvent& event);
+ void OnSave(wxCommandEvent& event);
+ void OnSaveAs(wxCommandEvent& event);
+
+ void OnBold(wxCommandEvent& event);
+ void OnItalic(wxCommandEvent& event);
+ void OnUnderline(wxCommandEvent& event);
+
+ void OnUpdateBold(wxUpdateUIEvent& event);
+ void OnUpdateItalic(wxUpdateUIEvent& event);
+ void OnUpdateUnderline(wxUpdateUIEvent& event);
+
+ void OnAlignLeft(wxCommandEvent& event);
+ void OnAlignCentre(wxCommandEvent& event);
+ void OnAlignRight(wxCommandEvent& event);
+
+ void OnUpdateAlignLeft(wxUpdateUIEvent& event);
+ void OnUpdateAlignCentre(wxUpdateUIEvent& event);
+ void OnUpdateAlignRight(wxUpdateUIEvent& event);
+
+ void OnFont(wxCommandEvent& event);
+ void OnIndentMore(wxCommandEvent& event);
+ void OnIndentLess(wxCommandEvent& event);
+
+ void OnLineSpacingHalf(wxCommandEvent& event);
+ void OnLineSpacingDouble(wxCommandEvent& event);
+ void OnLineSpacingSingle(wxCommandEvent& event);
+
+ void OnParagraphSpacingMore(wxCommandEvent& event);
+ void OnParagraphSpacingLess(wxCommandEvent& event);
+
+ void OnViewHTML(wxCommandEvent& event);
+
+ // Forward command events to the current rich text control, if any
+ bool ProcessEvent(wxEvent& event);
+
+private:
+ // any class wishing to process wxWidgets events must use this macro
+ DECLARE_EVENT_TABLE()
+
+ wxRichTextCtrl* m_richTextCtrl;
+};
+
+// ----------------------------------------------------------------------------
+// constants
+// ----------------------------------------------------------------------------
+
+// IDs for the controls and the menu commands
+enum
+{
+ // menu items
+ ID_Quit = wxID_EXIT,
+ ID_About = wxID_ABOUT,
+
+ ID_FORMAT_BOLD = 100,
+ ID_FORMAT_ITALIC,
+ ID_FORMAT_UNDERLINE,
+ ID_FORMAT_FONT,
+
+ ID_FORMAT_ALIGN_LEFT,
+ ID_FORMAT_ALIGN_CENTRE,
+ ID_FORMAT_ALIGN_RIGHT,
+
+ ID_FORMAT_INDENT_MORE,
+ ID_FORMAT_INDENT_LESS,
+
+ ID_FORMAT_PARAGRAPH_SPACING_MORE,
+ ID_FORMAT_PARAGRAPH_SPACING_LESS,
+
+ ID_FORMAT_LINE_SPACING_HALF,
+ ID_FORMAT_LINE_SPACING_DOUBLE,
+ ID_FORMAT_LINE_SPACING_SINGLE,
+
+ ID_VIEW_HTML
+};
+
+// ----------------------------------------------------------------------------
+// event tables and other macros for wxWidgets
+// ----------------------------------------------------------------------------
+
+// the event tables connect the wxWidgets events with the functions (event
+// handlers) which process them. It can be also done at run-time, but for the
+// simple menu events like this the static method is much simpler.
+BEGIN_EVENT_TABLE(MyFrame, wxFrame)
+ EVT_MENU(ID_Quit, MyFrame::OnQuit)
+ EVT_MENU(ID_About, MyFrame::OnAbout)
+
+ EVT_MENU(wxID_OPEN, MyFrame::OnOpen)
+ EVT_MENU(wxID_SAVE, MyFrame::OnSave)
+ EVT_MENU(wxID_SAVEAS, MyFrame::OnSaveAs)
+
+ EVT_MENU(ID_FORMAT_BOLD, MyFrame::OnBold)
+ EVT_MENU(ID_FORMAT_ITALIC, MyFrame::OnItalic)
+ EVT_MENU(ID_FORMAT_UNDERLINE, MyFrame::OnUnderline)
+
+ EVT_UPDATE_UI(ID_FORMAT_BOLD, MyFrame::OnUpdateBold)
+ EVT_UPDATE_UI(ID_FORMAT_ITALIC, MyFrame::OnUpdateItalic)
+ EVT_UPDATE_UI(ID_FORMAT_UNDERLINE, MyFrame::OnUpdateUnderline)
+
+ EVT_MENU(ID_FORMAT_ALIGN_LEFT, MyFrame::OnAlignLeft)
+ EVT_MENU(ID_FORMAT_ALIGN_CENTRE, MyFrame::OnAlignCentre)
+ EVT_MENU(ID_FORMAT_ALIGN_RIGHT, MyFrame::OnAlignRight)
+
+ EVT_UPDATE_UI(ID_FORMAT_ALIGN_LEFT, MyFrame::OnUpdateAlignLeft)
+ EVT_UPDATE_UI(ID_FORMAT_ALIGN_CENTRE, MyFrame::OnUpdateAlignCentre)
+ EVT_UPDATE_UI(ID_FORMAT_ALIGN_RIGHT, MyFrame::OnUpdateAlignRight)
+
+ EVT_MENU(ID_FORMAT_FONT, MyFrame::OnFont)
+ EVT_MENU(ID_FORMAT_INDENT_MORE, MyFrame::OnIndentMore)
+ EVT_MENU(ID_FORMAT_INDENT_LESS, MyFrame::OnIndentLess)
+
+ EVT_MENU(ID_FORMAT_LINE_SPACING_HALF, MyFrame::OnLineSpacingHalf)
+ EVT_MENU(ID_FORMAT_LINE_SPACING_SINGLE, MyFrame::OnLineSpacingSingle)
+ EVT_MENU(ID_FORMAT_LINE_SPACING_DOUBLE, MyFrame::OnLineSpacingDouble)
+
+ EVT_MENU(ID_FORMAT_PARAGRAPH_SPACING_MORE, MyFrame::OnParagraphSpacingMore)
+ EVT_MENU(ID_FORMAT_PARAGRAPH_SPACING_LESS, MyFrame::OnParagraphSpacingLess)
+
+ EVT_MENU(ID_VIEW_HTML, MyFrame::OnViewHTML)
+END_EVENT_TABLE()
+
+// Create a new application object: this macro will allow wxWidgets to create
+// the application object during program execution (it's better than using a
+// static object for many reasons) and also implements the accessor function
+// wxGetApp() which will return the reference of the right type (i.e. MyApp and
+// not wxApp)
+IMPLEMENT_APP(MyApp)
+
+// ============================================================================
+// implementation
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// the application class
+// ----------------------------------------------------------------------------
+
+// 'Main program' equivalent: the program execution "starts" here
+bool MyApp::OnInit()
+{
+ m_styleSheet = new wxRichTextStyleSheet;
+
+ CreateStyles();
+
+ // Add extra handlers (plain text is automatically added)
+ wxRichTextBuffer::AddHandler(new wxRichTextXMLHandler);
+ wxRichTextBuffer::AddHandler(new wxRichTextHTMLHandler);
+
+ // Add image handlers
+#if wxUSE_LIBPNG
+ wxImage::AddHandler( new wxPNGHandler );
+#endif
+
+#if wxUSE_LIBJPEG
+ wxImage::AddHandler( new wxJPEGHandler );
+#endif
+
+#if wxUSE_GIF
+ wxImage::AddHandler( new wxGIFHandler );
+#endif
+
+ // create the main application window
+ MyFrame *frame = new MyFrame(_T("wxRichTextCtrl Sample"), wxID_ANY, wxDefaultPosition, wxSize(600, 500));
+
+ // and show it (the frames, unlike simple controls, are not shown when
+ // created initially)
+ frame->Show(true);
+
+ // success: wxApp::OnRun() will be called which will enter the main message
+ // loop and the application will run. If we returned false here, the
+ // application would exit immediately.
+ return true;
+}
+
+int MyApp::OnExit()
+{
+ delete m_styleSheet;
+ return 0;
+}
+
+void MyApp::CreateStyles()
+{
+ // Paragraph styles
+
+ wxFont romanFont(12, wxROMAN, wxNORMAL, wxNORMAL);
+ wxFont swissFont(12, wxSWISS, wxNORMAL, wxNORMAL);
+
+ wxRichTextParagraphStyleDefinition* normalPara = new wxRichTextParagraphStyleDefinition(wxT("Normal"));
+ wxRichTextAttr normalAttr;
+ normalAttr.SetFontFaceName(romanFont.GetFaceName());
+ normalAttr.SetFontSize(12);
+ // Let's set all attributes for this style
+ normalAttr.SetFlags(wxTEXT_ATTR_FONT | wxTEXT_ATTR_BACKGROUND_COLOUR | wxTEXT_ATTR_TEXT_COLOUR|wxTEXT_ATTR_ALIGNMENT|wxTEXT_ATTR_LEFT_INDENT|wxTEXT_ATTR_RIGHT_INDENT|wxTEXT_ATTR_TABS|
+ wxTEXT_ATTR_PARA_SPACING_BEFORE|wxTEXT_ATTR_PARA_SPACING_AFTER|wxTEXT_ATTR_LINE_SPACING|
+ wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_BULLET_NUMBER);
+ normalPara->SetStyle(normalAttr);
+
+ m_styleSheet->AddParagraphStyle(normalPara);
+
+ wxRichTextParagraphStyleDefinition* indentedPara = new wxRichTextParagraphStyleDefinition(wxT("Indented"));
+ wxRichTextAttr indentedAttr;
+ indentedAttr.SetFontFaceName(romanFont.GetFaceName());
+ indentedAttr.SetFontSize(12);
+ indentedAttr.SetLeftIndent(100, 0);
+ // We only want to affect indentation
+ indentedAttr.SetFlags(wxTEXT_ATTR_LEFT_INDENT|wxTEXT_ATTR_RIGHT_INDENT);
+ indentedPara->SetStyle(indentedAttr);
+
+ m_styleSheet->AddParagraphStyle(indentedPara);
+
+ wxRichTextParagraphStyleDefinition* flIndentedPara = new wxRichTextParagraphStyleDefinition(wxT("First Line Indented"));
+ wxRichTextAttr flIndentedAttr;
+ flIndentedAttr.SetFontFaceName(swissFont.GetFaceName());
+ flIndentedAttr.SetFontSize(12);
+ flIndentedAttr.SetLeftIndent(100, -100);
+ // We only want to affect indentation
+ flIndentedAttr.SetFlags(wxTEXT_ATTR_LEFT_INDENT|wxTEXT_ATTR_RIGHT_INDENT);
+ flIndentedPara->SetStyle(flIndentedAttr);
+
+ m_styleSheet->AddParagraphStyle(flIndentedPara);
+
+ // Character styles
+
+ wxRichTextCharacterStyleDefinition* boldDef = new wxRichTextCharacterStyleDefinition(wxT("Bold"));
+ wxRichTextAttr boldAttr;
+ boldAttr.SetFontFaceName(romanFont.GetFaceName());
+ boldAttr.SetFontSize(12);
+ boldAttr.SetFontWeight(wxBOLD);
+ // We only want to affect boldness
+ boldAttr.SetFlags(wxTEXT_ATTR_FONT_WEIGHT);
+ boldDef->SetStyle(boldAttr);
+
+ m_styleSheet->AddCharacterStyle(boldDef);
+
+ wxRichTextCharacterStyleDefinition* italicDef = new wxRichTextCharacterStyleDefinition(wxT("Italic"));
+ wxRichTextAttr italicAttr;
+ italicAttr.SetFontFaceName(romanFont.GetFaceName());
+ italicAttr.SetFontSize(12);
+ italicAttr.SetFontStyle(wxITALIC);
+ // We only want to affect italics
+ italicAttr.SetFlags(wxTEXT_ATTR_FONT_ITALIC);
+ italicDef->SetStyle(italicAttr);
+
+ m_styleSheet->AddCharacterStyle(italicDef);
+
+ wxRichTextCharacterStyleDefinition* redDef = new wxRichTextCharacterStyleDefinition(wxT("Red Bold"));
+ wxRichTextAttr redAttr;
+ redAttr.SetFontFaceName(romanFont.GetFaceName());
+ redAttr.SetFontSize(12);
+ redAttr.SetFontWeight(wxBOLD);
+ redAttr.SetTextColour(*wxRED);
+ // We only want to affect colour, weight and face
+ redAttr.SetFlags(wxTEXT_ATTR_FONT_FACE|wxTEXT_ATTR_FONT_WEIGHT|wxTEXT_ATTR_TEXT_COLOUR);
+ redDef->SetStyle(redAttr);
+
+ m_styleSheet->AddCharacterStyle(redDef);
+}
+
+// ----------------------------------------------------------------------------
+// main frame
+// ----------------------------------------------------------------------------
+
+// frame constructor
+MyFrame::MyFrame(const wxString& title, wxWindowID id, const wxPoint& pos,
+ const wxSize& size, long style)
+ : wxFrame(NULL, id, title, pos, size, style)
+{
+ // set the frame icon
+ SetIcon(sample_xpm);
+
+ // create a menu bar
+ wxMenu *fileMenu = new wxMenu;
+
+ // the "About" item should be in the help menu
+ wxMenu *helpMenu = new wxMenu;
+ helpMenu->Append(ID_About, _T("&About...\tF1"), _T("Show about dialog"));
+
+ fileMenu->Append(wxID_OPEN, _T("&Open\tCtrl+O"), _T("Open a file"));
+ fileMenu->Append(wxID_SAVE, _T("&Save\tCtrl+S"), _T("Save a file"));
+ fileMenu->Append(wxID_SAVEAS, _T("&Save As...\tF12"), _T("Save to a new file"));
+ fileMenu->AppendSeparator();
+ fileMenu->Append(ID_VIEW_HTML, _T("&View as HTML"), _T("View HTML"));
+ fileMenu->AppendSeparator();
+ fileMenu->Append(ID_Quit, _T("E&xit\tAlt-X"), _T("Quit this program"));
+
+ wxMenu* editMenu = new wxMenu;
+ editMenu->Append(wxID_UNDO, _("&Undo\tCtrl+Z"));
+ editMenu->Append(wxID_REDO, _("&Redo\tCtrl+Y"));
+ editMenu->AppendSeparator();
+ editMenu->Append(wxID_CUT, _("Cu&t\tCtrl+X"));
+ editMenu->Append(wxID_COPY, _("&Copy\tCtrl+C"));
+ editMenu->Append(wxID_PASTE, _("&Paste\tCtrl+V"));
+
+ editMenu->Append(wxID_CLEAR, _("&Delete\tDel"));
+
+ editMenu->AppendSeparator();
+ editMenu->Append(wxID_SELECTALL, _("Select A&ll\tCtrl+A"));
+#if 0
+ editMenu->AppendSeparator();
+ editMenu->Append(wxID_FIND, _("&Find...\tCtrl+F"));
+ editMenu->Append(stID_FIND_REPLACE, _("&Replace...\tCtrl+R"));
+#endif
+
+ wxMenu* formatMenu = new wxMenu;
+ formatMenu->AppendCheckItem(ID_FORMAT_BOLD, _("&Bold\tCtrl+B"));
+ formatMenu->AppendCheckItem(ID_FORMAT_ITALIC, _("&Italic\tCtrl+I"));
+ formatMenu->AppendCheckItem(ID_FORMAT_UNDERLINE, _("&Underline\tCtrl+U"));
+ formatMenu->AppendSeparator();
+ formatMenu->AppendCheckItem(ID_FORMAT_ALIGN_LEFT, _("L&eft Align"));
+ formatMenu->AppendCheckItem(ID_FORMAT_ALIGN_RIGHT, _("&Right Align"));
+ formatMenu->AppendCheckItem(ID_FORMAT_ALIGN_CENTRE, _("&Centre"));
+ formatMenu->AppendSeparator();
+ formatMenu->Append(ID_FORMAT_INDENT_MORE, _("I&ndent &More"));
+ formatMenu->Append(ID_FORMAT_INDENT_LESS, _("Indent &Less"));
+ formatMenu->AppendSeparator();
+ formatMenu->Append(ID_FORMAT_PARAGRAPH_SPACING_MORE, _("Increase Paragraph &Spacing"));
+ formatMenu->Append(ID_FORMAT_PARAGRAPH_SPACING_LESS, _("Decrease &Paragraph Spacing"));
+ formatMenu->AppendSeparator();
+ formatMenu->Append(ID_FORMAT_LINE_SPACING_SINGLE, _("Normal Line Spacing"));
+ formatMenu->Append(ID_FORMAT_LINE_SPACING_HALF, _("1.5 Line Spacing"));
+ formatMenu->Append(ID_FORMAT_LINE_SPACING_DOUBLE, _("Double Line Spacing"));
+ formatMenu->AppendSeparator();
+ formatMenu->Append(ID_FORMAT_FONT, _("&Font..."));
+
+ // now append the freshly created menu to the menu bar...
+ wxMenuBar *menuBar = new wxMenuBar();
+ menuBar->Append(fileMenu, _T("&File"));
+ menuBar->Append(editMenu, _T("&Edit"));
+ menuBar->Append(formatMenu, _T("F&ormat"));
+ menuBar->Append(helpMenu, _T("&Help"));
+
+ // ... and attach this menu bar to the frame
+ SetMenuBar(menuBar);
+
+ // create a status bar just for fun (by default with 1 pane only)
+ CreateStatusBar(2);
+ SetStatusText(_T("Welcome to wxRichTextCtrl!"));
+
+ wxToolBar* toolBar = CreateToolBar();
+
+ toolBar->AddTool(wxID_OPEN, wxBitmap(open_xpm), wxNullBitmap, FALSE, -1, -1, (wxObject *) NULL, _("Open"));
+ toolBar->AddTool(wxID_SAVEAS, wxBitmap(save_xpm), wxNullBitmap, FALSE, -1, -1, (wxObject *) NULL, _("Save"));
+ toolBar->AddSeparator();
+ toolBar->AddTool(wxID_CUT, wxBitmap(cut_xpm), wxNullBitmap, FALSE, -1, -1, (wxObject *) NULL, _("Cut"));
+ toolBar->AddTool(wxID_COPY, wxBitmap(copy_xpm), wxNullBitmap, FALSE, -1, -1, (wxObject *) NULL, _("Copy"));
+ toolBar->AddTool(wxID_PASTE, wxBitmap(paste_xpm), wxNullBitmap, FALSE, -1, -1, (wxObject *) NULL, _("Paste"));
+ toolBar->AddSeparator();
+ toolBar->AddTool(wxID_UNDO, wxBitmap(undo_xpm), wxNullBitmap, FALSE, -1, -1, (wxObject *) NULL, _("Undo"));
+ toolBar->AddTool(wxID_REDO, wxBitmap(redo_xpm), wxNullBitmap, FALSE, -1, -1, (wxObject *) NULL, _("Redo"));
+ toolBar->AddSeparator();
+ toolBar->AddTool(ID_FORMAT_BOLD, wxBitmap(bold_xpm), wxNullBitmap, TRUE, -1, -1, (wxObject *) NULL, _("Bold"));
+ toolBar->AddTool(ID_FORMAT_ITALIC, wxBitmap(italic_xpm), wxNullBitmap, TRUE, -1, -1, (wxObject *) NULL, _("Italic"));
+ toolBar->AddTool(ID_FORMAT_UNDERLINE, wxBitmap(underline_xpm), wxNullBitmap, TRUE, -1, -1, (wxObject *) NULL, _("Underline"));
+ toolBar->AddSeparator();
+ toolBar->AddTool(ID_FORMAT_ALIGN_LEFT, wxBitmap(alignleft_xpm), wxNullBitmap, TRUE, -1, -1, (wxObject *) NULL, _("Align Left"));
+ toolBar->AddTool(ID_FORMAT_ALIGN_CENTRE, wxBitmap(centre_xpm), wxNullBitmap, TRUE, -1, -1, (wxObject *) NULL, _("Centre"));
+ toolBar->AddTool(ID_FORMAT_ALIGN_RIGHT, wxBitmap(alignright_xpm), wxNullBitmap, TRUE, -1, -1, (wxObject *) NULL, _("Align Right"));
+ toolBar->AddSeparator();
+ toolBar->AddTool(ID_FORMAT_INDENT_LESS, wxBitmap(indentless_xpm), wxNullBitmap, FALSE, -1, -1, (wxObject *) NULL, _("Indent Less"));
+ toolBar->AddTool(ID_FORMAT_INDENT_MORE, wxBitmap(indentmore_xpm), wxNullBitmap, FALSE, -1, -1, (wxObject *) NULL, _("Indent More"));
+ toolBar->AddSeparator();
+ toolBar->AddTool(ID_FORMAT_FONT, wxBitmap(font_xpm), wxNullBitmap, FALSE, -1, -1, (wxObject *) NULL, _("Font"));
+
+ toolBar->Realize();
+
+ wxSplitterWindow* splitter = new wxSplitterWindow(this, wxID_ANY, wxDefaultPosition, wxSize(100, 100), wxSP_NO_XP_THEME|wxSP_3D|wxSP_LIVE_UPDATE);
+
+
+ wxFont textFont = wxFont(12, wxROMAN, wxNORMAL, wxNORMAL);
+ wxFont boldFont = wxFont(12, wxROMAN, wxNORMAL, wxBOLD);
+ wxFont italicFont = wxFont(12, wxROMAN, wxITALIC, wxNORMAL);
+
+ m_richTextCtrl = new wxRichTextCtrl(splitter, wxID_ANY, wxDefaultPosition, wxSize(200, 200), wxVSCROLL|wxHSCROLL|wxNO_BORDER);
+ m_richTextCtrl->SetFont(wxFont(12, wxROMAN, wxNORMAL, wxNORMAL));
+
+ wxRichTextStyleListBox* styleListBox = new wxRichTextStyleListBox(splitter, wxID_ANY);
+ splitter->SplitVertically(m_richTextCtrl, styleListBox, 400);
+
+ styleListBox->SetStyleSheet(wxGetApp().GetStyleSheet());
+ styleListBox->SetRichTextCtrl(m_richTextCtrl);
+ styleListBox->UpdateStyles();
+
+ wxRichTextCtrl& r = *m_richTextCtrl;
+
+ r.BeginSuppressUndo();
+
+ r.BeginParagraphSpacing(0, 20);
+
+ r.BeginAlignment(wxTEXT_ALIGNMENT_CENTRE);
+ r.BeginBold();
+
+ r.BeginFontSize(14);
+ r.WriteText(wxT("Welcome to wxRichTextCtrl, a wxWidgets control for editing and presenting styled text and images"));
+ r.EndFontSize();
+ r.Newline();
+
+ r.BeginItalic();
+ r.WriteText(wxT("by Julian Smart"));
+ r.EndItalic();
+
+ r.EndBold();
+
+ r.Newline();
+ r.WriteImage(wxBitmap(zebra_xpm));
+
+ r.EndAlignment();
+
+ r.Newline();
+ r.Newline();
+
+ r.WriteText(wxT("What can you do with this thing? "));
+ r.WriteImage(wxBitmap(smiley_xpm));
+ r.WriteText(wxT(" Well, you can change text "));
+
+ r.BeginTextColour(wxColour(255, 0, 0));
+ r.WriteText(wxT("colour, like this red bit."));
+ r.EndTextColour();
+
+ r.BeginTextColour(wxColour(0, 0, 255));
+ r.WriteText(wxT(" And this blue bit."));
+ r.EndTextColour();
+
+ r.WriteText(wxT(" Naturally you can make things "));
+ r.BeginBold();
+ r.WriteText(wxT("bold "));
+ r.EndBold();
+ r.BeginItalic();
+ r.WriteText(wxT("or italic "));
+ r.EndItalic();
+ r.BeginUnderline();
+ r.WriteText(wxT("or underlined."));
+ r.EndUnderline();
+
+ r.BeginFontSize(14);
+ r.WriteText(wxT(" Different font sizes on the same line is allowed, too."));
+ r.EndFontSize();
+
+ r.WriteText(wxT(" Next we'll show an indented paragraph."));
+
+ r.BeginLeftIndent(60);
+ r.Newline();
+
+ r.WriteText(wxT("It was in January, the most down-trodden month of an Edinburgh winter. An attractive woman came into the cafe, which is nothing remarkable."));
+ r.EndLeftIndent();
+
+ r.Newline();
+
+ r.WriteText(wxT("Next, we'll show a first-line indent, achieved using BeginLeftIndent(100, -40)."));
+
+ r.BeginLeftIndent(100, -40);
+ r.Newline();
+
+ r.WriteText(wxT("It was in January, the most down-trodden month of an Edinburgh winter. An attractive woman came into the cafe, which is nothing remarkable."));
+ r.EndLeftIndent();
+
+ r.Newline();
+
+ r.WriteText(wxT("Numbered bullets are possible, again using subindents:"));
+
+ r.BeginNumberedBullet(1, 100, 60);
+ r.Newline();
+
+ r.WriteText(wxT("This is my first item. Note that wxRichTextCtrl doesn't automatically do numbering, but this will be added later."));
+ r.EndNumberedBullet();
+
+ r.BeginNumberedBullet(2, 100, 60);
+ r.Newline();
+
+ r.WriteText(wxT("This is my second item."));
+ r.EndNumberedBullet();
+
+ r.Newline();
+
+ r.WriteText(wxT("The following paragraph is right-indented:"));
+
+ r.BeginRightIndent(200);
+ r.Newline();
+
+ r.WriteText(wxT("It was in January, the most down-trodden month of an Edinburgh winter. An attractive woman came into the cafe, which is nothing remarkable."));
+ r.EndRightIndent();
+
+ r.Newline();
+
+ r.WriteText(wxT("The following paragraph is right-aligned with 1.5 line spacing:"));
+
+ r.BeginAlignment(wxTEXT_ALIGNMENT_RIGHT);
+ r.BeginLineSpacing(wxTEXT_ATTR_LINE_SPACING_HALF);
+ r.Newline();
+
+ r.WriteText(wxT("It was in January, the most down-trodden month of an Edinburgh winter. An attractive woman came into the cafe, which is nothing remarkable."));
+ r.EndLineSpacing();
+ r.EndAlignment();
+
+ r.Newline();
+ r.WriteText(wxT("Other notable features of wxRichTextCtrl include:"));
+
+ r.BeginSymbolBullet(wxT('*'), 100, 60);
+ r.Newline();
+ r.WriteText(wxT("Compatibility with wxTextCtrl API"));
+ r.EndSymbolBullet();
+
+ r.BeginSymbolBullet(wxT('*'), 100, 60);
+ r.Newline();
+ r.WriteText(wxT("Easy stack-based BeginXXX()...EndXXX() style setting in addition to SetStyle()"));
+ r.EndSymbolBullet();
+
+ r.BeginSymbolBullet(wxT('*'), 100, 60);
+ r.Newline();
+ r.WriteText(wxT("XML loading and saving"));
+ r.EndSymbolBullet();
+
+ r.BeginSymbolBullet(wxT('*'), 100, 60);
+ r.Newline();
+ r.WriteText(wxT("Undo/Redo, with batching option and Undo suppressing"));
+ r.EndSymbolBullet();
+
+ r.BeginSymbolBullet(wxT('*'), 100, 60);
+ r.Newline();
+ r.WriteText(wxT("Clipboard copy and paste"));
+ r.EndSymbolBullet();
+
+ r.BeginSymbolBullet(wxT('*'), 100, 60);
+ r.Newline();
+ r.WriteText(wxT("wxRichTextStyleSheet with named character and paragraph styles, and control for applying named styles"));
+ r.EndSymbolBullet();
+
+ r.BeginSymbolBullet(wxT('*'), 100, 60);
+ r.Newline();
+ r.WriteText(wxT("A design that can easily be extended to other content types, ultimately with text boxes, tables, controls, and so on"));
+ r.EndSymbolBullet();
+
+ r.Newline();
+
+ r.WriteText(wxT("Note: this sample content was generated programmatically from within the MyFrame constructor in the demo. The images were loaded from inline XPMs. Enjoy wxRichTextCtrl!"));
+
+ r.EndParagraphSpacing();
+
+ r.EndSuppressUndo();
+}
+
+
+// event handlers
+
+void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
+{
+ // true is to force the frame to close
+ Close(true);
+}
+
+void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
+{
+ wxString msg;
+ msg.Printf( _T("This is a demo for wxRichTextCtrl, a control for editing styled text.\n(c) Julian Smart, 2005"));
+ wxMessageBox(msg, _T("About wxRichTextCtrl Sample"), wxOK | wxICON_INFORMATION, this);
+}
+
+// Forward command events to the current rich text control, if any
+bool MyFrame::ProcessEvent(wxEvent& event)
+{
+ if (event.IsCommandEvent() && !event.IsKindOf(CLASSINFO(wxChildFocusEvent)))
+ {
+ // Problem: we can get infinite recursion because the events
+ // climb back up to this frame, and repeat.
+ // Assume that command events don't cause another command event
+ // to be called, so we can rely on inCommand not being overwritten
+
+ static int s_eventType = 0;
+ static wxWindowID s_id = 0;
+
+ if (s_id != event.GetId() && s_eventType != event.GetEventType())
+ {
+ s_eventType = event.GetEventType();
+ s_id = event.GetId();
+
+ wxWindow* focusWin = wxFindFocusDescendant(this);
+ if (focusWin && focusWin->ProcessEvent(event))
+ {
+ //s_command = NULL;
+ s_eventType = 0;
+ s_id = 0;
+ return TRUE;
+ }
+
+ s_eventType = 0;
+ s_id = 0;
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+
+ return wxFrame::ProcessEvent(event);
+}
+
+void MyFrame::OnOpen(wxCommandEvent& event)
+{
+ wxString filter = wxRichTextBuffer::GetExtWildcard(false, false);
+ if (!filter.IsEmpty())
+ filter += wxT("|");
+ filter += wxT("All files (*.*)|*.*");
+
+ wxString path = wxEmptyString;
+ wxString filename = wxEmptyString;
+
+ wxFileDialog dialog(this,
+ _("Choose a filename"),
+ path,
+ filename,
+ filter,
+ wxOPEN);
+
+ if (dialog.ShowModal() == wxID_OK)
+ {
+ wxString path = dialog.GetPath();
+
+ if (!path.IsEmpty())
+ {
+ m_richTextCtrl->LoadFile(path);
+ }
+ }
+}
+
+void MyFrame::OnSave(wxCommandEvent& event)
+{
+ if (m_richTextCtrl->GetFilename().IsEmpty())
+ {
+ OnSaveAs(event);
+ return;
+ }
+ m_richTextCtrl->SaveFile();
+}
+
+void MyFrame::OnSaveAs(wxCommandEvent& event)
+{
+ wxString filter = wxRichTextBuffer::GetExtWildcard(false, true);
+ wxString path = wxEmptyString;
+ wxString filename = wxEmptyString;
+
+ wxFileDialog dialog(this,
+ _("Choose a filename"),
+ path,
+ filename,
+ filter,
+ wxSAVE);
+
+ if (dialog.ShowModal() == wxID_OK)
+ {
+ wxString path = dialog.GetPath();
+
+ if (!path.IsEmpty())
+ {
+ m_richTextCtrl->SaveFile(path);
+ }
+ }
+}
+
+void MyFrame::OnBold(wxCommandEvent& event)
+{
+ m_richTextCtrl->ApplyBoldToSelection();
+}
+
+void MyFrame::OnItalic(wxCommandEvent& event)
+{
+ m_richTextCtrl->ApplyItalicToSelection();
+}
+
+void MyFrame::OnUnderline(wxCommandEvent& event)
+{
+ m_richTextCtrl->ApplyUnderlineToSelection();
+}
+
+
+void MyFrame::OnUpdateBold(wxUpdateUIEvent& event)
+{
+ event.Check(m_richTextCtrl->IsSelectionBold());
+}
+
+void MyFrame::OnUpdateItalic(wxUpdateUIEvent& event)
+{
+ event.Check(m_richTextCtrl->IsSelectionItalics());
+}
+
+void MyFrame::OnUpdateUnderline(wxUpdateUIEvent& event)
+{
+ event.Check(m_richTextCtrl->IsSelectionUnderlined());
+}
+
+void MyFrame::OnAlignLeft(wxCommandEvent& event)
+{
+ m_richTextCtrl->ApplyAlignmentToSelection(wxTEXT_ALIGNMENT_LEFT);
+}
+
+void MyFrame::OnAlignCentre(wxCommandEvent& event)
+{
+ m_richTextCtrl->ApplyAlignmentToSelection(wxTEXT_ALIGNMENT_CENTRE);
+}
+
+void MyFrame::OnAlignRight(wxCommandEvent& event)
+{
+ m_richTextCtrl->ApplyAlignmentToSelection(wxTEXT_ALIGNMENT_RIGHT);
+}
+
+void MyFrame::OnUpdateAlignLeft(wxUpdateUIEvent& event)
+{
+ event.Check(m_richTextCtrl->IsSelectionAligned(wxTEXT_ALIGNMENT_LEFT));
+}
+
+void MyFrame::OnUpdateAlignCentre(wxUpdateUIEvent& event)
+{
+ event.Check(m_richTextCtrl->IsSelectionAligned(wxTEXT_ALIGNMENT_CENTRE));
+}
+
+void MyFrame::OnUpdateAlignRight(wxUpdateUIEvent& event)
+{
+ event.Check(m_richTextCtrl->IsSelectionAligned(wxTEXT_ALIGNMENT_RIGHT));
+}
+
+void MyFrame::OnFont(wxCommandEvent& event)
+{
+ if (!m_richTextCtrl->HasSelection())
+ return;
+
+ wxRichTextRange range = m_richTextCtrl->GetSelectionRange();
+ wxFontData fontData;
+
+ wxTextAttrEx attr;
+ attr.SetFlags(wxTEXT_ATTR_FONT);
+
+ if (m_richTextCtrl->GetStyle(m_richTextCtrl->GetInsertionPoint(), attr))
+ fontData.SetInitialFont(attr.GetFont());
+
+ wxFontDialog dialog(this, fontData);
+ if (dialog.ShowModal() == wxID_OK)
+ {
+ fontData = dialog.GetFontData();
+ attr.SetFlags(wxTEXT_ATTR_FONT);
+ attr.SetFont(fontData.GetChosenFont());
+ if (attr.GetFont().Ok())
+ {
+ m_richTextCtrl->SetStyle(range, attr);
+ }
+ }
+}
+
+void MyFrame::OnIndentMore(wxCommandEvent& event)
+{
+ wxTextAttrEx attr;
+ attr.SetFlags(wxTEXT_ATTR_LEFT_INDENT);
+
+ if (m_richTextCtrl->GetStyle(m_richTextCtrl->GetInsertionPoint(), attr))
+ {
+ wxRichTextRange range(m_richTextCtrl->GetInsertionPoint(), m_richTextCtrl->GetInsertionPoint());
+ if (m_richTextCtrl->HasSelection())
+ range = m_richTextCtrl->GetSelectionRange();
+
+ wxFontData fontData;
+ attr.SetLeftIndent(attr.GetLeftIndent() + 100);
+
+ attr.SetFlags(wxTEXT_ATTR_LEFT_INDENT);
+ m_richTextCtrl->SetStyle(range, attr);
+ }
+}
+
+void MyFrame::OnIndentLess(wxCommandEvent& event)
+{
+ wxTextAttrEx attr;
+ attr.SetFlags(wxTEXT_ATTR_LEFT_INDENT);
+
+ if (m_richTextCtrl->GetStyle(m_richTextCtrl->GetInsertionPoint(), attr))
+ {
+ wxRichTextRange range(m_richTextCtrl->GetInsertionPoint(), m_richTextCtrl->GetInsertionPoint());
+ if (m_richTextCtrl->HasSelection())
+ range = m_richTextCtrl->GetSelectionRange();
+
+ if (attr.GetLeftIndent() >= 100)
+ {
+ wxFontData fontData;
+ attr.SetLeftIndent(attr.GetLeftIndent() - 100);
+
+ m_richTextCtrl->SetStyle(range, attr);
+ }
+ }
+}
+
+void MyFrame::OnLineSpacingHalf(wxCommandEvent& event)
+{
+ wxTextAttrEx attr;
+ attr.SetFlags(wxTEXT_ATTR_LINE_SPACING);
+
+ if (m_richTextCtrl->GetStyle(m_richTextCtrl->GetInsertionPoint(), attr))
+ {
+ wxRichTextRange range(m_richTextCtrl->GetInsertionPoint(), m_richTextCtrl->GetInsertionPoint());
+ if (m_richTextCtrl->HasSelection())
+ range = m_richTextCtrl->GetSelectionRange();
+
+ wxFontData fontData;
+ attr.SetFlags(wxTEXT_ATTR_LINE_SPACING);
+ attr.SetLineSpacing(15);
+
+ m_richTextCtrl->SetStyle(range, attr);
+ }
+}
+
+void MyFrame::OnLineSpacingDouble(wxCommandEvent& event)
+{
+ wxTextAttrEx attr;
+ attr.SetFlags(wxTEXT_ATTR_LINE_SPACING);
+
+ if (m_richTextCtrl->GetStyle(m_richTextCtrl->GetInsertionPoint(), attr))
+ {
+ wxRichTextRange range(m_richTextCtrl->GetInsertionPoint(), m_richTextCtrl->GetInsertionPoint());
+ if (m_richTextCtrl->HasSelection())
+ range = m_richTextCtrl->GetSelectionRange();
+
+ wxFontData fontData;
+ attr.SetFlags(wxTEXT_ATTR_LINE_SPACING);
+ attr.SetLineSpacing(20);
+
+ m_richTextCtrl->SetStyle(range, attr);
+ }
+}
+
+void MyFrame::OnLineSpacingSingle(wxCommandEvent& event)
+{
+ wxTextAttrEx attr;
+ attr.SetFlags(wxTEXT_ATTR_LINE_SPACING);
+
+ if (m_richTextCtrl->GetStyle(m_richTextCtrl->GetInsertionPoint(), attr))
+ {
+ wxRichTextRange range(m_richTextCtrl->GetInsertionPoint(), m_richTextCtrl->GetInsertionPoint());
+ if (m_richTextCtrl->HasSelection())
+ range = m_richTextCtrl->GetSelectionRange();
+
+ wxFontData fontData;
+ attr.SetFlags(wxTEXT_ATTR_LINE_SPACING);
+ attr.SetLineSpacing(0); // Can also use 10
+
+ m_richTextCtrl->SetStyle(range, attr);
+ }
+}
+
+void MyFrame::OnParagraphSpacingMore(wxCommandEvent& event)
+{
+ wxTextAttrEx attr;
+ attr.SetFlags(wxTEXT_ATTR_PARA_SPACING_AFTER);
+
+ if (m_richTextCtrl->GetStyle(m_richTextCtrl->GetInsertionPoint(), attr))
+ {
+ wxRichTextRange range(m_richTextCtrl->GetInsertionPoint(), m_richTextCtrl->GetInsertionPoint());
+ if (m_richTextCtrl->HasSelection())
+ range = m_richTextCtrl->GetSelectionRange();
+
+ wxFontData fontData;
+ attr.SetParagraphSpacingAfter(attr.GetParagraphSpacingAfter() + 20);
+
+ attr.SetFlags(wxTEXT_ATTR_PARA_SPACING_AFTER);
+ m_richTextCtrl->SetStyle(range, attr);
+ }
+}
+
+void MyFrame::OnParagraphSpacingLess(wxCommandEvent& event)
+{
+ wxTextAttrEx attr;
+ attr.SetFlags(wxTEXT_ATTR_PARA_SPACING_AFTER);
+
+ if (m_richTextCtrl->GetStyle(m_richTextCtrl->GetInsertionPoint(), attr))
+ {
+ wxRichTextRange range(m_richTextCtrl->GetInsertionPoint(), m_richTextCtrl->GetInsertionPoint());
+ if (m_richTextCtrl->HasSelection())
+ range = m_richTextCtrl->GetSelectionRange();
+
+ if (attr.GetParagraphSpacingAfter() >= 20)
+ {
+ wxFontData fontData;
+ attr.SetParagraphSpacingAfter(attr.GetParagraphSpacingAfter() - 20);
+
+ attr.SetFlags(wxTEXT_ATTR_PARA_SPACING_AFTER);
+ m_richTextCtrl->SetStyle(range, attr);
+ }
+ }
+}
+
+void MyFrame::OnViewHTML(wxCommandEvent& event)
+{
+ wxDialog dialog(this, wxID_ANY, _("HTML"), wxDefaultPosition, wxSize(500, 400), wxDEFAULT_DIALOG_STYLE);
+
+ wxBoxSizer* boxSizer = new wxBoxSizer(wxVERTICAL);
+ dialog.SetSizer(boxSizer);
+
+ wxHtmlWindow* win = new wxHtmlWindow(& dialog, wxID_ANY, wxDefaultPosition, wxSize(500, 400), wxSUNKEN_BORDER);
+ boxSizer->Add(win, 1, wxALL, 5);
+
+ wxButton* cancelButton = new wxButton(& dialog, wxID_CANCEL, wxT("&Close"));
+ boxSizer->Add(cancelButton, 0, wxALL|wxCENTRE, 5);
+
+ wxString text;
+ wxStringOutputStream strStream(& text);
+
+ wxRichTextHTMLHandler htmlHandler;
+ if (htmlHandler.SaveFile(& m_richTextCtrl->GetBuffer(), strStream))
+ {
+ win->SetPage(text);
+ }
+
+ boxSizer->Fit(& dialog);
+
+ dialog.ShowModal();
+}
+
diff --git a/samples/richtext/richtext.dsp b/samples/richtext/richtext.dsp
new file mode 100644
index 0000000000..9aca3c05cf
--- /dev/null
+++ b/samples/richtext/richtext.dsp
@@ -0,0 +1,480 @@
+# Microsoft Developer Studio Project File - Name="richtext" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Application" 0x0101
+
+CFG=richtext - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "richtext.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "richtext.mak" CFG="richtext - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "richtext - Win32 DLL Universal Unicode Release" (based on "Win32 (x86) Application")
+!MESSAGE "richtext - Win32 DLL Universal Unicode Debug" (based on "Win32 (x86) Application")
+!MESSAGE "richtext - Win32 DLL Universal Release" (based on "Win32 (x86) Application")
+!MESSAGE "richtext - Win32 DLL Universal Debug" (based on "Win32 (x86) Application")
+!MESSAGE "richtext - Win32 DLL Unicode Release" (based on "Win32 (x86) Application")
+!MESSAGE "richtext - Win32 DLL Unicode Debug" (based on "Win32 (x86) Application")
+!MESSAGE "richtext - Win32 DLL Release" (based on "Win32 (x86) Application")
+!MESSAGE "richtext - Win32 DLL Debug" (based on "Win32 (x86) Application")
+!MESSAGE "richtext - Win32 Universal Unicode Release" (based on "Win32 (x86) Application")
+!MESSAGE "richtext - Win32 Universal Unicode Debug" (based on "Win32 (x86) Application")
+!MESSAGE "richtext - Win32 Universal Release" (based on "Win32 (x86) Application")
+!MESSAGE "richtext - Win32 Universal Debug" (based on "Win32 (x86) Application")
+!MESSAGE "richtext - Win32 Unicode Release" (based on "Win32 (x86) Application")
+!MESSAGE "richtext - Win32 Unicode Debug" (based on "Win32 (x86) Application")
+!MESSAGE "richtext - Win32 Release" (based on "Win32 (x86) Application")
+!MESSAGE "richtext - Win32 Debug" (based on "Win32 (x86) Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "richtext - Win32 DLL Universal Unicode Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "vc_mswunivudll"
+# PROP BASE Intermediate_Dir "vc_mswunivudll\richtext"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "vc_mswunivudll"
+# PROP Intermediate_Dir "vc_mswunivudll\richtext"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /FD /MD /Fdvc_mswunivudll\richtext.pdb /O2 /GR /EHsc /I ".\..\..\include" /I ".\..\..\lib\vc_dll\mswunivu" /W4 /I "." /I ".\..\..\samples" /D "WIN32" /D "__WXMSW__" /D "__WXUNIVERSAL__" /D "_UNICODE" /D "WXUSINGDLL" /D "_WINDOWS" /D "NOPCH" /c
+# ADD CPP /nologo /FD /MD /Fdvc_mswunivudll\richtext.pdb /O2 /GR /EHsc /I ".\..\..\include" /I ".\..\..\lib\vc_dll\mswunivu" /W4 /I "." /I ".\..\..\samples" /D "WIN32" /D "__WXMSW__" /D "__WXUNIVERSAL__" /D "_UNICODE" /D "WXUSINGDLL" /D "_WINDOWS" /D "NOPCH" /c
+# ADD BASE MTL /nologo /D "WIN32" /D "__WXMSW__" /D "__WXUNIVERSAL__" /D "_UNICODE" /D "WXUSINGDLL" /D "_WINDOWS" /D "NOPCH" /mktyplib203 /win32
+# ADD MTL /nologo /D "WIN32" /D "__WXMSW__" /D "__WXUNIVERSAL__" /D "_UNICODE" /D "WXUSINGDLL" /D "_WINDOWS" /D "NOPCH" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "__WXMSW__" /d "__WXUNIVERSAL__" /d "_UNICODE" /i ".\..\..\include" /i ".\..\..\lib\vc_dll\mswunivu" /i "." /d "WXUSINGDLL" /d "_WINDOWS" /i ".\..\..\samples" /d NOPCH
+# ADD RSC /l 0x409 /d "__WXMSW__" /d "__WXUNIVERSAL__" /d "_UNICODE" /i ".\..\..\include" /i ".\..\..\lib\vc_dll\mswunivu" /i "." /d "WXUSINGDLL" /d "_WINDOWS" /i ".\..\..\samples" /d NOPCH
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 wxmswuniv27u_adv.lib wxbase27u_xml.lib wxmswuniv27u_html.lib wxmswuniv27u_core.lib wxbase27u.lib wxtiff.lib wxjpeg.lib wxpng.lib wxzlib.lib wxregexu.lib wxexpat.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib oleacc.lib odbc32.lib /nologo /machine:i386 /out:"vc_mswunivudll\richtext.exe" /libpath:".\..\..\lib\vc_dll" /subsystem:windows
+# ADD LINK32 wxmswuniv27u_adv.lib wxbase27u_xml.lib wxmswuniv27u_html.lib wxmswuniv27u_core.lib wxbase27u.lib wxtiff.lib wxjpeg.lib wxpng.lib wxzlib.lib wxregexu.lib wxexpat.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib oleacc.lib odbc32.lib /nologo /machine:i386 /out:"vc_mswunivudll\richtext.exe" /libpath:".\..\..\lib\vc_dll" /subsystem:windows
+
+!ELSEIF "$(CFG)" == "richtext - Win32 DLL Universal Unicode Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "vc_mswunivuddll"
+# PROP BASE Intermediate_Dir "vc_mswunivuddll\richtext"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "vc_mswunivuddll"
+# PROP Intermediate_Dir "vc_mswunivuddll\richtext"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /FD /MDd /Zi /Gm /GZ /Fdvc_mswunivuddll\richtext.pdb /Od /GR /EHsc /I ".\..\..\include" /I ".\..\..\lib\vc_dll\mswunivud" /W4 /I "." /I ".\..\..\samples" /D "WIN32" /D "_DEBUG" /D "__WXMSW__" /D "__WXUNIVERSAL__" /D "__WXDEBUG__" /D "_UNICODE" /D "WXUSINGDLL" /D "_WINDOWS" /D "NOPCH" /c
+# ADD CPP /nologo /FD /MDd /Zi /Gm /GZ /Fdvc_mswunivuddll\richtext.pdb /Od /GR /EHsc /I ".\..\..\include" /I ".\..\..\lib\vc_dll\mswunivud" /W4 /I "." /I ".\..\..\samples" /D "WIN32" /D "_DEBUG" /D "__WXMSW__" /D "__WXUNIVERSAL__" /D "__WXDEBUG__" /D "_UNICODE" /D "WXUSINGDLL" /D "_WINDOWS" /D "NOPCH" /c
+# ADD BASE MTL /nologo /D "WIN32" /D "_DEBUG" /D "__WXMSW__" /D "__WXUNIVERSAL__" /D "__WXDEBUG__" /D "_UNICODE" /D "WXUSINGDLL" /D "_WINDOWS" /D "NOPCH" /mktyplib203 /win32
+# ADD MTL /nologo /D "WIN32" /D "_DEBUG" /D "__WXMSW__" /D "__WXUNIVERSAL__" /D "__WXDEBUG__" /D "_UNICODE" /D "WXUSINGDLL" /D "_WINDOWS" /D "NOPCH" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG" /d "__WXMSW__" /d "__WXUNIVERSAL__" /d "__WXDEBUG__" /d "_UNICODE" /i ".\..\..\include" /i ".\..\..\lib\vc_dll\mswunivud" /i "." /d "WXUSINGDLL" /d "_WINDOWS" /i ".\..\..\samples" /d NOPCH
+# ADD RSC /l 0x409 /d "_DEBUG" /d "__WXMSW__" /d "__WXUNIVERSAL__" /d "__WXDEBUG__" /d "_UNICODE" /i ".\..\..\include" /i ".\..\..\lib\vc_dll\mswunivud" /i "." /d "WXUSINGDLL" /d "_WINDOWS" /i ".\..\..\samples" /d NOPCH
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 wxmswuniv27ud_adv.lib wxbase27ud_xml.lib wxmswuniv27ud_html.lib wxmswuniv27ud_core.lib wxbase27ud.lib wxtiffd.lib wxjpegd.lib wxpngd.lib wxzlibd.lib wxregexud.lib wxexpatd.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib oleacc.lib odbc32.lib /nologo /machine:i386 /out:"vc_mswunivuddll\richtext.exe" /debug /libpath:".\..\..\lib\vc_dll" /subsystem:windows
+# ADD LINK32 wxmswuniv27ud_adv.lib wxbase27ud_xml.lib wxmswuniv27ud_html.lib wxmswuniv27ud_core.lib wxbase27ud.lib wxtiffd.lib wxjpegd.lib wxpngd.lib wxzlibd.lib wxregexud.lib wxexpatd.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib oleacc.lib odbc32.lib /nologo /machine:i386 /out:"vc_mswunivuddll\richtext.exe" /debug /libpath:".\..\..\lib\vc_dll" /subsystem:windows
+
+!ELSEIF "$(CFG)" == "richtext - Win32 DLL Universal Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "vc_mswunivdll"
+# PROP BASE Intermediate_Dir "vc_mswunivdll\richtext"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "vc_mswunivdll"
+# PROP Intermediate_Dir "vc_mswunivdll\richtext"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /FD /MD /Fdvc_mswunivdll\richtext.pdb /O2 /GR /EHsc /I ".\..\..\include" /I ".\..\..\lib\vc_dll\mswuniv" /W4 /I "." /I ".\..\..\samples" /D "WIN32" /D "__WXMSW__" /D "__WXUNIVERSAL__" /D "WXUSINGDLL" /D "_WINDOWS" /D "NOPCH" /c
+# ADD CPP /nologo /FD /MD /Fdvc_mswunivdll\richtext.pdb /O2 /GR /EHsc /I ".\..\..\include" /I ".\..\..\lib\vc_dll\mswuniv" /W4 /I "." /I ".\..\..\samples" /D "WIN32" /D "__WXMSW__" /D "__WXUNIVERSAL__" /D "WXUSINGDLL" /D "_WINDOWS" /D "NOPCH" /c
+# ADD BASE MTL /nologo /D "WIN32" /D "__WXMSW__" /D "__WXUNIVERSAL__" /D "WXUSINGDLL" /D "_WINDOWS" /D "NOPCH" /mktyplib203 /win32
+# ADD MTL /nologo /D "WIN32" /D "__WXMSW__" /D "__WXUNIVERSAL__" /D "WXUSINGDLL" /D "_WINDOWS" /D "NOPCH" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "__WXMSW__" /d "__WXUNIVERSAL__" /i ".\..\..\include" /i ".\..\..\lib\vc_dll\mswuniv" /i "." /d "WXUSINGDLL" /d "_WINDOWS" /i ".\..\..\samples" /d NOPCH
+# ADD RSC /l 0x409 /d "__WXMSW__" /d "__WXUNIVERSAL__" /i ".\..\..\include" /i ".\..\..\lib\vc_dll\mswuniv" /i "." /d "WXUSINGDLL" /d "_WINDOWS" /i ".\..\..\samples" /d NOPCH
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 wxmswuniv27_adv.lib wxbase27_xml.lib wxmswuniv27_html.lib wxmswuniv27_core.lib wxbase27.lib wxtiff.lib wxjpeg.lib wxpng.lib wxzlib.lib wxregex.lib wxexpat.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib oleacc.lib odbc32.lib /nologo /machine:i386 /out:"vc_mswunivdll\richtext.exe" /libpath:".\..\..\lib\vc_dll" /subsystem:windows
+# ADD LINK32 wxmswuniv27_adv.lib wxbase27_xml.lib wxmswuniv27_html.lib wxmswuniv27_core.lib wxbase27.lib wxtiff.lib wxjpeg.lib wxpng.lib wxzlib.lib wxregex.lib wxexpat.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib oleacc.lib odbc32.lib /nologo /machine:i386 /out:"vc_mswunivdll\richtext.exe" /libpath:".\..\..\lib\vc_dll" /subsystem:windows
+
+!ELSEIF "$(CFG)" == "richtext - Win32 DLL Universal Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "vc_mswunivddll"
+# PROP BASE Intermediate_Dir "vc_mswunivddll\richtext"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "vc_mswunivddll"
+# PROP Intermediate_Dir "vc_mswunivddll\richtext"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /FD /MDd /Zi /Gm /GZ /Fdvc_mswunivddll\richtext.pdb /Od /GR /EHsc /I ".\..\..\include" /I ".\..\..\lib\vc_dll\mswunivd" /W4 /I "." /I ".\..\..\samples" /D "WIN32" /D "_DEBUG" /D "__WXMSW__" /D "__WXUNIVERSAL__" /D "__WXDEBUG__" /D "WXUSINGDLL" /D "_WINDOWS" /D "NOPCH" /c
+# ADD CPP /nologo /FD /MDd /Zi /Gm /GZ /Fdvc_mswunivddll\richtext.pdb /Od /GR /EHsc /I ".\..\..\include" /I ".\..\..\lib\vc_dll\mswunivd" /W4 /I "." /I ".\..\..\samples" /D "WIN32" /D "_DEBUG" /D "__WXMSW__" /D "__WXUNIVERSAL__" /D "__WXDEBUG__" /D "WXUSINGDLL" /D "_WINDOWS" /D "NOPCH" /c
+# ADD BASE MTL /nologo /D "WIN32" /D "_DEBUG" /D "__WXMSW__" /D "__WXUNIVERSAL__" /D "__WXDEBUG__" /D "WXUSINGDLL" /D "_WINDOWS" /D "NOPCH" /mktyplib203 /win32
+# ADD MTL /nologo /D "WIN32" /D "_DEBUG" /D "__WXMSW__" /D "__WXUNIVERSAL__" /D "__WXDEBUG__" /D "WXUSINGDLL" /D "_WINDOWS" /D "NOPCH" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG" /d "__WXMSW__" /d "__WXUNIVERSAL__" /d "__WXDEBUG__" /i ".\..\..\include" /i ".\..\..\lib\vc_dll\mswunivd" /i "." /d "WXUSINGDLL" /d "_WINDOWS" /i ".\..\..\samples" /d NOPCH
+# ADD RSC /l 0x409 /d "_DEBUG" /d "__WXMSW__" /d "__WXUNIVERSAL__" /d "__WXDEBUG__" /i ".\..\..\include" /i ".\..\..\lib\vc_dll\mswunivd" /i "." /d "WXUSINGDLL" /d "_WINDOWS" /i ".\..\..\samples" /d NOPCH
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 wxmswuniv27d_adv.lib wxbase27d_xml.lib wxmswuniv27d_html.lib wxmswuniv27d_core.lib wxbase27d.lib wxtiffd.lib wxjpegd.lib wxpngd.lib wxzlibd.lib wxregexd.lib wxexpatd.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib oleacc.lib odbc32.lib /nologo /machine:i386 /out:"vc_mswunivddll\richtext.exe" /debug /libpath:".\..\..\lib\vc_dll" /subsystem:windows
+# ADD LINK32 wxmswuniv27d_adv.lib wxbase27d_xml.lib wxmswuniv27d_html.lib wxmswuniv27d_core.lib wxbase27d.lib wxtiffd.lib wxjpegd.lib wxpngd.lib wxzlibd.lib wxregexd.lib wxexpatd.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib oleacc.lib odbc32.lib /nologo /machine:i386 /out:"vc_mswunivddll\richtext.exe" /debug /libpath:".\..\..\lib\vc_dll" /subsystem:windows
+
+!ELSEIF "$(CFG)" == "richtext - Win32 DLL Unicode Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "vc_mswudll"
+# PROP BASE Intermediate_Dir "vc_mswudll\richtext"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "vc_mswudll"
+# PROP Intermediate_Dir "vc_mswudll\richtext"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /FD /MD /Fdvc_mswudll\richtext.pdb /O2 /GR /EHsc /I ".\..\..\include" /I ".\..\..\lib\vc_dll\mswu" /W4 /I "." /I ".\..\..\samples" /D "WIN32" /D "__WXMSW__" /D "_UNICODE" /D "WXUSINGDLL" /D "_WINDOWS" /D "NOPCH" /c
+# ADD CPP /nologo /FD /MD /Fdvc_mswudll\richtext.pdb /O2 /GR /EHsc /I ".\..\..\include" /I ".\..\..\lib\vc_dll\mswu" /W4 /I "." /I ".\..\..\samples" /D "WIN32" /D "__WXMSW__" /D "_UNICODE" /D "WXUSINGDLL" /D "_WINDOWS" /D "NOPCH" /c
+# ADD BASE MTL /nologo /D "WIN32" /D "__WXMSW__" /D "_UNICODE" /D "WXUSINGDLL" /D "_WINDOWS" /D "NOPCH" /mktyplib203 /win32
+# ADD MTL /nologo /D "WIN32" /D "__WXMSW__" /D "_UNICODE" /D "WXUSINGDLL" /D "_WINDOWS" /D "NOPCH" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "__WXMSW__" /d "_UNICODE" /i ".\..\..\include" /i ".\..\..\lib\vc_dll\mswu" /i "." /d "WXUSINGDLL" /d "_WINDOWS" /i ".\..\..\samples" /d NOPCH
+# ADD RSC /l 0x409 /d "__WXMSW__" /d "_UNICODE" /i ".\..\..\include" /i ".\..\..\lib\vc_dll\mswu" /i "." /d "WXUSINGDLL" /d "_WINDOWS" /i ".\..\..\samples" /d NOPCH
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 wxmsw27u_adv.lib wxbase27u_xml.lib wxmsw27u_html.lib wxmsw27u_core.lib wxbase27u.lib wxtiff.lib wxjpeg.lib wxpng.lib wxzlib.lib wxregexu.lib wxexpat.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib oleacc.lib odbc32.lib /nologo /machine:i386 /out:"vc_mswudll\richtext.exe" /libpath:".\..\..\lib\vc_dll" /subsystem:windows
+# ADD LINK32 wxmsw27u_adv.lib wxbase27u_xml.lib wxmsw27u_html.lib wxmsw27u_core.lib wxbase27u.lib wxtiff.lib wxjpeg.lib wxpng.lib wxzlib.lib wxregexu.lib wxexpat.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib oleacc.lib odbc32.lib /nologo /machine:i386 /out:"vc_mswudll\richtext.exe" /libpath:".\..\..\lib\vc_dll" /subsystem:windows
+
+!ELSEIF "$(CFG)" == "richtext - Win32 DLL Unicode Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "vc_mswuddll"
+# PROP BASE Intermediate_Dir "vc_mswuddll\richtext"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "vc_mswuddll"
+# PROP Intermediate_Dir "vc_mswuddll\richtext"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /FD /MDd /Zi /Gm /GZ /Fdvc_mswuddll\richtext.pdb /Od /GR /EHsc /I ".\..\..\include" /I ".\..\..\lib\vc_dll\mswud" /W4 /I "." /I ".\..\..\samples" /D "WIN32" /D "_DEBUG" /D "__WXMSW__" /D "__WXDEBUG__" /D "_UNICODE" /D "WXUSINGDLL" /D "_WINDOWS" /D "NOPCH" /c
+# ADD CPP /nologo /FD /MDd /Zi /Gm /GZ /Fdvc_mswuddll\richtext.pdb /Od /GR /EHsc /I ".\..\..\include" /I ".\..\..\lib\vc_dll\mswud" /W4 /I "." /I ".\..\..\samples" /D "WIN32" /D "_DEBUG" /D "__WXMSW__" /D "__WXDEBUG__" /D "_UNICODE" /D "WXUSINGDLL" /D "_WINDOWS" /D "NOPCH" /c
+# ADD BASE MTL /nologo /D "WIN32" /D "_DEBUG" /D "__WXMSW__" /D "__WXDEBUG__" /D "_UNICODE" /D "WXUSINGDLL" /D "_WINDOWS" /D "NOPCH" /mktyplib203 /win32
+# ADD MTL /nologo /D "WIN32" /D "_DEBUG" /D "__WXMSW__" /D "__WXDEBUG__" /D "_UNICODE" /D "WXUSINGDLL" /D "_WINDOWS" /D "NOPCH" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG" /d "__WXMSW__" /d "__WXDEBUG__" /d "_UNICODE" /i ".\..\..\include" /i ".\..\..\lib\vc_dll\mswud" /i "." /d "WXUSINGDLL" /d "_WINDOWS" /i ".\..\..\samples" /d NOPCH
+# ADD RSC /l 0x409 /d "_DEBUG" /d "__WXMSW__" /d "__WXDEBUG__" /d "_UNICODE" /i ".\..\..\include" /i ".\..\..\lib\vc_dll\mswud" /i "." /d "WXUSINGDLL" /d "_WINDOWS" /i ".\..\..\samples" /d NOPCH
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 wxmsw27ud_adv.lib wxbase27ud_xml.lib wxmsw27ud_html.lib wxmsw27ud_core.lib wxbase27ud.lib wxtiffd.lib wxjpegd.lib wxpngd.lib wxzlibd.lib wxregexud.lib wxexpatd.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib oleacc.lib odbc32.lib /nologo /machine:i386 /out:"vc_mswuddll\richtext.exe" /debug /libpath:".\..\..\lib\vc_dll" /subsystem:windows
+# ADD LINK32 wxmsw27ud_adv.lib wxbase27ud_xml.lib wxmsw27ud_html.lib wxmsw27ud_core.lib wxbase27ud.lib wxtiffd.lib wxjpegd.lib wxpngd.lib wxzlibd.lib wxregexud.lib wxexpatd.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib oleacc.lib odbc32.lib /nologo /machine:i386 /out:"vc_mswuddll\richtext.exe" /debug /libpath:".\..\..\lib\vc_dll" /subsystem:windows
+
+!ELSEIF "$(CFG)" == "richtext - Win32 DLL Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "vc_mswdll"
+# PROP BASE Intermediate_Dir "vc_mswdll\richtext"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "vc_mswdll"
+# PROP Intermediate_Dir "vc_mswdll\richtext"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /FD /MD /Fdvc_mswdll\richtext.pdb /O2 /GR /EHsc /I ".\..\..\include" /I ".\..\..\lib\vc_dll\msw" /W4 /I "." /I ".\..\..\samples" /D "WIN32" /D "__WXMSW__" /D "WXUSINGDLL" /D "_WINDOWS" /D "NOPCH" /c
+# ADD CPP /nologo /FD /MD /Fdvc_mswdll\richtext.pdb /O2 /GR /EHsc /I ".\..\..\include" /I ".\..\..\lib\vc_dll\msw" /W4 /I "." /I ".\..\..\samples" /D "WIN32" /D "__WXMSW__" /D "WXUSINGDLL" /D "_WINDOWS" /D "NOPCH" /c
+# ADD BASE MTL /nologo /D "WIN32" /D "__WXMSW__" /D "WXUSINGDLL" /D "_WINDOWS" /D "NOPCH" /mktyplib203 /win32
+# ADD MTL /nologo /D "WIN32" /D "__WXMSW__" /D "WXUSINGDLL" /D "_WINDOWS" /D "NOPCH" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "__WXMSW__" /i ".\..\..\include" /i ".\..\..\lib\vc_dll\msw" /i "." /d "WXUSINGDLL" /d "_WINDOWS" /i ".\..\..\samples" /d NOPCH
+# ADD RSC /l 0x409 /d "__WXMSW__" /i ".\..\..\include" /i ".\..\..\lib\vc_dll\msw" /i "." /d "WXUSINGDLL" /d "_WINDOWS" /i ".\..\..\samples" /d NOPCH
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 wxmsw27_adv.lib wxbase27_xml.lib wxmsw27_html.lib wxmsw27_core.lib wxbase27.lib wxtiff.lib wxjpeg.lib wxpng.lib wxzlib.lib wxregex.lib wxexpat.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib oleacc.lib odbc32.lib /nologo /machine:i386 /out:"vc_mswdll\richtext.exe" /libpath:".\..\..\lib\vc_dll" /subsystem:windows
+# ADD LINK32 wxmsw27_adv.lib wxbase27_xml.lib wxmsw27_html.lib wxmsw27_core.lib wxbase27.lib wxtiff.lib wxjpeg.lib wxpng.lib wxzlib.lib wxregex.lib wxexpat.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib oleacc.lib odbc32.lib /nologo /machine:i386 /out:"vc_mswdll\richtext.exe" /libpath:".\..\..\lib\vc_dll" /subsystem:windows
+
+!ELSEIF "$(CFG)" == "richtext - Win32 DLL Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "vc_mswddll"
+# PROP BASE Intermediate_Dir "vc_mswddll\richtext"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "vc_mswddll"
+# PROP Intermediate_Dir "vc_mswddll\richtext"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /FD /MDd /Zi /Gm /GZ /Fdvc_mswddll\richtext.pdb /Od /GR /EHsc /I ".\..\..\include" /I ".\..\..\lib\vc_dll\mswd" /W4 /I "." /I ".\..\..\samples" /D "WIN32" /D "_DEBUG" /D "__WXMSW__" /D "__WXDEBUG__" /D "WXUSINGDLL" /D "_WINDOWS" /D "NOPCH" /c
+# ADD CPP /nologo /FD /MDd /Zi /Gm /GZ /Fdvc_mswddll\richtext.pdb /Od /GR /EHsc /I ".\..\..\include" /I ".\..\..\lib\vc_dll\mswd" /W4 /I "." /I ".\..\..\samples" /D "WIN32" /D "_DEBUG" /D "__WXMSW__" /D "__WXDEBUG__" /D "WXUSINGDLL" /D "_WINDOWS" /D "NOPCH" /c
+# ADD BASE MTL /nologo /D "WIN32" /D "_DEBUG" /D "__WXMSW__" /D "__WXDEBUG__" /D "WXUSINGDLL" /D "_WINDOWS" /D "NOPCH" /mktyplib203 /win32
+# ADD MTL /nologo /D "WIN32" /D "_DEBUG" /D "__WXMSW__" /D "__WXDEBUG__" /D "WXUSINGDLL" /D "_WINDOWS" /D "NOPCH" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG" /d "__WXMSW__" /d "__WXDEBUG__" /i ".\..\..\include" /i ".\..\..\lib\vc_dll\mswd" /i "." /d "WXUSINGDLL" /d "_WINDOWS" /i ".\..\..\samples" /d NOPCH
+# ADD RSC /l 0x409 /d "_DEBUG" /d "__WXMSW__" /d "__WXDEBUG__" /i ".\..\..\include" /i ".\..\..\lib\vc_dll\mswd" /i "." /d "WXUSINGDLL" /d "_WINDOWS" /i ".\..\..\samples" /d NOPCH
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 wxmsw27d_adv.lib wxbase27d_xml.lib wxmsw27d_html.lib wxmsw27d_core.lib wxbase27d.lib wxtiffd.lib wxjpegd.lib wxpngd.lib wxzlibd.lib wxregexd.lib wxexpatd.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib oleacc.lib odbc32.lib /nologo /machine:i386 /out:"vc_mswddll\richtext.exe" /debug /libpath:".\..\..\lib\vc_dll" /subsystem:windows
+# ADD LINK32 wxmsw27d_adv.lib wxbase27d_xml.lib wxmsw27d_html.lib wxmsw27d_core.lib wxbase27d.lib wxtiffd.lib wxjpegd.lib wxpngd.lib wxzlibd.lib wxregexd.lib wxexpatd.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib oleacc.lib odbc32.lib /nologo /machine:i386 /out:"vc_mswddll\richtext.exe" /debug /libpath:".\..\..\lib\vc_dll" /subsystem:windows
+
+!ELSEIF "$(CFG)" == "richtext - Win32 Universal Unicode Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "vc_mswunivu"
+# PROP BASE Intermediate_Dir "vc_mswunivu\richtext"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "vc_mswunivu"
+# PROP Intermediate_Dir "vc_mswunivu\richtext"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /FD /MD /Fdvc_mswunivu\richtext.pdb /O2 /GR /EHsc /I ".\..\..\include" /I ".\..\..\lib\vc_lib\mswunivu" /W4 /I "." /I ".\..\..\samples" /D "WIN32" /D "__WXMSW__" /D "__WXUNIVERSAL__" /D "_UNICODE" /D "_WINDOWS" /D "NOPCH" /c
+# ADD CPP /nologo /FD /MD /Fdvc_mswunivu\richtext.pdb /O2 /GR /EHsc /I ".\..\..\include" /I ".\..\..\lib\vc_lib\mswunivu" /W4 /I "." /I ".\..\..\samples" /D "WIN32" /D "__WXMSW__" /D "__WXUNIVERSAL__" /D "_UNICODE" /D "_WINDOWS" /D "NOPCH" /c
+# ADD BASE MTL /nologo /D "WIN32" /D "__WXMSW__" /D "__WXUNIVERSAL__" /D "_UNICODE" /D "_WINDOWS" /D "NOPCH" /mktyplib203 /win32
+# ADD MTL /nologo /D "WIN32" /D "__WXMSW__" /D "__WXUNIVERSAL__" /D "_UNICODE" /D "_WINDOWS" /D "NOPCH" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "__WXMSW__" /d "__WXUNIVERSAL__" /d "_UNICODE" /i ".\..\..\include" /i ".\..\..\lib\vc_lib\mswunivu" /i "." /d "_WINDOWS" /i ".\..\..\samples" /d NOPCH
+# ADD RSC /l 0x409 /d "__WXMSW__" /d "__WXUNIVERSAL__" /d "_UNICODE" /i ".\..\..\include" /i ".\..\..\lib\vc_lib\mswunivu" /i "." /d "_WINDOWS" /i ".\..\..\samples" /d NOPCH
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 wxmswuniv27u_adv.lib wxbase27u_xml.lib wxmswuniv27u_html.lib wxmswuniv27u_core.lib wxbase27u.lib wxtiff.lib wxjpeg.lib wxpng.lib wxzlib.lib wxregexu.lib wxexpat.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib oleacc.lib odbc32.lib /nologo /machine:i386 /out:"vc_mswunivu\richtext.exe" /libpath:".\..\..\lib\vc_lib" /subsystem:windows
+# ADD LINK32 wxmswuniv27u_adv.lib wxbase27u_xml.lib wxmswuniv27u_html.lib wxmswuniv27u_core.lib wxbase27u.lib wxtiff.lib wxjpeg.lib wxpng.lib wxzlib.lib wxregexu.lib wxexpat.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib oleacc.lib odbc32.lib /nologo /machine:i386 /out:"vc_mswunivu\richtext.exe" /libpath:".\..\..\lib\vc_lib" /subsystem:windows
+
+!ELSEIF "$(CFG)" == "richtext - Win32 Universal Unicode Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "vc_mswunivud"
+# PROP BASE Intermediate_Dir "vc_mswunivud\richtext"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "vc_mswunivud"
+# PROP Intermediate_Dir "vc_mswunivud\richtext"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /FD /MDd /Zi /Gm /GZ /Fdvc_mswunivud\richtext.pdb /Od /GR /EHsc /I ".\..\..\include" /I ".\..\..\lib\vc_lib\mswunivud" /W4 /I "." /I ".\..\..\samples" /D "WIN32" /D "_DEBUG" /D "__WXMSW__" /D "__WXUNIVERSAL__" /D "__WXDEBUG__" /D "_UNICODE" /D "_WINDOWS" /D "NOPCH" /c
+# ADD CPP /nologo /FD /MDd /Zi /Gm /GZ /Fdvc_mswunivud\richtext.pdb /Od /GR /EHsc /I ".\..\..\include" /I ".\..\..\lib\vc_lib\mswunivud" /W4 /I "." /I ".\..\..\samples" /D "WIN32" /D "_DEBUG" /D "__WXMSW__" /D "__WXUNIVERSAL__" /D "__WXDEBUG__" /D "_UNICODE" /D "_WINDOWS" /D "NOPCH" /c
+# ADD BASE MTL /nologo /D "WIN32" /D "_DEBUG" /D "__WXMSW__" /D "__WXUNIVERSAL__" /D "__WXDEBUG__" /D "_UNICODE" /D "_WINDOWS" /D "NOPCH" /mktyplib203 /win32
+# ADD MTL /nologo /D "WIN32" /D "_DEBUG" /D "__WXMSW__" /D "__WXUNIVERSAL__" /D "__WXDEBUG__" /D "_UNICODE" /D "_WINDOWS" /D "NOPCH" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG" /d "__WXMSW__" /d "__WXUNIVERSAL__" /d "__WXDEBUG__" /d "_UNICODE" /i ".\..\..\include" /i ".\..\..\lib\vc_lib\mswunivud" /i "." /d "_WINDOWS" /i ".\..\..\samples" /d NOPCH
+# ADD RSC /l 0x409 /d "_DEBUG" /d "__WXMSW__" /d "__WXUNIVERSAL__" /d "__WXDEBUG__" /d "_UNICODE" /i ".\..\..\include" /i ".\..\..\lib\vc_lib\mswunivud" /i "." /d "_WINDOWS" /i ".\..\..\samples" /d NOPCH
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 wxmswuniv27ud_adv.lib wxbase27ud_xml.lib wxmswuniv27ud_html.lib wxmswuniv27ud_core.lib wxbase27ud.lib wxtiffd.lib wxjpegd.lib wxpngd.lib wxzlibd.lib wxregexud.lib wxexpatd.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib oleacc.lib odbc32.lib /nologo /machine:i386 /out:"vc_mswunivud\richtext.exe" /debug /libpath:".\..\..\lib\vc_lib" /subsystem:windows
+# ADD LINK32 wxmswuniv27ud_adv.lib wxbase27ud_xml.lib wxmswuniv27ud_html.lib wxmswuniv27ud_core.lib wxbase27ud.lib wxtiffd.lib wxjpegd.lib wxpngd.lib wxzlibd.lib wxregexud.lib wxexpatd.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib oleacc.lib odbc32.lib /nologo /machine:i386 /out:"vc_mswunivud\richtext.exe" /debug /libpath:".\..\..\lib\vc_lib" /subsystem:windows
+
+!ELSEIF "$(CFG)" == "richtext - Win32 Universal Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "vc_mswuniv"
+# PROP BASE Intermediate_Dir "vc_mswuniv\richtext"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "vc_mswuniv"
+# PROP Intermediate_Dir "vc_mswuniv\richtext"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /FD /MD /Fdvc_mswuniv\richtext.pdb /O2 /GR /EHsc /I ".\..\..\include" /I ".\..\..\lib\vc_lib\mswuniv" /W4 /I "." /I ".\..\..\samples" /D "WIN32" /D "__WXMSW__" /D "__WXUNIVERSAL__" /D "_WINDOWS" /D "NOPCH" /c
+# ADD CPP /nologo /FD /MD /Fdvc_mswuniv\richtext.pdb /O2 /GR /EHsc /I ".\..\..\include" /I ".\..\..\lib\vc_lib\mswuniv" /W4 /I "." /I ".\..\..\samples" /D "WIN32" /D "__WXMSW__" /D "__WXUNIVERSAL__" /D "_WINDOWS" /D "NOPCH" /c
+# ADD BASE MTL /nologo /D "WIN32" /D "__WXMSW__" /D "__WXUNIVERSAL__" /D "_WINDOWS" /D "NOPCH" /mktyplib203 /win32
+# ADD MTL /nologo /D "WIN32" /D "__WXMSW__" /D "__WXUNIVERSAL__" /D "_WINDOWS" /D "NOPCH" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "__WXMSW__" /d "__WXUNIVERSAL__" /i ".\..\..\include" /i ".\..\..\lib\vc_lib\mswuniv" /i "." /d "_WINDOWS" /i ".\..\..\samples" /d NOPCH
+# ADD RSC /l 0x409 /d "__WXMSW__" /d "__WXUNIVERSAL__" /i ".\..\..\include" /i ".\..\..\lib\vc_lib\mswuniv" /i "." /d "_WINDOWS" /i ".\..\..\samples" /d NOPCH
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 wxmswuniv27_adv.lib wxbase27_xml.lib wxmswuniv27_html.lib wxmswuniv27_core.lib wxbase27.lib wxtiff.lib wxjpeg.lib wxpng.lib wxzlib.lib wxregex.lib wxexpat.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib oleacc.lib odbc32.lib /nologo /machine:i386 /out:"vc_mswuniv\richtext.exe" /libpath:".\..\..\lib\vc_lib" /subsystem:windows
+# ADD LINK32 wxmswuniv27_adv.lib wxbase27_xml.lib wxmswuniv27_html.lib wxmswuniv27_core.lib wxbase27.lib wxtiff.lib wxjpeg.lib wxpng.lib wxzlib.lib wxregex.lib wxexpat.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib oleacc.lib odbc32.lib /nologo /machine:i386 /out:"vc_mswuniv\richtext.exe" /libpath:".\..\..\lib\vc_lib" /subsystem:windows
+
+!ELSEIF "$(CFG)" == "richtext - Win32 Universal Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "vc_mswunivd"
+# PROP BASE Intermediate_Dir "vc_mswunivd\richtext"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "vc_mswunivd"
+# PROP Intermediate_Dir "vc_mswunivd\richtext"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /FD /MDd /Zi /Gm /GZ /Fdvc_mswunivd\richtext.pdb /Od /GR /EHsc /I ".\..\..\include" /I ".\..\..\lib\vc_lib\mswunivd" /W4 /I "." /I ".\..\..\samples" /D "WIN32" /D "_DEBUG" /D "__WXMSW__" /D "__WXUNIVERSAL__" /D "__WXDEBUG__" /D "_WINDOWS" /D "NOPCH" /c
+# ADD CPP /nologo /FD /MDd /Zi /Gm /GZ /Fdvc_mswunivd\richtext.pdb /Od /GR /EHsc /I ".\..\..\include" /I ".\..\..\lib\vc_lib\mswunivd" /W4 /I "." /I ".\..\..\samples" /D "WIN32" /D "_DEBUG" /D "__WXMSW__" /D "__WXUNIVERSAL__" /D "__WXDEBUG__" /D "_WINDOWS" /D "NOPCH" /c
+# ADD BASE MTL /nologo /D "WIN32" /D "_DEBUG" /D "__WXMSW__" /D "__WXUNIVERSAL__" /D "__WXDEBUG__" /D "_WINDOWS" /D "NOPCH" /mktyplib203 /win32
+# ADD MTL /nologo /D "WIN32" /D "_DEBUG" /D "__WXMSW__" /D "__WXUNIVERSAL__" /D "__WXDEBUG__" /D "_WINDOWS" /D "NOPCH" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG" /d "__WXMSW__" /d "__WXUNIVERSAL__" /d "__WXDEBUG__" /i ".\..\..\include" /i ".\..\..\lib\vc_lib\mswunivd" /i "." /d "_WINDOWS" /i ".\..\..\samples" /d NOPCH
+# ADD RSC /l 0x409 /d "_DEBUG" /d "__WXMSW__" /d "__WXUNIVERSAL__" /d "__WXDEBUG__" /i ".\..\..\include" /i ".\..\..\lib\vc_lib\mswunivd" /i "." /d "_WINDOWS" /i ".\..\..\samples" /d NOPCH
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 wxmswuniv27d_adv.lib wxbase27d_xml.lib wxmswuniv27d_html.lib wxmswuniv27d_core.lib wxbase27d.lib wxtiffd.lib wxjpegd.lib wxpngd.lib wxzlibd.lib wxregexd.lib wxexpatd.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib oleacc.lib odbc32.lib /nologo /machine:i386 /out:"vc_mswunivd\richtext.exe" /debug /libpath:".\..\..\lib\vc_lib" /subsystem:windows
+# ADD LINK32 wxmswuniv27d_adv.lib wxbase27d_xml.lib wxmswuniv27d_html.lib wxmswuniv27d_core.lib wxbase27d.lib wxtiffd.lib wxjpegd.lib wxpngd.lib wxzlibd.lib wxregexd.lib wxexpatd.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib oleacc.lib odbc32.lib /nologo /machine:i386 /out:"vc_mswunivd\richtext.exe" /debug /libpath:".\..\..\lib\vc_lib" /subsystem:windows
+
+!ELSEIF "$(CFG)" == "richtext - Win32 Unicode Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "vc_mswu"
+# PROP BASE Intermediate_Dir "vc_mswu\richtext"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "vc_mswu"
+# PROP Intermediate_Dir "vc_mswu\richtext"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /FD /MD /Fdvc_mswu\richtext.pdb /O2 /GR /EHsc /I ".\..\..\include" /I ".\..\..\lib\vc_lib\mswu" /W4 /I "." /I ".\..\..\samples" /D "WIN32" /D "__WXMSW__" /D "_UNICODE" /D "_WINDOWS" /D "NOPCH" /c
+# ADD CPP /nologo /FD /MD /Fdvc_mswu\richtext.pdb /O2 /GR /EHsc /I ".\..\..\include" /I ".\..\..\lib\vc_lib\mswu" /W4 /I "." /I ".\..\..\samples" /D "WIN32" /D "__WXMSW__" /D "_UNICODE" /D "_WINDOWS" /D "NOPCH" /c
+# ADD BASE MTL /nologo /D "WIN32" /D "__WXMSW__" /D "_UNICODE" /D "_WINDOWS" /D "NOPCH" /mktyplib203 /win32
+# ADD MTL /nologo /D "WIN32" /D "__WXMSW__" /D "_UNICODE" /D "_WINDOWS" /D "NOPCH" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "__WXMSW__" /d "_UNICODE" /i ".\..\..\include" /i ".\..\..\lib\vc_lib\mswu" /i "." /d "_WINDOWS" /i ".\..\..\samples" /d NOPCH
+# ADD RSC /l 0x409 /d "__WXMSW__" /d "_UNICODE" /i ".\..\..\include" /i ".\..\..\lib\vc_lib\mswu" /i "." /d "_WINDOWS" /i ".\..\..\samples" /d NOPCH
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 wxmsw27u_adv.lib wxbase27u_xml.lib wxmsw27u_html.lib wxmsw27u_core.lib wxbase27u.lib wxtiff.lib wxjpeg.lib wxpng.lib wxzlib.lib wxregexu.lib wxexpat.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib oleacc.lib odbc32.lib /nologo /machine:i386 /out:"vc_mswu\richtext.exe" /libpath:".\..\..\lib\vc_lib" /subsystem:windows
+# ADD LINK32 wxmsw27u_adv.lib wxbase27u_xml.lib wxmsw27u_html.lib wxmsw27u_core.lib wxbase27u.lib wxtiff.lib wxjpeg.lib wxpng.lib wxzlib.lib wxregexu.lib wxexpat.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib oleacc.lib odbc32.lib /nologo /machine:i386 /out:"vc_mswu\richtext.exe" /libpath:".\..\..\lib\vc_lib" /subsystem:windows
+
+!ELSEIF "$(CFG)" == "richtext - Win32 Unicode Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "vc_mswud"
+# PROP BASE Intermediate_Dir "vc_mswud\richtext"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "vc_mswud"
+# PROP Intermediate_Dir "vc_mswud\richtext"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /FD /MDd /Zi /Gm /GZ /Fdvc_mswud\richtext.pdb /Od /GR /EHsc /I ".\..\..\include" /I ".\..\..\lib\vc_lib\mswud" /W4 /I "." /I ".\..\..\samples" /D "WIN32" /D "_DEBUG" /D "__WXMSW__" /D "__WXDEBUG__" /D "_UNICODE" /D "_WINDOWS" /D "NOPCH" /c
+# ADD CPP /nologo /FD /MDd /Zi /Gm /GZ /Fdvc_mswud\richtext.pdb /Od /GR /EHsc /I ".\..\..\include" /I ".\..\..\lib\vc_lib\mswud" /W4 /I "." /I ".\..\..\samples" /D "WIN32" /D "_DEBUG" /D "__WXMSW__" /D "__WXDEBUG__" /D "_UNICODE" /D "_WINDOWS" /D "NOPCH" /c
+# ADD BASE MTL /nologo /D "WIN32" /D "_DEBUG" /D "__WXMSW__" /D "__WXDEBUG__" /D "_UNICODE" /D "_WINDOWS" /D "NOPCH" /mktyplib203 /win32
+# ADD MTL /nologo /D "WIN32" /D "_DEBUG" /D "__WXMSW__" /D "__WXDEBUG__" /D "_UNICODE" /D "_WINDOWS" /D "NOPCH" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG" /d "__WXMSW__" /d "__WXDEBUG__" /d "_UNICODE" /i ".\..\..\include" /i ".\..\..\lib\vc_lib\mswud" /i "." /d "_WINDOWS" /i ".\..\..\samples" /d NOPCH
+# ADD RSC /l 0x409 /d "_DEBUG" /d "__WXMSW__" /d "__WXDEBUG__" /d "_UNICODE" /i ".\..\..\include" /i ".\..\..\lib\vc_lib\mswud" /i "." /d "_WINDOWS" /i ".\..\..\samples" /d NOPCH
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 wxmsw27ud_adv.lib wxbase27ud_xml.lib wxmsw27ud_html.lib wxmsw27ud_core.lib wxbase27ud.lib wxtiffd.lib wxjpegd.lib wxpngd.lib wxzlibd.lib wxregexud.lib wxexpatd.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib oleacc.lib odbc32.lib /nologo /machine:i386 /out:"vc_mswud\richtext.exe" /debug /libpath:".\..\..\lib\vc_lib" /subsystem:windows
+# ADD LINK32 wxmsw27ud_adv.lib wxbase27ud_xml.lib wxmsw27ud_html.lib wxmsw27ud_core.lib wxbase27ud.lib wxtiffd.lib wxjpegd.lib wxpngd.lib wxzlibd.lib wxregexud.lib wxexpatd.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib oleacc.lib odbc32.lib /nologo /machine:i386 /out:"vc_mswud\richtext.exe" /debug /libpath:".\..\..\lib\vc_lib" /subsystem:windows
+
+!ELSEIF "$(CFG)" == "richtext - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "vc_msw"
+# PROP BASE Intermediate_Dir "vc_msw\richtext"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "vc_msw"
+# PROP Intermediate_Dir "vc_msw\richtext"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /FD /MD /Fdvc_msw\richtext.pdb /O2 /GR /EHsc /I ".\..\..\include" /I ".\..\..\lib\vc_lib\msw" /W4 /I "." /I ".\..\..\samples" /D "WIN32" /D "__WXMSW__" /D "_WINDOWS" /D "NOPCH" /c
+# ADD CPP /nologo /FD /MD /Fdvc_msw\richtext.pdb /O2 /GR /EHsc /I ".\..\..\include" /I ".\..\..\lib\vc_lib\msw" /W4 /I "." /I ".\..\..\samples" /D "WIN32" /D "__WXMSW__" /D "_WINDOWS" /D "NOPCH" /c
+# ADD BASE MTL /nologo /D "WIN32" /D "__WXMSW__" /D "_WINDOWS" /D "NOPCH" /mktyplib203 /win32
+# ADD MTL /nologo /D "WIN32" /D "__WXMSW__" /D "_WINDOWS" /D "NOPCH" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "__WXMSW__" /i ".\..\..\include" /i ".\..\..\lib\vc_lib\msw" /i "." /d "_WINDOWS" /i ".\..\..\samples" /d NOPCH
+# ADD RSC /l 0x409 /d "__WXMSW__" /i ".\..\..\include" /i ".\..\..\lib\vc_lib\msw" /i "." /d "_WINDOWS" /i ".\..\..\samples" /d NOPCH
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 wxmsw27_adv.lib wxbase27_xml.lib wxmsw27_html.lib wxmsw27_core.lib wxbase27.lib wxtiff.lib wxjpeg.lib wxpng.lib wxzlib.lib wxregex.lib wxexpat.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib oleacc.lib odbc32.lib /nologo /machine:i386 /out:"vc_msw\richtext.exe" /libpath:".\..\..\lib\vc_lib" /subsystem:windows
+# ADD LINK32 wxmsw27_adv.lib wxbase27_xml.lib wxmsw27_html.lib wxmsw27_core.lib wxbase27.lib wxtiff.lib wxjpeg.lib wxpng.lib wxzlib.lib wxregex.lib wxexpat.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib oleacc.lib odbc32.lib /nologo /machine:i386 /out:"vc_msw\richtext.exe" /libpath:".\..\..\lib\vc_lib" /subsystem:windows
+
+!ELSEIF "$(CFG)" == "richtext - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "vc_mswd"
+# PROP BASE Intermediate_Dir "vc_mswd\richtext"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "vc_mswd"
+# PROP Intermediate_Dir "vc_mswd\richtext"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /FD /MDd /Zi /Gm /GZ /Fdvc_mswd\richtext.pdb /Od /GR /EHsc /I ".\..\..\include" /I ".\..\..\lib\vc_lib\mswd" /W4 /I "." /I ".\..\..\samples" /D "WIN32" /D "_DEBUG" /D "__WXMSW__" /D "__WXDEBUG__" /D "_WINDOWS" /D "NOPCH" /c
+# ADD CPP /nologo /FD /MDd /Zi /Gm /GZ /Fdvc_mswd\richtext.pdb /Od /GR /EHsc /I ".\..\..\include" /I ".\..\..\lib\vc_lib\mswd" /W4 /I "." /I ".\..\..\samples" /D "WIN32" /D "_DEBUG" /D "__WXMSW__" /D "__WXDEBUG__" /D "_WINDOWS" /D "NOPCH" /c
+# ADD BASE MTL /nologo /D "WIN32" /D "_DEBUG" /D "__WXMSW__" /D "__WXDEBUG__" /D "_WINDOWS" /D "NOPCH" /mktyplib203 /win32
+# ADD MTL /nologo /D "WIN32" /D "_DEBUG" /D "__WXMSW__" /D "__WXDEBUG__" /D "_WINDOWS" /D "NOPCH" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG" /d "__WXMSW__" /d "__WXDEBUG__" /i ".\..\..\include" /i ".\..\..\lib\vc_lib\mswd" /i "." /d "_WINDOWS" /i ".\..\..\samples" /d NOPCH
+# ADD RSC /l 0x409 /d "_DEBUG" /d "__WXMSW__" /d "__WXDEBUG__" /i ".\..\..\include" /i ".\..\..\lib\vc_lib\mswd" /i "." /d "_WINDOWS" /i ".\..\..\samples" /d NOPCH
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 wxmsw27d_adv.lib wxbase27d_xml.lib wxmsw27d_html.lib wxmsw27d_core.lib wxbase27d.lib wxtiffd.lib wxjpegd.lib wxpngd.lib wxzlibd.lib wxregexd.lib wxexpatd.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib oleacc.lib odbc32.lib /nologo /machine:i386 /out:"vc_mswd\richtext.exe" /debug /libpath:".\..\..\lib\vc_lib" /subsystem:windows
+# ADD LINK32 wxmsw27d_adv.lib wxbase27d_xml.lib wxmsw27d_html.lib wxmsw27d_core.lib wxbase27d.lib wxtiffd.lib wxjpegd.lib wxpngd.lib wxzlibd.lib wxregexd.lib wxexpatd.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib winspool.lib winmm.lib shell32.lib comctl32.lib ole32.lib oleaut32.lib uuid.lib rpcrt4.lib advapi32.lib wsock32.lib oleacc.lib odbc32.lib /nologo /machine:i386 /out:"vc_mswd\richtext.exe" /debug /libpath:".\..\..\lib\vc_lib" /subsystem:windows
+
+!ENDIF
+
+# Begin Target
+
+# Name "richtext - Win32 DLL Universal Unicode Release"
+# Name "richtext - Win32 DLL Universal Unicode Debug"
+# Name "richtext - Win32 DLL Universal Release"
+# Name "richtext - Win32 DLL Universal Debug"
+# Name "richtext - Win32 DLL Unicode Release"
+# Name "richtext - Win32 DLL Unicode Debug"
+# Name "richtext - Win32 DLL Release"
+# Name "richtext - Win32 DLL Debug"
+# Name "richtext - Win32 Universal Unicode Release"
+# Name "richtext - Win32 Universal Unicode Debug"
+# Name "richtext - Win32 Universal Release"
+# Name "richtext - Win32 Universal Debug"
+# Name "richtext - Win32 Unicode Release"
+# Name "richtext - Win32 Unicode Debug"
+# Name "richtext - Win32 Release"
+# Name "richtext - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\richtext.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\richtext.rc
+# End Source File
+# End Group
+# End Target
+# End Project
+
diff --git a/samples/richtext/richtext.dsw b/samples/richtext/richtext.dsw
new file mode 100644
index 0000000000..7140d99f13
--- /dev/null
+++ b/samples/richtext/richtext.dsw
@@ -0,0 +1,91 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+Project: "richtext"=richtext.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+Begin Project Dependency
+Project_Dep_Name adv
+End Project Dependency
+Begin Project Dependency
+Project_Dep_Name xml
+End Project Dependency
+Begin Project Dependency
+Project_Dep_Name html
+End Project Dependency
+Begin Project Dependency
+Project_Dep_Name core
+End Project Dependency
+Begin Project Dependency
+Project_Dep_Name base
+End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "adv"=..\..\build\msw\wx_adv.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "xml"=..\..\build\msw\wx_xml.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "html"=..\..\build\msw\wx_html.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "core"=..\..\build\msw\wx_core.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "base"=..\..\build\msw\wx_base.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/samples/richtext/richtext.rc b/samples/richtext/richtext.rc
new file mode 100644
index 0000000000..d89216abf2
--- /dev/null
+++ b/samples/richtext/richtext.rc
@@ -0,0 +1,3 @@
+aaaa ICON "../sample.ico"
+#include "wx/msw/wx.rc"
+
diff --git a/samples/richtext/todo.txt b/samples/richtext/todo.txt
new file mode 100644
index 0000000000..6c3b40396f
--- /dev/null
+++ b/samples/richtext/todo.txt
@@ -0,0 +1,122 @@
+
+wxRichTextCtrl To-Do List
+=========================
+
+
+BUGS:
+=====
+
+- Setting selection with mouse sometimes causes negative selection
+ lengths. See wxRichTextCtrl::ExtendSelection.
+- Word forward can miss first word on line.
+- Doesn't correctly undo deletion of bulleted paragraphs.
+
+
+IMPROVEMENTS:
+=============
+
+- Add bakefiles, choose location for further development - wxCode or wxWidgets.
+- Document.
+- Rework scrolling code so (a) can scroll one line at a time (uneven
+ scroll increments and (b) can derive from wxTextCtrl if this is desirable.
+- Implement auto-scroll when selecting.
+- Implement auto-wrap option (currently it's always on) with option for
+ horizontal scrollbars.
+- Implement overwrite mode.
+- Collapse multiple keystrokes into single undo command.
+- Unicode adaptation (e.g. in OnChar).
+- Implement "Paste Special".
+- Pass wxRichTextDrawContext to Draw and Layout with data such as
+ selection colour.
+- Have virtual functions for customisation, e.g. CreateTextObject, CreateImageObject,
+ CreateParagraph object. Should be a separate factory object that can be
+ plugged in.
+- Implement more event generation.
+- Implement roman numerals in bullet points.
+- Make as wxTextCtrl-compatible as possible.
+- Allow use as single-line edit control, so potentially the class can
+ replace both wxUniversal wxTextCtrl implementations.
+- Adjust behaviour on each platform to conform to native standards.
+ This might be a run-time option.
+- Custom attributes (simply add an integer field to wxRichTextAttr and
+ maybe wxTextAttrEx).
+- Improve image support: margins, resizing, storage of image as native format
+ data (e.g. JPEG) so no lossiness.
+- Ensure read-only mode works.
+- Make more efficient, e.g. don't try to draw lines outside the client area;
+ don't store whole paragraph in Undo stack if just changing the paragraph's style.
+- Allow specification of word separators, and whether hyphenation will be done
+ (language-dependent).
+
+
+API ISSUES:
+===========
+
+- Consider whether to merge wxTextAttrEx with wxTextAttr.
+- Implementation-independent API for traversing rich text controls, e.g. getting spans
+ of text, retrieving images, etc.
+- FindString: search for a substring within a range, returning a range. Will make it
+ easy to add styles to text you just added.
+- WriteString, WriteImage should really return a range. However, can query insertion point.
+- Bullets: suggest we are able to specify symbols in a specific named font
+ (maybe keep mapping from name to actual symbol font in style manager).
+ We wouldn't want to store a wxFont with each paragraph, just for the symbol,
+ since this will be inefficient. If no font is specified, use the font
+ for this paragraph.
+ Also allow bitmaps to be specified for bullets.
+- For adding floating text boxes and images, suggest e.g.
+ bool wxRichTextBuffer::AddFloatingImage(long paragraphPosition, const wxImage& image);
+
+
+FEATURES:
+=========
+
+- XML I/O (a prototype implementation done, maybe the syntax/verbosity could be improved)
+- HTML (I)/O and clipboard support
+- RTF (I)/O and clipboard support
+- OpenOffice.org I/O
+- C++ output, i.e. generate set of buffer commands from a buffer
+ so that the data can be included in an app, possibly with translation.
+- Line breaks. How will we implement these? Suggest special non-ASCII code.
+- Hard (non-breaking) spaces. Insert special code in text string. Shift-Return.
+- Borders on paragraphs, with special attention to how they join up.
+- Bitmap bullets.
+- Tab stops, with ruler display.
+- Rules (lines).
+- Hyperlinks.
+- Ready-made find and replace implementation
+- Ready-made toolbars, standard identifiers.
+- Floating text boxes and images, with appropriate wrapping: have a separate list of
+ these, and when formatting each line, simply restrict the available space accordingly.
+ Boxes can optionally be tied to a paragraph so they move relative to it (may need to introduce
+ paragraph identifiers for that).
+- Set of convenience controls and dialogs including wxRichTextStyleListBox,
+ wxRichTextParagraphFormattingDialog, wxRichTextStyleEditorDialog,
+ wxRichTextBulletDialog, wxRichTextInsertSymbolDialog, wxRichTextTabsDialog
+ (also displayed as a page on wxRichTextParagraphFormattingDialog),
+- Template manager.
+
+- ADVANCED: Implement tables. This is a kind of paragraph that knows how to lay
+ out a 2D grid of paragraph layout boxes. A few assumptions
+ about the hierarchy will need correcting but in general, paragraph
+ layout is self-contained and should just work within table cells.
+- ADVANCED: Implement printing, including headers and footers.
+- ADVANCED: Implement page view mode where pagination is shown while editing.
+- ADVANCED: Field codes that can be hidden/shown: for example, cross-references,
+ HTML links, page number, heading number, etc.
+- ADVANCED: Implement justification, by distributing space over the line (draw one
+ word at a time but store in the usual way).
+- ADVANCED: Implement headings.
+- ADVANCED: Zoom.
+- ADVANCED: Versioning and markup facility (for e.g. collaboration).
+- ADVANCED: Columns.
+- ADVANCED: Table of contents, index.
+- ADVANCED: Autocorrect.
+
+
+DEMO:
+=====
+
+- Drop-down lists for style, face, font size.
+- Rework formatting icons.
+
diff --git a/setup.h.in b/setup.h.in
index 1b8e5a8d0a..ffac7e3072 100644
--- a/setup.h.in
+++ b/setup.h.in
@@ -410,6 +410,8 @@
#define wxUSE_GLCANVAS 0
+#define wxUSE_RICHTEXT 0
+
#define wxUSE_CLIPBOARD 0
diff --git a/src/richtext/richtextbuffer.cpp b/src/richtext/richtextbuffer.cpp
new file mode 100644
index 0000000000..e4fc8acedb
--- /dev/null
+++ b/src/richtext/richtextbuffer.cpp
@@ -0,0 +1,4962 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name: richtextbuffer.cpp
+// Purpose: Buffer for wxRichTextCtrl
+// Author: Julian Smart
+// Modified by:
+// Created: 2005-09-30
+// RCS-ID:
+// Copyright: (c) Julian Smart
+// Licence: wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+// For compilers that support precompilation, includes "wx.h".
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+ #pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+ #include "wx/wx.h"
+#endif
+
+#include "wx/image.h"
+
+#if wxUSE_RICHTEXT
+
+#include "wx/filename.h"
+#include "wx/clipbrd.h"
+#include "wx/wfstream.h"
+#include "wx/module.h"
+#include "wx/mstream.h"
+#include "wx/sstream.h"
+
+#include "wx/richtext/richtextbuffer.h"
+#include "wx/richtext/richtextctrl.h"
+#include "wx/richtext/richtextstyles.h"
+
+#include "wx/listimpl.cpp"
+
+WX_DEFINE_LIST(wxRichTextObjectList);
+WX_DEFINE_LIST(wxRichTextLineList);
+
+/*!
+ * wxRichTextObject
+ * This is the base for drawable objects.
+ */
+
+IMPLEMENT_CLASS(wxRichTextObject, wxObject)
+
+wxRichTextObject::wxRichTextObject(wxRichTextObject* parent)
+{
+ m_dirty = false;
+ m_refCount = 1;
+ m_parent = parent;
+ m_leftMargin = 0;
+ m_rightMargin = 0;
+ m_topMargin = 0;
+ m_bottomMargin = 0;
+ m_descent = 0;
+}
+
+wxRichTextObject::~wxRichTextObject()
+{
+}
+
+void wxRichTextObject::Dereference()
+{
+ m_refCount --;
+ if (m_refCount <= 0)
+ delete this;
+}
+
+/// Copy
+void wxRichTextObject::Copy(const wxRichTextObject& obj)
+{
+ m_size = obj.m_size;
+ m_pos = obj.m_pos;
+ m_dirty = obj.m_dirty;
+ m_range = obj.m_range;
+ m_attributes = obj.m_attributes;
+ m_descent = obj.m_descent;
+
+ if (!m_attributes.GetFont().Ok())
+ wxLogDebug(wxT("No font!"));
+ if (!obj.m_attributes.GetFont().Ok())
+ wxLogDebug(wxT("Parent has no font!"));
+}
+
+void wxRichTextObject::SetMargins(int margin)
+{
+ m_leftMargin = m_rightMargin = m_topMargin = m_bottomMargin = margin;
+}
+
+void wxRichTextObject::SetMargins(int leftMargin, int rightMargin, int topMargin, int bottomMargin)
+{
+ m_leftMargin = leftMargin;
+ m_rightMargin = rightMargin;
+ m_topMargin = topMargin;
+ m_bottomMargin = bottomMargin;
+}
+
+// Convert units in tends of a millimetre to device units
+int wxRichTextObject::ConvertTenthsMMToPixels(wxDC& dc, int units)
+{
+ int ppi = dc.GetPPI().x;
+
+ // There are ppi pixels in 254.1 "1/10 mm"
+
+ double pixels = ((double) units * (double)ppi) / 254.1;
+
+ return (int) pixels;
+}
+
+/// Dump to output stream for debugging
+void wxRichTextObject::Dump(wxTextOutputStream& stream)
+{
+ stream << GetClassInfo()->GetClassName() << wxT("\n");
+ stream << wxString::Format(wxT("Size: %d,%d. Position: %d,%d, Range: %ld,%ld"), m_size.x, m_size.y, m_pos.x, m_pos.y, m_range.GetStart(), m_range.GetEnd()) << wxT("\n");
+ stream << wxString::Format(wxT("Text colour: %d,%d,%d."), (int) m_attributes.GetTextColour().Red(), (int) m_attributes.GetTextColour().Green(), (int) m_attributes.GetTextColour().Blue()) << wxT("\n");
+}
+
+
+/*!
+ * wxRichTextCompositeObject
+ * This is the base for drawable objects.
+ */
+
+IMPLEMENT_CLASS(wxRichTextCompositeObject, wxRichTextObject)
+
+wxRichTextCompositeObject::wxRichTextCompositeObject(wxRichTextObject* parent):
+ wxRichTextObject(parent)
+{
+}
+
+wxRichTextCompositeObject::~wxRichTextCompositeObject()
+{
+ DeleteChildren();
+}
+
+/// Get the nth child
+wxRichTextObject* wxRichTextCompositeObject::GetChild(size_t n) const
+{
+ wxASSERT ( n < m_children.GetCount() );
+
+ return m_children.Item(n)->GetData();
+}
+
+/// Append a child, returning the position
+size_t wxRichTextCompositeObject::AppendChild(wxRichTextObject* child)
+{
+ m_children.Append(child);
+ child->SetParent(this);
+ return m_children.GetCount() - 1;
+}
+
+/// Insert the child in front of the given object, or at the beginning
+bool wxRichTextCompositeObject::InsertChild(wxRichTextObject* child, wxRichTextObject* inFrontOf)
+{
+ if (inFrontOf)
+ {
+ wxRichTextObjectList::compatibility_iterator node = m_children.Find(inFrontOf);
+ m_children.Insert(node, child);
+ }
+ else
+ m_children.Insert(child);
+ child->SetParent(this);
+
+ return true;
+}
+
+/// Delete the child
+bool wxRichTextCompositeObject::RemoveChild(wxRichTextObject* child, bool deleteChild)
+{
+ wxRichTextObjectList::compatibility_iterator node = m_children.Find(child);
+ if (node)
+ {
+ if (deleteChild)
+ delete node->GetData();
+ delete node;
+
+ return true;
+ }
+ return false;
+}
+
+/// Delete all children
+bool wxRichTextCompositeObject::DeleteChildren()
+{
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ while (node)
+ {
+ wxRichTextObjectList::compatibility_iterator oldNode = node;
+
+ wxRichTextObject* child = node->GetData();
+ child->Dereference(); // Only delete if reference count is zero
+
+ node = node->GetNext();
+ delete oldNode;
+ }
+
+ return true;
+}
+
+/// Get the child count
+size_t wxRichTextCompositeObject::GetChildCount() const
+{
+ return m_children.GetCount();
+}
+
+/// Copy
+void wxRichTextCompositeObject::Copy(const wxRichTextCompositeObject& obj)
+{
+ wxRichTextObject::Copy(obj);
+
+ DeleteChildren();
+
+ wxRichTextObjectList::compatibility_iterator node = obj.m_children.GetFirst();
+ while (node)
+ {
+ wxRichTextObject* child = node->GetData();
+ m_children.Append(child->Clone());
+
+ node = node->GetNext();
+ }
+}
+
+/// Hit-testing: returns a flag indicating hit test details, plus
+/// information about position
+int wxRichTextCompositeObject::HitTest(wxDC& dc, const wxPoint& pt, long& textPosition)
+{
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ while (node)
+ {
+ wxRichTextObject* child = node->GetData();
+
+ int ret = child->HitTest(dc, pt, textPosition);
+ if (ret != wxRICHTEXT_HITTEST_NONE)
+ return ret;
+
+ node = node->GetNext();
+ }
+
+ return wxRICHTEXT_HITTEST_NONE;
+}
+
+/// Finds the absolute position and row height for the given character position
+bool wxRichTextCompositeObject::FindPosition(wxDC& dc, long index, wxPoint& pt, int* height, bool forceLineStart)
+{
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ while (node)
+ {
+ wxRichTextObject* child = node->GetData();
+
+ if (child->FindPosition(dc, index, pt, height, forceLineStart))
+ return true;
+
+ node = node->GetNext();
+ }
+
+ return false;
+}
+
+/// Calculate range
+void wxRichTextCompositeObject::CalculateRange(long start, long& end)
+{
+ long current = start;
+ long lastEnd = current;
+
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ while (node)
+ {
+ wxRichTextObject* child = node->GetData();
+ long childEnd = 0;
+
+ child->CalculateRange(current, childEnd);
+ lastEnd = childEnd;
+
+ current = childEnd + 1;
+
+ node = node->GetNext();
+ }
+
+ end = lastEnd;
+
+ // An object with no children has zero length
+ if (m_children.GetCount() == 0)
+ end --;
+
+ m_range.SetRange(start, end);
+}
+
+/// Delete range from layout.
+bool wxRichTextCompositeObject::DeleteRange(const wxRichTextRange& range)
+{
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+
+ while (node)
+ {
+ wxRichTextObject* obj = (wxRichTextObject*) node->GetData();
+ wxRichTextObjectList::compatibility_iterator next = node->GetNext();
+
+ // Delete the range in each paragraph
+
+ // When a chunk has been deleted, internally the content does not
+ // now match the ranges.
+ // However, so long as deletion is not done on the same object twice this is OK.
+ // If you may delete content from the same object twice, recalculate
+ // the ranges inbetween DeleteRange calls by calling CalculateRanges, and
+ // adjust the range you're deleting accordingly.
+
+ if (!obj->GetRange().IsOutside(range))
+ {
+ obj->DeleteRange(range);
+
+ // Delete an empty object, or paragraph within this range.
+ if (obj->IsEmpty() ||
+ (range.GetStart() <= obj->GetRange().GetStart() && range.GetEnd() >= obj->GetRange().GetEnd()))
+ {
+ // An empty paragraph has length 1, so won't be deleted unless the
+ // whole range is deleted.
+ RemoveChild(obj, true);
+ }
+ }
+
+ node = next;
+ }
+
+ return true;
+}
+
+/// Get any text in this object for the given range
+wxString wxRichTextCompositeObject::GetTextForRange(const wxRichTextRange& range) const
+{
+ wxString text;
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ while (node)
+ {
+ wxRichTextObject* child = node->GetData();
+ wxRichTextRange childRange = range;
+ if (!child->GetRange().IsOutside(range))
+ {
+ childRange.LimitTo(child->GetRange());
+
+ wxString childText = child->GetTextForRange(childRange);
+
+ text += childText;
+ }
+ node = node->GetNext();
+ }
+
+ return text;
+}
+
+/// Recursively merge all pieces that can be merged.
+bool wxRichTextCompositeObject::Defragment()
+{
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ while (node)
+ {
+ wxRichTextObject* child = node->GetData();
+ wxRichTextCompositeObject* composite = wxDynamicCast(child, wxRichTextCompositeObject);
+ if (composite)
+ composite->Defragment();
+
+ if (node->GetNext())
+ {
+ wxRichTextObject* nextChild = node->GetNext()->GetData();
+ if (child->CanMerge(nextChild) && child->Merge(nextChild))
+ {
+ nextChild->Dereference();
+ delete node->GetNext();
+
+ // Don't set node -- we'll see if we can merge again with the next
+ // child.
+ }
+ else
+ node = node->GetNext();
+ }
+ else
+ node = node->GetNext();
+ }
+
+ return true;
+}
+
+/// Dump to output stream for debugging
+void wxRichTextCompositeObject::Dump(wxTextOutputStream& stream)
+{
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ while (node)
+ {
+ wxRichTextObject* child = node->GetData();
+ child->Dump(stream);
+ node = node->GetNext();
+ }
+}
+
+
+/*!
+ * wxRichTextBox
+ * This defines a 2D space to lay out objects
+ */
+
+IMPLEMENT_DYNAMIC_CLASS(wxRichTextBox, wxRichTextCompositeObject)
+
+wxRichTextBox::wxRichTextBox(wxRichTextObject* parent):
+ wxRichTextCompositeObject(parent)
+{
+}
+
+/// Draw the item
+bool wxRichTextBox::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& WXUNUSED(rect), int descent, int style)
+{
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ while (node)
+ {
+ wxRichTextObject* child = node->GetData();
+
+ wxRect childRect = wxRect(child->GetPosition(), child->GetCachedSize());
+ child->Draw(dc, range, selectionRange, childRect, descent, style);
+
+ node = node->GetNext();
+ }
+ return true;
+}
+
+/// Lay the item out
+bool wxRichTextBox::Layout(wxDC& dc, const wxRect& rect, int style)
+{
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ while (node)
+ {
+ wxRichTextObject* child = node->GetData();
+ child->Layout(dc, rect, style);
+
+ node = node->GetNext();
+ }
+ m_dirty = false;
+ return true;
+}
+
+/// Get/set the size for the given range. Assume only has one child.
+bool wxRichTextBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags) const
+{
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ if (node)
+ {
+ wxRichTextObject* child = node->GetData();
+ return child->GetRangeSize(range, size, descent, dc, flags);
+ }
+ else
+ return false;
+}
+
+/// Copy
+void wxRichTextBox::Copy(const wxRichTextBox& obj)
+{
+ wxRichTextCompositeObject::Copy(obj);
+}
+
+
+/*!
+ * wxRichTextParagraphLayoutBox
+ * This box knows how to lay out paragraphs.
+ */
+
+IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraphLayoutBox, wxRichTextBox)
+
+wxRichTextParagraphLayoutBox::wxRichTextParagraphLayoutBox(wxRichTextObject* parent):
+ wxRichTextBox(parent)
+{
+ Init();
+}
+
+/// Initialize the object.
+void wxRichTextParagraphLayoutBox::Init()
+{
+ m_ctrl = NULL;
+
+ // For now, assume is the only box and has no initial size.
+ m_range = wxRichTextRange(0, -1);
+
+ m_leftMargin = 4;
+ m_rightMargin = 4;
+ m_topMargin = 4;
+ m_bottomMargin = 4;
+}
+
+/// Draw the item
+bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& WXUNUSED(rect), int descent, int style)
+{
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ while (node)
+ {
+ wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
+ wxASSERT (child != NULL);
+
+ if (child && !child->GetRange().IsOutside(range))
+ {
+ wxRect childRect(child->GetPosition(), child->GetCachedSize());
+
+ child->Draw(dc, child->GetRange(), selectionRange, childRect, descent, style);
+ }
+
+ node = node->GetNext();
+ }
+ return true;
+}
+
+/// Lay the item out
+bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, const wxRect& rect, int style)
+{
+ wxRect availableSpace(rect.x + m_leftMargin,
+ rect.y + m_topMargin,
+ rect.width - m_leftMargin - m_rightMargin,
+ rect.height - m_topMargin - m_bottomMargin);
+
+ int maxWidth = 0;
+
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ while (node)
+ {
+ // Assume this box only contains paragraphs
+
+ wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
+ wxASSERT (child != NULL);
+
+ child->Layout(dc, availableSpace, style);
+
+ // Layout must set the cached size
+ availableSpace.y += child->GetCachedSize().y;
+ maxWidth = wxMax(maxWidth, child->GetCachedSize().x);
+
+ node = node->GetNext();
+ }
+
+ SetCachedSize(wxSize(maxWidth, availableSpace.y));
+
+ m_dirty = false;
+
+ return true;
+}
+
+/// Copy
+void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox& obj)
+{
+ wxRichTextBox::Copy(obj);
+}
+
+/// Get/set the size for the given range.
+bool wxRichTextParagraphLayoutBox::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags) const
+{
+ wxSize sz;
+
+ wxRichTextObjectList::compatibility_iterator startPara = NULL;
+ wxRichTextObjectList::compatibility_iterator endPara = NULL;
+
+ // First find the first paragraph whose starting position is within the range.
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ while (node)
+ {
+ // child is a paragraph
+ wxRichTextObject* child = node->GetData();
+ const wxRichTextRange& r = child->GetRange();
+
+ if (r.GetStart() <= range.GetStart() && r.GetEnd() >= range.GetStart())
+ {
+ startPara = node;
+ break;
+ }
+
+ node = node->GetNext();
+ }
+
+ // Next find the last paragraph containing part of the range
+ node = m_children.GetFirst();
+ while (node)
+ {
+ // child is a paragraph
+ wxRichTextObject* child = node->GetData();
+ const wxRichTextRange& r = child->GetRange();
+
+ if (r.GetStart() <= range.GetEnd() && r.GetEnd() >= range.GetEnd())
+ {
+ endPara = node;
+ break;
+ }
+
+ node = node->GetNext();
+ }
+
+ if (!startPara || !endPara)
+ return false;
+
+ // Now we can add up the sizes
+ for (node = startPara; node ; node = node->GetNext())
+ {
+ // child is a paragraph
+ wxRichTextObject* child = node->GetData();
+ const wxRichTextRange& childRange = child->GetRange();
+ wxRichTextRange rangeToFind = range;
+ rangeToFind.LimitTo(childRange);
+
+ wxSize childSize;
+
+ int childDescent = 0;
+ child->GetRangeSize(rangeToFind, childSize, childDescent, dc, flags);
+
+ descent = wxMax(childDescent, descent);
+
+ sz.x = wxMax(sz.x, childSize.x);
+ sz.y += childSize.y;
+
+ if (node == endPara)
+ break;
+ }
+
+ size = sz;
+
+ return true;
+}
+
+/// Get the paragraph at the given position
+wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtPosition(long pos, bool caretPosition) const
+{
+ if (caretPosition)
+ pos ++;
+
+ // First find the first paragraph whose starting position is within the range.
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ while (node)
+ {
+ // child is a paragraph
+ wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
+ wxASSERT (child != NULL);
+
+ // Return first child in buffer if position is -1
+ // if (pos == -1)
+ // return child;
+
+ if (child->GetRange().Contains(pos))
+ return child;
+
+ node = node->GetNext();
+ }
+ return NULL;
+}
+
+/// Get the line at the given position
+wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtPosition(long pos, bool caretPosition) const
+{
+ if (caretPosition)
+ pos ++;
+
+ // First find the first paragraph whose starting position is within the range.
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ while (node)
+ {
+ // child is a paragraph
+ wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
+ wxASSERT (child != NULL);
+
+ wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
+ while (node2)
+ {
+ wxRichTextLine* line = node2->GetData();
+
+ if (line->GetRange().Contains(pos) ||
+
+ // If the position is end-of-paragraph, then return the last line of
+ // of the paragraph.
+ (line->GetRange().GetEnd() == child->GetRange().GetEnd()-1) && (pos == child->GetRange().GetEnd()))
+ return line;
+
+ node2 = node2->GetNext();
+ }
+
+ node = node->GetNext();
+ }
+
+ int lineCount = GetLineCount();
+ if (lineCount > 0)
+ return GetLineForVisibleLineNumber(lineCount-1);
+ else
+ return NULL;
+}
+
+/// Get the line at the given y pixel position, or the last line.
+wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineAtYPosition(int y) const
+{
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ while (node)
+ {
+ wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
+ wxASSERT (child != NULL);
+
+ wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
+ while (node2)
+ {
+ wxRichTextLine* line = node2->GetData();
+
+ wxRect rect(line->GetRect());
+
+ if (y <= rect.GetBottom())
+ return line;
+
+ node2 = node2->GetNext();
+ }
+
+ node = node->GetNext();
+ }
+
+ // Return last line
+ int lineCount = GetLineCount();
+ if (lineCount > 0)
+ return GetLineForVisibleLineNumber(lineCount-1);
+ else
+ return NULL;
+}
+
+/// Get the number of visible lines
+int wxRichTextParagraphLayoutBox::GetLineCount() const
+{
+ int count = 0;
+
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ while (node)
+ {
+ wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
+ wxASSERT (child != NULL);
+
+ count += child->GetLines().GetCount();
+ node = node->GetNext();
+ }
+ return count;
+}
+
+
+/// Get the paragraph for a given line
+wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphForLine(wxRichTextLine* line) const
+{
+ return GetParagraphAtPosition(line->GetRange().GetStart());
+}
+
+/// Get the line size at the given position
+wxSize wxRichTextParagraphLayoutBox::GetLineSizeAtPosition(long pos, bool caretPosition) const
+{
+ wxRichTextLine* line = GetLineAtPosition(pos, caretPosition);
+ if (line)
+ {
+ return line->GetSize();
+ }
+ else
+ return wxSize(0, 0);
+}
+
+
+/// Convenience function to add a paragraph of text
+wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraph(const wxString& text)
+{
+ wxTextAttrEx style(GetAttributes());
+
+ // Apply default style. If the style has no attributes set,
+ // then the attributes will remain the 'basic style' (i.e. the
+ // layout box's style).
+ wxRichTextApplyStyle(style, GetDefaultStyle());
+
+ wxRichTextParagraph* para = new wxRichTextParagraph(text, this, & style);
+
+ AppendChild(para);
+
+ UpdateRanges();
+ SetDirty(true);
+
+ return para->GetRange();
+}
+
+/// Adds multiple paragraphs, based on newlines.
+wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraphs(const wxString& text)
+{
+ wxTextAttrEx style(GetAttributes());
+ //wxLogDebug("Initial style = %s", style.GetFont().GetFaceName());
+ //wxLogDebug("Initial size = %d", style.GetFont().GetPointSize());
+
+ // Apply default style. If the style has no attributes set,
+ // then the attributes will remain the 'basic style' (i.e. the
+ // layout box's style).
+ wxRichTextApplyStyle(style, GetDefaultStyle());
+
+ //wxLogDebug("Style after applying default style = %s", style.GetFont().GetFaceName());
+ //wxLogDebug("Size after applying default style = %d", style.GetFont().GetPointSize());
+
+ wxRichTextParagraph* firstPara = NULL;
+ wxRichTextParagraph* lastPara = NULL;
+
+ wxRichTextRange range(-1, -1);
+ size_t i = 0;
+ size_t len = text.Length();
+ wxString line;
+ while (i < len)
+ {
+ wxChar ch = text[i];
+ if (ch == wxT('\n') || ch == wxT('\r'))
+ {
+ wxRichTextParagraph* para = new wxRichTextParagraph(line, this, & style);
+
+ AppendChild(para);
+ if (!firstPara)
+ firstPara = para;
+ lastPara = para;
+ line = wxEmptyString;
+ }
+ else
+ line += ch;
+
+ i ++;
+ }
+ if (!line.IsEmpty())
+ {
+ lastPara = new wxRichTextParagraph(line, this, & style);
+ //wxLogDebug("Para Face = %s", lastPara->GetAttributes().GetFont().GetFaceName());
+ AppendChild(lastPara);
+ }
+
+ if (firstPara)
+ range.SetStart(firstPara->GetRange().GetStart());
+ else if (lastPara)
+ range.SetStart(lastPara->GetRange().GetStart());
+
+ if (lastPara)
+ range.SetEnd(lastPara->GetRange().GetEnd());
+ else if (firstPara)
+ range.SetEnd(firstPara->GetRange().GetEnd());
+
+ UpdateRanges();
+ SetDirty(false);
+
+ return GetRange();
+}
+
+/// Convenience function to add an image
+wxRichTextRange wxRichTextParagraphLayoutBox::AddImage(const wxImage& image)
+{
+ wxTextAttrEx style(GetAttributes());
+
+ // Apply default style. If the style has no attributes set,
+ // then the attributes will remain the 'basic style' (i.e. the
+ // layout box's style).
+ wxRichTextApplyStyle(style, GetDefaultStyle());
+
+ wxRichTextParagraph* para = new wxRichTextParagraph(this, & style);
+ AppendChild(para);
+ para->AppendChild(new wxRichTextImage(image, this));
+
+ UpdateRanges();
+ SetDirty(true);
+
+ return para->GetRange();
+}
+
+
+/// Insert fragment into this box at the given position. If partialParagraph is true,
+/// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph
+/// marker.
+/// TODO: if fragment is inserted inside styled fragment, must apply that style to
+/// to the data (if it has a default style, anyway).
+
+bool wxRichTextParagraphLayoutBox::InsertFragment(long position, wxRichTextFragment& fragment)
+{
+ SetDirty(true);
+
+ // First, find the first paragraph whose starting position is within the range.
+ wxRichTextParagraph* para = GetParagraphAtPosition(position);
+ if (para)
+ {
+ wxRichTextObjectList::compatibility_iterator node = m_children.Find(para);
+
+ // Now split at this position, returning the object to insert the new
+ // ones in front of.
+ wxRichTextObject* nextObject = para->SplitAt(position);
+
+ // Special case: partial paragraph, just one paragraph. Might be a small amount of
+ // text, for example, so let's optimize.
+
+ if (fragment.GetPartialParagraph() && fragment.GetChildren().GetCount() == 1)
+ {
+ // Add the first para to this para...
+ wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
+ if (!firstParaNode)
+ return false;
+
+ // Iterate through the fragment paragraph inserting the content into this paragraph.
+ wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
+ wxASSERT (firstPara != NULL);
+
+ wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
+ while (objectNode)
+ {
+ wxRichTextObject* newObj = objectNode->GetData()->Clone();
+
+ if (!nextObject)
+ {
+ // Append
+ para->AppendChild(newObj);
+ }
+ else
+ {
+ // Insert before nextObject
+ para->InsertChild(newObj, nextObject);
+ }
+
+ objectNode = objectNode->GetNext();
+ }
+
+ return true;
+ }
+ else
+ {
+ // Procedure for inserting a fragment consisting of a number of
+ // paragraphs:
+ //
+ // 1. Remove and save the content that's after the insertion point, for adding
+ // back once we've added the fragment.
+ // 2. Add the content from the first fragment paragraph to the current
+ // paragraph.
+ // 3. Add remaining fragment paragraphs after the current paragraph.
+ // 4. Add back the saved content from the first paragraph. If partialParagraph
+ // is true, add it to the last paragraph added and not a new one.
+
+ // 1. Remove and save objects after split point.
+ wxList savedObjects;
+ if (nextObject)
+ para->MoveToList(nextObject, savedObjects);
+
+ // 2. Add the content from the 1st fragment paragraph.
+ wxRichTextObjectList::compatibility_iterator firstParaNode = fragment.GetChildren().GetFirst();
+ if (!firstParaNode)
+ return false;
+
+ wxRichTextParagraph* firstPara = wxDynamicCast(firstParaNode->GetData(), wxRichTextParagraph);
+ wxASSERT(firstPara != NULL);
+
+ wxRichTextObjectList::compatibility_iterator objectNode = firstPara->GetChildren().GetFirst();
+ while (objectNode)
+ {
+ wxRichTextObject* newObj = objectNode->GetData()->Clone();
+
+ // Append
+ para->AppendChild(newObj);
+
+ objectNode = objectNode->GetNext();
+ }
+
+ // 3. Add remaining fragment paragraphs after the current paragraph.
+ wxRichTextObjectList::compatibility_iterator nextParagraphNode = node->GetNext();
+ wxRichTextObject* nextParagraph = NULL;
+ if (nextParagraphNode)
+ nextParagraph = nextParagraphNode->GetData();
+
+ wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst()->GetNext();
+ wxRichTextParagraph* finalPara = para;
+
+ // If there was only one paragraph, we need to insert a new one.
+ if (!i)
+ {
+ finalPara = new wxRichTextParagraph;
+
+ // TODO: These attributes should come from the subsequent paragraph
+ // when originally deleted, since the subsequent para takes on
+ // the previous para's attributes.
+ finalPara->SetAttributes(firstPara->GetAttributes());
+
+ if (nextParagraph)
+ InsertChild(finalPara, nextParagraph);
+ else
+ AppendChild(finalPara);
+ }
+ else while (i)
+ {
+ wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
+ wxASSERT( para != NULL );
+
+ finalPara = (wxRichTextParagraph*) para->Clone();
+
+ if (nextParagraph)
+ InsertChild(finalPara, nextParagraph);
+ else
+ AppendChild(finalPara);
+
+ i = i->GetNext();
+ }
+
+ // 4. Add back the remaining content.
+ if (finalPara)
+ {
+ finalPara->MoveFromList(savedObjects);
+
+ // Ensure there's at least one object
+ if (finalPara->GetChildCount() == 0)
+ {
+ wxRichTextPlainText* text = new wxRichTextPlainText(wxT(""));
+ text->SetAttributes(finalPara->GetAttributes());
+
+ finalPara->AppendChild(text);
+ }
+ }
+
+ return true;
+ }
+ }
+ else
+ {
+ // Append
+ wxRichTextObjectList::compatibility_iterator i = fragment.GetChildren().GetFirst();
+ while (i)
+ {
+ wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
+ wxASSERT( para != NULL );
+
+ AppendChild(para->Clone());
+
+ i = i->GetNext();
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'.
+/// If there was an incomplete paragraph at the end, partialParagraph is set to true.
+bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange& range, wxRichTextFragment& fragment)
+{
+ wxRichTextObjectList::compatibility_iterator i = GetChildren().GetFirst();
+ while (i)
+ {
+ wxRichTextParagraph* para = wxDynamicCast(i->GetData(), wxRichTextParagraph);
+ wxASSERT( para != NULL );
+
+ if (!para->GetRange().IsOutside(range))
+ {
+ fragment.AppendChild(para->Clone());
+ }
+ i = i->GetNext();
+ }
+
+ // Now top and tail the first and last paragraphs in our new fragment (which might be the same).
+ if (!fragment.IsEmpty())
+ {
+ wxRichTextRange topTailRange(range);
+
+ wxRichTextParagraph* firstPara = wxDynamicCast(fragment.GetChildren().GetFirst()->GetData(), wxRichTextParagraph);
+ wxASSERT( firstPara != NULL );
+
+ // Chop off the start of the paragraph
+ if (topTailRange.GetStart() > firstPara->GetRange().GetStart())
+ {
+ wxRichTextRange r(firstPara->GetRange().GetStart(), topTailRange.GetStart()-1);
+ firstPara->DeleteRange(r);
+
+ // Make sure the numbering is correct
+ long end;
+ fragment.CalculateRange(firstPara->GetRange().GetStart(), end);
+
+ // Now, we've deleted some positions, so adjust the range
+ // accordingly.
+ topTailRange.SetEnd(topTailRange.GetEnd() - r.GetLength());
+ }
+
+ wxRichTextParagraph* lastPara = wxDynamicCast(fragment.GetChildren().GetLast()->GetData(), wxRichTextParagraph);
+ wxASSERT( lastPara != NULL );
+
+ if (topTailRange.GetEnd() < (lastPara->GetRange().GetEnd()-1))
+ {
+ wxRichTextRange r(topTailRange.GetEnd()+1, lastPara->GetRange().GetEnd()-1); /* -1 since actual text ends 1 position before end of para marker */
+ lastPara->DeleteRange(r);
+
+ // Make sure the numbering is correct
+ long end;
+ fragment.CalculateRange(firstPara->GetRange().GetStart(), end);
+
+ // We only have part of a paragraph at the end
+ fragment.SetPartialParagraph(true);
+ }
+ else
+ {
+ if (topTailRange.GetEnd() == (lastPara->GetRange().GetEnd() - 1))
+ // We have a partial paragraph (don't save last new paragraph marker)
+ fragment.SetPartialParagraph(true);
+ else
+ // We have a complete paragraph
+ fragment.SetPartialParagraph(false);
+ }
+ }
+
+ return true;
+}
+
+/// Given a position, get the number of the visible line (potentially many to a paragraph),
+/// starting from zero at the start of the buffer.
+long wxRichTextParagraphLayoutBox::GetVisibleLineNumber(long pos, bool caretPosition, bool startOfLine) const
+{
+ if (caretPosition)
+ pos ++;
+
+ int lineCount = 0;
+
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ while (node)
+ {
+ wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
+ wxASSERT( child != NULL );
+
+ if (child->GetRange().Contains(pos))
+ {
+ wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
+ while (node2)
+ {
+ wxRichTextLine* line = node2->GetData();
+
+ if (line->GetRange().Contains(pos))
+ {
+ // If the caret is displayed at the end of the previous wrapped line,
+ // we want to return the line it's _displayed_ at (not the actual line
+ // containing the position).
+ if (line->GetRange().GetStart() == pos && !startOfLine && child->GetRange().GetStart() != pos)
+ return lineCount - 1;
+ else
+ return lineCount;
+ }
+
+ lineCount ++;
+
+ node2 = node2->GetNext();
+ }
+ // If we didn't find it in the lines, it must be
+ // the last position of the paragraph. So return the last line.
+ return lineCount-1;
+ }
+ else
+ lineCount += child->GetLines().GetCount();
+
+ node = node->GetNext();
+ }
+
+ // Not found
+ return -1;
+}
+
+/// Given a line number, get the corresponding wxRichTextLine object.
+wxRichTextLine* wxRichTextParagraphLayoutBox::GetLineForVisibleLineNumber(long lineNumber) const
+{
+ int lineCount = 0;
+
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ while (node)
+ {
+ wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph);
+ wxASSERT(child != NULL);
+
+ if (lineNumber < (int) (child->GetLines().GetCount() + lineCount))
+ {
+ wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst();
+ while (node2)
+ {
+ wxRichTextLine* line = node2->GetData();
+
+ if (lineCount == lineNumber)
+ return line;
+
+ lineCount ++;
+
+ node2 = node2->GetNext();
+ }
+ }
+ else
+ lineCount += child->GetLines().GetCount();
+
+ node = node->GetNext();
+ }
+
+ // Didn't find it
+ return NULL;
+}
+
+/// Delete range from layout.
+bool wxRichTextParagraphLayoutBox::DeleteRange(const wxRichTextRange& range)
+{
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+
+ while (node)
+ {
+ wxRichTextParagraph* obj = wxDynamicCast(node->GetData(), wxRichTextParagraph);
+ wxASSERT (obj != NULL);
+
+ wxRichTextObjectList::compatibility_iterator next = node->GetNext();
+
+ // Delete the range in each paragraph
+
+ if (!obj->GetRange().IsOutside(range))
+ {
+ // Deletes the content of this object within the given range
+ obj->DeleteRange(range);
+
+ // If the whole paragraph is within the range to delete,
+ // delete the whole thing.
+ if (range.GetStart() <= obj->GetRange().GetStart() && range.GetEnd() >= obj->GetRange().GetEnd())
+ {
+ // Delete the whole object
+ RemoveChild(obj, true);
+ }
+ // If the range includes the paragraph end, we need to join this
+ // and the next paragraph.
+ else if (range.Contains(obj->GetRange().GetEnd()))
+ {
+ // We need to move the objects from the next paragraph
+ // to this paragraph
+
+ if (next)
+ {
+ wxRichTextParagraph* nextParagraph = wxDynamicCast(next->GetData(), wxRichTextParagraph);
+ next = next->GetNext();
+ if (nextParagraph)
+ {
+ // Delete the stuff we need to delete
+ nextParagraph->DeleteRange(range);
+
+ // Move the objects to the previous para
+ wxRichTextObjectList::compatibility_iterator node1 = nextParagraph->GetChildren().GetFirst();
+
+ while (node1)
+ {
+ wxRichTextObject* obj1 = node1->GetData();
+
+ // If the object is empty, optimise it out
+ if (obj1->IsEmpty())
+ {
+ delete obj1;
+ }
+ else
+ {
+ obj->AppendChild(obj1);
+ }
+
+ wxRichTextObjectList::compatibility_iterator next1 = node1->GetNext();
+ delete node1;
+
+ node1 = next1;
+ }
+
+ // Delete the paragraph
+ RemoveChild(nextParagraph, true);
+
+ }
+ }
+
+ }
+ }
+
+ node = next;
+ }
+
+ return true;
+}
+
+/// Get any text in this object for the given range
+wxString wxRichTextParagraphLayoutBox::GetTextForRange(const wxRichTextRange& range) const
+{
+ int lineCount = 0;
+ wxString text;
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ while (node)
+ {
+ wxRichTextObject* child = node->GetData();
+ if (!child->GetRange().IsOutside(range))
+ {
+ if (lineCount > 0)
+ text += wxT("\n");
+ wxRichTextRange childRange = range;
+ childRange.LimitTo(child->GetRange());
+
+ wxString childText = child->GetTextForRange(childRange);
+
+ text += childText;
+
+ lineCount ++;
+ }
+ node = node->GetNext();
+ }
+
+ return text;
+}
+
+/// Get all the text
+wxString wxRichTextParagraphLayoutBox::GetText() const
+{
+ return GetTextForRange(GetRange());
+}
+
+/// Get the paragraph by number
+wxRichTextParagraph* wxRichTextParagraphLayoutBox::GetParagraphAtLine(long paragraphNumber) const
+{
+ if ((size_t) paragraphNumber <= GetChildCount())
+ return NULL;
+
+ return (wxRichTextParagraph*) GetChild((size_t) paragraphNumber);
+}
+
+/// Get the length of the paragraph
+int wxRichTextParagraphLayoutBox::GetParagraphLength(long paragraphNumber) const
+{
+ wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
+ if (para)
+ return para->GetRange().GetLength() - 1; // don't include newline
+ else
+ return 0;
+}
+
+/// Get the text of the paragraph
+wxString wxRichTextParagraphLayoutBox::GetParagraphText(long paragraphNumber) const
+{
+ wxRichTextParagraph* para = GetParagraphAtLine(paragraphNumber);
+ if (para)
+ return para->GetTextForRange(para->GetRange());
+ else
+ return wxEmptyString;
+}
+
+/// Convert zero-based line column and paragraph number to a position.
+long wxRichTextParagraphLayoutBox::XYToPosition(long x, long y) const
+{
+ wxRichTextParagraph* para = GetParagraphAtLine(y);
+ if (para)
+ {
+ return para->GetRange().GetStart() + x;
+ }
+ else
+ return -1;
+}
+
+/// Convert zero-based position to line column and paragraph number
+bool wxRichTextParagraphLayoutBox::PositionToXY(long pos, long* x, long* y) const
+{
+ wxRichTextParagraph* para = GetParagraphAtPosition(pos);
+ if (para)
+ {
+ int count = 0;
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ while (node)
+ {
+ wxRichTextObject* child = node->GetData();
+ if (child == para)
+ break;
+ count ++;
+ node = node->GetNext();
+ }
+
+ *y = count;
+ *x = pos - para->GetRange().GetStart();
+
+ return true;
+ }
+ else
+ return false;
+}
+
+/// Get the leaf object in a paragraph at this position.
+/// Given a line number, get the corresponding wxRichTextLine object.
+wxRichTextObject* wxRichTextParagraphLayoutBox::GetLeafObjectAtPosition(long position) const
+{
+ wxRichTextParagraph* para = GetParagraphAtPosition(position);
+ if (para)
+ {
+ wxRichTextObjectList::compatibility_iterator node = para->GetChildren().GetFirst();
+
+ while (node)
+ {
+ wxRichTextObject* child = node->GetData();
+ if (child->GetRange().Contains(position))
+ return child;
+
+ node = node->GetNext();
+ }
+ if (position == para->GetRange().GetEnd() && para->GetChildCount() > 0)
+ return para->GetChildren().GetLast()->GetData();
+ }
+ return NULL;
+}
+
+/// Set character or paragraph text attributes: apply character styles only to immediate text nodes
+bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange& range, const wxRichTextAttr& style, bool withUndo)
+{
+ bool characterStyle = false;
+ bool paragraphStyle = false;
+
+ if (style.IsCharacterStyle())
+ characterStyle = true;
+ if (style.IsParagraphStyle())
+ paragraphStyle = true;
+
+ // If we are associated with a control, make undoable; otherwise, apply immediately
+ // to the data.
+
+ bool haveControl = (GetRichTextCtrl() != NULL);
+
+ wxRichTextAction* action = NULL;
+
+ if (haveControl && withUndo)
+ {
+ action = new wxRichTextAction(NULL, _("Change Style"), wxRICHTEXT_CHANGE_STYLE, & GetRichTextCtrl()->GetBuffer(), GetRichTextCtrl());
+ action->SetRange(range);
+ action->SetPosition(GetRichTextCtrl()->GetCaretPosition());
+ }
+
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ while (node)
+ {
+ wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
+ wxASSERT (para != NULL);
+
+ if (para && para->GetChildCount() > 0)
+ {
+ // Stop searching if we're beyond the range of interest
+ if (para->GetRange().GetStart() > range.GetEnd())
+ break;
+
+ if (!para->GetRange().IsOutside(range))
+ {
+ // We'll be using a copy of the paragraph to make style changes,
+ // not updating the buffer directly.
+ wxRichTextParagraph* newPara = NULL;
+
+ if (haveControl && withUndo)
+ {
+ newPara = new wxRichTextParagraph(*para);
+ action->GetNewParagraphs().AppendChild(newPara);
+
+ // Also store the old ones for Undo
+ action->GetOldParagraphs().AppendChild(new wxRichTextParagraph(*para));
+ }
+ else
+ newPara = para;
+
+ if (paragraphStyle)
+ wxRichTextApplyStyle(newPara->GetAttributes(), style);
+
+ if (characterStyle && range.GetStart() != newPara->GetRange().GetEnd())
+ {
+ wxRichTextRange childRange(range);
+ childRange.LimitTo(newPara->GetRange());
+
+ // Find the starting position and if necessary split it so
+ // we can start applying a different style.
+ // TODO: check that the style actually changes or is different
+ // from style outside of range
+ wxRichTextObject* firstObject = NULL;
+ wxRichTextObject* lastObject = NULL;
+
+ if (childRange.GetStart() == newPara->GetRange().GetStart())
+ firstObject = newPara->GetChildren().GetFirst()->GetData();
+ else
+ firstObject = newPara->SplitAt(range.GetStart());
+
+ // Increment by 1 because we're apply the style one _after_ the split point
+ long splitPoint = childRange.GetEnd();
+ if (splitPoint != newPara->GetRange().GetEnd())
+ splitPoint ++;
+
+ // Find last object
+ if (splitPoint == newPara->GetRange().GetEnd() || splitPoint == (newPara->GetRange().GetEnd() - 1))
+ lastObject = newPara->GetChildren().GetLast()->GetData();
+ else
+ // lastObject is set as a side-effect of splitting. It's
+ // returned as the object before the new object.
+ (void) newPara->SplitAt(splitPoint, & lastObject);
+
+ wxASSERT(firstObject != NULL);
+ wxASSERT(lastObject != NULL);
+
+ if (!firstObject || !lastObject)
+ continue;
+
+ wxRichTextObjectList::compatibility_iterator firstNode = newPara->GetChildren().Find(firstObject);
+ wxRichTextObjectList::compatibility_iterator lastNode = newPara->GetChildren().Find(lastObject);
+
+ wxASSERT(firstNode != NULL);
+ wxASSERT(lastNode != NULL);
+
+ wxRichTextObjectList::compatibility_iterator node2 = firstNode;
+
+ while (node2)
+ {
+ wxRichTextObject* child = node2->GetData();
+
+ wxRichTextApplyStyle(child->GetAttributes(), style);
+ if (node2 == lastNode)
+ break;
+
+ node2 = node2->GetNext();
+ }
+ }
+ }
+ }
+
+ node = node->GetNext();
+ }
+
+ // Do action, or delay it until end of batch.
+ if (haveControl && withUndo)
+ GetRichTextCtrl()->GetBuffer().SubmitAction(action);
+
+ return true;
+}
+
+/// Set text attributes
+bool wxRichTextParagraphLayoutBox::SetStyle(const wxRichTextRange& range, const wxTextAttrEx& style, bool withUndo)
+{
+ wxRichTextAttr richStyle = style;
+ return SetStyle(range, richStyle, withUndo);
+}
+
+/// Get the text attributes for this position.
+bool wxRichTextParagraphLayoutBox::GetStyle(long position, wxTextAttrEx& style) const
+{
+ wxRichTextObject* obj = NULL;
+ if (style.IsParagraphStyle())
+ obj = GetParagraphAtPosition(position);
+ else
+ obj = GetLeafObjectAtPosition(position);
+ if (obj)
+ {
+ style = obj->GetAttributes();
+ return true;
+ }
+ else
+ return false;
+}
+
+/// Get the text attributes for this position.
+bool wxRichTextParagraphLayoutBox::GetStyle(long position, wxRichTextAttr& style) const
+{
+ wxRichTextObject* obj = NULL;
+ if (style.IsParagraphStyle())
+ obj = GetParagraphAtPosition(position);
+ else
+ obj = GetLeafObjectAtPosition(position);
+ if (obj)
+ {
+ style = obj->GetAttributes();
+ return true;
+ }
+ else
+ return false;
+}
+
+/// Set default style
+bool wxRichTextParagraphLayoutBox::SetDefaultStyle(const wxTextAttrEx& style)
+{
+ m_defaultAttributes = style;
+
+ return true;
+}
+
+/// Test if this whole range has character attributes of the specified kind. If any
+/// of the attributes are different within the range, the test fails. You
+/// can use this to implement, for example, bold button updating. style must have
+/// flags indicating which attributes are of interest.
+bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
+{
+ int foundCount = 0;
+ int matchingCount = 0;
+
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ while (node)
+ {
+ wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
+ wxASSERT (para != NULL);
+
+ if (para)
+ {
+ // Stop searching if we're beyond the range of interest
+ if (para->GetRange().GetStart() > range.GetEnd())
+ return foundCount == matchingCount;
+
+ if (!para->GetRange().IsOutside(range))
+ {
+ wxRichTextObjectList::compatibility_iterator node2 = para->GetChildren().GetFirst();
+
+ while (node2)
+ {
+ wxRichTextObject* child = node2->GetData();
+ if (!child->GetRange().IsOutside(range) && child->IsKindOf(CLASSINFO(wxRichTextPlainText)))
+ {
+ foundCount ++;
+ if (wxTextAttrEqPartial(child->GetAttributes(), style, style.GetFlags()))
+ matchingCount ++;
+ }
+
+ node2 = node2->GetNext();
+ }
+ }
+ }
+
+ node = node->GetNext();
+ }
+
+ return foundCount == matchingCount;
+}
+
+bool wxRichTextParagraphLayoutBox::HasCharacterAttributes(const wxRichTextRange& range, const wxTextAttrEx& style) const
+{
+ wxRichTextAttr richStyle = style;
+ return HasCharacterAttributes(range, richStyle);
+}
+
+/// Test if this whole range has paragraph attributes of the specified kind. If any
+/// of the attributes are different within the range, the test fails. You
+/// can use this to implement, for example, centering button updating. style must have
+/// flags indicating which attributes are of interest.
+bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange& range, const wxRichTextAttr& style) const
+{
+ int foundCount = 0;
+ int matchingCount = 0;
+
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ while (node)
+ {
+ wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
+ wxASSERT (para != NULL);
+
+ if (para)
+ {
+ // Stop searching if we're beyond the range of interest
+ if (para->GetRange().GetStart() > range.GetEnd())
+ return foundCount == matchingCount;
+
+ if (!para->GetRange().IsOutside(range))
+ {
+ foundCount ++;
+ if (wxTextAttrEqPartial(para->GetAttributes(), style, style.GetFlags()))
+ matchingCount ++;
+ }
+ }
+
+ node = node->GetNext();
+ }
+ return foundCount == matchingCount;
+}
+
+bool wxRichTextParagraphLayoutBox::HasParagraphAttributes(const wxRichTextRange& range, const wxTextAttrEx& style) const
+{
+ wxRichTextAttr richStyle = style;
+ return HasParagraphAttributes(range, richStyle);
+}
+
+void wxRichTextParagraphLayoutBox::Clear()
+{
+ DeleteChildren();
+}
+
+void wxRichTextParagraphLayoutBox::Reset()
+{
+ Clear();
+
+ AddParagraph(wxT(""));
+}
+
+/*!
+ * wxRichTextFragment class declaration
+ * This is a lind of paragraph layout box used for storing
+ * paragraphs for Undo/Redo, for example.
+ */
+
+IMPLEMENT_DYNAMIC_CLASS(wxRichTextFragment, wxRichTextParagraphLayoutBox)
+
+/// Initialise
+void wxRichTextFragment::Init()
+{
+ m_partialParagraph = false;
+}
+
+/// Copy
+void wxRichTextFragment::Copy(const wxRichTextFragment& obj)
+{
+ wxRichTextParagraphLayoutBox::Copy(obj);
+
+ m_partialParagraph = obj.m_partialParagraph;
+}
+
+/*!
+ * wxRichTextParagraph
+ * This object represents a single paragraph (or in a straight text editor, a line).
+ */
+
+IMPLEMENT_DYNAMIC_CLASS(wxRichTextParagraph, wxRichTextBox)
+
+wxRichTextParagraph::wxRichTextParagraph(wxRichTextObject* parent, wxTextAttrEx* style):
+ wxRichTextBox(parent)
+{
+ if (parent && !style)
+ SetAttributes(parent->GetAttributes());
+ if (style)
+ SetAttributes(*style);
+}
+
+wxRichTextParagraph::wxRichTextParagraph(const wxString& text, wxRichTextObject* parent, wxTextAttrEx* style):
+ wxRichTextBox(parent)
+{
+ if (parent && !style)
+ SetAttributes(parent->GetAttributes());
+ if (style)
+ SetAttributes(*style);
+
+ AppendChild(new wxRichTextPlainText(text, this));
+}
+
+wxRichTextParagraph::~wxRichTextParagraph()
+{
+ ClearLines();
+}
+
+/// Draw the item
+bool wxRichTextParagraph::Draw(wxDC& dc, const wxRichTextRange& WXUNUSED(range), const wxRichTextRange& selectionRange, const wxRect& WXUNUSED(rect), int WXUNUSED(descent), int style)
+{
+ // Draw the bullet, if any
+ if (GetAttributes().GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
+ {
+ if (GetAttributes().GetLeftSubIndent() != 0)
+ {
+ int spaceBeforePara = ConvertTenthsMMToPixels(dc, GetAttributes().GetParagraphSpacingBefore());
+ // int spaceAfterPara = ConvertTenthsMMToPixels(dc, GetAttributes().GetParagraphSpacingAfter());
+ int leftIndent = ConvertTenthsMMToPixels(dc, GetAttributes().GetLeftIndent());
+ // int leftSubIndent = ConvertTenthsMMToPixels(dc, GetAttributes().GetLeftSubIndent());
+ // int rightIndent = ConvertTenthsMMToPixels(dc, GetAttributes().GetRightIndent());
+
+ if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP)
+ {
+ // TODO
+ }
+ else
+ {
+ wxString bulletText = GetBulletText();
+ if (!bulletText.IsEmpty())
+ {
+ if (GetAttributes().GetFont().Ok())
+ dc.SetFont(GetAttributes().GetFont());
+
+ if (GetAttributes().GetTextColour().Ok())
+ dc.SetTextForeground(GetAttributes().GetTextColour());
+
+ dc.SetBackgroundMode(wxTRANSPARENT);
+
+ // Get line height from first line, if any
+ wxRichTextLine* line = m_cachedLines.GetFirst() ? (wxRichTextLine* ) m_cachedLines.GetFirst()->GetData() : (wxRichTextLine*) NULL;
+
+ wxPoint linePos;
+ int lineHeight = 0;
+ if (line)
+ {
+ lineHeight = line->GetSize().y;
+ linePos = line->GetPosition() + GetPosition();
+ }
+ else
+ {
+ lineHeight = dc.GetCharHeight();
+ linePos = GetPosition();
+ linePos.y += spaceBeforePara;
+ }
+
+ int charHeight = dc.GetCharHeight();
+
+ int x = GetPosition().x + leftIndent;
+ int y = linePos.y + (lineHeight - charHeight);
+
+ dc.DrawText(bulletText, x, y);
+ }
+ }
+ }
+ }
+
+ // Draw the range for each line, one object at a time.
+
+ wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
+ while (node)
+ {
+ wxRichTextLine* line = node->GetData();
+
+ int maxDescent = line->GetDescent();
+
+ // Lines are specified relative to the paragraph
+
+ wxPoint linePosition = line->GetPosition() + GetPosition();
+ wxPoint objectPosition = linePosition;
+
+ // Loop through objects until we get to the one within range
+ wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
+ while (node2)
+ {
+ wxRichTextObject* child = node2->GetData();
+ if (!child->GetRange().IsOutside(line->GetRange()))
+ {
+ // Draw this part of the line at the correct position
+ wxRichTextRange objectRange(child->GetRange());
+ objectRange.LimitTo(line->GetRange());
+
+ wxSize objectSize;
+ int descent = 0;
+ child->GetRangeSize(objectRange, objectSize, descent, dc, wxRICHTEXT_UNFORMATTED);
+
+ // Use the child object's width, but the whole line's height
+ wxRect childRect(objectPosition, wxSize(objectSize.x, line->GetSize().y));
+ child->Draw(dc, objectRange, selectionRange, childRect, maxDescent, style);
+
+ objectPosition.x += objectSize.x;
+ }
+ else if (child->GetRange().GetStart() > line->GetRange().GetEnd())
+ // Can break out of inner loop now since we've passed this line's range
+ break;
+
+ node2 = node2->GetNext();
+ }
+
+ node = node->GetNext();
+ }
+
+ return true;
+}
+
+/// Lay the item out
+bool wxRichTextParagraph::Layout(wxDC& dc, const wxRect& rect, int style)
+{
+ ClearLines();
+
+ // Increase the size of the paragraph due to spacing
+ int spaceBeforePara = ConvertTenthsMMToPixels(dc, GetAttributes().GetParagraphSpacingBefore());
+ int spaceAfterPara = ConvertTenthsMMToPixels(dc, GetAttributes().GetParagraphSpacingAfter());
+ int leftIndent = ConvertTenthsMMToPixels(dc, GetAttributes().GetLeftIndent());
+ int leftSubIndent = ConvertTenthsMMToPixels(dc, GetAttributes().GetLeftSubIndent());
+ int rightIndent = ConvertTenthsMMToPixels(dc, GetAttributes().GetRightIndent());
+
+ int lineSpacing = 0;
+
+ // Let's assume line spacing of 10 is normal, 15 is 1.5, 20 is 2, etc.
+ if (GetAttributes().GetLineSpacing() > 10 && GetAttributes().GetFont().Ok())
+ {
+ dc.SetFont(GetAttributes().GetFont());
+ lineSpacing = (ConvertTenthsMMToPixels(dc, dc.GetCharHeight()) * GetAttributes().GetLineSpacing())/10;
+ }
+
+ // Available space for text on each line differs.
+ int availableTextSpaceFirstLine = rect.GetWidth() - leftIndent - rightIndent;
+
+ // Bullets start the text at the same position as subsequent lines
+ if (GetAttributes().GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
+ availableTextSpaceFirstLine -= leftSubIndent;
+
+ int availableTextSpaceSubsequentLines = rect.GetWidth() - leftIndent - rightIndent - leftSubIndent;
+
+ // Start position for each line relative to the paragraph
+ int startPositionFirstLine = leftIndent;
+ int startPositionSubsequentLines = leftIndent + leftSubIndent;
+
+ // If we have a bullet in this paragraph, the start position for the first line's text
+ // is actually leftIndent + leftSubIndent.
+ if (GetAttributes().GetBulletStyle() != wxTEXT_ATTR_BULLET_STYLE_NONE)
+ startPositionFirstLine = startPositionSubsequentLines;
+
+ //bool restrictWidth = wxRichTextHasStyle(style, wxRICHTEXT_FIXED_WIDTH);
+ //bool restrictHeight = wxRichTextHasStyle(style, wxRICHTEXT_FIXED_HEIGHT);
+
+ long lastEndPos = GetRange().GetStart()-1;
+ long lastCompletedEndPos = lastEndPos;
+
+ int currentWidth = 0;
+ SetPosition(rect.GetPosition());
+
+ wxPoint currentPosition(0, spaceBeforePara); // We will calculate lines relative to paragraph
+ int lineHeight = 0;
+ int maxWidth = 0;
+ int maxDescent = 0;
+
+ int lineCount = 0;
+
+ // Split up lines
+
+ // We may need to go back to a previous child, in which case create the new line,
+ // find the child corresponding to the start position of the string, and
+ // continue.
+
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ while (node)
+ {
+ wxRichTextObject* child = node->GetData();
+
+ // If this is e.g. a composite text box, it will need to be laid out itself.
+ // But if just a text fragment or image, for example, this will
+ // do nothing. NB: won't we need to set the position after layout?
+ // since for example if position is dependent on vertical line size, we
+ // can't tell the position until the size is determined. So possibly introduce
+ // another layout phase.
+
+ child->Layout(dc, rect, style);
+
+ // Available width depends on whether we're on the first or subsequent lines
+ int availableSpaceForText = (lineCount == 0 ? availableTextSpaceFirstLine : availableTextSpaceSubsequentLines);
+
+ currentPosition.x = (lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines);
+
+ // We may only be looking at part of a child, if we searched back for wrapping
+ // and found a suitable point some way into the child. So get the size for the fragment
+ // if necessary.
+
+ wxSize childSize;
+ int childDescent = 0;
+ if (lastEndPos == child->GetRange().GetStart() - 1)
+ {
+ childSize = child->GetCachedSize();
+ childDescent = child->GetDescent();
+ }
+ else
+ GetRangeSize(wxRichTextRange(lastEndPos+1, child->GetRange().GetEnd()), childSize, childDescent, dc, wxRICHTEXT_UNFORMATTED);
+
+ if (childSize.x + currentWidth > availableSpaceForText)
+ {
+ long wrapPosition = 0;
+
+ // Find a place to wrap. This may walk back to previous children,
+ // for example if a word spans several objects.
+ if (!FindWrapPosition(wxRichTextRange(lastCompletedEndPos+1, child->GetRange().GetEnd()), dc, availableSpaceForText, wrapPosition))
+ {
+ // If the function failed, just cut it off at the end of this child.
+ wrapPosition = child->GetRange().GetEnd();
+ }
+
+ // FindWrapPosition can still return a value that will put us in an endless wrapping loop
+ if (wrapPosition <= lastCompletedEndPos)
+ wrapPosition = wxMax(lastCompletedEndPos+1,child->GetRange().GetEnd());
+
+ // wxLogDebug(wxT("Split at %ld"), wrapPosition);
+
+ // Let's find the actual size of the current line now
+ wxSize actualSize;
+ wxRichTextRange actualRange(lastCompletedEndPos+1, wrapPosition);
+ GetRangeSize(actualRange, actualSize, childDescent, dc, wxRICHTEXT_UNFORMATTED);
+ currentWidth = actualSize.x;
+ lineHeight = wxMax(lineHeight, actualSize.y);
+ maxDescent = wxMax(childDescent, maxDescent);
+
+ // Add a new line
+ wxRichTextLine* line = new wxRichTextLine(this);
+ line->SetRange(actualRange);
+ line->SetPosition(currentPosition);
+ line->SetSize(wxSize(currentWidth, lineHeight));
+ line->SetDescent(maxDescent);
+
+ m_cachedLines.Append(line);
+
+ // Now move down a line. TODO: add margins, spacing
+ currentPosition.y += lineHeight;
+ currentPosition.y += lineSpacing;
+ currentWidth = 0;
+ maxDescent = 0;
+ maxWidth = wxMax(maxWidth, currentWidth);
+
+ lineCount ++;
+
+ // TODO: account for zero-length objects, such as fields
+ wxASSERT(wrapPosition > lastCompletedEndPos);
+
+ lastEndPos = wrapPosition;
+ lastCompletedEndPos = lastEndPos;
+
+ lineHeight = 0;
+
+ // May need to set the node back to a previous one, due to searching back in wrapping
+ wxRichTextObject* childAfterWrapPosition = FindObjectAtPosition(wrapPosition+1);
+ if (childAfterWrapPosition)
+ node = m_children.Find(childAfterWrapPosition);
+ else
+ node = node->GetNext();
+ }
+ else
+ {
+ // We still fit, so don't add a line, and keep going
+ currentWidth += childSize.x;
+ lineHeight = wxMax(lineHeight, childSize.y);
+ maxDescent = wxMax(childDescent, maxDescent);
+
+ maxWidth = wxMax(maxWidth, currentWidth);
+ lastEndPos = child->GetRange().GetEnd();
+
+ node = node->GetNext();
+ }
+ }
+
+ // Add the last line - it's the current pos -> last para pos
+ // Substract -1 because the last position is always the end-paragraph position.
+ if (lastCompletedEndPos <= GetRange().GetEnd()-1)
+ {
+ currentPosition.x = (lineCount == 0 ? startPositionFirstLine : startPositionSubsequentLines);
+
+ wxRichTextLine* line = new wxRichTextLine(this);
+
+ line->SetRange(lastCompletedEndPos+1, GetRange().GetEnd()-1);
+ line->SetPosition(currentPosition);
+
+ if (lineHeight == 0)
+ {
+ if (GetAttributes().GetFont().Ok())
+ dc.SetFont(GetAttributes().GetFont());
+ lineHeight = dc.GetCharHeight();
+ }
+ if (maxDescent == 0)
+ {
+ int w, h;
+ dc.GetTextExtent(wxT("X"), & w, &h, & maxDescent);
+ }
+
+ line->SetSize(wxSize(currentWidth, lineHeight));
+ line->SetDescent(maxDescent);
+ currentPosition.y += lineHeight;
+ currentPosition.y += lineSpacing;
+ lineCount ++;
+
+ m_cachedLines.Append(line);
+ }
+
+ // Apply styles to wrapped lines
+ ApplyParagraphStyle(rect);
+
+ SetCachedSize(wxSize(maxWidth, currentPosition.y + spaceBeforePara + spaceAfterPara));
+
+ m_dirty = false;
+
+ return true;
+}
+
+/// Apply paragraph styles, such as centering, to wrapped lines
+void wxRichTextParagraph::ApplyParagraphStyle(const wxRect& rect)
+{
+ if (!GetAttributes().HasAlignment())
+ return;
+
+ wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
+ while (node)
+ {
+ wxRichTextLine* line = node->GetData();
+
+ wxPoint pos = line->GetPosition();
+ wxSize size = line->GetSize();
+
+ // centering, right-justification
+ if (GetAttributes().HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
+ {
+ pos.x = (rect.GetWidth() - size.x)/2 + pos.x;
+ line->SetPosition(pos);
+ }
+ else if (GetAttributes().HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_RIGHT)
+ {
+ pos.x = rect.GetRight() - size.x;
+ line->SetPosition(pos);
+ }
+
+ node = node->GetNext();
+ }
+}
+
+/// Insert text at the given position
+bool wxRichTextParagraph::InsertText(long pos, const wxString& text)
+{
+ wxRichTextObject* childToUse = NULL;
+ wxRichTextObjectList::compatibility_iterator nodeToUse = NULL;
+
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ while (node)
+ {
+ wxRichTextObject* child = node->GetData();
+ if (child->GetRange().Contains(pos) && child->GetRange().GetLength() > 0)
+ {
+ childToUse = child;
+ nodeToUse = node;
+ break;
+ }
+
+ node = node->GetNext();
+ }
+
+ if (childToUse)
+ {
+ wxRichTextPlainText* textObject = wxDynamicCast(childToUse, wxRichTextPlainText);
+ if (textObject)
+ {
+ int posInString = pos - textObject->GetRange().GetStart();
+
+ wxString newText = textObject->GetText().Mid(0, posInString) +
+ text + textObject->GetText().Mid(posInString);
+ textObject->SetText(newText);
+
+ int textLength = text.Length();
+
+ textObject->SetRange(wxRichTextRange(textObject->GetRange().GetStart(),
+ textObject->GetRange().GetEnd() + textLength));
+
+ // Increment the end range of subsequent fragments in this paragraph.
+ // We'll set the paragraph range itself at a higher level.
+
+ wxRichTextObjectList::compatibility_iterator node = nodeToUse->GetNext();
+ while (node)
+ {
+ wxRichTextObject* child = node->GetData();
+ child->SetRange(wxRichTextRange(textObject->GetRange().GetStart() + textLength,
+ textObject->GetRange().GetEnd() + textLength));
+
+ node = node->GetNext();
+ }
+
+ return true;
+ }
+ else
+ {
+ // TODO: if not a text object, insert at closest position, e.g. in front of it
+ }
+ }
+ else
+ {
+ // Add at end.
+ // Don't pass parent initially to suppress auto-setting of parent range.
+ // We'll do that at a higher level.
+ wxRichTextPlainText* textObject = new wxRichTextPlainText(text, this);
+
+ AppendChild(textObject);
+ return true;
+ }
+
+ return false;
+}
+
+void wxRichTextParagraph::Copy(const wxRichTextParagraph& obj)
+{
+ wxRichTextBox::Copy(obj);
+}
+
+/// Clear the cached lines
+void wxRichTextParagraph::ClearLines()
+{
+ WX_CLEAR_LIST(wxRichTextLineList, m_cachedLines);
+}
+
+/// Get/set the object size for the given range. Returns false if the range
+/// is invalid for this object.
+bool wxRichTextParagraph::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int flags) const
+{
+ if (!range.IsWithin(GetRange()))
+ return false;
+
+ if (flags & wxRICHTEXT_UNFORMATTED)
+ {
+ // Just use unformatted data, assume no line breaks
+ // TODO: take into account line breaks
+
+ wxSize sz;
+
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ while (node)
+ {
+ wxRichTextObject* child = node->GetData();
+ if (!child->GetRange().IsOutside(range))
+ {
+ wxSize childSize;
+
+ wxRichTextRange rangeToUse = range;
+ rangeToUse.LimitTo(child->GetRange());
+ int childDescent = 0;
+
+ if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, flags))
+ {
+ sz.y = wxMax(sz.y, childSize.y);
+ sz.x += childSize.x;
+ descent = wxMax(descent, childDescent);
+ }
+ }
+
+ node = node->GetNext();
+ }
+ size = sz;
+ }
+ else
+ {
+ // Use formatted data, with line breaks
+ wxSize sz;
+
+ // We're going to loop through each line, and then for each line,
+ // call GetRangeSize for the fragment that comprises that line.
+ // Only we have to do that multiple times within the line, because
+ // the line may be broken into pieces. For now ignore line break commands
+ // (so we can assume that getting the unformatted size for a fragment
+ // within a line is the actual size)
+
+ wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
+ while (node)
+ {
+ wxRichTextLine* line = node->GetData();
+ if (!line->GetRange().IsOutside(range))
+ {
+ wxSize lineSize;
+
+ wxRichTextObjectList::compatibility_iterator node2 = m_children.GetFirst();
+ while (node2)
+ {
+ wxRichTextObject* child = node2->GetData();
+
+ if (!child->GetRange().IsOutside(line->GetRange()))
+ {
+ wxRichTextRange rangeToUse = line->GetRange();
+ rangeToUse.LimitTo(child->GetRange());
+
+ wxSize childSize;
+ int childDescent = 0;
+ if (child->GetRangeSize(rangeToUse, childSize, childDescent, dc, flags))
+ {
+ lineSize.y = wxMax(lineSize.y, childSize.y);
+ lineSize.x += childSize.x;
+ }
+ descent = wxMax(descent, childDescent);
+ }
+
+ node2 = node2->GetNext();
+ }
+
+ // Increase size by a line (TODO: paragraph spacing)
+ sz.y += lineSize.y;
+ sz.x = wxMax(sz.x, lineSize.x);
+ }
+ node = node->GetNext();
+ }
+ size = sz;
+ }
+ return true;
+}
+
+/// Finds the absolute position and row height for the given character position
+bool wxRichTextParagraph::FindPosition(wxDC& dc, long index, wxPoint& pt, int* height, bool forceLineStart)
+{
+ if (index == -1)
+ {
+ wxRichTextLine* line = ((wxRichTextParagraphLayoutBox*)GetParent())->GetLineAtPosition(0);
+ if (line)
+ *height = line->GetSize().y;
+ else
+ *height = dc.GetCharHeight();
+
+ // -1 means 'the start of the buffer'.
+ pt = GetPosition();
+ if (line)
+ pt = pt + line->GetPosition();
+
+ *height = dc.GetCharHeight();
+
+ return true;
+ }
+
+ // The final position in a paragraph is taken to mean the position
+ // at the start of the next paragraph.
+ if (index == GetRange().GetEnd())
+ {
+ wxRichTextParagraphLayoutBox* parent = wxDynamicCast(GetParent(), wxRichTextParagraphLayoutBox);
+ wxASSERT( parent != NULL );
+
+ // Find the height at the next paragraph, if any
+ wxRichTextLine* line = parent->GetLineAtPosition(index + 1);
+ if (line)
+ {
+ *height = line->GetSize().y;
+ pt = line->GetAbsolutePosition();
+ }
+ else
+ {
+ *height = dc.GetCharHeight();
+ int indent = ConvertTenthsMMToPixels(dc, m_attributes.GetLeftIndent());
+ pt = wxPoint(indent, GetCachedSize().y);
+ }
+
+ return true;
+ }
+
+ if (index < GetRange().GetStart() || index > GetRange().GetEnd())
+ return false;
+
+ wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
+ while (node)
+ {
+ wxRichTextLine* line = node->GetData();
+ if (index >= line->GetRange().GetStart() && index <= line->GetRange().GetEnd())
+ {
+ // If this is the last point in the line, and we're forcing the
+ // returned value to be the start of the next line, do the required
+ // thing.
+ if (index == line->GetRange().GetEnd() && forceLineStart)
+ {
+ if (node->GetNext())
+ {
+ wxRichTextLine* nextLine = node->GetNext()->GetData();
+ *height = nextLine->GetSize().y;
+ pt = nextLine->GetAbsolutePosition();
+ return true;
+ }
+ }
+
+ pt.y = line->GetPosition().y + GetPosition().y;
+
+ wxRichTextRange r(line->GetRange().GetStart(), index);
+ wxSize rangeSize;
+ int descent = 0;
+
+ // We find the size of the line up to this point,
+ // then we can add this size to the line start position and
+ // paragraph start position to find the actual position.
+
+ if (GetRangeSize(r, rangeSize, descent, dc, wxRICHTEXT_UNFORMATTED))
+ {
+ pt.x = line->GetPosition().x + GetPosition().x + rangeSize.x;
+ *height = line->GetSize().y;
+
+ return true;
+ }
+
+ }
+
+ node = node->GetNext();
+ }
+
+ return false;
+}
+
+/// Hit-testing: returns a flag indicating hit test details, plus
+/// information about position
+int wxRichTextParagraph::HitTest(wxDC& dc, const wxPoint& pt, long& textPosition)
+{
+ wxPoint paraPos = GetPosition();
+
+ wxRichTextLineList::compatibility_iterator node = m_cachedLines.GetFirst();
+ while (node)
+ {
+ wxRichTextLine* line = node->GetData();
+ wxPoint linePos = paraPos + line->GetPosition();
+ wxSize lineSize = line->GetSize();
+
+ if (pt.y >= linePos.y && pt.y <= linePos.y + lineSize.y)
+ {
+ if (pt.x < linePos.x)
+ {
+ textPosition = line->GetRange().GetStart();
+ return wxRICHTEXT_HITTEST_BEFORE;
+ }
+ else if (pt.x >= (linePos.x + lineSize.x))
+ {
+ textPosition = line->GetRange().GetEnd();
+ return wxRICHTEXT_HITTEST_AFTER;
+ }
+ else
+ {
+ long i;
+ int lastX = linePos.x;
+ for (i = line->GetRange().GetStart(); i <= line->GetRange().GetEnd(); i++)
+ {
+ wxSize childSize;
+ int descent = 0;
+
+ wxRichTextRange rangeToUse(line->GetRange().GetStart(), i);
+
+ GetRangeSize(rangeToUse, childSize, descent, dc, wxRICHTEXT_UNFORMATTED);
+
+ int nextX = childSize.x + linePos.x;
+
+ if (pt.x >= lastX && pt.x <= nextX)
+ {
+ textPosition = i;
+
+ // So now we know it's between i-1 and i.
+ // Let's see if we can be more precise about
+ // which side of the position it's on.
+
+ int midPoint = (nextX - lastX)/2 + lastX;
+ if (pt.x >= midPoint)
+ return wxRICHTEXT_HITTEST_AFTER;
+ else
+ return wxRICHTEXT_HITTEST_BEFORE;
+ }
+ else
+ {
+ lastX = nextX;
+ }
+ }
+ }
+ }
+
+ node = node->GetNext();
+ }
+
+ return wxRICHTEXT_HITTEST_NONE;
+}
+
+/// Split an object at this position if necessary, and return
+/// the previous object, or NULL if inserting at beginning.
+wxRichTextObject* wxRichTextParagraph::SplitAt(long pos, wxRichTextObject** previousObject)
+{
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ while (node)
+ {
+ wxRichTextObject* child = node->GetData();
+
+ if (pos == child->GetRange().GetStart())
+ {
+ if (previousObject)
+ *previousObject = child;
+
+ return child;
+ }
+
+ if (child->GetRange().Contains(pos))
+ {
+ // This should create a new object, transferring part of
+ // the content to the old object and the rest to the new object.
+ wxRichTextObject* newObject = child->DoSplit(pos);
+
+ // If we couldn't split this object, just insert in front of it.
+ if (!newObject)
+ {
+ // Maybe this is an empty string, try the next one
+ // return child;
+ }
+ else
+ {
+ // Insert the new object after 'child'
+ if (node->GetNext())
+ m_children.Insert(node->GetNext(), newObject);
+ else
+ m_children.Append(newObject);
+ newObject->SetParent(this);
+
+ if (previousObject)
+ *previousObject = child;
+
+ return newObject;
+ }
+ }
+
+ node = node->GetNext();
+ }
+ if (previousObject)
+ *previousObject = NULL;
+ return NULL;
+}
+
+/// Move content to a list from obj on
+void wxRichTextParagraph::MoveToList(wxRichTextObject* obj, wxList& list)
+{
+ wxRichTextObjectList::compatibility_iterator node = m_children.Find(obj);
+ while (node)
+ {
+ wxRichTextObject* child = node->GetData();
+ list.Append(child);
+
+ wxRichTextObjectList::compatibility_iterator oldNode = node;
+
+ node = node->GetNext();
+
+ m_children.DeleteNode(oldNode);
+ }
+}
+
+/// Add content back from list
+void wxRichTextParagraph::MoveFromList(wxList& list)
+{
+ for (wxNode* node = list.GetFirst(); node; node = node->GetNext())
+ {
+ AppendChild((wxRichTextObject*) node->GetData());
+ }
+}
+
+/// Calculate range
+void wxRichTextParagraph::CalculateRange(long start, long& end)
+{
+ wxRichTextCompositeObject::CalculateRange(start, end);
+
+ // Add one for end of paragraph
+ end ++;
+
+ m_range.SetRange(start, end);
+}
+
+/// Find the object at the given position
+wxRichTextObject* wxRichTextParagraph::FindObjectAtPosition(long position)
+{
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ while (node)
+ {
+ wxRichTextObject* obj = node->GetData();
+ if (obj->GetRange().Contains(position))
+ return obj;
+
+ node = node->GetNext();
+ }
+ return NULL;
+}
+
+/// Get the plain text searching from the start or end of the range.
+/// The resulting string may be shorter than the range given.
+bool wxRichTextParagraph::GetContiguousPlainText(wxString& text, const wxRichTextRange& range, bool fromStart)
+{
+ text = wxEmptyString;
+
+ if (fromStart)
+ {
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetFirst();
+ while (node)
+ {
+ wxRichTextObject* obj = node->GetData();
+ if (!obj->GetRange().IsOutside(range))
+ {
+ wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
+ if (textObj)
+ {
+ text += textObj->GetTextForRange(range);
+ }
+ else
+ return true;
+ }
+
+ node = node->GetNext();
+ }
+ }
+ else
+ {
+ wxRichTextObjectList::compatibility_iterator node = m_children.GetLast();
+ while (node)
+ {
+ wxRichTextObject* obj = node->GetData();
+ if (!obj->GetRange().IsOutside(range))
+ {
+ wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
+ if (textObj)
+ {
+ text = textObj->GetTextForRange(range) + text;
+ }
+ else
+ return true;
+ }
+
+ node = node->GetPrevious();
+ }
+ }
+
+ return true;
+}
+
+/// Find a suitable wrap position.
+bool wxRichTextParagraph::FindWrapPosition(const wxRichTextRange& range, wxDC& dc, int availableSpace, long& wrapPosition)
+{
+ // Find the first position where the line exceeds the available space.
+ wxSize sz;
+ long i;
+ long breakPosition = range.GetEnd();
+ for (i = range.GetStart(); i <= range.GetEnd(); i++)
+ {
+ int descent = 0;
+ GetRangeSize(wxRichTextRange(range.GetStart(), i), sz, descent, dc, wxRICHTEXT_UNFORMATTED);
+
+ if (sz.x > availableSpace)
+ {
+ breakPosition = i-1;
+ break;
+ }
+ }
+
+ // Now we know the last position on the line.
+ // Let's try to find a word break.
+
+ wxString plainText;
+ if (GetContiguousPlainText(plainText, wxRichTextRange(range.GetStart(), breakPosition), false))
+ {
+ int spacePos = plainText.Find(wxT(' '), true);
+ if (spacePos != wxNOT_FOUND)
+ {
+ int positionsFromEndOfString = plainText.Length() - spacePos - 1;
+ breakPosition = breakPosition - positionsFromEndOfString;
+ }
+ }
+
+ wrapPosition = breakPosition;
+
+ return true;
+}
+
+/// Get the bullet text for this paragraph.
+wxString wxRichTextParagraph::GetBulletText()
+{
+ if (GetAttributes().GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_NONE ||
+ (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_BITMAP))
+ return wxEmptyString;
+
+ int number = GetAttributes().GetBulletNumber();
+
+ wxString text;
+ if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ARABIC)
+ {
+ text.Printf(wxT("%d"), number);
+ }
+ else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER)
+ {
+ // TODO: Unicode, and also check if number > 26
+ text.Printf(wxT("%c"), (wxChar) (number+64));
+ }
+ else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER)
+ {
+ // TODO: Unicode, and also check if number > 26
+ text.Printf(wxT("%c"), (wxChar) (number+96));
+ }
+ else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER)
+ {
+ // TODO: convert from number to roman numeral
+ if (number == 1)
+ text = wxT("I");
+ else if (number == 2)
+ text = wxT("II");
+ else if (number == 3)
+ text = wxT("III");
+ else if (number == 4)
+ text = wxT("IV");
+ else
+ text = wxT("TODO");
+ }
+ else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER)
+ {
+ // TODO: convert from number to roman numeral
+ if (number == 1)
+ text = wxT("i");
+ else if (number == 2)
+ text = wxT("ii");
+ else if (number == 3)
+ text = wxT("iii");
+ else if (number == 4)
+ text = wxT("iv");
+ else
+ text = wxT("TODO");
+ }
+ else if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_SYMBOL)
+ {
+ text = GetAttributes().GetBulletSymbol();
+ }
+
+ if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PARENTHESES)
+ {
+ text = wxT("(") + text + wxT(")");
+ }
+ if (GetAttributes().GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_PERIOD)
+ {
+ text += wxT(".");
+ }
+
+ return text;
+}
+
+
+/*!
+ * wxRichTextLine
+ * This object represents a line in a paragraph, and stores
+ * offsets from the start of the paragraph representing the
+ * start and end positions of the line.
+ */
+
+wxRichTextLine::wxRichTextLine(wxRichTextParagraph* parent)
+{
+ Init();
+
+ m_parent = parent;
+}
+
+/// Initialisation
+void wxRichTextLine::Init()
+{
+ m_parent = NULL;
+ m_descent = 0;
+}
+
+/// Copy
+void wxRichTextLine::Copy(const wxRichTextLine& obj)
+{
+ m_range = obj.m_range;
+}
+
+/// Get the absolute object position
+wxPoint wxRichTextLine::GetAbsolutePosition() const
+{
+ return m_parent->GetPosition() + m_pos;
+}
+
+/*!
+ * wxRichTextPlainText
+ * This object represents a single piece of text.
+ */
+
+IMPLEMENT_DYNAMIC_CLASS(wxRichTextPlainText, wxRichTextObject)
+
+wxRichTextPlainText::wxRichTextPlainText(const wxString& text, wxRichTextObject* parent, wxTextAttrEx* style):
+ wxRichTextObject(parent)
+{
+ if (parent && !style)
+ SetAttributes(parent->GetAttributes());
+ if (style)
+ SetAttributes(*style);
+
+ m_text = text;
+}
+
+/// Draw the item
+bool wxRichTextPlainText::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& rect, int descent, int WXUNUSED(style))
+{
+ int offset = GetRange().GetStart();
+
+ long len = range.GetLength();
+ wxString stringChunk = m_text.Mid(range.GetStart() - offset, (size_t) len);
+
+ int charHeight = dc.GetCharHeight();
+
+ int x = rect.x;
+ int y = rect.y + (rect.height - charHeight - (descent - m_descent));
+
+ // Test for the optimized situations where all is selected, or none
+ // is selected.
+
+ if (GetAttributes().GetFont().Ok())
+ dc.SetFont(GetAttributes().GetFont());
+
+ // (a) All selected.
+ if (selectionRange.GetStart() <= range.GetStart() && selectionRange.GetEnd() >= range.GetEnd())
+ {
+ // Draw all selected
+ dc.SetBrush(*wxBLACK_BRUSH);
+ dc.SetPen(*wxBLACK_PEN);
+ wxCoord w, h;
+ dc.GetTextExtent(stringChunk, & w, & h);
+ wxRect selRect(x, rect.y, w, rect.GetHeight());
+ dc.DrawRectangle(selRect);
+ dc.SetTextForeground(*wxWHITE);
+ dc.SetBackgroundMode(wxTRANSPARENT);
+ dc.DrawText(stringChunk, x, y);
+ }
+ // (b) None selected.
+ else if (selectionRange.GetEnd() < range.GetStart() || selectionRange.GetStart() > range.GetEnd())
+ {
+ // Draw all unselected
+ dc.SetTextForeground(GetAttributes().GetTextColour());
+ dc.SetBackgroundMode(wxTRANSPARENT);
+ dc.DrawText(stringChunk, x, y);
+ }
+ else
+ {
+ // (c) Part selected, part not
+ // Let's draw unselected chunk, selected chunk, then unselected chunk.
+
+ dc.SetBackgroundMode(wxTRANSPARENT);
+
+ // 1. Initial unselected chunk, if any, up until start of selection.
+ if (selectionRange.GetStart() > range.GetStart() && selectionRange.GetStart() <= range.GetEnd())
+ {
+ int r1 = range.GetStart();
+ int s1 = selectionRange.GetStart()-1;
+ int fragmentLen = s1 - r1 + 1;
+ if (fragmentLen < 0)
+ wxLogDebug(wxT("Mid(%d, %d"), (int)(r1 - offset), (int)fragmentLen);
+ wxString stringFragment = m_text.Mid(r1 - offset, fragmentLen);
+
+ dc.SetTextForeground(GetAttributes().GetTextColour());
+ dc.DrawText(stringFragment, x, y);
+
+ wxCoord w, h;
+ dc.GetTextExtent(stringFragment, & w, & h);
+ x += w;
+ }
+
+ // 2. Selected chunk, if any.
+ if (selectionRange.GetEnd() >= range.GetStart())
+ {
+ int s1 = wxMax(selectionRange.GetStart(), range.GetStart());
+ int s2 = wxMin(selectionRange.GetEnd(), range.GetEnd());
+
+ int fragmentLen = s2 - s1 + 1;
+ if (fragmentLen < 0)
+ wxLogDebug(wxT("Mid(%d, %d"), (int)(s1 - offset), (int)fragmentLen);
+ wxString stringFragment = m_text.Mid(s1 - offset, fragmentLen);
+
+ wxCoord w, h;
+ dc.GetTextExtent(stringFragment, & w, & h);
+ wxRect selRect(x, rect.y, w, rect.GetHeight());
+
+ dc.SetBrush(*wxBLACK_BRUSH);
+ dc.SetPen(*wxBLACK_PEN);
+ dc.DrawRectangle(selRect);
+ dc.SetTextForeground(*wxWHITE);
+ dc.DrawText(stringFragment, x, y);
+
+ x += w;
+ }
+
+ // 3. Remaining unselected chunk, if any
+ if (selectionRange.GetEnd() < range.GetEnd())
+ {
+ int s2 = wxMin(selectionRange.GetEnd()+1, range.GetEnd());
+ int r2 = range.GetEnd();
+
+ int fragmentLen = r2 - s2 + 1;
+ if (fragmentLen < 0)
+ wxLogDebug(wxT("Mid(%d, %d"), (int)(s2 - offset), (int)fragmentLen);
+ wxString stringFragment = m_text.Mid(s2 - offset, fragmentLen);
+
+ dc.SetTextForeground(GetAttributes().GetTextColour());
+ dc.DrawText(stringFragment, x, y);
+ }
+ }
+
+ return true;
+}
+
+/// Lay the item out
+bool wxRichTextPlainText::Layout(wxDC& dc, const wxRect& WXUNUSED(rect), int WXUNUSED(style))
+{
+ if (GetAttributes().GetFont().Ok())
+ dc.SetFont(GetAttributes().GetFont());
+
+ wxCoord w, h;
+ dc.GetTextExtent(m_text, & w, & h, & m_descent);
+ m_size = wxSize(w, dc.GetCharHeight());
+
+ return true;
+}
+
+/// Copy
+void wxRichTextPlainText::Copy(const wxRichTextPlainText& obj)
+{
+ wxRichTextObject::Copy(obj);
+
+ m_text = obj.m_text;
+}
+
+/// Get/set the object size for the given range. Returns false if the range
+/// is invalid for this object.
+bool wxRichTextPlainText::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& descent, wxDC& dc, int WXUNUSED(flags)) const
+{
+ if (!range.IsWithin(GetRange()))
+ return false;
+
+ // Always assume unformatted text, since at this level we have no knowledge
+ // of line breaks - and we don't need it, since we'll calculate size within
+ // formatted text by doing it in chunks according to the line ranges
+
+ if (GetAttributes().GetFont().Ok())
+ dc.SetFont(GetAttributes().GetFont());
+
+ int startPos = range.GetStart() - GetRange().GetStart();
+ long len = range.GetLength();
+ wxString stringChunk = m_text.Mid(startPos, (size_t) len);
+ wxCoord w, h;
+ dc.GetTextExtent(stringChunk, & w, & h, & descent);
+ size = wxSize(w, dc.GetCharHeight());
+
+ return true;
+}
+
+/// Do a split, returning an object containing the second part, and setting
+/// the first part in 'this'.
+wxRichTextObject* wxRichTextPlainText::DoSplit(long pos)
+{
+ int index = pos - GetRange().GetStart();
+ if (index < 0 || index >= (int) m_text.Length())
+ return NULL;
+
+ wxString firstPart = m_text.Mid(0, index);
+ wxString secondPart = m_text.Mid(index);
+
+ m_text = firstPart;
+
+ wxRichTextPlainText* newObject = new wxRichTextPlainText(secondPart);
+ newObject->SetAttributes(GetAttributes());
+
+ newObject->SetRange(wxRichTextRange(pos, GetRange().GetEnd()));
+ GetRange().SetEnd(pos-1);
+
+ return newObject;
+}
+
+/// Calculate range
+void wxRichTextPlainText::CalculateRange(long start, long& end)
+{
+ end = start + m_text.Length() - 1;
+ m_range.SetRange(start, end);
+}
+
+/// Delete range
+bool wxRichTextPlainText::DeleteRange(const wxRichTextRange& range)
+{
+ wxRichTextRange r = range;
+
+ r.LimitTo(GetRange());
+
+ if (r.GetStart() == GetRange().GetStart() && r.GetEnd() == GetRange().GetEnd())
+ {
+ m_text.Empty();
+ return true;
+ }
+
+ long startIndex = r.GetStart() - GetRange().GetStart();
+ long len = r.GetLength();
+
+ m_text = m_text.Mid(0, startIndex) + m_text.Mid(startIndex+len);
+ return true;
+}
+
+/// Get text for the given range.
+wxString wxRichTextPlainText::GetTextForRange(const wxRichTextRange& range) const
+{
+ wxRichTextRange r = range;
+
+ r.LimitTo(GetRange());
+
+ long startIndex = r.GetStart() - GetRange().GetStart();
+ long len = r.GetLength();
+
+ return m_text.Mid(startIndex, len);
+}
+
+/// Returns true if this object can merge itself with the given one.
+bool wxRichTextPlainText::CanMerge(wxRichTextObject* object) const
+{
+ return object->GetClassInfo() == CLASSINFO(wxRichTextPlainText) &&
+ (m_text.IsEmpty() || wxTextAttrEq(GetAttributes(), object->GetAttributes()));
+}
+
+/// Returns true if this object merged itself with the given one.
+/// The calling code will then delete the given object.
+bool wxRichTextPlainText::Merge(wxRichTextObject* object)
+{
+ wxRichTextPlainText* textObject = wxDynamicCast(object, wxRichTextPlainText);
+ wxASSERT( textObject != NULL );
+
+ if (textObject)
+ {
+ m_text += textObject->GetText();
+ return true;
+ }
+ else
+ return false;
+}
+
+/// Dump to output stream for debugging
+void wxRichTextPlainText::Dump(wxTextOutputStream& stream)
+{
+ wxRichTextObject::Dump(stream);
+ stream << m_text << wxT("\n");
+}
+
+/*!
+ * wxRichTextBuffer
+ * This is a kind of box, used to represent the whole buffer
+ */
+
+IMPLEMENT_DYNAMIC_CLASS(wxRichTextBuffer, wxRichTextParagraphLayoutBox)
+
+wxList wxRichTextBuffer::sm_handlers;
+
+/// Initialisation
+void wxRichTextBuffer::Init()
+{
+ m_commandProcessor = new wxCommandProcessor;
+ m_styleSheet = NULL;
+ m_modified = false;
+ m_batchedCommandDepth = 0;
+ m_batchedCommand = NULL;
+ m_suppressUndo = 0;
+}
+
+/// Initialisation
+wxRichTextBuffer::~wxRichTextBuffer()
+{
+ delete m_commandProcessor;
+ delete m_batchedCommand;
+
+ ClearStyleStack();
+}
+
+void wxRichTextBuffer::Clear()
+{
+ DeleteChildren();
+ GetCommandProcessor()->ClearCommands();
+ Modify(false);
+}
+
+void wxRichTextBuffer::Reset()
+{
+ DeleteChildren();
+ AddParagraph(wxT(""));
+ GetCommandProcessor()->ClearCommands();
+ Modify(false);
+}
+
+/// Submit command to insert the given text
+bool wxRichTextBuffer::InsertTextWithUndo(long pos, const wxString& text, wxRichTextCtrl* ctrl)
+{
+ wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, this, ctrl, false);
+
+ action->GetNewParagraphs().AddParagraphs(text);
+ if (action->GetNewParagraphs().GetChildCount() == 1)
+ action->GetNewParagraphs().SetPartialParagraph(true);
+
+ action->SetPosition(pos);
+
+ // Set the range we'll need to delete in Undo
+ action->SetRange(wxRichTextRange(pos, pos + text.Length() - 1));
+
+ SubmitAction(action);
+
+ return true;
+}
+
+/// Submit command to insert the given text
+bool wxRichTextBuffer::InsertNewlineWithUndo(long pos, wxRichTextCtrl* ctrl)
+{
+ wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, this, ctrl, false);
+
+ wxTextAttrEx attr(GetBasicStyle());
+ wxRichTextApplyStyle(attr, GetDefaultStyle());
+
+ wxRichTextParagraph* newPara = new wxRichTextParagraph(wxT(""), this, & attr);
+ action->GetNewParagraphs().AppendChild(newPara);
+ action->GetNewParagraphs().UpdateRanges();
+ action->GetNewParagraphs().SetPartialParagraph(false);
+ action->SetPosition(pos);
+
+ // Set the range we'll need to delete in Undo
+ action->SetRange(wxRichTextRange(pos, pos));
+
+ SubmitAction(action);
+
+ return true;
+}
+
+/// Submit command to insert the given image
+bool wxRichTextBuffer::InsertImageWithUndo(long pos, const wxRichTextImageBlock& imageBlock, wxRichTextCtrl* ctrl)
+{
+ wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, this, ctrl, false);
+
+ wxTextAttrEx attr(GetBasicStyle());
+ wxRichTextApplyStyle(attr, GetDefaultStyle());
+
+ wxRichTextParagraph* newPara = new wxRichTextParagraph(this, & attr);
+ wxRichTextImage* imageObject = new wxRichTextImage(imageBlock, newPara);
+ newPara->AppendChild(imageObject);
+ action->GetNewParagraphs().AppendChild(newPara);
+ action->GetNewParagraphs().UpdateRanges();
+
+ action->GetNewParagraphs().SetPartialParagraph(true);
+
+ action->SetPosition(pos);
+
+ // Set the range we'll need to delete in Undo
+ action->SetRange(wxRichTextRange(pos, pos));
+
+ SubmitAction(action);
+
+ return true;
+}
+
+/// Submit command to delete this range
+bool wxRichTextBuffer::DeleteRangeWithUndo(const wxRichTextRange& range, long initialCaretPosition, long WXUNUSED(newCaretPositon), wxRichTextCtrl* ctrl)
+{
+ wxRichTextAction* action = new wxRichTextAction(NULL, _("Delete"), wxRICHTEXT_DELETE, this, ctrl);
+
+ action->SetPosition(initialCaretPosition);
+
+ // Set the range to delete
+ action->SetRange(range);
+
+ // Copy the fragment that we'll need to restore in Undo
+ CopyFragment(range, action->GetOldParagraphs());
+
+ // Special case: if there is only one (non-partial) paragraph,
+ // we must save the *next* paragraph's style, because that
+ // is the style we must apply when inserting the content back
+ // when undoing the delete. (This is because we're merging the
+ // paragraph with the previous paragraph and throwing away
+ // the style, and we need to restore it.)
+ if (!action->GetOldParagraphs().GetPartialParagraph() && action->GetOldParagraphs().GetChildCount() == 1)
+ {
+ wxRichTextParagraph* lastPara = GetParagraphAtPosition(range.GetStart());
+ if (lastPara)
+ {
+ wxRichTextParagraph* nextPara = GetParagraphAtPosition(range.GetEnd()+1);
+ if (nextPara)
+ {
+ wxRichTextParagraph* para = (wxRichTextParagraph*) action->GetOldParagraphs().GetChild(0);
+ para->SetAttributes(nextPara->GetAttributes());
+ }
+ }
+ }
+
+ SubmitAction(action);
+
+ return true;
+}
+
+/// Collapse undo/redo commands
+bool wxRichTextBuffer::BeginBatchUndo(const wxString& cmdName)
+{
+ if (m_batchedCommandDepth == 0)
+ {
+ wxASSERT(m_batchedCommand == NULL);
+ if (m_batchedCommand)
+ {
+ GetCommandProcessor()->Submit(m_batchedCommand);
+ }
+ m_batchedCommand = new wxRichTextCommand(cmdName);
+ }
+
+ m_batchedCommandDepth ++;
+
+ return true;
+}
+
+/// Collapse undo/redo commands
+bool wxRichTextBuffer::EndBatchUndo()
+{
+ m_batchedCommandDepth --;
+
+ wxASSERT(m_batchedCommandDepth >= 0);
+ wxASSERT(m_batchedCommand != NULL);
+
+ if (m_batchedCommandDepth == 0)
+ {
+ GetCommandProcessor()->Submit(m_batchedCommand);
+ m_batchedCommand = NULL;
+ }
+
+ return true;
+}
+
+/// Submit immediately, or delay according to whether collapsing is on
+bool wxRichTextBuffer::SubmitAction(wxRichTextAction* action)
+{
+ if (BatchingUndo() && m_batchedCommand && !SuppressingUndo())
+ m_batchedCommand->AddAction(action);
+ else
+ {
+ wxRichTextCommand* cmd = new wxRichTextCommand(action->GetName());
+ cmd->AddAction(action);
+
+ // Only store it if we're not suppressing undo.
+ return GetCommandProcessor()->Submit(cmd, !SuppressingUndo());
+ }
+
+ return true;
+}
+
+/// Begin suppressing undo/redo commands.
+bool wxRichTextBuffer::BeginSuppressUndo()
+{
+ m_suppressUndo ++;
+
+ return true;
+}
+
+/// End suppressing undo/redo commands.
+bool wxRichTextBuffer::EndSuppressUndo()
+{
+ m_suppressUndo --;
+
+ return true;
+}
+
+/// Begin using a style
+bool wxRichTextBuffer::BeginStyle(const wxTextAttrEx& style)
+{
+ wxTextAttrEx newStyle(GetDefaultStyle());
+
+ // Save the old default style
+ m_attributeStack.Append((wxObject*) new wxTextAttrEx(GetDefaultStyle()));
+
+ wxRichTextApplyStyle(newStyle, style);
+ newStyle.SetFlags(style.GetFlags()|newStyle.GetFlags());
+
+ SetDefaultStyle(newStyle);
+
+ // wxLogDebug("Default style size = %d", GetDefaultStyle().GetFont().GetPointSize());
+
+ return true;
+}
+
+/// End the style
+bool wxRichTextBuffer::EndStyle()
+{
+ if (m_attributeStack.GetFirst() == NULL)
+ {
+ wxLogDebug(_("Too many EndStyle calls!"));
+ return false;
+ }
+
+ wxNode* node = m_attributeStack.GetLast();
+ wxTextAttrEx* attr = (wxTextAttrEx*)node->GetData();
+ delete node;
+
+ SetDefaultStyle(*attr);
+
+ delete attr;
+ return true;
+}
+
+/// End all styles
+bool wxRichTextBuffer::EndAllStyles()
+{
+ while (m_attributeStack.GetCount() != 0)
+ EndStyle();
+ return true;
+}
+
+/// Clear the style stack
+void wxRichTextBuffer::ClearStyleStack()
+{
+ for (wxNode* node = m_attributeStack.GetFirst(); node; node = node->GetNext())
+ delete (wxTextAttrEx*) node->GetData();
+ m_attributeStack.Clear();
+}
+
+/// Begin using bold
+bool wxRichTextBuffer::BeginBold()
+{
+ wxFont font(GetBasicStyle().GetFont());
+ font.SetWeight(wxBOLD);
+
+ wxTextAttrEx attr;
+ attr.SetFont(font,wxTEXT_ATTR_FONT_WEIGHT);
+
+ return BeginStyle(attr);
+}
+
+/// Begin using italic
+bool wxRichTextBuffer::BeginItalic()
+{
+ wxFont font(GetBasicStyle().GetFont());
+ font.SetStyle(wxITALIC);
+
+ wxTextAttrEx attr;
+ attr.SetFont(font, wxTEXT_ATTR_FONT_ITALIC);
+
+ return BeginStyle(attr);
+}
+
+/// Begin using underline
+bool wxRichTextBuffer::BeginUnderline()
+{
+ wxFont font(GetBasicStyle().GetFont());
+ font.SetUnderlined(true);
+
+ wxTextAttrEx attr;
+ attr.SetFont(font, wxTEXT_ATTR_FONT_UNDERLINE);
+
+ return BeginStyle(attr);
+}
+
+/// Begin using point size
+bool wxRichTextBuffer::BeginFontSize(int pointSize)
+{
+ wxFont font(GetBasicStyle().GetFont());
+ font.SetPointSize(pointSize);
+
+ wxTextAttrEx attr;
+ attr.SetFont(font, wxTEXT_ATTR_FONT_SIZE);
+
+ return BeginStyle(attr);
+}
+
+/// Begin using this font
+bool wxRichTextBuffer::BeginFont(const wxFont& font)
+{
+ wxTextAttrEx attr;
+ attr.SetFlags(wxTEXT_ATTR_FONT);
+ attr.SetFont(font);
+
+ return BeginStyle(attr);
+}
+
+/// Begin using this colour
+bool wxRichTextBuffer::BeginTextColour(const wxColour& colour)
+{
+ wxTextAttrEx attr;
+ attr.SetFlags(wxTEXT_ATTR_TEXT_COLOUR);
+ attr.SetTextColour(colour);
+
+ return BeginStyle(attr);
+}
+
+/// Begin using alignment
+bool wxRichTextBuffer::BeginAlignment(wxTextAttrAlignment alignment)
+{
+ wxTextAttrEx attr;
+ attr.SetFlags(wxTEXT_ATTR_ALIGNMENT);
+ attr.SetAlignment(alignment);
+
+ return BeginStyle(attr);
+}
+
+/// Begin left indent
+bool wxRichTextBuffer::BeginLeftIndent(int leftIndent, int leftSubIndent)
+{
+ wxTextAttrEx attr;
+ attr.SetFlags(wxTEXT_ATTR_LEFT_INDENT);
+ attr.SetLeftIndent(leftIndent, leftSubIndent);
+
+ return BeginStyle(attr);
+}
+
+/// Begin right indent
+bool wxRichTextBuffer::BeginRightIndent(int rightIndent)
+{
+ wxTextAttrEx attr;
+ attr.SetFlags(wxTEXT_ATTR_RIGHT_INDENT);
+ attr.SetRightIndent(rightIndent);
+
+ return BeginStyle(attr);
+}
+
+/// Begin paragraph spacing
+bool wxRichTextBuffer::BeginParagraphSpacing(int before, int after)
+{
+ long flags = 0;
+ if (before != 0)
+ flags |= wxTEXT_ATTR_PARA_SPACING_BEFORE;
+ if (after != 0)
+ flags |= wxTEXT_ATTR_PARA_SPACING_AFTER;
+
+ wxTextAttrEx attr;
+ attr.SetFlags(flags);
+ attr.SetParagraphSpacingBefore(before);
+ attr.SetParagraphSpacingAfter(after);
+
+ return BeginStyle(attr);
+}
+
+/// Begin line spacing
+bool wxRichTextBuffer::BeginLineSpacing(int lineSpacing)
+{
+ wxTextAttrEx attr;
+ attr.SetFlags(wxTEXT_ATTR_LINE_SPACING);
+ attr.SetLineSpacing(lineSpacing);
+
+ return BeginStyle(attr);
+}
+
+/// Begin numbered bullet
+bool wxRichTextBuffer::BeginNumberedBullet(int bulletNumber, int leftIndent, int leftSubIndent, int bulletStyle)
+{
+ wxTextAttrEx attr;
+ attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_BULLET_NUMBER|wxTEXT_ATTR_LEFT_INDENT);
+ attr.SetBulletStyle(bulletStyle);
+ attr.SetBulletNumber(bulletNumber);
+ attr.SetLeftIndent(leftIndent, leftSubIndent);
+
+ return BeginStyle(attr);
+}
+
+/// Begin symbol bullet
+bool wxRichTextBuffer::BeginSymbolBullet(wxChar symbol, int leftIndent, int leftSubIndent, int bulletStyle)
+{
+ wxTextAttrEx attr;
+ attr.SetFlags(wxTEXT_ATTR_BULLET_STYLE|wxTEXT_ATTR_BULLET_SYMBOL|wxTEXT_ATTR_LEFT_INDENT);
+ attr.SetBulletStyle(bulletStyle);
+ attr.SetLeftIndent(leftIndent, leftSubIndent);
+ attr.SetBulletSymbol(symbol);
+
+ return BeginStyle(attr);
+}
+
+/// Begin named character style
+bool wxRichTextBuffer::BeginCharacterStyle(const wxString& characterStyle)
+{
+ if (GetStyleSheet())
+ {
+ wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->FindCharacterStyle(characterStyle);
+ if (def)
+ {
+ wxTextAttrEx attr;
+ def->GetStyle().CopyTo(attr);
+ return BeginStyle(attr);
+ }
+ }
+ return false;
+}
+
+/// Begin named paragraph style
+bool wxRichTextBuffer::BeginParagraphStyle(const wxString& paragraphStyle)
+{
+ if (GetStyleSheet())
+ {
+ wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->FindParagraphStyle(paragraphStyle);
+ if (def)
+ {
+ wxTextAttrEx attr;
+ def->GetStyle().CopyTo(attr);
+ return BeginStyle(attr);
+ }
+ }
+ return false;
+}
+
+/// Adds a handler to the end
+void wxRichTextBuffer::AddHandler(wxRichTextFileHandler *handler)
+{
+ sm_handlers.Append(handler);
+}
+
+/// Inserts a handler at the front
+void wxRichTextBuffer::InsertHandler(wxRichTextFileHandler *handler)
+{
+ sm_handlers.Insert( handler );
+}
+
+/// Removes a handler
+bool wxRichTextBuffer::RemoveHandler(const wxString& name)
+{
+ wxRichTextFileHandler *handler = FindHandler(name);
+ if (handler)
+ {
+ sm_handlers.DeleteObject(handler);
+ delete handler;
+ return true;
+ }
+ else
+ return false;
+}
+
+/// Finds a handler by filename or, if supplied, type
+wxRichTextFileHandler *wxRichTextBuffer::FindHandlerFilenameOrType(const wxString& filename, int imageType)
+{
+ if (imageType != wxRICHTEXT_TYPE_ANY)
+ return FindHandler(imageType);
+ else
+ {
+ wxString path, file, ext;
+ wxSplitPath(filename, & path, & file, & ext);
+ return FindHandler(ext, imageType);
+ }
+}
+
+
+/// Finds a handler by name
+wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& name)
+{
+ wxList::compatibility_iterator node = sm_handlers.GetFirst();
+ while (node)
+ {
+ wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
+ if (handler->GetName().Lower() == name.Lower()) return handler;
+
+ node = node->GetNext();
+ }
+ return NULL;
+}
+
+/// Finds a handler by extension and type
+wxRichTextFileHandler* wxRichTextBuffer::FindHandler(const wxString& extension, int type)
+{
+ wxList::compatibility_iterator node = sm_handlers.GetFirst();
+ while (node)
+ {
+ wxRichTextFileHandler *handler = (wxRichTextFileHandler*)node->GetData();
+ if ( handler->GetExtension().Lower() == extension.Lower() &&
+ (type == wxRICHTEXT_TYPE_ANY || handler->GetType() == type) )
+ return handler;
+ node = node->GetNext();
+ }
+ return 0;
+}
+
+/// Finds a handler by type
+wxRichTextFileHandler* wxRichTextBuffer::FindHandler(int type)
+{
+ wxList::compatibility_iterator node = sm_handlers.GetFirst();
+ while (node)
+ {
+ wxRichTextFileHandler *handler = (wxRichTextFileHandler *)node->GetData();
+ if (handler->GetType() == type) return handler;
+ node = node->GetNext();
+ }
+ return NULL;
+}
+
+void wxRichTextBuffer::InitStandardHandlers()
+{
+ if (!FindHandler(wxRICHTEXT_TYPE_TEXT))
+ AddHandler(new wxRichTextPlainTextHandler);
+}
+
+void wxRichTextBuffer::CleanUpHandlers()
+{
+ wxList::compatibility_iterator node = sm_handlers.GetFirst();
+ while (node)
+ {
+ wxRichTextFileHandler* handler = (wxRichTextFileHandler*)node->GetData();
+ wxList::compatibility_iterator next = node->GetNext();
+ delete handler;
+ node = next;
+ }
+
+ sm_handlers.Clear();
+}
+
+wxString wxRichTextBuffer::GetExtWildcard(bool combine, bool save)
+{
+ wxString wildcard;
+
+ wxList::compatibility_iterator node = GetHandlers().GetFirst();
+ int count = 0;
+ while (node)
+ {
+ wxRichTextFileHandler* handler = (wxRichTextFileHandler*) node->GetData();
+ if (handler->IsVisible() && ((save && handler->CanSave()) || !save && handler->CanLoad()))
+ {
+ if (combine)
+ {
+ if (count > 0)
+ wildcard += wxT(";");
+ wildcard += wxT("*.") + handler->GetExtension();
+ }
+ else
+ {
+ if (count > 0)
+ wildcard += wxT("|");
+ wildcard += handler->GetName();
+ wildcard += wxT(" ");
+ wildcard += _("files");
+ wildcard += wxT(" (*.");
+ wildcard += handler->GetExtension();
+ wildcard += wxT(")|*.");
+ wildcard += handler->GetExtension();
+ }
+ count ++;
+ }
+
+ node = node->GetNext();
+ }
+
+ if (combine)
+ wildcard = wxT("(") + wildcard + wxT(")|") + wildcard;
+ return wildcard;
+}
+
+/// Load a file
+bool wxRichTextBuffer::LoadFile(const wxString& filename, int type)
+{
+ wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
+ if (handler)
+ return handler->LoadFile(this, filename);
+ else
+ return false;
+}
+
+/// Save a file
+bool wxRichTextBuffer::SaveFile(const wxString& filename, int type)
+{
+ wxRichTextFileHandler* handler = FindHandlerFilenameOrType(filename, type);
+ if (handler)
+ return handler->SaveFile(this, filename);
+ else
+ return false;
+}
+
+/// Load from a stream
+bool wxRichTextBuffer::LoadFile(wxInputStream& stream, int type)
+{
+ wxRichTextFileHandler* handler = FindHandler(type);
+ if (handler)
+ return handler->LoadFile(this, stream);
+ else
+ return false;
+}
+
+/// Save to a stream
+bool wxRichTextBuffer::SaveFile(wxOutputStream& stream, int type)
+{
+ wxRichTextFileHandler* handler = FindHandler(type);
+ if (handler)
+ return handler->SaveFile(this, stream);
+ else
+ return false;
+}
+
+/// Copy the range to the clipboard
+bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange& range)
+{
+ bool success = false;
+ wxString text = GetTextForRange(range);
+ if (wxTheClipboard->Open())
+ {
+ success = wxTheClipboard->SetData(new wxTextDataObject(text));
+ wxTheClipboard->Close();
+ }
+ return success;
+}
+
+/// Paste the clipboard content to the buffer
+bool wxRichTextBuffer::PasteFromClipboard(long position)
+{
+ bool success = false;
+ if (CanPasteFromClipboard())
+ {
+ if (wxTheClipboard->Open())
+ {
+ if (wxTheClipboard->IsSupported(wxDF_TEXT))
+ {
+ wxTextDataObject data;
+ wxTheClipboard->GetData(data);
+ wxString text(data.GetText());
+
+ InsertTextWithUndo(position+1, text, GetRichTextCtrl());
+
+ success = true;
+ }
+ else if (wxTheClipboard->IsSupported(wxDF_BITMAP))
+ {
+ wxBitmapDataObject data;
+ wxTheClipboard->GetData(data);
+ wxBitmap bitmap(data.GetBitmap());
+ wxImage image(bitmap.ConvertToImage());
+
+ wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Image"), wxRICHTEXT_INSERT, this, GetRichTextCtrl(), false);
+
+ action->GetNewParagraphs().AddImage(image);
+
+ if (action->GetNewParagraphs().GetChildCount() == 1)
+ action->GetNewParagraphs().SetPartialParagraph(true);
+
+ action->SetPosition(position);
+
+ // Set the range we'll need to delete in Undo
+ action->SetRange(wxRichTextRange(position, position));
+
+ SubmitAction(action);
+
+ success = true;
+ }
+ wxTheClipboard->Close();
+ }
+ }
+ return success;
+}
+
+/// Can we paste from the clipboard?
+bool wxRichTextBuffer::CanPasteFromClipboard() const
+{
+ bool canPaste = FALSE;
+ if (wxTheClipboard->Open())
+ {
+ if (wxTheClipboard->IsSupported(wxDF_TEXT) || wxTheClipboard->IsSupported(wxDF_BITMAP))
+ {
+ canPaste = TRUE;
+ }
+ wxTheClipboard->Close();
+ }
+ return canPaste;
+}
+
+/// Dumps contents of buffer for debugging purposes
+void wxRichTextBuffer::Dump()
+{
+ wxString text;
+ {
+ wxStringOutputStream stream(& text);
+ wxTextOutputStream textStream(stream);
+ Dump(textStream);
+ }
+
+ wxLogDebug(text);
+}
+
+
+/*
+ * Module to initialise and clean up handlers
+ */
+
+class wxRichTextModule: public wxModule
+{
+DECLARE_DYNAMIC_CLASS(wxRichTextModule)
+public:
+ wxRichTextModule() {}
+ bool OnInit() { wxRichTextBuffer::InitStandardHandlers(); return true; };
+ void OnExit() { wxRichTextBuffer::CleanUpHandlers(); };
+};
+
+IMPLEMENT_DYNAMIC_CLASS(wxRichTextModule, wxModule)
+
+
+/*!
+ * Commands for undo/redo
+ *
+ */
+
+wxRichTextCommand::wxRichTextCommand(const wxString& name, wxRichTextCommandId id, wxRichTextBuffer* buffer,
+ wxRichTextCtrl* ctrl, bool ignoreFirstTime): wxCommand(TRUE, name)
+{
+ /* wxRichTextAction* action = */ new wxRichTextAction(this, name, id, buffer, ctrl, ignoreFirstTime);
+}
+
+wxRichTextCommand::wxRichTextCommand(const wxString& name): wxCommand(TRUE, name)
+{
+}
+
+wxRichTextCommand::~wxRichTextCommand()
+{
+ ClearActions();
+}
+
+void wxRichTextCommand::AddAction(wxRichTextAction* action)
+{
+ if (!m_actions.Member(action))
+ m_actions.Append(action);
+}
+
+bool wxRichTextCommand::Do()
+{
+ for (wxNode* node = m_actions.GetFirst(); node; node = node->GetNext())
+ {
+ wxRichTextAction* action = (wxRichTextAction*) node->GetData();
+ action->Do();
+ }
+
+ return true;
+}
+
+bool wxRichTextCommand::Undo()
+{
+ for (wxNode* node = m_actions.GetLast(); node; node = node->GetPrevious())
+ {
+ wxRichTextAction* action = (wxRichTextAction*) node->GetData();
+ action->Undo();
+ }
+
+ return true;
+}
+
+void wxRichTextCommand::ClearActions()
+{
+ WX_CLEAR_LIST(wxList, m_actions);
+}
+
+/*!
+ * Individual action
+ *
+ */
+
+wxRichTextAction::wxRichTextAction(wxRichTextCommand* cmd, const wxString& name, wxRichTextCommandId id, wxRichTextBuffer* buffer,
+ wxRichTextCtrl* ctrl, bool ignoreFirstTime)
+{
+ m_buffer = buffer;
+ m_ignoreThis = ignoreFirstTime;
+ m_cmdId = id;
+ m_position = -1;
+ m_ctrl = ctrl;
+ m_name = name;
+ m_newParagraphs.SetDefaultStyle(buffer->GetDefaultStyle());
+ m_newParagraphs.SetBasicStyle(buffer->GetBasicStyle());
+ if (cmd)
+ cmd->AddAction(this);
+}
+
+wxRichTextAction::~wxRichTextAction()
+{
+}
+
+bool wxRichTextAction::Do()
+{
+ m_buffer->Modify(true);
+
+ switch (m_cmdId)
+ {
+ case wxRICHTEXT_INSERT:
+ {
+ m_buffer->InsertFragment(GetPosition(), m_newParagraphs);
+ m_buffer->UpdateRanges();
+
+ long newCaretPosition = GetPosition() + m_newParagraphs.GetRange().GetLength() - 1;
+ if (m_newParagraphs.GetPartialParagraph())
+ newCaretPosition --;
+
+ UpdateAppearance(newCaretPosition, true /* send update event */);
+
+ break;
+ }
+ case wxRICHTEXT_DELETE:
+ {
+ m_buffer->DeleteRange(GetRange());
+ m_buffer->UpdateRanges();
+
+ UpdateAppearance(GetRange().GetStart()-1, true /* send update event */);
+
+ break;
+ }
+ case wxRICHTEXT_CHANGE_STYLE:
+ {
+ ApplyParagraphs(GetNewParagraphs());
+
+ UpdateAppearance(GetPosition());
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ return true;
+}
+
+bool wxRichTextAction::Undo()
+{
+ m_buffer->Modify(true);
+
+ switch (m_cmdId)
+ {
+ case wxRICHTEXT_INSERT:
+ {
+ m_buffer->DeleteRange(GetRange());
+ m_buffer->UpdateRanges();
+
+ long newCaretPosition = GetPosition() - 1;
+ // if (m_newParagraphs.GetPartialParagraph())
+ // newCaretPosition --;
+
+ UpdateAppearance(newCaretPosition, true /* send update event */);
+
+ break;
+ }
+ case wxRICHTEXT_DELETE:
+ {
+ m_buffer->InsertFragment(GetRange().GetStart(), m_oldParagraphs);
+ m_buffer->UpdateRanges();
+
+ UpdateAppearance(GetPosition(), true /* send update event */);
+
+ break;
+ }
+ case wxRICHTEXT_CHANGE_STYLE:
+ {
+ ApplyParagraphs(GetOldParagraphs());
+
+ UpdateAppearance(GetPosition());
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ return true;
+}
+
+/// Update the control appearance
+void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent)
+{
+ if (m_ctrl)
+ {
+ m_ctrl->SetCaretPosition(caretPosition);
+ if (!m_ctrl->IsFrozen())
+ {
+ m_ctrl->Layout();
+ m_ctrl->PositionCaret();
+ m_ctrl->Refresh();
+
+ if (sendUpdateEvent)
+ m_ctrl->SendUpdateEvent();
+ }
+ }
+}
+
+/// Replace the buffer paragraphs with the new ones.
+void wxRichTextAction::ApplyParagraphs(const wxRichTextFragment& fragment)
+{
+ wxRichTextObjectList::compatibility_iterator node = fragment.GetChildren().GetFirst();
+ while (node)
+ {
+ wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
+ wxASSERT (para != NULL);
+
+ // We'll replace the existing paragraph by finding the paragraph at this position,
+ // delete its node data, and setting a copy as the new node data.
+ // TODO: make more efficient by simply swapping old and new paragraph objects.
+
+ wxRichTextParagraph* existingPara = m_buffer->GetParagraphAtPosition(para->GetRange().GetStart());
+ if (existingPara)
+ {
+ wxRichTextObjectList::compatibility_iterator bufferParaNode = m_buffer->GetChildren().Find(existingPara);
+ if (bufferParaNode)
+ {
+ wxRichTextParagraph* newPara = new wxRichTextParagraph(*para);
+ newPara->SetParent(m_buffer);
+
+ bufferParaNode->SetData(newPara);
+
+ delete existingPara;
+ }
+ }
+
+ node = node->GetNext();
+ }
+}
+
+
+/*!
+ * wxRichTextRange
+ * This stores beginning and end positions for a range of data.
+ */
+
+/// Limit this range to be within 'range'
+bool wxRichTextRange::LimitTo(const wxRichTextRange& range)
+{
+ if (m_start < range.m_start)
+ m_start = range.m_start;
+
+ if (m_end > range.m_end)
+ m_end = range.m_end;
+
+ return true;
+}
+
+/*!
+ * wxRichTextImage implementation
+ * This object represents an image.
+ */
+
+IMPLEMENT_DYNAMIC_CLASS(wxRichTextImage, wxRichTextObject)
+
+wxRichTextImage::wxRichTextImage(const wxImage& image, wxRichTextObject* parent):
+ wxRichTextObject(parent)
+{
+ m_image = image;
+}
+
+wxRichTextImage::wxRichTextImage(const wxRichTextImageBlock& imageBlock, wxRichTextObject* parent):
+ wxRichTextObject(parent)
+{
+ m_imageBlock = imageBlock;
+ m_imageBlock.Load(m_image);
+}
+
+/// Load wxImage from the block
+bool wxRichTextImage::LoadFromBlock()
+{
+ m_imageBlock.Load(m_image);
+ return m_imageBlock.Ok();
+}
+
+/// Make block from the wxImage
+bool wxRichTextImage::MakeBlock()
+{
+ if (m_imageBlock.GetImageType() == wxBITMAP_TYPE_ANY || m_imageBlock.GetImageType() == -1)
+ m_imageBlock.SetImageType(wxBITMAP_TYPE_PNG);
+
+ m_imageBlock.MakeImageBlock(m_image, m_imageBlock.GetImageType());
+ return m_imageBlock.Ok();
+}
+
+
+/// Draw the item
+bool wxRichTextImage::Draw(wxDC& dc, const wxRichTextRange& range, const wxRichTextRange& selectionRange, const wxRect& rect, int WXUNUSED(descent), int WXUNUSED(style))
+{
+ if (!m_image.Ok() && m_imageBlock.Ok())
+ LoadFromBlock();
+
+ if (!m_image.Ok())
+ return false;
+
+ if (m_image.Ok() && !m_bitmap.Ok())
+ m_bitmap = wxBitmap(m_image);
+
+ int y = rect.y + (rect.height - m_image.GetHeight());
+
+ if (m_bitmap.Ok())
+ dc.DrawBitmap(m_bitmap, rect.x, y, true);
+
+ if (selectionRange.Contains(range.GetStart()))
+ {
+ dc.SetBrush(*wxBLACK_BRUSH);
+ dc.SetPen(*wxBLACK_PEN);
+ dc.SetLogicalFunction(wxINVERT);
+ dc.DrawRectangle(rect);
+ dc.SetLogicalFunction(wxCOPY);
+ }
+
+ return true;
+}
+
+/// Lay the item out
+bool wxRichTextImage::Layout(wxDC& WXUNUSED(dc), const wxRect& rect, int WXUNUSED(style))
+{
+ if (!m_image.Ok())
+ LoadFromBlock();
+
+ if (m_image.Ok())
+ {
+ SetCachedSize(wxSize(m_image.GetWidth(), m_image.GetHeight()));
+ SetPosition(rect.GetPosition());
+ }
+
+ return true;
+}
+
+/// Get/set the object size for the given range. Returns false if the range
+/// is invalid for this object.
+bool wxRichTextImage::GetRangeSize(const wxRichTextRange& range, wxSize& size, int& WXUNUSED(descent), wxDC& WXUNUSED(dc), int WXUNUSED(flags)) const
+{
+ if (!range.IsWithin(GetRange()))
+ return false;
+
+ if (!m_image.Ok())
+ return false;
+
+ size.x = m_image.GetWidth();
+ size.y = m_image.GetHeight();
+
+ return true;
+}
+
+/// Copy
+void wxRichTextImage::Copy(const wxRichTextImage& obj)
+{
+ m_image = obj.m_image;
+ m_imageBlock = obj.m_imageBlock;
+}
+
+/*!
+ * Utilities
+ *
+ */
+
+/// Compare two attribute objects
+bool wxTextAttrEq(const wxTextAttrEx& attr1, const wxTextAttrEx& attr2)
+{
+ return (
+ attr1.GetTextColour() == attr2.GetTextColour() &&
+ attr1.GetBackgroundColour() == attr2.GetBackgroundColour() &&
+ attr1.GetFont() == attr2.GetFont() &&
+ attr1.GetAlignment() == attr2.GetAlignment() &&
+ attr1.GetLeftIndent() == attr2.GetLeftIndent() &&
+ attr1.GetRightIndent() == attr2.GetRightIndent() &&
+ attr1.GetLeftSubIndent() == attr2.GetLeftSubIndent() &&
+ attr1.GetTabs().GetCount() == attr2.GetTabs().GetCount() && // heuristic
+ attr1.GetLineSpacing() == attr2.GetLineSpacing() &&
+ attr1.GetParagraphSpacingAfter() == attr2.GetParagraphSpacingAfter() &&
+ attr1.GetParagraphSpacingBefore() == attr2.GetParagraphSpacingBefore() &&
+ attr1.GetBulletStyle() == attr2.GetBulletStyle() &&
+ attr1.GetBulletNumber() == attr2.GetBulletNumber() &&
+ attr1.GetBulletSymbol() == attr2.GetBulletSymbol() &&
+ attr1.GetCharacterStyleName() == attr2.GetCharacterStyleName() &&
+ attr1.GetParagraphStyleName() == attr2.GetParagraphStyleName());
+}
+
+bool wxTextAttrEq(const wxTextAttrEx& attr1, const wxRichTextAttr& attr2)
+{
+ return (
+ attr1.GetTextColour() == attr2.GetTextColour() &&
+ attr1.GetBackgroundColour() == attr2.GetBackgroundColour() &&
+ attr1.GetFont().GetPointSize() == attr2.GetFontSize() &&
+ attr1.GetFont().GetStyle() == attr2.GetFontStyle() &&
+ attr1.GetFont().GetWeight() == attr2.GetFontWeight() &&
+ attr1.GetFont().GetFaceName() == attr2.GetFontFaceName() &&
+ attr1.GetFont().GetUnderlined() == attr2.GetFontUnderlined() &&
+ attr1.GetAlignment() == attr2.GetAlignment() &&
+ attr1.GetLeftIndent() == attr2.GetLeftIndent() &&
+ attr1.GetRightIndent() == attr2.GetRightIndent() &&
+ attr1.GetLeftSubIndent() == attr2.GetLeftSubIndent() &&
+ attr1.GetTabs().GetCount() == attr2.GetTabs().GetCount() && // heuristic
+ attr1.GetLineSpacing() == attr2.GetLineSpacing() &&
+ attr1.GetParagraphSpacingAfter() == attr2.GetParagraphSpacingAfter() &&
+ attr1.GetParagraphSpacingBefore() == attr2.GetParagraphSpacingBefore() &&
+ attr1.GetBulletStyle() == attr2.GetBulletStyle() &&
+ attr1.GetBulletNumber() == attr2.GetBulletNumber() &&
+ attr1.GetBulletSymbol() == attr2.GetBulletSymbol() &&
+ attr1.GetCharacterStyleName() == attr2.GetCharacterStyleName() &&
+ attr1.GetParagraphStyleName() == attr2.GetParagraphStyleName());
+}
+
+/// Compare two attribute objects, but take into account the flags
+/// specifying attributes of interest.
+bool wxTextAttrEqPartial(const wxTextAttrEx& attr1, const wxTextAttrEx& attr2, int flags)
+{
+ if ((flags & wxTEXT_ATTR_TEXT_COLOUR) && attr1.GetTextColour() != attr2.GetTextColour())
+ return false;
+
+ if ((flags & wxTEXT_ATTR_BACKGROUND_COLOUR) && attr1.GetBackgroundColour() != attr2.GetBackgroundColour())
+ return false;
+
+ if ((flags & wxTEXT_ATTR_FONT_FACE) && attr1.GetFont().Ok() && attr2.GetFont().Ok() &&
+ attr1.GetFont().GetFaceName() != attr2.GetFont().GetFaceName())
+ return false;
+
+ if ((flags & wxTEXT_ATTR_FONT_SIZE) && attr1.GetFont().Ok() && attr2.GetFont().Ok() &&
+ attr1.GetFont().GetPointSize() != attr2.GetFont().GetPointSize())
+ return false;
+
+ if ((flags & wxTEXT_ATTR_FONT_WEIGHT) && attr1.GetFont().Ok() && attr2.GetFont().Ok() &&
+ attr1.GetFont().GetWeight() != attr2.GetFont().GetWeight())
+ return false;
+
+ if ((flags & wxTEXT_ATTR_FONT_ITALIC) && attr1.GetFont().Ok() && attr2.GetFont().Ok() &&
+ attr1.GetFont().GetStyle() != attr2.GetFont().GetStyle())
+ return false;
+
+ if ((flags & wxTEXT_ATTR_FONT_UNDERLINE) && attr1.GetFont().Ok() && attr2.GetFont().Ok() &&
+ attr1.GetFont().GetUnderlined() != attr2.GetFont().GetUnderlined())
+ return false;
+
+ if ((flags & wxTEXT_ATTR_ALIGNMENT) && attr1.GetAlignment() != attr2.GetAlignment())
+ return false;
+
+ if ((flags & wxTEXT_ATTR_LEFT_INDENT) &&
+ ((attr1.GetLeftIndent() != attr2.GetLeftIndent()) || (attr1.GetLeftSubIndent() != attr2.GetLeftSubIndent())))
+ return false;
+
+ if ((flags & wxTEXT_ATTR_RIGHT_INDENT) &&
+ (attr1.GetRightIndent() != attr2.GetRightIndent()))
+ return false;
+
+ if ((flags && wxTEXT_ATTR_PARA_SPACING_AFTER) &&
+ (attr1.GetParagraphSpacingAfter() != attr2.GetParagraphSpacingAfter()))
+ return false;
+
+ if ((flags && wxTEXT_ATTR_PARA_SPACING_BEFORE) &&
+ (attr1.GetParagraphSpacingBefore() != attr2.GetParagraphSpacingBefore()))
+ return false;
+
+ if ((flags && wxTEXT_ATTR_LINE_SPACING) &&
+ (attr1.GetLineSpacing() != attr2.GetLineSpacing()))
+ return false;
+
+ if ((flags && wxTEXT_ATTR_CHARACTER_STYLE_NAME) &&
+ (attr1.GetCharacterStyleName() != attr2.GetCharacterStyleName()))
+ return false;
+
+ if ((flags && wxTEXT_ATTR_PARAGRAPH_STYLE_NAME) &&
+ (attr1.GetParagraphStyleName() != attr2.GetParagraphStyleName()))
+ return false;
+
+ if ((flags & wxTEXT_ATTR_BULLET_STYLE) &&
+ (attr1.GetBulletStyle() != attr2.GetBulletStyle()))
+ return false;
+
+ if ((flags & wxTEXT_ATTR_BULLET_NUMBER) &&
+ (attr1.GetBulletNumber() != attr2.GetBulletNumber()))
+ return false;
+
+ if ((flags & wxTEXT_ATTR_BULLET_SYMBOL) &&
+ (attr1.GetBulletSymbol() != attr2.GetBulletSymbol()))
+ return false;
+
+/* TODO
+ if ((flags & wxTEXT_ATTR_TABS) &&
+ return false;
+*/
+
+ return true;
+}
+
+bool wxTextAttrEqPartial(const wxTextAttrEx& attr1, const wxRichTextAttr& attr2, int flags)
+{
+ if ((flags & wxTEXT_ATTR_TEXT_COLOUR) && attr1.GetTextColour() != attr2.GetTextColour())
+ return false;
+
+ if ((flags & wxTEXT_ATTR_BACKGROUND_COLOUR) && attr1.GetBackgroundColour() != attr2.GetBackgroundColour())
+ return false;
+
+ if ((flags & (wxTEXT_ATTR_FONT)) && !attr1.GetFont().Ok())
+ return false;
+
+ if ((flags & wxTEXT_ATTR_FONT_FACE) && attr1.GetFont().Ok() &&
+ attr1.GetFont().GetFaceName() != attr2.GetFontFaceName())
+ return false;
+
+ if ((flags & wxTEXT_ATTR_FONT_SIZE) && attr1.GetFont().Ok() &&
+ attr1.GetFont().GetPointSize() != attr2.GetFontSize())
+ return false;
+
+ if ((flags & wxTEXT_ATTR_FONT_WEIGHT) && attr1.GetFont().Ok() &&
+ attr1.GetFont().GetWeight() != attr2.GetFontWeight())
+ return false;
+
+ if ((flags & wxTEXT_ATTR_FONT_ITALIC) && attr1.GetFont().Ok() &&
+ attr1.GetFont().GetStyle() != attr2.GetFontStyle())
+ return false;
+
+ if ((flags & wxTEXT_ATTR_FONT_UNDERLINE) && attr1.GetFont().Ok() &&
+ attr1.GetFont().GetUnderlined() != attr2.GetFontUnderlined())
+ return false;
+
+ if ((flags & wxTEXT_ATTR_ALIGNMENT) && attr1.GetAlignment() != attr2.GetAlignment())
+ return false;
+
+ if ((flags & wxTEXT_ATTR_LEFT_INDENT) &&
+ ((attr1.GetLeftIndent() != attr2.GetLeftIndent()) || (attr1.GetLeftSubIndent() != attr2.GetLeftSubIndent())))
+ return false;
+
+ if ((flags & wxTEXT_ATTR_RIGHT_INDENT) &&
+ (attr1.GetRightIndent() != attr2.GetRightIndent()))
+ return false;
+
+ if ((flags && wxTEXT_ATTR_PARA_SPACING_AFTER) &&
+ (attr1.GetParagraphSpacingAfter() != attr2.GetParagraphSpacingAfter()))
+ return false;
+
+ if ((flags && wxTEXT_ATTR_PARA_SPACING_BEFORE) &&
+ (attr1.GetParagraphSpacingBefore() != attr2.GetParagraphSpacingBefore()))
+ return false;
+
+ if ((flags && wxTEXT_ATTR_LINE_SPACING) &&
+ (attr1.GetLineSpacing() != attr2.GetLineSpacing()))
+ return false;
+
+ if ((flags && wxTEXT_ATTR_CHARACTER_STYLE_NAME) &&
+ (attr1.GetCharacterStyleName() != attr2.GetCharacterStyleName()))
+ return false;
+
+ if ((flags && wxTEXT_ATTR_PARAGRAPH_STYLE_NAME) &&
+ (attr1.GetParagraphStyleName() != attr2.GetParagraphStyleName()))
+ return false;
+
+ if ((flags & wxTEXT_ATTR_BULLET_STYLE) &&
+ (attr1.GetBulletStyle() != attr2.GetBulletStyle()))
+ return false;
+
+ if ((flags & wxTEXT_ATTR_BULLET_NUMBER) &&
+ (attr1.GetBulletNumber() != attr2.GetBulletNumber()))
+ return false;
+
+ if ((flags & wxTEXT_ATTR_BULLET_SYMBOL) &&
+ (attr1.GetBulletSymbol() != attr2.GetBulletSymbol()))
+ return false;
+
+/* TODO
+ if ((flags & wxTEXT_ATTR_TABS) &&
+ return false;
+*/
+
+ return true;
+}
+
+
+/// Apply one style to another
+bool wxRichTextApplyStyle(wxTextAttrEx& destStyle, const wxTextAttrEx& style)
+{
+ // Whole font
+ if (style.GetFont().Ok() && ((style.GetFlags() & (wxTEXT_ATTR_FONT)) == (wxTEXT_ATTR_FONT)))
+ destStyle.SetFont(style.GetFont());
+ else if (style.GetFont().Ok())
+ {
+ wxFont font = destStyle.GetFont();
+
+ if (style.GetFlags() & wxTEXT_ATTR_FONT_FACE)
+ font.SetFaceName(style.GetFont().GetFaceName());
+
+ if (style.GetFlags() & wxTEXT_ATTR_FONT_SIZE)
+ font.SetPointSize(style.GetFont().GetPointSize());
+
+ if (style.GetFlags() & wxTEXT_ATTR_FONT_ITALIC)
+ font.SetStyle(style.GetFont().GetStyle());
+
+ if (style.GetFlags() & wxTEXT_ATTR_FONT_WEIGHT)
+ font.SetWeight(style.GetFont().GetWeight());
+
+ if (style.GetFlags() & wxTEXT_ATTR_FONT_UNDERLINE)
+ font.SetUnderlined(style.GetFont().GetUnderlined());
+
+ if (font != destStyle.GetFont())
+ destStyle.SetFont(font);
+ }
+
+ if ( style.GetTextColour().Ok() && style.HasTextColour())
+ destStyle.SetTextColour(style.GetTextColour());
+
+ if ( style.GetBackgroundColour().Ok() && style.HasBackgroundColour())
+ destStyle.SetBackgroundColour(style.GetBackgroundColour());
+
+ if (style.HasAlignment())
+ destStyle.SetAlignment(style.GetAlignment());
+
+ if (style.HasTabs())
+ destStyle.SetTabs(style.GetTabs());
+
+ if (style.HasLeftIndent())
+ destStyle.SetLeftIndent(style.GetLeftIndent(), style.GetLeftSubIndent());
+
+ if (style.HasRightIndent())
+ destStyle.SetRightIndent(style.GetRightIndent());
+
+ if (style.HasParagraphSpacingAfter())
+ destStyle.SetParagraphSpacingAfter(style.GetParagraphSpacingAfter());
+
+ if (style.HasParagraphSpacingBefore())
+ destStyle.SetParagraphSpacingBefore(style.GetParagraphSpacingBefore());
+
+ if (style.HasLineSpacing())
+ destStyle.SetLineSpacing(style.GetLineSpacing());
+
+ if (style.HasCharacterStyleName())
+ destStyle.SetCharacterStyleName(style.GetCharacterStyleName());
+
+ if (style.HasParagraphStyleName())
+ destStyle.SetParagraphStyleName(style.GetParagraphStyleName());
+
+ if (style.HasBulletStyle())
+ {
+ destStyle.SetBulletStyle(style.GetBulletStyle());
+ destStyle.SetBulletSymbol(style.GetBulletSymbol());
+ }
+
+ if (style.HasBulletNumber())
+ destStyle.SetBulletNumber(style.GetBulletNumber());
+
+ return true;
+}
+
+bool wxRichTextApplyStyle(wxRichTextAttr& destStyle, const wxTextAttrEx& style)
+{
+ wxTextAttrEx destStyle2;
+ destStyle.CopyTo(destStyle2);
+ wxRichTextApplyStyle(destStyle2, style);
+ destStyle = destStyle2;
+ return true;
+}
+
+bool wxRichTextApplyStyle(wxTextAttrEx& destStyle, const wxRichTextAttr& style)
+{
+
+ // Whole font. Avoiding setting individual attributes if possible, since
+ // it recreates the font each time.
+ if ((style.GetFlags() & (wxTEXT_ATTR_FONT)) == (wxTEXT_ATTR_FONT))
+ {
+ destStyle.SetFont(wxFont(style.GetFontSize(), destStyle.GetFont().Ok() ? destStyle.GetFont().GetFamily() : wxDEFAULT,
+ style.GetFontStyle(), style.GetFontWeight(), style.GetFontUnderlined(), style.GetFontFaceName()));
+ }
+ else if (style.GetFlags() & (wxTEXT_ATTR_FONT))
+ {
+ wxFont font = destStyle.GetFont();
+
+ if (style.GetFlags() & wxTEXT_ATTR_FONT_FACE)
+ font.SetFaceName(style.GetFontFaceName());
+
+ if (style.GetFlags() & wxTEXT_ATTR_FONT_SIZE)
+ font.SetPointSize(style.GetFontSize());
+
+ if (style.GetFlags() & wxTEXT_ATTR_FONT_ITALIC)
+ font.SetStyle(style.GetFontStyle());
+
+ if (style.GetFlags() & wxTEXT_ATTR_FONT_WEIGHT)
+ font.SetWeight(style.GetFontWeight());
+
+ if (style.GetFlags() & wxTEXT_ATTR_FONT_UNDERLINE)
+ font.SetUnderlined(style.GetFontUnderlined());
+
+ if (font != destStyle.GetFont())
+ destStyle.SetFont(font);
+ }
+
+ if ( style.GetTextColour().Ok() && style.HasTextColour())
+ destStyle.SetTextColour(style.GetTextColour());
+
+ if ( style.GetBackgroundColour().Ok() && style.HasBackgroundColour())
+ destStyle.SetBackgroundColour(style.GetBackgroundColour());
+
+ if (style.HasAlignment())
+ destStyle.SetAlignment(style.GetAlignment());
+
+ if (style.HasTabs())
+ destStyle.SetTabs(style.GetTabs());
+
+ if (style.HasLeftIndent())
+ destStyle.SetLeftIndent(style.GetLeftIndent(), style.GetLeftSubIndent());
+
+ if (style.HasRightIndent())
+ destStyle.SetRightIndent(style.GetRightIndent());
+
+ if (style.HasParagraphSpacingAfter())
+ destStyle.SetParagraphSpacingAfter(style.GetParagraphSpacingAfter());
+
+ if (style.HasParagraphSpacingBefore())
+ destStyle.SetParagraphSpacingBefore(style.GetParagraphSpacingBefore());
+
+ if (style.HasLineSpacing())
+ destStyle.SetLineSpacing(style.GetLineSpacing());
+
+ if (style.HasCharacterStyleName())
+ destStyle.SetCharacterStyleName(style.GetCharacterStyleName());
+
+ if (style.HasParagraphStyleName())
+ destStyle.SetParagraphStyleName(style.GetParagraphStyleName());
+
+ if (style.HasBulletStyle())
+ {
+ destStyle.SetBulletStyle(style.GetBulletStyle());
+ destStyle.SetBulletSymbol(style.GetBulletSymbol());
+ }
+
+ if (style.HasBulletNumber())
+ destStyle.SetBulletNumber(style.GetBulletNumber());
+
+ return true;
+}
+
+
+/*!
+ * wxRichTextAttr stores attributes without a wxFont object, so is a much more
+ * efficient way to query styles.
+ */
+
+// ctors
+wxRichTextAttr::wxRichTextAttr(const wxColour& colText,
+ const wxColour& colBack,
+ wxTextAttrAlignment alignment): m_textAlignment(alignment), m_colText(colText), m_colBack(colBack)
+{
+ Init();
+
+ if (m_colText.Ok()) m_flags |= wxTEXT_ATTR_TEXT_COLOUR;
+ if (m_colBack.Ok()) m_flags |= wxTEXT_ATTR_BACKGROUND_COLOUR;
+ if (alignment != wxTEXT_ALIGNMENT_DEFAULT)
+ m_flags |= wxTEXT_ATTR_ALIGNMENT;
+}
+
+wxRichTextAttr::wxRichTextAttr(const wxTextAttrEx& attr)
+{
+ Init();
+
+ (*this) = attr;
+}
+
+// operations
+void wxRichTextAttr::Init()
+{
+ m_textAlignment = wxTEXT_ALIGNMENT_DEFAULT;
+ m_flags = 0;
+ m_leftIndent = 0;
+ m_leftSubIndent = 0;
+ m_rightIndent = 0;
+
+ m_fontSize = 12;
+ m_fontStyle = wxNORMAL;
+ m_fontWeight = wxNORMAL;
+ m_fontUnderlined = false;
+
+ m_paragraphSpacingAfter = 0;
+ m_paragraphSpacingBefore = 0;
+ m_lineSpacing = 0;
+ m_bulletStyle = wxTEXT_ATTR_BULLET_STYLE_NONE;
+ m_bulletNumber = 0;
+ m_bulletSymbol = wxT('*');
+}
+
+// operators
+void wxRichTextAttr::operator= (const wxRichTextAttr& attr)
+{
+ m_colText = attr.m_colText;
+ m_colBack = attr.m_colBack;
+ m_textAlignment = attr.m_textAlignment;
+ m_leftIndent = attr.m_leftIndent;
+ m_leftSubIndent = attr.m_leftSubIndent;
+ m_rightIndent = attr.m_rightIndent;
+ m_tabs = attr.m_tabs;
+ m_flags = attr.m_flags;
+
+ m_fontSize = attr.m_fontSize;
+ m_fontStyle = attr.m_fontStyle;
+ m_fontWeight = attr.m_fontWeight;
+ m_fontUnderlined = attr.m_fontUnderlined;
+ m_fontFaceName = attr.m_fontFaceName;
+
+ m_paragraphSpacingAfter = attr.m_paragraphSpacingAfter;
+ m_paragraphSpacingBefore = attr.m_paragraphSpacingBefore;
+ m_lineSpacing = attr.m_lineSpacing;
+ m_characterStyleName = attr.m_characterStyleName;
+ m_paragraphStyleName = attr.m_paragraphStyleName;
+ m_bulletStyle = attr.m_bulletStyle;
+ m_bulletNumber = attr.m_bulletNumber;
+ m_bulletSymbol = attr.m_bulletSymbol;
+}
+
+// operators
+void wxRichTextAttr::operator= (const wxTextAttrEx& attr)
+{
+ m_colText = attr.GetTextColour();
+ m_colBack = attr.GetBackgroundColour();
+ m_textAlignment = attr.GetAlignment();
+ m_leftIndent = attr.GetLeftIndent();
+ m_leftSubIndent = attr.GetLeftSubIndent();
+ m_rightIndent = attr.GetRightIndent();
+ m_tabs = attr.GetTabs();
+ m_flags = attr.GetFlags();
+
+ m_paragraphSpacingAfter = attr.GetParagraphSpacingAfter();
+ m_paragraphSpacingBefore = attr.GetParagraphSpacingBefore();
+ m_lineSpacing = attr.GetLineSpacing();
+ m_characterStyleName = attr.GetCharacterStyleName();
+ m_paragraphStyleName = attr.GetParagraphStyleName();
+
+ if (attr.GetFont().Ok())
+ GetFontAttributes(attr.GetFont());
+}
+
+// Making a wxTextAttrEx object.
+wxRichTextAttr::operator wxTextAttrEx () const
+{
+ wxTextAttrEx attr;
+ CopyTo(attr);
+ return attr;
+}
+
+// Copy to a wxTextAttr
+void wxRichTextAttr::CopyTo(wxTextAttrEx& attr) const
+{
+ attr.SetTextColour(GetTextColour());
+ attr.SetBackgroundColour(GetBackgroundColour());
+ attr.SetAlignment(GetAlignment());
+ attr.SetTabs(GetTabs());
+ attr.SetLeftIndent(GetLeftIndent(), GetLeftSubIndent());
+ attr.SetRightIndent(GetRightIndent());
+ attr.SetFont(CreateFont());
+ attr.SetFlags(GetFlags()); // Important: set after SetFont, since SetFont sets flags
+
+ attr.SetParagraphSpacingAfter(m_paragraphSpacingAfter);
+ attr.SetParagraphSpacingBefore(m_paragraphSpacingBefore);
+ attr.SetLineSpacing(m_lineSpacing);
+ attr.SetBulletStyle(m_bulletStyle);
+ attr.SetBulletNumber(m_bulletNumber);
+ attr.SetBulletSymbol(m_bulletSymbol);
+ attr.SetCharacterStyleName(m_characterStyleName);
+ attr.SetParagraphStyleName(m_paragraphStyleName);
+
+}
+
+// Create font from font attributes.
+wxFont wxRichTextAttr::CreateFont() const
+{
+ wxFont font(m_fontSize, wxDEFAULT, m_fontStyle, m_fontWeight, m_fontUnderlined, m_fontFaceName);
+ return font;
+}
+
+// Get attributes from font.
+bool wxRichTextAttr::GetFontAttributes(const wxFont& font)
+{
+ if (!font.Ok())
+ return false;
+
+ m_fontSize = font.GetPointSize();
+ m_fontStyle = font.GetStyle();
+ m_fontWeight = font.GetWeight();
+ m_fontUnderlined = font.GetUnderlined();
+ m_fontFaceName = font.GetFaceName();
+
+ return true;
+}
+
+/*!
+ * wxTextAttrEx is an extended version of wxTextAttr with more paragraph attributes.
+ */
+
+wxTextAttrEx::wxTextAttrEx(const wxTextAttrEx& attr): wxTextAttr(attr)
+{
+ m_paragraphSpacingAfter = attr.m_paragraphSpacingAfter;
+ m_paragraphSpacingBefore = attr.m_paragraphSpacingBefore;
+ m_lineSpacing = attr.m_lineSpacing;
+ m_paragraphStyleName = attr.m_paragraphStyleName;
+ m_characterStyleName = attr.m_characterStyleName;
+ m_bulletStyle = attr.m_bulletStyle;
+ m_bulletNumber = attr.m_bulletNumber;
+ m_bulletSymbol = attr.m_bulletSymbol;
+}
+
+// Initialise this object.
+void wxTextAttrEx::Init()
+{
+ m_paragraphSpacingAfter = 0;
+ m_paragraphSpacingBefore = 0;
+ m_lineSpacing = 0;
+ m_bulletStyle = wxTEXT_ATTR_BULLET_STYLE_NONE;
+ m_bulletNumber = 0;
+ m_bulletSymbol = 0;
+ m_bulletSymbol = wxT('*');
+}
+
+// Assignment from a wxTextAttrEx object
+void wxTextAttrEx::operator= (const wxTextAttrEx& attr)
+{
+ wxTextAttr::operator= (attr);
+
+ m_paragraphSpacingAfter = attr.m_paragraphSpacingAfter;
+ m_paragraphSpacingBefore = attr.m_paragraphSpacingBefore;
+ m_lineSpacing = attr.m_lineSpacing;
+ m_characterStyleName = attr.m_characterStyleName;
+ m_paragraphStyleName = attr.m_paragraphStyleName;
+ m_bulletStyle = attr.m_bulletStyle;
+ m_bulletNumber = attr.m_bulletNumber;
+ m_bulletSymbol = attr.m_bulletSymbol;
+}
+
+// Assignment from a wxTextAttr object.
+void wxTextAttrEx::operator= (const wxTextAttr& attr)
+{
+ wxTextAttr::operator= (attr);
+}
+
+/*!
+ * wxRichTextFileHandler
+ * Base class for file handlers
+ */
+
+IMPLEMENT_CLASS(wxRichTextFileHandler, wxObject)
+
+bool wxRichTextFileHandler::LoadFile(wxRichTextBuffer *buffer, const wxString& filename)
+{
+ wxFFileInputStream stream(filename);
+ if (stream.Ok())
+ return LoadFile(buffer, stream);
+ else
+ return false;
+}
+
+bool wxRichTextFileHandler::SaveFile(wxRichTextBuffer *buffer, const wxString& filename)
+{
+ wxFFileOutputStream stream(filename);
+ if (stream.Ok())
+ return SaveFile(buffer, stream);
+ else
+ return false;
+}
+
+/// Can we handle this filename (if using files)? By default, checks the extension.
+bool wxRichTextFileHandler::CanHandle(const wxString& filename) const
+{
+ wxString path, file, ext;
+ wxSplitPath(filename, & path, & file, & ext);
+
+ return (ext.Lower() == GetExtension());
+}
+
+/*!
+ * wxRichTextTextHandler
+ * Plain text handler
+ */
+
+IMPLEMENT_CLASS(wxRichTextPlainTextHandler, wxRichTextFileHandler)
+
+#if wxUSE_STREAMS
+bool wxRichTextPlainTextHandler::LoadFile(wxRichTextBuffer *buffer, wxInputStream& stream)
+{
+ if (!stream.IsOk())
+ return false;
+
+ wxString str;
+ int ch = 0;
+
+ while (!stream.Eof())
+ {
+ ch = stream.GetC();
+
+ if (ch > 0)
+ str += ch;
+ }
+
+ buffer->Clear();
+ buffer->AddParagraphs(str);
+ buffer->UpdateRanges();
+
+ return true;
+
+}
+
+bool wxRichTextPlainTextHandler::SaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream)
+{
+ if (!stream.IsOk())
+ return false;
+
+ wxString text = buffer->GetText();
+ wxCharBuffer buf = text.ToAscii();
+
+ stream.Write((const char*) buf, text.Length());
+ return true;
+}
+
+#endif
+
+/*
+ * Stores information about an image, in binary in-memory form
+ */
+
+wxRichTextImageBlock::wxRichTextImageBlock()
+{
+ Init();
+}
+
+wxRichTextImageBlock::wxRichTextImageBlock(const wxRichTextImageBlock& block):wxObject()
+{
+ Init();
+ Copy(block);
+}
+
+wxRichTextImageBlock::~wxRichTextImageBlock()
+{
+ if (m_data)
+ {
+ delete[] m_data;
+ m_data = NULL;
+ }
+}
+
+void wxRichTextImageBlock::Init()
+{
+ m_data = NULL;
+ m_dataSize = 0;
+ m_imageType = -1;
+}
+
+void wxRichTextImageBlock::Clear()
+{
+ if (m_data)
+ delete m_data;
+ m_data = NULL;
+ m_dataSize = 0;
+ m_imageType = -1;
+}
+
+
+// Load the original image into a memory block.
+// If the image is not a JPEG, we must convert it into a JPEG
+// to conserve space.
+// If it's not a JPEG we can make use of 'image', already scaled, so we don't have to
+// load the image a 2nd time.
+
+bool wxRichTextImageBlock::MakeImageBlock(const wxString& filename, int imageType, wxImage& image, bool convertToJPEG)
+{
+ m_imageType = imageType;
+
+ wxString filenameToRead(filename);
+ bool removeFile = FALSE;
+
+ if (imageType == -1)
+ return FALSE; // Could not determine image type
+
+ if ((imageType != wxBITMAP_TYPE_JPEG) && convertToJPEG)
+ {
+ wxString tempFile;
+ bool success = wxGetTempFileName(_("image"), tempFile) ;
+
+ wxASSERT(success);
+
+ wxUnusedVar(success);
+
+ image.SaveFile(tempFile, wxBITMAP_TYPE_JPEG);
+ filenameToRead = tempFile;
+ removeFile = TRUE;
+
+ m_imageType = wxBITMAP_TYPE_JPEG;
+ }
+ wxFile file;
+ if (!file.Open(filenameToRead))
+ return FALSE;
+
+ m_dataSize = (size_t) file.Length();
+ file.Close();
+
+ if (m_data)
+ delete[] m_data;
+ m_data = ReadBlock(filenameToRead, m_dataSize);
+
+ if (removeFile)
+ wxRemoveFile(filenameToRead);
+
+ return (m_data != NULL);
+}
+
+// Make an image block from the wxImage in the given
+// format.
+bool wxRichTextImageBlock::MakeImageBlock(wxImage& image, int imageType, int quality)
+{
+ m_imageType = imageType;
+ image.SetOption(wxT("quality"), quality);
+
+ if (imageType == -1)
+ return FALSE; // Could not determine image type
+
+ wxString tempFile;
+ bool success = wxGetTempFileName(_("image"), tempFile) ;
+
+ wxASSERT(success);
+ wxUnusedVar(success);
+
+ if (!image.SaveFile(tempFile, m_imageType))
+ {
+ if (wxFileExists(tempFile))
+ wxRemoveFile(tempFile);
+ return FALSE;
+ }
+
+ wxFile file;
+ if (!file.Open(tempFile))
+ return FALSE;
+
+ m_dataSize = (size_t) file.Length();
+ file.Close();
+
+ if (m_data)
+ delete[] m_data;
+ m_data = ReadBlock(tempFile, m_dataSize);
+
+ wxRemoveFile(tempFile);
+
+ return (m_data != NULL);
+}
+
+
+// Write to a file
+bool wxRichTextImageBlock::Write(const wxString& filename)
+{
+ return WriteBlock(filename, m_data, m_dataSize);
+}
+
+void wxRichTextImageBlock::Copy(const wxRichTextImageBlock& block)
+{
+ m_imageType = block.m_imageType;
+ if (m_data)
+ {
+ delete[] m_data;
+ m_data = NULL;
+ }
+ m_dataSize = block.m_dataSize;
+ if (m_dataSize == 0)
+ return;
+
+ m_data = new unsigned char[m_dataSize];
+ unsigned int i;
+ for (i = 0; i < m_dataSize; i++)
+ m_data[i] = block.m_data[i];
+}
+
+//// Operators
+void wxRichTextImageBlock::operator=(const wxRichTextImageBlock& block)
+{
+ Copy(block);
+}
+
+// Load a wxImage from the block
+bool wxRichTextImageBlock::Load(wxImage& image)
+{
+ if (!m_data)
+ return FALSE;
+
+ // Read in the image.
+#if 1
+ wxMemoryInputStream mstream(m_data, m_dataSize);
+ bool success = image.LoadFile(mstream, GetImageType());
+#else
+ wxString tempFile;
+ bool success = wxGetTempFileName(_("image"), tempFile) ;
+ wxASSERT(success);
+
+ if (!WriteBlock(tempFile, m_data, m_dataSize))
+ {
+ return FALSE;
+ }
+ success = image.LoadFile(tempFile, GetImageType());
+ wxRemoveFile(tempFile);
+#endif
+
+ return success;
+}
+
+// Write data in hex to a stream
+bool wxRichTextImageBlock::WriteHex(wxOutputStream& stream)
+{
+ wxString hex;
+ int i;
+ for (i = 0; i < (int) m_dataSize; i++)
+ {
+ hex = wxDecToHex(m_data[i]);
+ wxCharBuffer buf = hex.ToAscii();
+
+ stream.Write((const char*) buf, hex.Length());
+ }
+
+ return true;
+}
+
+// Read data in hex from a stream
+bool wxRichTextImageBlock::ReadHex(wxInputStream& stream, int length, int imageType)
+{
+ int dataSize = length/2;
+
+ if (m_data)
+ delete[] m_data;
+
+ wxString str(wxT(" "));
+ m_data = new unsigned char[dataSize];
+ int i;
+ for (i = 0; i < dataSize; i ++)
+ {
+ str[0] = stream.GetC();
+ str[1] = stream.GetC();
+
+ m_data[i] = wxHexToDec(str);
+ }
+
+ m_dataSize = dataSize;
+ m_imageType = imageType;
+
+ return true;
+}
+
+
+// Allocate and read from stream as a block of memory
+unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream& stream, size_t size)
+{
+ unsigned char* block = new unsigned char[size];
+ if (!block)
+ return NULL;
+
+ stream.Read(block, size);
+
+ return block;
+}
+
+unsigned char* wxRichTextImageBlock::ReadBlock(const wxString& filename, size_t size)
+{
+ wxFileInputStream stream(filename);
+ if (!stream.Ok())
+ return NULL;
+
+ return ReadBlock(stream, size);
+}
+
+// Write memory block to stream
+bool wxRichTextImageBlock::WriteBlock(wxOutputStream& stream, unsigned char* block, size_t size)
+{
+ stream.Write((void*) block, size);
+ return stream.IsOk();
+
+}
+
+// Write memory block to file
+bool wxRichTextImageBlock::WriteBlock(const wxString& filename, unsigned char* block, size_t size)
+{
+ wxFileOutputStream outStream(filename);
+ if (!outStream.Ok())
+ return FALSE;
+
+ return WriteBlock(outStream, block, size);
+}
+
+#endif
+ // wxUSE_RICHTEXT
+
diff --git a/src/richtext/richtextctrl.cpp b/src/richtext/richtextctrl.cpp
new file mode 100644
index 0000000000..e376c262f8
--- /dev/null
+++ b/src/richtext/richtextctrl.cpp
@@ -0,0 +1,2406 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name: richeditctrl.cpp
+// Purpose: A rich edit control
+// Author: Julian Smart
+// Modified by:
+// Created: 2005-09-30
+// RCS-ID:
+// Copyright: (c) Julian Smart
+// Licence: wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+// For compilers that support precompilation, includes "wx.h".
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+ #pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+ #include "wx/wx.h"
+#endif
+
+#include "wx/image.h"
+
+#if wxUSE_RICHTEXT
+
+#include "wx/textfile.h"
+#include "wx/ffile.h"
+#include "wx/settings.h"
+#include "wx/filename.h"
+#include "wx/dcbuffer.h"
+
+#include "wx/richtext/richtextctrl.h"
+#include "wx/arrimpl.cpp"
+
+DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_ITEM_SELECTED)
+DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_ITEM_DESELECTED)
+DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_LEFT_CLICK)
+DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_MIDDLE_CLICK)
+DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_RIGHT_CLICK)
+DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_LEFT_DCLICK)
+DEFINE_EVENT_TYPE(wxEVT_COMMAND_RICHTEXT_RETURN)
+
+#if wxRICHTEXT_DERIVES_FROM_TEXTCTRLBASE
+IMPLEMENT_CLASS( wxRichTextCtrl, wxControl )
+#else
+IMPLEMENT_CLASS( wxRichTextCtrl, wxScrolledWindow )
+#endif
+
+IMPLEMENT_CLASS( wxRichTextEvent, wxNotifyEvent )
+
+#if wxRICHTEXT_DERIVES_FROM_TEXTCTRLBASE
+BEGIN_EVENT_TABLE( wxRichTextCtrl, wxControl )
+#else
+BEGIN_EVENT_TABLE( wxRichTextCtrl, wxScrolledWindow )
+#endif
+ EVT_PAINT(wxRichTextCtrl::OnPaint)
+ EVT_ERASE_BACKGROUND(wxRichTextCtrl::OnEraseBackground)
+ EVT_LEFT_DOWN(wxRichTextCtrl::OnLeftClick)
+ EVT_MOTION(wxRichTextCtrl::OnMoveMouse)
+ EVT_LEFT_UP(wxRichTextCtrl::OnLeftUp)
+ EVT_RIGHT_DOWN(wxRichTextCtrl::OnRightClick)
+ EVT_MIDDLE_DOWN(wxRichTextCtrl::OnMiddleClick)
+ EVT_LEFT_DCLICK(wxRichTextCtrl::OnLeftDClick)
+ EVT_CHAR(wxRichTextCtrl::OnChar)
+ EVT_SIZE(wxRichTextCtrl::OnSize)
+ EVT_SET_FOCUS(wxRichTextCtrl::OnSetFocus)
+ EVT_KILL_FOCUS(wxRichTextCtrl::OnKillFocus)
+ EVT_CONTEXT_MENU(wxRichTextCtrl::OnContextMenu)
+
+ EVT_MENU(wxID_UNDO, wxRichTextCtrl::OnUndo)
+ EVT_UPDATE_UI(wxID_UNDO, wxRichTextCtrl::OnUpdateUndo)
+
+ EVT_MENU(wxID_REDO, wxRichTextCtrl::OnRedo)
+ EVT_UPDATE_UI(wxID_REDO, wxRichTextCtrl::OnUpdateRedo)
+
+ EVT_MENU(wxID_COPY, wxRichTextCtrl::OnCopy)
+ EVT_UPDATE_UI(wxID_COPY, wxRichTextCtrl::OnUpdateCopy)
+
+ EVT_MENU(wxID_PASTE, wxRichTextCtrl::OnPaste)
+ EVT_UPDATE_UI(wxID_PASTE, wxRichTextCtrl::OnUpdatePaste)
+
+ EVT_MENU(wxID_CUT, wxRichTextCtrl::OnCut)
+ EVT_UPDATE_UI(wxID_CUT, wxRichTextCtrl::OnUpdateCut)
+
+ EVT_MENU(wxID_CLEAR, wxRichTextCtrl::OnClear)
+ EVT_UPDATE_UI(wxID_CLEAR, wxRichTextCtrl::OnUpdateClear)
+
+ EVT_MENU(wxID_SELECTALL, wxRichTextCtrl::OnSelectAll)
+ EVT_UPDATE_UI(wxID_SELECTALL, wxRichTextCtrl::OnUpdateSelectAll)
+END_EVENT_TABLE()
+
+/*!
+ * wxRichTextCtrl
+ */
+
+wxRichTextCtrl::wxRichTextCtrl()
+#if wxRICHTEXT_DERIVES_FROM_TEXTCTRLBASE
+ : wxScrollHelper(this)
+#endif
+{
+ Init();
+}
+
+wxRichTextCtrl::wxRichTextCtrl( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style)
+#if wxRICHTEXT_DERIVES_FROM_TEXTCTRLBASE
+ : wxScrollHelper(this)
+#endif
+{
+ Init();
+ Create(parent, id, pos, size, style);
+}
+
+/// Creation
+bool wxRichTextCtrl::Create( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style)
+{
+#if wxRICHTEXT_DERIVES_FROM_TEXTCTRLBASE
+ if (!wxTextCtrlBase::Create(parent, id, pos, size, style|wxFULL_REPAINT_ON_RESIZE
+ ))
+ return false;
+#else
+ if (!wxScrolledWindow::Create(parent, id, pos, size, style|wxFULL_REPAINT_ON_RESIZE
+ ))
+ return false;
+#endif
+
+ if (!GetFont().Ok())
+ {
+ SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
+ }
+
+ GetBuffer().SetRichTextCtrl(this);
+
+ wxTextAttrEx attributes;
+ attributes.SetFont(GetFont());
+ attributes.SetTextColour(*wxBLACK);
+ attributes.SetBackgroundColour(*wxWHITE);
+ attributes.SetAlignment(wxTEXT_ALIGNMENT_LEFT);
+ attributes.SetFlags(wxTEXT_ATTR_ALL);
+
+ SetDefaultStyle(attributes);
+ SetBasicStyle(attributes);
+
+ SetBackgroundColour(*wxWHITE);
+ SetBackgroundStyle(wxBG_STYLE_CUSTOM);
+
+ // Tell the sizers to use the given or best size
+ SetBestFittingSize(size);
+
+ // Create a buffer
+ RecreateBuffer(size);
+
+ wxCaret* caret = new wxCaret(this, wxRICHTEXT_DEFAULT_CARET_WIDTH, 16);
+ SetCaret(caret);
+ caret->Show();
+ PositionCaret();
+
+ SetCursor(wxCursor(wxCURSOR_IBEAM));
+
+ return true;
+}
+
+wxRichTextCtrl::~wxRichTextCtrl()
+{
+ delete m_contextMenu;
+}
+
+/// Member initialisation
+void wxRichTextCtrl::Init()
+{
+ m_freezeCount = 0;
+ m_contextMenu = NULL;
+ m_caret = NULL;
+ m_caretPosition = -1;
+ m_selectionRange.SetRange(-2, -2);
+ m_selectionAnchor = -2;
+ m_editable = true;
+ m_caretAtLineStart = false;
+ m_dragging = false;
+}
+
+/// Call Freeze to prevent refresh
+void wxRichTextCtrl::Freeze()
+{
+ m_freezeCount ++;
+}
+
+/// Call Thaw to refresh
+void wxRichTextCtrl::Thaw(bool refresh)
+{
+ m_freezeCount --;
+
+ if (m_freezeCount == 0 && refresh)
+ {
+ SetupScrollbars();
+ Refresh();
+ }
+}
+
+/// Clear all text
+void wxRichTextCtrl::Clear()
+{
+ m_buffer.Reset();
+ m_buffer.SetDirty(true);
+ m_caretPosition = -1;
+ m_caretAtLineStart = false;
+ m_selectionRange.SetRange(-2, -2);
+
+ if (m_freezeCount == 0)
+ {
+ SetupScrollbars();
+ Refresh();
+ }
+ SendUpdateEvent();
+}
+
+/// Painting
+void wxRichTextCtrl::OnPaint(wxPaintEvent& WXUNUSED(event))
+{
+ wxBufferedPaintDC dc(this, m_bufferBitmap);
+
+ PrepareDC(dc);
+
+ if (m_freezeCount > 0)
+ return;
+
+ dc.SetFont(GetFont());
+
+ // Paint the background
+ PaintBackground(dc);
+
+ wxRegion dirtyRegion = GetUpdateRegion();
+
+ wxRect availableSpace(wxPoint(0, 0), GetClientSize());
+ if (GetBuffer().GetDirty())
+ {
+ GetBuffer().Layout(dc, availableSpace, wxRICHTEXT_FIXED_WIDTH|wxRICHTEXT_VARIABLE_HEIGHT);
+ GetBuffer().SetDirty(false);
+ SetupScrollbars();
+ PositionCaret();
+ }
+
+ GetBuffer().Draw(dc, GetBuffer().GetRange(), GetSelectionRange(), availableSpace, 0 /* descent */, 0 /* flags */);
+}
+
+// Empty implementation, to prevent flicker
+void wxRichTextCtrl::OnEraseBackground(wxEraseEvent& WXUNUSED(event))
+{
+}
+
+void wxRichTextCtrl::OnSetFocus(wxFocusEvent& WXUNUSED(event))
+{
+ if (!IsFrozen())
+ Refresh();
+}
+
+void wxRichTextCtrl::OnKillFocus(wxFocusEvent& WXUNUSED(event))
+{
+ if (!IsFrozen())
+ Refresh();
+}
+
+/// Left-click
+void wxRichTextCtrl::OnLeftClick(wxMouseEvent& event)
+{
+ SetFocus();
+
+ wxClientDC dc(this);
+ PrepareDC(dc);
+ dc.SetFont(GetFont());
+
+ long position = 0;
+ int hit = GetBuffer().HitTest(dc, event.GetLogicalPosition(dc), position);
+
+ if (hit != wxRICHTEXT_HITTEST_NONE)
+ {
+ m_dragStart = event.GetLogicalPosition(dc);
+ m_dragging = true;
+ CaptureMouse();
+
+ SelectNone();
+
+ bool caretAtLineStart = false;
+
+ if (hit & wxRICHTEXT_HITTEST_BEFORE)
+ {
+ // If we're at the start of a line (but not first in para)
+ // then we should keep the caret showing at the start of the line
+ // by showing the m_caretAtLineStart flag.
+ wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(position);
+ wxRichTextLine* line = GetBuffer().GetLineAtPosition(position);
+
+ if (line && para && line->GetRange().GetStart() == position && para->GetRange().GetStart() != position)
+ caretAtLineStart = true;
+ position --;
+ }
+
+ MoveCaret(position, caretAtLineStart);
+ SetDefaultStyleToCursorStyle();
+
+#if 0
+ wxWindow* p = GetParent();
+ while (p && !p->IsKindOf(CLASSINFO(wxFrame)))
+ p = p->GetParent();
+
+ wxFrame* frame = wxDynamicCast(p, wxFrame);
+ if (frame)
+ {
+ wxString msg = wxString::Format(wxT("Found position %ld"), position);
+ frame->SetStatusText(msg, 1);
+ }
+#endif
+ }
+
+ event.Skip();
+}
+
+/// Left-up
+void wxRichTextCtrl::OnLeftUp(wxMouseEvent& WXUNUSED(event))
+{
+ if (m_dragging)
+ {
+ m_dragging = false;
+ if (GetCapture() == this)
+ ReleaseMouse();
+ }
+}
+
+/// Left-click
+void wxRichTextCtrl::OnMoveMouse(wxMouseEvent& event)
+{
+ if (!event.Dragging())
+ {
+ event.Skip();
+ return;
+ }
+
+ wxClientDC dc(this);
+ PrepareDC(dc);
+ dc.SetFont(GetFont());
+
+ long position = 0;
+ wxPoint logicalPt = event.GetLogicalPosition(dc);
+ int hit = GetBuffer().HitTest(dc, logicalPt, position);
+
+ if (m_dragging && hit != wxRICHTEXT_HITTEST_NONE)
+ {
+ // TODO: test closeness
+
+ bool caretAtLineStart = false;
+
+ if (hit & wxRICHTEXT_HITTEST_BEFORE)
+ {
+ // If we're at the start of a line (but not first in para)
+ // then we should keep the caret showing at the start of the line
+ // by showing the m_caretAtLineStart flag.
+ wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(position);
+ wxRichTextLine* line = GetBuffer().GetLineAtPosition(position);
+
+ if (line && para && line->GetRange().GetStart() == position && para->GetRange().GetStart() != position)
+ caretAtLineStart = true;
+ position --;
+ }
+
+ if (m_caretPosition != position)
+ {
+ bool extendSel = ExtendSelection(m_caretPosition, position, wxRICHTEXT_SHIFT_DOWN);
+
+ MoveCaret(position, caretAtLineStart);
+ SetDefaultStyleToCursorStyle();
+
+ if (extendSel)
+ Refresh();
+ }
+ }
+}
+
+/// Right-click
+void wxRichTextCtrl::OnRightClick(wxMouseEvent& event)
+{
+ SetFocus();
+ event.Skip();
+}
+
+/// Left-double-click
+void wxRichTextCtrl::OnLeftDClick(wxMouseEvent& event)
+{
+ event.Skip();
+}
+
+/// Middle-click
+void wxRichTextCtrl::OnMiddleClick(wxMouseEvent& event)
+{
+ event.Skip();
+}
+
+/// Key press
+void wxRichTextCtrl::OnChar(wxKeyEvent& event)
+{
+ int flags = 0;
+ if (event.ControlDown())
+ flags |= wxRICHTEXT_CTRL_DOWN;
+ if (event.ShiftDown())
+ flags |= wxRICHTEXT_SHIFT_DOWN;
+ if (event.AltDown())
+ flags |= wxRICHTEXT_ALT_DOWN;
+
+ if (event.GetKeyCode() == WXK_LEFT ||
+ event.GetKeyCode() == WXK_RIGHT ||
+ event.GetKeyCode() == WXK_UP ||
+ event.GetKeyCode() == WXK_DOWN ||
+ event.GetKeyCode() == WXK_HOME ||
+ event.GetKeyCode() == WXK_PAGEUP ||
+ event.GetKeyCode() == WXK_PAGEDOWN ||
+ event.GetKeyCode() == WXK_PRIOR ||
+ event.GetKeyCode() == WXK_NEXT ||
+ event.GetKeyCode() == WXK_END)
+ {
+ Navigate(event.GetKeyCode(), flags);
+ }
+ else if (event.GetKeyCode() == WXK_RETURN)
+ {
+ BeginBatchUndo(_("Insert Text"));
+
+ long newPos = m_caretPosition;
+
+ DeleteSelectedContent(& newPos);
+
+ GetBuffer().InsertNewlineWithUndo(newPos+1, this);
+
+ wxRichTextEvent cmdEvent(
+ wxEVT_COMMAND_RICHTEXT_RETURN,
+ GetId());
+ cmdEvent.SetEventObject(this);
+ cmdEvent.SetFlags(flags);
+ GetEventHandler()->ProcessEvent(cmdEvent);
+
+ EndBatchUndo();
+ SetDefaultStyleToCursorStyle();
+ }
+ else if (event.GetKeyCode() == WXK_BACK)
+ {
+ BeginBatchUndo(_("Delete Text"));
+
+ // Submit range in character positions, which are greater than caret positions,
+ // so subtract 1 for deleted character and add 1 for conversion to character position.
+ if (m_caretPosition > -1 && !HasSelection())
+ {
+ GetBuffer().DeleteRangeWithUndo(wxRichTextRange(m_caretPosition, m_caretPosition),
+ m_caretPosition, // Current caret position
+ m_caretPosition-1, // New caret position
+ this);
+ }
+ else
+ DeleteSelectedContent();
+
+ EndBatchUndo();
+
+ // Shouldn't this be in Do()?
+ if (GetLastPosition() == -1)
+ {
+ GetBuffer().Reset();
+
+ m_caretPosition = -1;
+ PositionCaret();
+ SetDefaultStyleToCursorStyle();
+ }
+
+ }
+ else if (event.GetKeyCode() == WXK_DELETE)
+ {
+ BeginBatchUndo(_("Delete Text"));
+
+ // Submit range in character positions, which are greater than caret positions,
+ if (m_caretPosition < GetBuffer().GetRange().GetEnd()+1 && !HasSelection())
+ {
+ GetBuffer().DeleteRangeWithUndo(wxRichTextRange(m_caretPosition+1, m_caretPosition+1),
+ m_caretPosition, // Current caret position
+ m_caretPosition+1, // New caret position
+ this);
+ }
+ else
+ DeleteSelectedContent();
+
+ EndBatchUndo();
+
+ // Shouldn't this be in Do()?
+ if (GetLastPosition() == -1)
+ {
+ GetBuffer().Reset();
+
+ m_caretPosition = -1;
+ PositionCaret();
+ SetDefaultStyleToCursorStyle();
+ }
+ }
+ else
+ {
+ BeginBatchUndo(_("Insert Text"));
+
+ long newPos = m_caretPosition;
+ DeleteSelectedContent(& newPos);
+
+ wxString str = (wxChar) event.GetKeyCode();
+ GetBuffer().InsertTextWithUndo(newPos+1, str, this);
+
+ EndBatchUndo();
+
+ SetDefaultStyleToCursorStyle();
+ }
+#if 0
+ else
+ event.Skip();
+#endif
+}
+
+/// Delete content if there is a selection, e.g. when pressing a key.
+bool wxRichTextCtrl::DeleteSelectedContent(long* newPos)
+{
+ if (HasSelection())
+ {
+ long pos = m_selectionRange.GetStart();
+ GetBuffer().DeleteRangeWithUndo(m_selectionRange,
+ m_caretPosition, // Current caret position
+ pos, // New caret position
+ this);
+ m_selectionRange.SetRange(-2, -2);
+
+ if (newPos)
+ *newPos = pos-1;
+ return true;
+ }
+ else
+ return false;
+}
+
+/// Keyboard navigation
+
+/*
+
+Left: left one character
+Right: right one character
+Up: up one line
+Down: down one line
+Ctrl-Left: left one word
+Ctrl-Right: right one word
+Ctrl-Up: previous paragraph start
+Ctrl-Down: next start of paragraph
+Home: start of line
+End: end of line
+Ctrl-Home: start of document
+Ctrl-End: end of document
+Page-Up: Up a screen
+Page-Down: Down a screen
+
+Maybe:
+
+Ctrl-Alt-PgUp: Start of window
+Ctrl-Alt-PgDn: End of window
+F8: Start selection mode
+Esc: End selection mode
+
+Adding Shift does the above but starts/extends selection.
+
+
+ */
+
+bool wxRichTextCtrl::Navigate(int keyCode, int flags)
+{
+ bool success = false;
+ Freeze();
+
+ if (keyCode == WXK_RIGHT)
+ {
+ if (flags & wxRICHTEXT_CTRL_DOWN)
+ success = WordRight(1, flags);
+ else
+ success = MoveRight(1, flags);
+ }
+ else if (keyCode == WXK_LEFT)
+ {
+ if (flags & wxRICHTEXT_CTRL_DOWN)
+ success = WordLeft(1, flags);
+ else
+ success = MoveLeft(1, flags);
+ }
+ else if (keyCode == WXK_UP)
+ {
+ if (flags & wxRICHTEXT_CTRL_DOWN)
+ success = MoveToParagraphStart(flags);
+ else
+ success = MoveUp(1, flags);
+ }
+ else if (keyCode == WXK_DOWN)
+ {
+ if (flags & wxRICHTEXT_CTRL_DOWN)
+ success = MoveToParagraphEnd(flags);
+ else
+ success = MoveDown(1, flags);
+ }
+ else if (keyCode == WXK_PAGEUP || keyCode == WXK_PRIOR)
+ {
+ success = PageUp(1, flags);
+ }
+ else if (keyCode == WXK_PAGEDOWN || keyCode == WXK_NEXT)
+ {
+ success = PageDown(1, flags);
+ }
+ else if (keyCode == WXK_HOME)
+ {
+ if (flags & wxRICHTEXT_CTRL_DOWN)
+ success = MoveHome(flags);
+ else
+ success = MoveToLineStart(flags);
+ }
+ else if (keyCode == WXK_END)
+ {
+ if (flags & wxRICHTEXT_CTRL_DOWN)
+ success = MoveEnd(flags);
+ else
+ success = MoveToLineEnd(flags);
+ }
+
+ if (success)
+ {
+ ScrollIntoView(m_caretPosition, keyCode);
+ SetDefaultStyleToCursorStyle();
+ }
+
+ // Only refresh if something changed
+ Thaw(success);
+
+ return success;
+}
+
+/// Extend the selection. Selections are in caret positions.
+bool wxRichTextCtrl::ExtendSelection(long oldPos, long newPos, int flags)
+{
+ if (flags & wxRICHTEXT_SHIFT_DOWN)
+ {
+ // If not currently selecting, start selecting
+ if (m_selectionRange.GetStart() == -2)
+ {
+ m_selectionAnchor = oldPos;
+
+ if (oldPos > newPos)
+ m_selectionRange.SetRange(newPos+1, oldPos);
+ else
+ m_selectionRange.SetRange(oldPos+1, newPos);
+ }
+ else
+ {
+ // Always ensure that the selection range start is greater than
+ // the end.
+ if (newPos > m_selectionAnchor)
+ m_selectionRange.SetRange(m_selectionAnchor+1, newPos);
+ else
+ m_selectionRange.SetRange(newPos+1, m_selectionAnchor);
+ }
+
+ if (m_selectionRange.GetStart() > m_selectionRange.GetEnd())
+ {
+ wxLogDebug(wxT("Strange selection range"));
+ }
+
+ return true;
+ }
+ else
+ return false;
+}
+
+/// Scroll into view, returning true if we scrolled.
+/// This takes a _caret_ position.
+bool wxRichTextCtrl::ScrollIntoView(long position, int keyCode)
+{
+ wxRichTextLine* line = GetVisibleLineForCaretPosition(position);
+
+ if (!line)
+ return false;
+
+ int ppuX, ppuY;
+ GetScrollPixelsPerUnit(& ppuX, & ppuY);
+
+ int startX, startY;
+ GetViewStart(& startX, & startY);
+ startX = 0;
+ startY = startY * ppuY;
+
+ int sx, sy;
+ GetVirtualSize(& sx, & sy);
+ sx = 0;
+ if (ppuY != 0)
+ sy = sy/ppuY;
+
+ wxRect rect = line->GetRect();
+
+ bool scrolled = false;
+
+ wxSize clientSize = GetClientSize();
+
+ // Going down
+ if (keyCode == WXK_DOWN || keyCode == WXK_RIGHT || keyCode == WXK_END || keyCode == WXK_NEXT || keyCode == WXK_PAGEDOWN)
+ {
+ if ((rect.y + rect.height) > (clientSize.y + startY))
+ {
+ // Make it scroll so this item is at the bottom
+ // of the window
+ int y = rect.y - (clientSize.y - rect.height);
+ SetScrollbars(ppuX, ppuY, sx, sy, 0, (int) (0.5 + y/ppuY));
+ }
+ else if (rect.y < startY)
+ {
+ // Make it scroll so this item is at the top
+ // of the window
+ int y = rect.y ;
+ SetScrollbars(ppuX, ppuY, sx, sy, 0, (int) (0.5 + y/ppuY));
+ }
+ scrolled = true;
+ }
+ // Going up
+ else if (keyCode == WXK_UP || keyCode == WXK_LEFT || keyCode == WXK_HOME || keyCode == WXK_PRIOR || keyCode == WXK_PAGEUP)
+ {
+ if (rect.y < startY)
+ {
+ // Make it scroll so this item is at the top
+ // of the window
+ int y = rect.y ;
+ SetScrollbars(ppuX, ppuY, sx, sy, 0, (int) (0.5 + y/ppuY));
+ }
+ else if ((rect.y + rect.height) > (clientSize.y + startY))
+ {
+ // Make it scroll so this item is at the bottom
+ // of the window
+ int y = rect.y - (clientSize.y - rect.height);
+ SetScrollbars(ppuX, ppuY, sx, sy, 0, (int) (0.5 + y/ppuY));
+ }
+ scrolled = true;
+ }
+ PositionCaret();
+
+ return scrolled;
+}
+
+/// Is the given position visible on the screen?
+bool wxRichTextCtrl::IsPositionVisible(long pos) const
+{
+ wxRichTextLine* line = GetVisibleLineForCaretPosition(pos-1);
+
+ if (!line)
+ return false;
+
+ int ppuX, ppuY;
+ GetScrollPixelsPerUnit(& ppuX, & ppuY);
+
+ int startX, startY;
+ GetViewStart(& startX, & startY);
+ startX = 0;
+ startY = startY * ppuY;
+
+ int sx, sy;
+ GetVirtualSize(& sx, & sy);
+ sx = 0;
+ if (ppuY != 0)
+ sy = sy/ppuY;
+
+ wxRect rect = line->GetRect();
+
+ wxSize clientSize = GetClientSize();
+
+ return !(((rect.y + rect.height) > (clientSize.y + startY)) || rect.y < startY);
+}
+
+void wxRichTextCtrl::SetCaretPosition(long position, bool showAtLineStart)
+{
+ m_caretPosition = position;
+ m_caretAtLineStart = showAtLineStart;
+}
+
+/// Move caret one visual step forward: this may mean setting a flag
+/// and keeping the same position if we're going from the end of one line
+/// to the start of the next, which may be the exact same caret position.
+void wxRichTextCtrl::MoveCaretForward(long oldPosition)
+{
+ wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(oldPosition);
+
+ // Only do the check if we're not at the end of the paragraph (where things work OK
+ // anyway)
+ if (para && (oldPosition != para->GetRange().GetEnd() - 1))
+ {
+ wxRichTextLine* line = GetBuffer().GetLineAtPosition(oldPosition);
+
+ if (line)
+ {
+ // We're at the end of a line. See whether we need to
+ // stay at the same actual caret position but change visual
+ // position, or not.
+ if (oldPosition == line->GetRange().GetEnd())
+ {
+ if (m_caretAtLineStart)
+ {
+ // We're already at the start of the line, so actually move on now.
+ m_caretPosition = oldPosition + 1;
+ m_caretAtLineStart = false;
+ }
+ else
+ {
+ // We're showing at the end of the line, so keep to
+ // the same position but indicate that we're to show
+ // at the start of the next line.
+ m_caretPosition = oldPosition;
+ m_caretAtLineStart = true;
+ }
+ SetDefaultStyleToCursorStyle();
+ return;
+ }
+ }
+ }
+ m_caretPosition ++;
+ SetDefaultStyleToCursorStyle();
+}
+
+/// Move caret one visual step backward: this may mean setting a flag
+/// and keeping the same position if we're going from the end of one line
+/// to the start of the next, which may be the exact same caret position.
+void wxRichTextCtrl::MoveCaretBack(long oldPosition)
+{
+ wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(oldPosition);
+
+ // Only do the check if we're not at the start of the paragraph (where things work OK
+ // anyway)
+ if (para && (oldPosition != para->GetRange().GetStart()))
+ {
+ wxRichTextLine* line = GetBuffer().GetLineAtPosition(oldPosition);
+
+ if (line)
+ {
+ // We're at the start of a line. See whether we need to
+ // stay at the same actual caret position but change visual
+ // position, or not.
+ if (oldPosition == line->GetRange().GetStart())
+ {
+ m_caretPosition = oldPosition-1;
+ m_caretAtLineStart = true;
+ return;
+ }
+ else if (oldPosition == line->GetRange().GetEnd())
+ {
+ if (m_caretAtLineStart)
+ {
+ // We're at the start of the line, so keep the same caret position
+ // but clear the start-of-line flag.
+ m_caretPosition = oldPosition;
+ m_caretAtLineStart = false;
+ }
+ else
+ {
+ // We're showing at the end of the line, so go back
+ // to the previous character position.
+ m_caretPosition = oldPosition - 1;
+ }
+ SetDefaultStyleToCursorStyle();
+ return;
+ }
+ }
+ }
+ m_caretPosition --;
+ SetDefaultStyleToCursorStyle();
+}
+
+/// Move right
+bool wxRichTextCtrl::MoveRight(int noPositions, int flags)
+{
+ long endPos = GetBuffer().GetRange().GetEnd();
+
+ if (m_caretPosition + noPositions < endPos)
+ {
+ long oldPos = m_caretPosition;
+ long newPos = m_caretPosition + noPositions;
+
+ bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
+ if (!extendSel)
+ SelectNone();
+
+ // Determine by looking at oldPos and m_caretPosition whether
+ // we moved from the end of a line to the start of the next line, in which case
+ // we want to adjust the caret position such that it is positioned at the
+ // start of the next line, rather than jumping past the first character of the
+ // line.
+ if (noPositions == 1 && !extendSel)
+ MoveCaretForward(oldPos);
+ else
+ SetCaretPosition(newPos);
+
+ PositionCaret();
+ SetDefaultStyleToCursorStyle();
+
+ if (!IsFrozen())
+ Refresh(); // TODO: optimize so that if we didn't change the selection, we don't refresh
+ return true;
+ }
+ else
+ return false;
+}
+
+/// Move left
+bool wxRichTextCtrl::MoveLeft(int noPositions, int flags)
+{
+ long startPos = -1;
+
+ if (m_caretPosition > startPos - noPositions + 1)
+ {
+ long oldPos = m_caretPosition;
+ long newPos = m_caretPosition - noPositions;
+ bool extendSel = ExtendSelection(m_caretPosition, newPos, flags);
+ if (!extendSel)
+ SelectNone();
+
+ if (noPositions == 1 && !extendSel)
+ MoveCaretBack(oldPos);
+ else
+ SetCaretPosition(newPos);
+
+ PositionCaret();
+ SetDefaultStyleToCursorStyle();
+
+ if (!IsFrozen())
+ Refresh();
+ return true;
+ }
+ else
+ return false;
+}
+
+/// Move up
+bool wxRichTextCtrl::MoveUp(int noLines, int flags)
+{
+ return MoveDown(- noLines, flags);
+}
+
+/// Move up
+bool wxRichTextCtrl::MoveDown(int noLines, int flags)
+{
+ long lineNumber = GetBuffer().GetVisibleLineNumber(m_caretPosition, true, m_caretAtLineStart);
+ wxPoint pt = GetCaret()->GetPosition();
+ long newLine = lineNumber + noLines;
+
+ if (lineNumber != -1)
+ {
+ if (noLines > 0)
+ {
+ long lastLine = GetBuffer().GetVisibleLineNumber(GetBuffer().GetRange().GetEnd());
+
+ if (newLine > lastLine)
+ return false;
+ }
+ else
+ {
+ if (newLine < 0)
+ return false;
+ }
+ }
+
+ wxRichTextLine* lineObj = GetBuffer().GetLineForVisibleLineNumber(newLine);
+ if (lineObj)
+ {
+ pt.y = lineObj->GetAbsolutePosition().y + 2;
+ }
+ else
+ return false;
+
+ long newPos = 0;
+ wxClientDC dc(this);
+ PrepareDC(dc);
+ dc.SetFont(GetFont());
+
+ int hitTest = GetBuffer().HitTest(dc, pt, newPos);
+
+ if (hitTest != wxRICHTEXT_HITTEST_NONE)
+ {
+ // If end of previous line, and hitTest is wxRICHTEXT_HITTEST_BEFORE,
+ // we want to be at the end of the last line but with m_caretAtLineStart set to true,
+ // so we view the caret at the start of the line.
+ bool caretLineStart = false;
+ if (hitTest == wxRICHTEXT_HITTEST_BEFORE)
+ {
+ wxRichTextLine* thisLine = GetBuffer().GetLineAtPosition(newPos-1);
+ if (thisLine && (newPos-1) == thisLine->GetRange().GetEnd())
+ {
+ // if (para->GetRange().GetStart() != thisLine->GetRange().GetStart())
+ {
+ newPos --;
+ caretLineStart = true;
+ }
+ }
+ else
+ {
+ wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(newPos);
+ if (para && para->GetRange().GetStart() == newPos)
+ newPos --;
+ }
+ }
+
+ long newSelEnd = newPos;
+
+ if (!ExtendSelection(m_caretPosition, newSelEnd, flags))
+ SelectNone();
+
+ SetCaretPosition(newPos, caretLineStart);
+ PositionCaret();
+ SetDefaultStyleToCursorStyle();
+
+ if (!IsFrozen())
+ Refresh();
+ return true;
+ }
+ else
+ return false;
+}
+
+/// Move to the end of the paragraph
+bool wxRichTextCtrl::MoveToParagraphEnd(int flags)
+{
+ wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(m_caretPosition, true);
+ if (para)
+ {
+ long newPos = para->GetRange().GetEnd() - 1;
+ if (!ExtendSelection(m_caretPosition, newPos, flags))
+ SelectNone();
+
+ SetCaretPosition(newPos);
+ PositionCaret();
+ SetDefaultStyleToCursorStyle();
+
+ if (!IsFrozen())
+ Refresh();
+ return true;
+ }
+
+ return false;
+}
+
+/// Move to the start of the paragraph
+bool wxRichTextCtrl::MoveToParagraphStart(int flags)
+{
+ wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(m_caretPosition, true);
+ if (para)
+ {
+ long newPos = para->GetRange().GetStart() - 1;
+ if (!ExtendSelection(m_caretPosition, newPos, flags))
+ SelectNone();
+
+ SetCaretPosition(newPos);
+ PositionCaret();
+ SetDefaultStyleToCursorStyle();
+
+ if (!IsFrozen())
+ Refresh();
+ return true;
+ }
+
+ return false;
+}
+
+/// Move to the end of the line
+bool wxRichTextCtrl::MoveToLineEnd(int flags)
+{
+ wxRichTextLine* line = GetVisibleLineForCaretPosition(m_caretPosition);
+
+ if (line)
+ {
+ long newPos = line->GetRange().GetEnd();
+ if (!ExtendSelection(m_caretPosition, newPos, flags))
+ SelectNone();
+
+ SetCaretPosition(newPos);
+ PositionCaret();
+ SetDefaultStyleToCursorStyle();
+
+ if (!IsFrozen())
+ Refresh();
+ return true;
+ }
+
+ return false;
+}
+
+/// Move to the start of the line
+bool wxRichTextCtrl::MoveToLineStart(int flags)
+{
+ wxRichTextLine* line = GetVisibleLineForCaretPosition(m_caretPosition);
+ if (line)
+ {
+ long newPos = line->GetRange().GetStart()-1;
+
+ if (!ExtendSelection(m_caretPosition, newPos, flags))
+ SelectNone();
+
+ wxRichTextParagraph* para = GetBuffer().GetParagraphForLine(line);
+
+ SetCaretPosition(newPos, para->GetRange().GetStart() != line->GetRange().GetStart());
+ PositionCaret();
+ SetDefaultStyleToCursorStyle();
+
+ if (!IsFrozen())
+ Refresh();
+ return true;
+ }
+
+ return false;
+}
+
+/// Move to the start of the buffer
+bool wxRichTextCtrl::MoveHome(int flags)
+{
+ if (m_caretPosition != -1)
+ {
+ if (!ExtendSelection(m_caretPosition, -1, flags))
+ SelectNone();
+
+ SetCaretPosition(-1);
+ PositionCaret();
+ SetDefaultStyleToCursorStyle();
+
+ if (!IsFrozen())
+ Refresh();
+ return true;
+ }
+ else
+ return false;
+}
+
+/// Move to the end of the buffer
+bool wxRichTextCtrl::MoveEnd(int flags)
+{
+ long endPos = GetBuffer().GetRange().GetEnd()-1;
+
+ if (m_caretPosition != endPos)
+ {
+ if (!ExtendSelection(m_caretPosition, endPos, flags))
+ SelectNone();
+
+ SetCaretPosition(endPos);
+ PositionCaret();
+ SetDefaultStyleToCursorStyle();
+
+ if (!IsFrozen())
+ Refresh();
+ return true;
+ }
+ else
+ return false;
+}
+
+/// Move noPages pages up
+bool wxRichTextCtrl::PageUp(int noPages, int flags)
+{
+ return PageDown(- noPages, flags);
+}
+
+/// Move noPages pages down
+bool wxRichTextCtrl::PageDown(int noPages, int flags)
+{
+ // Calculate which line occurs noPages * screen height further down.
+ wxRichTextLine* line = GetVisibleLineForCaretPosition(m_caretPosition);
+ if (line)
+ {
+ wxSize clientSize = GetClientSize();
+ int newY = line->GetAbsolutePosition().y + noPages*clientSize.y;
+
+ wxRichTextLine* newLine = GetBuffer().GetLineAtYPosition(newY);
+ if (newLine)
+ {
+ long pos = newLine->GetRange().GetStart()-1;
+ if (pos != m_caretPosition)
+ {
+ wxRichTextParagraph* para = GetBuffer().GetParagraphForLine(newLine);
+
+ if (!ExtendSelection(m_caretPosition, pos, flags))
+ SelectNone();
+
+ SetCaretPosition(pos, para->GetRange().GetStart() != newLine->GetRange().GetStart());
+ PositionCaret();
+ SetDefaultStyleToCursorStyle();
+
+ if (!IsFrozen())
+ Refresh();
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+// Finds the caret position for the next word
+long wxRichTextCtrl::FindNextWordPosition(int direction) const
+{
+ long endPos = GetBuffer().GetRange().GetEnd();
+
+ if (direction > 0)
+ {
+ long i = m_caretPosition+1+direction; // +1 for conversion to character pos
+
+ // First skip current text to space
+ while (i < endPos && i > -1)
+ {
+ // i is in character, not caret positions
+ wxString text = GetBuffer().GetTextForRange(wxRichTextRange(i, i));
+ if (text != wxT(" ") && !text.IsEmpty())
+ i += direction;
+ else
+ {
+ break;
+ }
+ }
+ while (i < endPos && i > -1)
+ {
+ // i is in character, not caret positions
+ wxString text = GetBuffer().GetTextForRange(wxRichTextRange(i, i));
+ if (text.IsEmpty()) // End of paragraph, or maybe an image
+ return wxMax(-1, i - 1);
+ else if (text == wxT(" ") || text.IsEmpty())
+ i += direction;
+ else
+ {
+ // Convert to caret position
+ return wxMax(-1, i - 1);
+ }
+ }
+ if (i >= endPos)
+ return endPos-1;
+ return i-1;
+ }
+ else
+ {
+ long i = m_caretPosition;
+
+ // First skip white space
+ while (i < endPos && i > -1)
+ {
+ // i is in character, not caret positions
+ wxString text = GetBuffer().GetTextForRange(wxRichTextRange(i, i));
+ if (text.IsEmpty()) // End of paragraph, or maybe an image
+ break;
+ else if (text == wxT(" ") || text.IsEmpty())
+ i += direction;
+ else
+ break;
+ }
+ // Next skip current text to space
+ while (i < endPos && i > -1)
+ {
+ // i is in character, not caret positions
+ wxString text = GetBuffer().GetTextForRange(wxRichTextRange(i, i));
+ if (text != wxT(" ") /* && !text.IsEmpty() */)
+ i += direction;
+ else
+ {
+ return i;
+ break;
+ }
+ }
+ if (i < -1)
+ return -1;
+ return i;
+ }
+}
+
+/// Move n words left
+bool wxRichTextCtrl::WordLeft(int WXUNUSED(n), int flags)
+{
+ long pos = FindNextWordPosition(-1);
+ if (pos != m_caretPosition)
+ {
+ wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(pos, true);
+
+ if (!ExtendSelection(m_caretPosition, pos, flags))
+ SelectNone();
+
+ SetCaretPosition(pos, para->GetRange().GetStart() != pos);
+ PositionCaret();
+ SetDefaultStyleToCursorStyle();
+
+ if (!IsFrozen())
+ Refresh();
+ return true;
+ }
+
+ return false;
+}
+
+/// Move n words right
+bool wxRichTextCtrl::WordRight(int WXUNUSED(n), int flags)
+{
+ long pos = FindNextWordPosition(1);
+ if (pos != m_caretPosition)
+ {
+ wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(pos, true);
+
+ if (!ExtendSelection(m_caretPosition, pos, flags))
+ SelectNone();
+
+ SetCaretPosition(pos, para->GetRange().GetStart() != pos);
+ PositionCaret();
+ SetDefaultStyleToCursorStyle();
+
+ if (!IsFrozen())
+ Refresh();
+ return true;
+ }
+
+ return false;
+}
+
+/// Sizing
+void wxRichTextCtrl::OnSize(wxSizeEvent& event)
+{
+ GetBuffer().SetDirty(true);
+
+ RecreateBuffer();
+
+ event.Skip();
+}
+
+/// Set up scrollbars, e.g. after a resize
+void wxRichTextCtrl::SetupScrollbars()
+{
+ if (m_freezeCount)
+ return;
+
+ if (GetBuffer().IsEmpty())
+ {
+ SetScrollbars(0, 0, 0, 0, 0, 0);
+ return;
+ }
+
+ // TODO: reimplement scrolling so we scroll by line, not by fixed number
+ // of pixels. See e.g. wxVScrolledWindow for ideas.
+ int pixelsPerUnit = 5; // 10;
+ wxSize clientSize = GetClientSize();
+
+ int maxHeight = GetBuffer().GetCachedSize().y;
+
+ int unitsY = maxHeight/pixelsPerUnit;
+
+ int startX, startY;
+ GetViewStart(& startX, & startY);
+
+ int maxPositionX = 0; // wxMax(sz.x - clientSize.x, 0);
+ int maxPositionY = (wxMax(maxHeight - clientSize.y, 0))/pixelsPerUnit;
+
+ // Move to previous scroll position if
+ // possible
+ SetScrollbars(0, pixelsPerUnit,
+ 0, unitsY,
+ wxMin(maxPositionX, startX), wxMin(maxPositionY, startY));
+}
+
+/// Paint the background
+void wxRichTextCtrl::PaintBackground(wxDC& dc)
+{
+ wxColour backgroundColour = GetBackgroundColour();
+ if (!backgroundColour.Ok())
+ backgroundColour = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
+
+ // Clear the background
+ dc.SetBrush(wxBrush(backgroundColour));
+ dc.SetPen(*wxTRANSPARENT_PEN);
+ wxRect windowRect(wxPoint(0, 0), GetClientSize());
+ windowRect.x -= 2; windowRect.y -= 2;
+ windowRect.width += 4; windowRect.height += 4;
+
+ // We need to shift the rectangle to take into account
+ // scrolling. Converting device to logical coordinates.
+ CalcUnscrolledPosition(windowRect.x, windowRect.y, & windowRect.x, & windowRect.y);
+ dc.DrawRectangle(windowRect);
+}
+
+/// Recreate buffer bitmap if necessary
+bool wxRichTextCtrl::RecreateBuffer(const wxSize& size)
+{
+ wxSize sz = size;
+ if (sz == wxDefaultSize)
+ sz = GetClientSize();
+
+ if (sz.x < 1 || sz.y < 1)
+ return false;
+
+ if (!m_bufferBitmap.Ok() || m_bufferBitmap.GetWidth() < sz.x || m_bufferBitmap.GetHeight() < sz.y)
+ m_bufferBitmap = wxBitmap(sz.x, sz.y);
+ return m_bufferBitmap.Ok();
+}
+
+// ----------------------------------------------------------------------------
+// file IO functions
+// ----------------------------------------------------------------------------
+
+bool wxRichTextCtrl::LoadFile(const wxString& filename, int type)
+{
+ bool success = GetBuffer().LoadFile(filename, type);
+ if (success)
+ m_filename = filename;
+
+ DiscardEdits();
+ SetInsertionPoint(0);
+ Layout();
+ PositionCaret();
+ Refresh();
+ SendUpdateEvent();
+
+ if (success)
+ return true;
+ else
+ {
+ wxLogError(_("File couldn't be loaded."));
+
+ return false;
+ }
+}
+
+bool wxRichTextCtrl::SaveFile(const wxString& filename, int type)
+{
+ wxString filenameToUse = filename.empty() ? m_filename : filename;
+ if ( filenameToUse.empty() )
+ {
+ // what kind of message to give? is it an error or a program bug?
+ wxLogDebug(wxT("Can't save textctrl to file without filename."));
+
+ return false;
+ }
+
+ if (GetBuffer().SaveFile(filenameToUse, type))
+ {
+ m_filename = filenameToUse;
+
+ DiscardEdits();
+
+ return true;
+
+ }
+
+ wxLogError(_("The text couldn't be saved."));
+
+ return false;
+}
+
+// ----------------------------------------------------------------------------
+// wxRichTextCtrl specific functionality
+// ----------------------------------------------------------------------------
+
+/// Add a new paragraph of text to the end of the buffer
+wxRichTextRange wxRichTextCtrl::AddParagraph(const wxString& text)
+{
+ return GetBuffer().AddParagraph(text);
+}
+
+/// Add an image
+wxRichTextRange wxRichTextCtrl::AddImage(const wxImage& image)
+{
+ return GetBuffer().AddImage(image);
+}
+
+// ----------------------------------------------------------------------------
+// selection and ranges
+// ----------------------------------------------------------------------------
+
+void wxRichTextCtrl::SelectAll()
+{
+ SetSelection(0, GetLastPosition());
+ m_selectionAnchor = -1;
+}
+
+/// Select none
+void wxRichTextCtrl::SelectNone()
+{
+ SetSelection(-2, -2);
+ m_selectionAnchor = -2;
+}
+
+wxString wxRichTextCtrl::GetStringSelection() const
+{
+ long from, to;
+ GetSelection(&from, &to);
+
+ return GetRange(from, to);
+}
+
+// do the window-specific processing after processing the update event
+void wxRichTextCtrl::DoUpdateWindowUI(wxUpdateUIEvent& event)
+{
+ if ( event.GetSetEnabled() )
+ Enable(event.GetEnabled());
+
+ if ( event.GetSetText() )
+ {
+ if ( event.GetText() != GetValue() )
+ SetValue(event.GetText());
+ }
+}
+
+// ----------------------------------------------------------------------------
+// hit testing
+// ----------------------------------------------------------------------------
+
+wxTextCtrlHitTestResult
+wxRichTextCtrl::HitTest(const wxPoint& pt, wxTextCoord *x, wxTextCoord *y) const
+{
+ // implement in terms of the other overload as the native ports typically
+ // can get the position and not (x, y) pair directly (although wxUniv
+ // directly gets x and y -- and so overrides this method as well)
+ long pos;
+ wxTextCtrlHitTestResult rc = HitTest(pt, &pos);
+
+ if ( rc != wxTE_HT_UNKNOWN )
+ {
+ PositionToXY(pos, x, y);
+ }
+
+ return rc;
+}
+
+wxTextCtrlHitTestResult
+wxRichTextCtrl::HitTest(const wxPoint& pt,
+ long * pos) const
+{
+ wxClientDC dc((wxRichTextCtrl*) this);
+ ((wxRichTextCtrl*)this)->PrepareDC(dc);
+
+ int hit = ((wxRichTextCtrl*)this)->GetBuffer().HitTest(dc, pt, *pos);
+ if (hit == wxRICHTEXT_HITTEST_BEFORE)
+ return wxTE_HT_BEFORE;
+ else if (hit == wxRICHTEXT_HITTEST_AFTER)
+ return wxTE_HT_BEYOND;
+ else if (hit == wxRICHTEXT_HITTEST_ON)
+ return wxTE_HT_ON_TEXT;
+ else
+ return wxTE_HT_UNKNOWN;
+}
+
+// ----------------------------------------------------------------------------
+// set/get the controls text
+// ----------------------------------------------------------------------------
+
+wxString wxRichTextCtrl::GetValue() const
+{
+ return GetBuffer().GetText();
+}
+
+wxString wxRichTextCtrl::GetRange(long from, long to) const
+{
+ return GetBuffer().GetTextForRange(wxRichTextRange(from, to));
+}
+
+void wxRichTextCtrl::SetValue(const wxString& value)
+{
+ Clear();
+
+ // if the text is long enough, it's faster to just set it instead of first
+ // comparing it with the old one (chances are that it will be different
+ // anyhow, this comparison is there to avoid flicker for small single-line
+ // edit controls mostly)
+ if ( (value.length() > 0x400) || (value != GetValue()) )
+ {
+ DoWriteText(value, false /* not selection only */);
+
+ // for compatibility, don't move the cursor when doing SetValue()
+ SetInsertionPoint(0);
+ }
+ else // same text
+ {
+ // still send an event for consistency
+ SendUpdateEvent();
+ }
+
+ // we should reset the modified flag even if the value didn't really change
+
+ // mark the control as being not dirty - we changed its text, not the
+ // user
+ DiscardEdits();
+}
+
+void wxRichTextCtrl::WriteText(const wxString& value)
+{
+ DoWriteText(value);
+}
+
+void wxRichTextCtrl::DoWriteText(const wxString& value, bool WXUNUSED(selectionOnly))
+{
+ GetBuffer().InsertTextWithUndo(m_caretPosition+1, value, this);
+}
+
+void wxRichTextCtrl::AppendText(const wxString& text)
+{
+ SetInsertionPointEnd();
+
+ WriteText(text);
+}
+
+/// Write an image at the current insertion point
+bool wxRichTextCtrl::WriteImage(const wxImage& image, int bitmapType)
+{
+ wxRichTextImageBlock imageBlock;
+
+ wxImage image2 = image;
+ if (imageBlock.MakeImageBlock(image2, bitmapType))
+ return WriteImage(imageBlock);
+ else
+ return false;
+}
+
+bool wxRichTextCtrl::WriteImage(const wxString& filename, int bitmapType)
+{
+ wxRichTextImageBlock imageBlock;
+
+ wxImage image;
+ if (imageBlock.MakeImageBlock(filename, bitmapType, image, false))
+ return WriteImage(imageBlock);
+ else
+ return false;
+}
+
+bool wxRichTextCtrl::WriteImage(const wxRichTextImageBlock& imageBlock)
+{
+ return GetBuffer().InsertImageWithUndo(m_caretPosition+1, imageBlock, this);
+}
+
+bool wxRichTextCtrl::WriteImage(const wxBitmap& bitmap, int bitmapType)
+{
+ if (bitmap.Ok())
+ {
+ wxRichTextImageBlock imageBlock;
+
+ wxImage image = bitmap.ConvertToImage();
+ if (image.Ok() && imageBlock.MakeImageBlock(image, bitmapType))
+ return WriteImage(imageBlock);
+ else
+ return false;
+ }
+ return false;
+}
+
+/// Insert a newline (actually paragraph) at the current insertion point.
+bool wxRichTextCtrl::Newline()
+{
+ return GetBuffer().InsertNewlineWithUndo(m_caretPosition+1, this);
+}
+
+
+// ----------------------------------------------------------------------------
+// Clipboard operations
+// ----------------------------------------------------------------------------
+
+void wxRichTextCtrl::Copy()
+{
+ if (CanCopy())
+ {
+ wxRichTextRange range = GetSelectionRange();
+ GetBuffer().CopyToClipboard(range);
+ }
+}
+
+void wxRichTextCtrl::Cut()
+{
+ if (CanCut())
+ {
+ wxRichTextRange range = GetSelectionRange();
+ GetBuffer().CopyToClipboard(range);
+
+ DeleteSelectedContent();
+ Layout();
+ Refresh();
+ }
+}
+
+void wxRichTextCtrl::Paste()
+{
+ if (CanPaste())
+ {
+ BeginBatchUndo(_("Paste"));
+
+ long newPos = m_caretPosition;
+ DeleteSelectedContent(& newPos);
+
+ GetBuffer().PasteFromClipboard(newPos);
+
+ EndBatchUndo();
+ }
+}
+
+void wxRichTextCtrl::DeleteSelection()
+{
+ if (CanDeleteSelection())
+ {
+ DeleteSelectedContent();
+ }
+}
+
+bool wxRichTextCtrl::HasSelection() const
+{
+ return m_selectionRange.GetStart() != -2 && m_selectionRange.GetEnd() != -2;
+}
+
+bool wxRichTextCtrl::CanCopy() const
+{
+ // Can copy if there's a selection
+ return HasSelection();
+}
+
+bool wxRichTextCtrl::CanCut() const
+{
+ return HasSelection() && IsEditable();
+}
+
+bool wxRichTextCtrl::CanPaste() const
+{
+ if ( !IsEditable() )
+ return false;
+
+ return GetBuffer().CanPasteFromClipboard();
+}
+
+bool wxRichTextCtrl::CanDeleteSelection() const
+{
+ return HasSelection() && IsEditable();
+}
+
+
+// ----------------------------------------------------------------------------
+// Accessors
+// ----------------------------------------------------------------------------
+
+void wxRichTextCtrl::SetEditable(bool editable)
+{
+ m_editable = editable;
+}
+
+void wxRichTextCtrl::SetInsertionPoint(long pos)
+{
+ SelectNone();
+
+ m_caretPosition = pos - 1;
+}
+
+void wxRichTextCtrl::SetInsertionPointEnd()
+{
+ long pos = GetLastPosition();
+ SetInsertionPoint(pos);
+}
+
+long wxRichTextCtrl::GetInsertionPoint() const
+{
+ return m_caretPosition+1;
+}
+
+wxTextPos wxRichTextCtrl::GetLastPosition() const
+{
+ return GetBuffer().GetRange().GetEnd();
+}
+
+// If the return values from and to are the same, there is no
+// selection.
+void wxRichTextCtrl::GetSelection(long* from, long* to) const
+{
+ *from = m_selectionRange.GetStart();
+ *to = m_selectionRange.GetEnd();
+}
+
+bool wxRichTextCtrl::IsEditable() const
+{
+ return m_editable;
+}
+
+// ----------------------------------------------------------------------------
+// selection
+// ----------------------------------------------------------------------------
+
+void wxRichTextCtrl::SetSelection(long from, long to)
+{
+ // if from and to are both -1, it means (in wxWidgets) that all text should
+ // be selected.
+ if ( (from == -1) && (to == -1) )
+ {
+ from = 0;
+ to = GetLastPosition();
+ }
+
+ DoSetSelection(from, to);
+}
+
+void wxRichTextCtrl::DoSetSelection(long from, long to, bool WXUNUSED(scrollCaret))
+{
+ m_selectionAnchor = from;
+ m_selectionRange.SetRange(from, to);
+ if (!IsFrozen())
+ Refresh();
+ PositionCaret();
+}
+
+// ----------------------------------------------------------------------------
+// Editing
+// ----------------------------------------------------------------------------
+
+void wxRichTextCtrl::Replace(long WXUNUSED(from), long WXUNUSED(to), const wxString& value)
+{
+ BeginBatchUndo(_("Replace"));
+
+ DeleteSelectedContent();
+
+ DoWriteText(value, true /* selection only */);
+
+ EndBatchUndo();
+}
+
+void wxRichTextCtrl::Remove(long from, long to)
+{
+ SelectNone();
+
+ GetBuffer().DeleteRangeWithUndo(wxRichTextRange(from, to),
+ m_caretPosition, // Current caret position
+ from, // New caret position
+ this);
+
+ Layout();
+ if (!IsFrozen())
+ Refresh();
+}
+
+bool wxRichTextCtrl::IsModified() const
+{
+ return m_buffer.IsModified();
+}
+
+void wxRichTextCtrl::MarkDirty()
+{
+ m_buffer.Modify(true);
+}
+
+void wxRichTextCtrl::DiscardEdits()
+{
+ m_buffer.Modify(false);
+ m_buffer.GetCommandProcessor()->ClearCommands();
+}
+
+int wxRichTextCtrl::GetNumberOfLines() const
+{
+ return GetBuffer().GetParagraphCount();
+}
+
+// ----------------------------------------------------------------------------
+// Positions <-> coords
+// ----------------------------------------------------------------------------
+
+long wxRichTextCtrl::XYToPosition(long x, long y) const
+{
+ return GetBuffer().XYToPosition(x, y);
+}
+
+bool wxRichTextCtrl::PositionToXY(long pos, long *x, long *y) const
+{
+ return GetBuffer().PositionToXY(pos, x, y);
+}
+
+// ----------------------------------------------------------------------------
+//
+// ----------------------------------------------------------------------------
+
+void wxRichTextCtrl::ShowPosition(long pos)
+{
+ if (!IsPositionVisible(pos))
+ ScrollIntoView(pos-1, WXK_DOWN);
+}
+
+int wxRichTextCtrl::GetLineLength(long lineNo) const
+{
+ return GetBuffer().GetParagraphLength(lineNo);
+}
+
+wxString wxRichTextCtrl::GetLineText(long lineNo) const
+{
+ return GetBuffer().GetParagraphText(lineNo);
+}
+
+// ----------------------------------------------------------------------------
+// Undo/redo
+// ----------------------------------------------------------------------------
+
+void wxRichTextCtrl::Undo()
+{
+ if (CanUndo())
+ {
+ GetCommandProcessor()->Undo();
+ }
+}
+
+void wxRichTextCtrl::Redo()
+{
+ if (CanRedo())
+ {
+ GetCommandProcessor()->Redo();
+ }
+}
+
+bool wxRichTextCtrl::CanUndo() const
+{
+ return GetCommandProcessor()->CanUndo();
+}
+
+bool wxRichTextCtrl::CanRedo() const
+{
+ return GetCommandProcessor()->CanRedo();
+}
+
+// ----------------------------------------------------------------------------
+// implemenation details
+// ----------------------------------------------------------------------------
+
+void wxRichTextCtrl::Command(wxCommandEvent & event)
+{
+ SetValue(event.GetString());
+ GetEventHandler()->ProcessEvent(event);
+}
+
+void wxRichTextCtrl::OnDropFiles(wxDropFilesEvent& event)
+{
+ // By default, load the first file into the text window.
+ if (event.GetNumberOfFiles() > 0)
+ {
+ LoadFile(event.GetFiles()[0]);
+ }
+}
+
+// ----------------------------------------------------------------------------
+// text control event processing
+// ----------------------------------------------------------------------------
+
+bool wxRichTextCtrl::SendUpdateEvent()
+{
+ wxCommandEvent event(wxEVT_COMMAND_TEXT_UPDATED, GetId());
+ InitCommandEvent(event);
+
+ return GetEventHandler()->ProcessEvent(event);
+}
+
+void wxRichTextCtrl::InitCommandEvent(wxCommandEvent& event) const
+{
+ event.SetEventObject((wxControlBase *)this); // const_cast
+
+ switch ( m_clientDataType )
+ {
+ case wxClientData_Void:
+ event.SetClientData(GetClientData());
+ break;
+
+ case wxClientData_Object:
+ event.SetClientObject(GetClientObject());
+ break;
+
+ case wxClientData_None:
+ // nothing to do
+ ;
+ }
+}
+
+
+wxSize wxRichTextCtrl::DoGetBestSize() const
+{
+ return wxSize(10, 10);
+}
+
+// ----------------------------------------------------------------------------
+// standard handlers for standard edit menu events
+// ----------------------------------------------------------------------------
+
+void wxRichTextCtrl::OnCut(wxCommandEvent& WXUNUSED(event))
+{
+ Cut();
+}
+
+void wxRichTextCtrl::OnClear(wxCommandEvent& WXUNUSED(event))
+{
+ DeleteSelection();
+}
+
+void wxRichTextCtrl::OnCopy(wxCommandEvent& WXUNUSED(event))
+{
+ Copy();
+}
+
+void wxRichTextCtrl::OnPaste(wxCommandEvent& WXUNUSED(event))
+{
+ Paste();
+}
+
+void wxRichTextCtrl::OnUndo(wxCommandEvent& WXUNUSED(event))
+{
+ Undo();
+}
+
+void wxRichTextCtrl::OnRedo(wxCommandEvent& WXUNUSED(event))
+{
+ Redo();
+}
+
+void wxRichTextCtrl::OnUpdateCut(wxUpdateUIEvent& event)
+{
+ event.Enable( CanCut() );
+}
+
+void wxRichTextCtrl::OnUpdateCopy(wxUpdateUIEvent& event)
+{
+ event.Enable( CanCopy() );
+}
+
+void wxRichTextCtrl::OnUpdateClear(wxUpdateUIEvent& event)
+{
+ event.Enable( CanDeleteSelection() );
+}
+
+void wxRichTextCtrl::OnUpdatePaste(wxUpdateUIEvent& event)
+{
+ event.Enable( CanPaste() );
+}
+
+void wxRichTextCtrl::OnUpdateUndo(wxUpdateUIEvent& event)
+{
+ event.Enable( CanUndo() );
+ event.SetText( GetCommandProcessor()->GetUndoMenuLabel() );
+}
+
+void wxRichTextCtrl::OnUpdateRedo(wxUpdateUIEvent& event)
+{
+ event.Enable( CanRedo() );
+ event.SetText( GetCommandProcessor()->GetRedoMenuLabel() );
+}
+
+void wxRichTextCtrl::OnSelectAll(wxCommandEvent& WXUNUSED(event))
+{
+ SelectAll();
+}
+
+void wxRichTextCtrl::OnUpdateSelectAll(wxUpdateUIEvent& event)
+{
+ event.Enable(GetLastPosition() > 0);
+}
+
+void wxRichTextCtrl::OnContextMenu(wxContextMenuEvent& WXUNUSED(event))
+{
+ if (!m_contextMenu)
+ {
+ m_contextMenu = new wxMenu;
+ m_contextMenu->Append(wxID_UNDO, _("&Undo"));
+ m_contextMenu->Append(wxID_REDO, _("&Redo"));
+ m_contextMenu->AppendSeparator();
+ m_contextMenu->Append(wxID_CUT, _("Cu&t"));
+ m_contextMenu->Append(wxID_COPY, _("&Copy"));
+ m_contextMenu->Append(wxID_PASTE, _("&Paste"));
+ m_contextMenu->Append(wxID_CLEAR, _("&Delete"));
+ m_contextMenu->AppendSeparator();
+ m_contextMenu->Append(wxID_SELECTALL, _("Select &All"));
+ }
+ PopupMenu(m_contextMenu);
+ return;
+}
+
+bool wxRichTextCtrl::SetStyle(long start, long end, const wxTextAttrEx& style)
+{
+ return GetBuffer().SetStyle(wxRichTextRange(start, end), style);
+}
+
+bool wxRichTextCtrl::SetStyle(const wxRichTextRange& range, const wxRichTextAttr& style)
+{
+ return GetBuffer().SetStyle(range, style);
+}
+
+bool wxRichTextCtrl::SetDefaultStyle(const wxTextAttrEx& style)
+{
+ return GetBuffer().SetDefaultStyle(style);
+}
+
+const wxTextAttrEx& wxRichTextCtrl::GetDefaultStyleEx() const
+{
+ return GetBuffer().GetDefaultStyle();
+}
+
+bool wxRichTextCtrl::GetStyle(long position, wxTextAttrEx& style) const
+{
+ return GetBuffer().GetStyle(position, style);
+}
+
+bool wxRichTextCtrl::GetStyle(long position, wxRichTextAttr& style) const
+{
+ return GetBuffer().GetStyle(position, style);
+}
+
+/// Set font, and also the buffer attributes
+bool wxRichTextCtrl::SetFont(const wxFont& font)
+{
+#if wxRICHTEXT_DERIVES_FROM_TEXTCTRLBASE
+ wxControl::SetFont(font);
+#else
+ wxScrolledWindow::SetFont(font);
+#endif
+
+ wxTextAttrEx attr = GetBuffer().GetAttributes();
+ attr.SetFont(font);
+ GetBuffer().SetBasicStyle(attr);
+ GetBuffer().SetDefaultStyle(attr);
+
+ return true;
+}
+
+/// Transform logical to physical (unscrolling)
+wxPoint wxRichTextCtrl::GetPhysicalPoint(const wxPoint& ptLogical)
+{
+ wxPoint pt;
+ CalcScrolledPosition(ptLogical.x, ptLogical.y, & pt.x, & pt.y);
+
+ return pt;
+}
+
+/// Transform physical to logical
+wxPoint wxRichTextCtrl::GetLogicalPoint(const wxPoint& ptPhysical)
+{
+ wxPoint pt;
+ CalcUnscrolledPosition(ptPhysical.x, ptPhysical.y, & pt.x, & pt.y);
+
+ return pt;
+}
+
+/// Position the caret
+void wxRichTextCtrl::PositionCaret()
+{
+ wxRect caretRect;
+ if (GetCaretPositionForIndex(GetCaretPosition(), caretRect))
+ {
+ wxPoint originalPt = caretRect.GetPosition();
+ wxPoint pt = GetPhysicalPoint(originalPt);
+
+ GetCaret()->Move(pt);
+ GetCaret()->SetSize(caretRect.GetSize());
+ }
+}
+
+/// Get the caret height and position for the given character position
+bool wxRichTextCtrl::GetCaretPositionForIndex(long position, wxRect& rect)
+{
+ wxClientDC dc(this);
+ dc.SetFont(GetFont());
+
+ PrepareDC(dc);
+
+ wxPoint pt;
+ int height = 0;
+
+ if (GetBuffer().FindPosition(dc, position, pt, & height, m_caretAtLineStart))
+ {
+ rect = wxRect(pt, wxSize(wxRICHTEXT_DEFAULT_CARET_WIDTH, height));
+ return true;
+ }
+ else
+ return false;
+}
+
+/// Gets the line for the visible caret position. If the caret is
+/// shown at the very end of the line, it means the next character is actually
+/// on the following line. So let's get the line we're expecting to find
+/// if this is the case.
+wxRichTextLine* wxRichTextCtrl::GetVisibleLineForCaretPosition(long caretPosition) const
+{
+ wxRichTextLine* line = GetBuffer().GetLineAtPosition(caretPosition, true);
+ wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(caretPosition, true);
+ if (line)
+ {
+ if (caretPosition == line->GetRange().GetStart()-1 &&
+ (para->GetRange().GetStart() != line->GetRange().GetStart()))
+ {
+ if (!m_caretAtLineStart)
+ line = GetBuffer().GetLineAtPosition(caretPosition-1, true);
+ }
+ }
+ return line;
+}
+
+
+/// Move the caret to the given character position
+bool wxRichTextCtrl::MoveCaret(long pos, bool showAtLineStart)
+{
+ if (GetBuffer().GetDirty())
+ Layout();
+
+ if (pos <= GetBuffer().GetRange().GetEnd())
+ {
+ SetCaretPosition(pos, showAtLineStart);
+
+ PositionCaret();
+
+ return true;
+ }
+ else
+ return false;
+}
+
+/// Layout the buffer: which we must do before certain operations, such as
+/// setting the caret position.
+bool wxRichTextCtrl::Layout()
+{
+ wxRect availableSpace(wxPoint(0, 0), GetClientSize());
+ if (availableSpace.width == 0)
+ availableSpace.width = 10;
+ if (availableSpace.height == 0)
+ availableSpace.height = 10;
+
+ wxClientDC dc(this);
+ dc.SetFont(GetFont());
+
+ PrepareDC(dc);
+
+ GetBuffer().Defragment();
+ GetBuffer().UpdateRanges(); // If items were deleted, ranges need recalculation
+ GetBuffer().Layout(dc, availableSpace, wxRICHTEXT_FIXED_WIDTH|wxRICHTEXT_VARIABLE_HEIGHT);
+ GetBuffer().SetDirty(false);
+
+ if (!IsFrozen())
+ SetupScrollbars();
+
+ return true;
+}
+
+/// Is all of the selection bold?
+bool wxRichTextCtrl::IsSelectionBold() const
+{
+ if (HasSelection())
+ {
+ wxRichTextAttr attr;
+ wxRichTextRange range = GetSelectionRange();
+ attr.SetFlags(wxTEXT_ATTR_FONT_WEIGHT);
+ attr.SetFontWeight(wxBOLD);
+
+ return HasCharacterAttributes(range, attr);
+ }
+ else
+ {
+ // If no selection, then we need to combine current style with default style
+ // to see what the effect would be if we started typing.
+ wxRichTextAttr attr;
+ attr.SetFlags(wxTEXT_ATTR_FONT_WEIGHT);
+ if (GetStyle(GetCaretPosition()+1, attr))
+ {
+ wxRichTextApplyStyle(attr, GetDefaultStyleEx());
+ return attr.GetFontWeight() == wxBOLD;
+ }
+ }
+ return false;
+}
+
+/// Is all of the selection italics?
+bool wxRichTextCtrl::IsSelectionItalics() const
+{
+ if (HasSelection())
+ {
+ wxRichTextRange range = GetSelectionRange();
+ wxRichTextAttr attr;
+ attr.SetFlags(wxTEXT_ATTR_FONT_ITALIC);
+ attr.SetFontStyle(wxITALIC);
+
+ return HasCharacterAttributes(range, attr);
+ }
+ else
+ {
+ // If no selection, then we need to combine current style with default style
+ // to see what the effect would be if we started typing.
+ wxRichTextAttr attr;
+ attr.SetFlags(wxTEXT_ATTR_FONT_ITALIC);
+ if (GetStyle(GetCaretPosition()+1, attr))
+ {
+ wxRichTextApplyStyle(attr, GetDefaultStyleEx());
+ return attr.GetFontStyle() == wxITALIC;
+ }
+ }
+ return false;
+}
+
+/// Is all of the selection underlined?
+bool wxRichTextCtrl::IsSelectionUnderlined() const
+{
+ if (HasSelection())
+ {
+ wxRichTextRange range = GetSelectionRange();
+ wxRichTextAttr attr;
+ attr.SetFlags(wxTEXT_ATTR_FONT_UNDERLINE);
+ attr.SetFontUnderlined(true);
+
+ return HasCharacterAttributes(range, attr);
+ }
+ else
+ {
+ // If no selection, then we need to combine current style with default style
+ // to see what the effect would be if we started typing.
+ wxRichTextAttr attr;
+ attr.SetFlags(wxTEXT_ATTR_FONT_UNDERLINE);
+ if (GetStyle(GetCaretPosition()+1, attr))
+ {
+ wxRichTextApplyStyle(attr, GetDefaultStyleEx());
+ return attr.GetFontUnderlined();
+ }
+ }
+ return false;
+}
+
+/// Apply bold to the selection
+bool wxRichTextCtrl::ApplyBoldToSelection()
+{
+ wxRichTextAttr attr;
+ attr.SetFlags(wxTEXT_ATTR_FONT_WEIGHT);
+ attr.SetFontWeight(IsSelectionBold() ? wxNORMAL : wxBOLD);
+
+ if (HasSelection())
+ return SetStyle(GetSelectionRange(), attr);
+ else
+ SetDefaultStyle(attr);
+ return true;
+}
+
+/// Apply italic to the selection
+bool wxRichTextCtrl::ApplyItalicToSelection()
+{
+ wxRichTextAttr attr;
+ attr.SetFlags(wxTEXT_ATTR_FONT_ITALIC);
+ attr.SetFontStyle(IsSelectionItalics() ? wxNORMAL : wxITALIC);
+
+ if (HasSelection())
+ return SetStyle(GetSelectionRange(), attr);
+ else
+ SetDefaultStyle(attr);
+ return true;
+}
+
+/// Apply underline to the selection
+bool wxRichTextCtrl::ApplyUnderlineToSelection()
+{
+ wxRichTextAttr attr;
+ attr.SetFlags(wxTEXT_ATTR_FONT_UNDERLINE);
+ attr.SetFontWeight(!IsSelectionUnderlined());
+
+ if (HasSelection())
+ return SetStyle(GetSelectionRange(), attr);
+ else
+ SetDefaultStyle(attr);
+ return true;
+}
+
+/// Is all of the selection aligned according to the specified flag?
+bool wxRichTextCtrl::IsSelectionAligned(wxTextAttrAlignment alignment) const
+{
+ if (HasSelection())
+ {
+ wxRichTextRange range = GetSelectionRange();
+ wxRichTextAttr attr;
+ attr.SetAlignment(alignment);
+
+ return HasParagraphAttributes(range, attr);
+ }
+ else
+ {
+ // If no selection, then we need to get information from the current paragraph.
+ wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(GetCaretPosition()+1);
+ if (para)
+ return para->GetAttributes().GetAlignment() == alignment;
+ }
+ return false;
+}
+
+/// Apply alignment to the selection
+bool wxRichTextCtrl::ApplyAlignmentToSelection(wxTextAttrAlignment alignment)
+{
+ wxRichTextAttr attr;
+ attr.SetAlignment(alignment);
+ if (HasSelection())
+ return SetStyle(GetSelectionRange(), attr);
+ else
+ {
+ wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(GetCaretPosition()+1);
+ if (para)
+ return SetStyle(para->GetRange(), attr);
+ }
+ return true;
+}
+
+/// Sets the default style to the style under the cursor
+bool wxRichTextCtrl::SetDefaultStyleToCursorStyle()
+{
+ wxTextAttrEx attr;
+ attr.SetFlags(wxTEXT_ATTR_CHARACTER);
+
+ if (GetStyle(GetCaretPosition(), attr))
+ {
+ SetDefaultStyle(attr);
+ return true;
+ }
+ else
+ return false;
+}
+
+#endif
+ // wxUSE_RICHTEXT
+
diff --git a/src/richtext/richtextstyles.cpp b/src/richtext/richtextstyles.cpp
new file mode 100644
index 0000000000..e1dd06b17e
--- /dev/null
+++ b/src/richtext/richtextstyles.cpp
@@ -0,0 +1,312 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name: richtextstyles.cpp
+// Purpose: Style management for wxRichTextCtrl
+// Author: Julian Smart
+// Modified by:
+// Created: 2005-09-30
+// RCS-ID:
+// Copyright: (c) Julian Smart
+// Licence: wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+// For compilers that support precompilation, includes "wx.h".
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+ #pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+ #include "wx/wx.h"
+#endif
+
+#include "wx/image.h"
+
+#if wxUSE_RICHTEXT
+
+#include "wx/filename.h"
+#include "wx/clipbrd.h"
+#include "wx/wfstream.h"
+#include "wx/module.h"
+
+#include "wx/richtext/richtextstyles.h"
+#include "wx/richtext/richtextctrl.h"
+
+IMPLEMENT_CLASS(wxRichTextStyleDefinition, wxObject)
+IMPLEMENT_CLASS(wxRichTextCharacterStyleDefinition, wxRichTextStyleDefinition)
+IMPLEMENT_CLASS(wxRichTextParagraphStyleDefinition, wxRichTextStyleDefinition)
+
+/*!
+ * The style manager
+ */
+
+IMPLEMENT_CLASS(wxRichTextStyleSheet, wxObject)
+
+/// Initialisation
+void wxRichTextStyleSheet::Init()
+{
+}
+
+/// Add a definition to one of the style lists
+bool wxRichTextStyleSheet::AddStyle(wxList& list, wxRichTextStyleDefinition* def)
+{
+ if (!list.Find(def))
+ list.Append(def);
+ return true;
+}
+
+/// Remove a style
+bool wxRichTextStyleSheet::RemoveStyle(wxList& list, wxRichTextStyleDefinition* def, bool deleteStyle)
+{
+ wxNode* node = list.Find(def);
+ if (node)
+ {
+ wxRichTextStyleDefinition* def = (wxRichTextStyleDefinition*) node->GetData();
+ delete node;
+ if (deleteStyle)
+ delete def;
+ return true;
+ }
+ else
+ return false;
+}
+
+/// Find a definition by name
+wxRichTextStyleDefinition* wxRichTextStyleSheet::FindStyle(const wxList& list, const wxString& name) const
+{
+ for (wxNode* node = list.GetFirst(); node; node = node->GetNext())
+ {
+ wxRichTextStyleDefinition* def = (wxRichTextStyleDefinition*) node->GetData();
+ if (def->GetName().Lower() == name.Lower())
+ return def;
+ }
+ return NULL;
+}
+
+/// Delete all styles
+void wxRichTextStyleSheet::DeleteStyles()
+{
+ WX_CLEAR_LIST(wxList, m_characterStyleDefinitions);
+ WX_CLEAR_LIST(wxList, m_paragraphStyleDefinitions);
+}
+
+#if wxUSE_HTML
+/*!
+ * wxRichTextStyleListBox class declaration
+ * A listbox to display styles.
+ */
+
+IMPLEMENT_CLASS(wxRichTextStyleListBox, wxHtmlListBox)
+
+BEGIN_EVENT_TABLE(wxRichTextStyleListBox, wxHtmlListBox)
+ EVT_LISTBOX(wxID_ANY, wxRichTextStyleListBox::OnSelect)
+ EVT_LEFT_DOWN(wxRichTextStyleListBox::OnLeftDown)
+END_EVENT_TABLE()
+
+wxRichTextStyleListBox::wxRichTextStyleListBox(wxWindow* parent, wxWindowID id, const wxPoint& pos,
+ const wxSize& size, long style): wxHtmlListBox(parent, id, pos, size, style)
+{
+ m_styleSheet = NULL;
+ m_richTextCtrl = NULL;
+}
+
+wxRichTextStyleListBox::~wxRichTextStyleListBox()
+{
+}
+
+/// Returns the HTML for this item
+wxString wxRichTextStyleListBox::OnGetItem(size_t n) const
+{
+ if (!GetStyleSheet())
+ return wxEmptyString;
+
+ // First paragraph styles, then character
+ if (n < GetStyleSheet()->GetParagraphStyleCount())
+ {
+ wxRichTextParagraphStyleDefinition* def = GetStyleSheet()->GetParagraphStyle(n);
+
+ wxString str = CreateHTML(def);
+ return str;
+ }
+
+ if ((n - GetStyleSheet()->GetParagraphStyleCount()) < GetStyleSheet()->GetCharacterStyleCount())
+ {
+ wxRichTextCharacterStyleDefinition* def = GetStyleSheet()->GetCharacterStyle(n - GetStyleSheet()->GetParagraphStyleCount());
+
+ wxString str = CreateHTML(def);
+ return str;
+ }
+ return wxEmptyString;
+}
+
+// Get style for index
+wxRichTextStyleDefinition* wxRichTextStyleListBox::GetStyle(size_t i) const
+{
+ if (!GetStyleSheet())
+ return NULL;
+
+ // First paragraph styles, then character
+ if (i < GetStyleSheet()->GetParagraphStyleCount())
+ return GetStyleSheet()->GetParagraphStyle(i);
+
+ if ((i - GetStyleSheet()->GetParagraphStyleCount()) < GetStyleSheet()->GetCharacterStyleCount())
+ return GetStyleSheet()->GetCharacterStyle(i - GetStyleSheet()->GetParagraphStyleCount());
+
+ return NULL;
+}
+
+/// Updates the list
+void wxRichTextStyleListBox::UpdateStyles()
+{
+ if (GetStyleSheet())
+ {
+ SetItemCount(GetStyleSheet()->GetParagraphStyleCount()+GetStyleSheet()->GetCharacterStyleCount());
+ Refresh();
+ }
+}
+
+// Convert a colour to a 6-digit hex string
+static wxString ColourToHexString(const wxColour& col)
+{
+ wxString hex;
+
+ hex += wxDecToHex(col.Red());
+ hex += wxDecToHex(col.Green());
+ hex += wxDecToHex(col.Blue());
+
+ return hex;
+}
+
+/// Creates a suitable HTML fragment for a definition
+wxString wxRichTextStyleListBox::CreateHTML(wxRichTextStyleDefinition* def) const
+{
+ wxString str(wxT(""));
+
+ if (def->GetStyle().GetLeftIndent() > 0)
+ {
+ wxClientDC dc((wxWindow*) this);
+
+ str << wxT(" | ");
+ }
+
+ str << wxT("");
+
+ int size = 5;
+
+ // Standard size is 12, say
+ size += 12 - def->GetStyle().GetFontSize();
+
+ str += wxT("GetStyle().GetFontFaceName() << wxT("\"");
+
+ if (def->GetStyle().GetTextColour().Ok())
+ str << wxT(" color=\"#") << ColourToHexString(def->GetStyle().GetTextColour()) << wxT("\"");
+
+ str << wxT(">");
+
+ bool hasBold = false;
+ bool hasItalic = false;
+ bool hasUnderline = false;
+
+ if (def->GetStyle().GetFontWeight() == wxBOLD)
+ hasBold = true;
+ if (def->GetStyle().GetFontStyle() == wxITALIC)
+ hasItalic = true;
+ if (def->GetStyle().GetFontUnderlined())
+ hasUnderline = true;
+
+ if (hasBold)
+ str << wxT("");
+ if (hasItalic)
+ str << wxT("");
+ if (hasUnderline)
+ str << wxT("");
+
+ str += def->GetName();
+
+ if (hasUnderline)
+ str << wxT("");
+ if (hasItalic)
+ str << wxT("");
+ if (hasBold)
+ str << wxT("");
+
+ str << wxT("");
+
+ str += wxT(" |
");
+ return str;
+}
+
+// Convert units in tends of a millimetre to device units
+int wxRichTextStyleListBox::ConvertTenthsMMToPixels(wxDC& dc, int units) const
+{
+ int ppi = dc.GetPPI().x;
+
+ // There are ppi pixels in 254.1 "1/10 mm"
+
+ double pixels = ((double) units * (double)ppi) / 254.1;
+
+ return (int) pixels;
+}
+
+/// React to selection
+void wxRichTextStyleListBox::OnSelect(wxCommandEvent& WXUNUSED(event))
+{
+#if 0
+ wxRichTextStyleDefinition* def = GetStyle(event.GetSelection());
+ if (def)
+ {
+ wxMessageBox(def->GetName());
+ }
+#endif
+}
+
+void wxRichTextStyleListBox::OnLeftDown(wxMouseEvent& event)
+{
+ wxVListBox::OnLeftDown(event);
+
+ int item = HitTest(event.GetPosition());
+
+ if ( item != wxNOT_FOUND )
+ {
+ wxRichTextStyleDefinition* def = GetStyle(item);
+ if (def && GetRichTextCtrl())
+ {
+ wxRichTextRange range(m_richTextCtrl->GetInsertionPoint(), m_richTextCtrl->GetInsertionPoint());
+
+ // Flags are defined within each definition, so only certain
+ // attributes are applied.
+ wxRichTextAttr attr(def->GetStyle());
+
+ if (m_richTextCtrl->HasSelection())
+ m_richTextCtrl->SetStyle(m_richTextCtrl->GetSelectionRange(), attr);
+ else
+ m_richTextCtrl->SetDefaultStyle(attr);
+
+ m_richTextCtrl->SetFocus();
+ }
+ }
+}
+
+#if 0
+wxColour wxRichTextStyleListBox::GetSelectedTextColour(const wxColour& colFg) const
+{
+ return *wxBLACK;
+}
+
+wxColour wxRichTextStyleListBox::GetSelectedTextBgColour(const wxColour& colBg) const
+{
+ return *wxWHITE;
+}
+#endif
+
+#endif
+ // wxUSE_HTML
+
+#endif
+ // wxUSE_RICHTEXT
+
diff --git a/src/richtext/richtextxml.cpp b/src/richtext/richtextxml.cpp
new file mode 100644
index 0000000000..2e171c5d07
--- /dev/null
+++ b/src/richtext/richtextxml.cpp
@@ -0,0 +1,808 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name: richtextxml.cpp
+// Purpose: XML and HTML I/O for wxRichTextCtrl
+// Author: Julian Smart
+// Modified by:
+// Created: 2005-09-30
+// RCS-ID:
+// Copyright: (c) Julian Smart
+// Licence: wxWindows licence
+/////////////////////////////////////////////////////////////////////////////
+
+// For compilers that support precompilation, includes "wx.h".
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+ #pragma hdrstop
+#endif
+
+#ifndef WX_PRECOMP
+ #include "wx/wx.h"
+#endif
+
+#include "wx/image.h"
+
+#if wxUSE_RICHTEXT
+
+#include "wx/filename.h"
+#include "wx/clipbrd.h"
+#include "wx/wfstream.h"
+#include "wx/sstream.h"
+#include "wx/module.h"
+#include "wx/txtstrm.h"
+#include "wx/xml/xml.h"
+
+#include "wx/richtext/richtextxml.h"
+
+IMPLEMENT_DYNAMIC_CLASS(wxRichTextXMLHandler, wxRichTextFileHandler)
+
+#if wxUSE_STREAMS
+bool wxRichTextXMLHandler::LoadFile(wxRichTextBuffer *buffer, wxInputStream& stream)
+{
+ if (!stream.IsOk())
+ return false;
+
+ buffer->Clear();
+
+ wxXmlDocument* xmlDoc = new wxXmlDocument;
+ bool success = true;
+
+ if (!xmlDoc->Load(stream, wxT("ISO-8859-1")))
+ {
+ success = false;
+ }
+ else
+ {
+ if (xmlDoc->GetRoot() && xmlDoc->GetRoot()->GetType() == wxXML_ELEMENT_NODE && xmlDoc->GetRoot()->GetName() == wxT("richtext"))
+ {
+ wxXmlNode* child = xmlDoc->GetRoot()->GetChildren();
+ while (child)
+ {
+ if (child->GetType() == wxXML_ELEMENT_NODE)
+ {
+ wxString name = child->GetName();
+ if (name == wxT("richtext-version"))
+ {
+ }
+ else
+ ImportXML(buffer, child);
+ }
+
+ child = child->GetNext();
+ }
+ }
+ else
+ {
+ success = false;
+ }
+ }
+
+ delete xmlDoc;
+
+ buffer->UpdateRanges();
+
+ return success;
+}
+
+/// Recursively import an object
+bool wxRichTextXMLHandler::ImportXML(wxRichTextBuffer* buffer, wxXmlNode* node)
+{
+ wxString name = node->GetName();
+
+ bool doneChildren = false;
+
+ if (name == wxT("paragraphlayout"))
+ {
+ }
+ else if (name == wxT("paragraph"))
+ {
+ wxRichTextParagraph* para = new wxRichTextParagraph(buffer);
+ buffer->AppendChild(para);
+
+ GetStyle(para->GetAttributes(), node, true);
+
+ wxXmlNode* child = node->GetChildren();
+ while (child)
+ {
+ wxString childName = child->GetName();
+ if (childName == wxT("text"))
+ {
+ wxString text;
+ wxXmlNode* textChild = child->GetChildren();
+ while (textChild)
+ {
+ if (textChild->GetType() == wxXML_TEXT_NODE ||
+ textChild->GetType() == wxXML_CDATA_SECTION_NODE)
+ {
+ wxString text2 = textChild->GetContent();
+
+ // Strip whitespace from end
+ if (text2.Length() > 0 && text2[text2.Length()-1] == wxT('\n'))
+ text2 = text2.Mid(0, text2.Length()-1);
+
+ if (text2.Length() > 0 && text2[0] == wxT('"'))
+ text2 = text2.Mid(1);
+ if (text2.Length() > 0 && text2[text2.Length()-1] == wxT('"'))
+ text2 = text2.Mid(0, text2.Length() - 1);
+
+ // TODO: further entity translation
+ text2.Replace(wxT("<"), wxT("<"));
+ text2.Replace(wxT(">"), wxT(">"));
+ text2.Replace(wxT("&"), wxT("&"));
+ text2.Replace(wxT("""), wxT("\""));
+
+ text += text2;
+ }
+ textChild = textChild->GetNext();
+ }
+
+ wxRichTextPlainText* textObject = new wxRichTextPlainText(text, para);
+ GetStyle(textObject->GetAttributes(), child, false);
+
+ para->AppendChild(textObject);
+ }
+ else if (childName == wxT("image"))
+ {
+ int imageType = wxBITMAP_TYPE_PNG;
+ wxString value = node->GetPropVal(wxT("imagetype"), wxEmptyString);
+ if (!value.IsEmpty())
+ imageType = wxAtoi(value);
+
+ wxString data;
+
+ wxXmlNode* imageChild = child->GetChildren();
+ while (imageChild)
+ {
+ wxString childName = imageChild->GetName();
+ if (childName == wxT("data"))
+ {
+ wxXmlNode* dataChild = imageChild->GetChildren();
+ while (dataChild)
+ {
+ data = dataChild->GetContent();
+ // wxLogDebug(data);
+ dataChild = dataChild->GetNext();
+ }
+
+ }
+ imageChild = imageChild->GetNext();
+ }
+
+ if (!data.IsEmpty())
+ {
+ wxRichTextImage* imageObj = new wxRichTextImage(para);
+ para->AppendChild(imageObj);
+
+ wxStringInputStream strStream(data);
+
+ imageObj->GetImageBlock().ReadHex(strStream, data.Length(), imageType);
+ }
+ }
+ child = child->GetNext();
+ }
+
+ doneChildren = true;
+ }
+
+ if (!doneChildren)
+ {
+ wxXmlNode* child = node->GetChildren();
+ while (child)
+ {
+ ImportXML(buffer, child);
+ child = child->GetNext();
+ }
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// xml support routines
+//-----------------------------------------------------------------------------
+
+bool wxRichTextXMLHandler::HasParam(wxXmlNode* node, const wxString& param)
+{
+ return (GetParamNode(node, param) != NULL);
+}
+
+wxXmlNode *wxRichTextXMLHandler::GetParamNode(wxXmlNode* node, const wxString& param)
+{
+ wxCHECK_MSG(node, NULL, wxT("You can't access node data before it was initialized!"));
+
+ wxXmlNode *n = node->GetChildren();
+
+ while (n)
+ {
+ if (n->GetType() == wxXML_ELEMENT_NODE && n->GetName() == param)
+ return n;
+ n = n->GetNext();
+ }
+ return NULL;
+}
+
+
+wxString wxRichTextXMLHandler::GetNodeContent(wxXmlNode *node)
+{
+ wxXmlNode *n = node;
+ if (n == NULL) return wxEmptyString;
+ n = n->GetChildren();
+
+ while (n)
+ {
+ if (n->GetType() == wxXML_TEXT_NODE ||
+ n->GetType() == wxXML_CDATA_SECTION_NODE)
+ return n->GetContent();
+ n = n->GetNext();
+ }
+ return wxEmptyString;
+}
+
+
+wxString wxRichTextXMLHandler::GetParamValue(wxXmlNode *node, const wxString& param)
+{
+ if (param.IsEmpty())
+ return GetNodeContent(node);
+ else
+ return GetNodeContent(GetParamNode(node, param));
+}
+
+wxString wxRichTextXMLHandler::GetText(wxXmlNode *node, const wxString& param, bool WXUNUSED(translate))
+{
+ wxXmlNode *parNode = GetParamNode(node, param);
+ if (!parNode)
+ parNode = node;
+ wxString str1(GetNodeContent(parNode));
+ return str1;
+}
+
+// write string to output:
+inline static void OutputString(wxOutputStream& stream, const wxString& str,
+ wxMBConv *convMem = NULL, wxMBConv *convFile = NULL)
+{
+ if (str.IsEmpty()) return;
+#if wxUSE_UNICODE
+ const wxWX2MBbuf buf(str.mb_str(convFile ? *convFile : wxConvUTF8));
+ stream.Write((const char*)buf, strlen((const char*)buf));
+#else
+ if ( convFile == NULL )
+ stream.Write(str.mb_str(), str.Len());
+ else
+ {
+ wxString str2(str.wc_str(*convMem), *convFile);
+ stream.Write(str2.mb_str(), str2.Len());
+ }
+#endif
+}
+
+// Same as above, but create entities first.
+// Translates '<' to "<", '>' to ">" and '&' to "&"
+static void OutputStringEnt(wxOutputStream& stream, const wxString& str,
+ wxMBConv *convMem = NULL, wxMBConv *convFile = NULL)
+{
+ wxString buf;
+ size_t i, last, len;
+ wxChar c;
+
+ len = str.Len();
+ last = 0;
+ for (i = 0; i < len; i++)
+ {
+ c = str.GetChar(i);
+ if (c == wxT('<') || c == wxT('>') || c == wxT('"') ||
+ (c == wxT('&') && (str.Mid(i+1, 4) != wxT("amp;"))))
+ {
+ OutputString(stream, str.Mid(last, i - last), convMem, convFile);
+ switch (c)
+ {
+ case wxT('<'):
+ OutputString(stream, wxT("<"), NULL, NULL);
+ break;
+ case wxT('>'):
+ OutputString(stream, wxT(">"), NULL, NULL);
+ break;
+ case wxT('&'):
+ OutputString(stream, wxT("&"), NULL, NULL);
+ break;
+ case wxT('"'):
+ OutputString(stream, wxT("""), NULL, NULL);
+ break;
+ default: break;
+ }
+ last = i + 1;
+ }
+ }
+ OutputString(stream, str.Mid(last, i - last), convMem, convFile);
+}
+
+inline static void OutputIndentation(wxOutputStream& stream, int indent)
+{
+ wxString str = wxT("\n");
+ for (int i = 0; i < indent; i++)
+ str << wxT(' ') << wxT(' ');
+ OutputString(stream, str, NULL, NULL);
+}
+
+static wxOutputStream& operator <<(wxOutputStream& stream, const wxString& s)
+{
+ stream.Write(s, s.Length());
+ return stream;
+}
+
+static wxOutputStream& operator <<(wxOutputStream& stream, long l)
+{
+ wxString str;
+ str.Printf(wxT("%ld"), l);
+ return stream << str;
+}
+
+static wxOutputStream& operator <<(wxOutputStream& stream, const char c)
+{
+ wxString str;
+ str.Printf(wxT("%c"), c);
+ return stream << str;
+}
+
+// Convert a colour to a 6-digit hex string
+static wxString ColourToHexString(const wxColour& col)
+{
+ wxString hex;
+
+ hex += wxDecToHex(col.Red());
+ hex += wxDecToHex(col.Green());
+ hex += wxDecToHex(col.Blue());
+
+ return hex;
+}
+
+// Convert 6-digit hex string to a colour
+wxColour HexStringToColour(const wxString& hex)
+{
+ unsigned int r = 0;
+ unsigned int g = 0;
+ unsigned int b = 0;
+ r = wxHexToDec(hex.Mid(0, 2));
+ g = wxHexToDec(hex.Mid(2, 2));
+ b = wxHexToDec(hex.Mid(4, 2));
+
+ return wxColour(r, g, b);
+}
+
+bool wxRichTextXMLHandler::SaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream)
+{
+ if (!stream.IsOk())
+ return false;
+
+ wxString version(wxT("1.0") ) ;
+#if wxUSE_UNICODE
+ wxString fileencoding(wxT("UTF-8")) ;
+ wxString memencoding(wxT("UTF-8")) ;
+#else
+ wxString fileencoding(wxT("ISO-8859-1")) ;
+ wxString memencoding(wxT("ISO-8859-1")) ;
+#endif
+ wxString s ;
+
+ wxMBConv *convMem = NULL, *convFile = NULL;
+#if wxUSE_UNICODE
+ convFile = new wxCSConv(fileencoding);
+#else
+ if ( fileencoding != memencoding )
+ {
+ convFile = new wxCSConv(fileencoding);
+ convMem = new wxCSConv(memencoding);
+ }
+#endif
+
+ s.Printf(wxT("\n"),
+ (const wxChar*) version, (const wxChar*) fileencoding );
+ OutputString(stream, s, NULL, NULL);
+ OutputString(stream, wxT("") , NULL, NULL);
+
+ int level = 1;
+ ExportXML(stream, convMem, convFile, *buffer, level);
+
+ OutputString(stream, wxT("\n") , NULL, NULL);
+ OutputString(stream, wxT("\n"), NULL, NULL);
+
+ delete convFile;
+ delete convMem;
+
+ return true;
+}
+
+/// Recursively export an object
+bool wxRichTextXMLHandler::ExportXML(wxOutputStream& stream, wxMBConv* convMem, wxMBConv* convFile, wxRichTextObject& obj, int indent)
+{
+ wxString objectName;
+ if (obj.IsKindOf(CLASSINFO(wxRichTextParagraphLayoutBox)))
+ objectName = wxT("paragraphlayout");
+ else if (obj.IsKindOf(CLASSINFO(wxRichTextParagraph)))
+ objectName = wxT("paragraph");
+ else if (obj.IsKindOf(CLASSINFO(wxRichTextPlainText)))
+ objectName = wxT("text");
+ else if (obj.IsKindOf(CLASSINFO(wxRichTextImage)))
+ objectName = wxT("image");
+ else
+ objectName = wxT("object");
+
+ if (obj.IsKindOf(CLASSINFO(wxRichTextPlainText)))
+ {
+ wxRichTextPlainText& text = (wxRichTextPlainText&) obj;
+
+ OutputIndentation(stream, indent);
+ stream << wxT("<") << objectName;
+
+ wxString style = CreateStyle(obj.GetAttributes(), false);
+
+ stream << style << wxT(">");
+
+ wxString str = text.GetText();
+ if (str.Length() > 0 && (str[0] == wxT(' ') || str[str.Length()-1] == wxT(' ')))
+ {
+ stream << wxT("\"");
+ OutputStringEnt(stream, str, convMem, convFile);
+ stream << wxT("\"");
+ }
+ else
+ OutputStringEnt(stream, str, convMem, convFile);
+ }
+ else if (obj.IsKindOf(CLASSINFO(wxRichTextImage)))
+ {
+ wxRichTextImage& imageObj = (wxRichTextImage&) obj;
+
+ if (imageObj.GetImage().Ok() && !imageObj.GetImageBlock().Ok())
+ imageObj.MakeBlock();
+
+ OutputIndentation(stream, indent);
+ stream << wxT("<") << objectName;
+ if (!imageObj.GetImageBlock().Ok())
+ {
+ // No data
+ stream << wxT(">");
+ }
+ else
+ {
+ stream << wxString::Format(wxT(" imagetype=\"%d\""), (int) imageObj.GetImageBlock().GetImageType()) << wxT(">");
+ }
+
+ OutputIndentation(stream, indent+1);
+ stream << wxT("");
+
+ imageObj.GetImageBlock().WriteHex(stream);
+
+ stream << wxT("");
+ }
+ else if (obj.IsKindOf(CLASSINFO(wxRichTextCompositeObject)))
+ {
+ OutputIndentation(stream, indent);
+ stream << wxT("<") << objectName;
+
+ bool isPara = false;
+ if (objectName == wxT("paragraph") || objectName == wxT("paragraphlayout"))
+ isPara = true;
+
+ wxString style = CreateStyle(obj.GetAttributes(), isPara);
+
+ stream << style << wxT(">");
+
+ wxRichTextCompositeObject& composite = (wxRichTextCompositeObject&) obj;
+ size_t i;
+ for (i = 0; i < composite.GetChildCount(); i++)
+ {
+ wxRichTextObject* child = composite.GetChild(i);
+ ExportXML(stream, convMem, convFile, *child, indent+1);
+ }
+ }
+
+ if (objectName != wxT("text"))
+ OutputIndentation(stream, indent);
+
+ stream << wxT("") << objectName << wxT(">");
+
+ return true;
+}
+
+/// Create style parameters
+wxString wxRichTextXMLHandler::CreateStyle(const wxTextAttrEx& attr, bool isPara)
+{
+ wxString str;
+ if (attr.GetTextColour().Ok())
+ {
+ str << wxT(" textcolor=\"#") << ColourToHexString(attr.GetTextColour()) << wxT("\"");
+ }
+ if (attr.GetBackgroundColour().Ok())
+ {
+ str << wxT(" bgcolor=\"#") << ColourToHexString(attr.GetBackgroundColour()) << wxT("\"");
+ }
+
+ if (attr.GetFont().Ok())
+ {
+ str << wxT(" fontsize=\"") << attr.GetFont().GetPointSize() << wxT("\"");
+ str << wxT(" fontfamily=\"") << attr.GetFont().GetFamily() << wxT("\"");
+ str << wxT(" fontstyle=\"") << attr.GetFont().GetStyle() << wxT("\"");
+ str << wxT(" fontweight=\"") << attr.GetFont().GetWeight() << wxT("\"");
+ str << wxT(" fontunderlined=\"") << (int) attr.GetFont().GetUnderlined() << wxT("\"");
+ str << wxT(" fontface=\"") << attr.GetFont().GetFaceName() << wxT("\"");
+ }
+
+ if (!attr.GetCharacterStyleName().IsEmpty())
+ str << wxT(" charactertyle=\"") << wxString(attr.GetCharacterStyleName()) << wxT("\"");
+
+ if (isPara)
+ {
+ str << wxT(" alignment=\"") << (int) attr.GetAlignment() << wxT("\"");
+ str << wxT(" leftindent=\"") << (int) attr.GetLeftIndent() << wxT("\"");
+ str << wxT(" leftsubindent=\"") << (int) attr.GetLeftSubIndent() << wxT("\"");
+ str << wxT(" rightindent=\"") << (int) attr.GetRightIndent() << wxT("\"");
+ str << wxT(" parspacingafter=\"") << (int) attr.GetParagraphSpacingAfter() << wxT("\"");
+ str << wxT(" parspacingbefore=\"") << (int) attr.GetParagraphSpacingBefore() << wxT("\"");
+ str << wxT(" linespacing=\"") << (int) attr.GetLineSpacing() << wxT("\"");
+ str << wxT(" bulletstyle=\"") << (int) attr.GetBulletStyle() << wxT("\"");
+ str << wxT(" bulletnumber=\"") << (int) attr.GetBulletNumber() << wxT("\"");
+ str << wxT(" bulletsymbol=\"") << wxString(attr.GetBulletSymbol()) << wxT("\"");
+
+ if (!attr.GetParagraphStyleName().IsEmpty())
+ str << wxT(" parstyle=\"") << wxString(attr.GetParagraphStyleName()) << wxT("\"");
+ }
+
+ return str;
+}
+
+/// Get style parameters
+bool wxRichTextXMLHandler::GetStyle(wxTextAttrEx& attr, wxXmlNode* node, bool isPara)
+{
+ wxString fontFacename;
+ int fontSize = 12;
+ int fontFamily = wxDEFAULT;
+ int fontWeight = wxNORMAL;
+ int fontStyle = wxNORMAL;
+ bool fontUnderlined = false;
+
+ fontFacename = node->GetPropVal(wxT("fontface"), wxEmptyString);
+
+ wxString value = node->GetPropVal(wxT("fontfamily"), wxEmptyString);
+ if (!value.IsEmpty())
+ fontFamily = wxAtoi(value);
+
+ value = node->GetPropVal(wxT("fontstyle"), wxEmptyString);
+ if (!value.IsEmpty())
+ fontStyle = wxAtoi(value);
+
+ value = node->GetPropVal(wxT("fontsize"), wxEmptyString);
+ if (!value.IsEmpty())
+ fontSize = wxAtoi(value);
+
+ value = node->GetPropVal(wxT("fontweight"), wxEmptyString);
+ if (!value.IsEmpty())
+ fontWeight = wxAtoi(value);
+
+ value = node->GetPropVal(wxT("fontunderlined"), wxEmptyString);
+ if (!value.IsEmpty())
+ fontUnderlined = wxAtoi(value) != 0;
+
+ attr.SetFont(* wxTheFontList->FindOrCreateFont(fontSize, fontFamily, fontStyle, fontWeight, fontUnderlined, fontFacename));
+
+ value = node->GetPropVal(wxT("textcolor"), wxEmptyString);
+ if (!value.IsEmpty())
+ {
+ if (value[0] == wxT('#'))
+ attr.SetTextColour(HexStringToColour(value.Mid(1)));
+ else
+ attr.SetTextColour(value);
+ }
+
+ value = node->GetPropVal(wxT("backgroundcolor"), wxEmptyString);
+ if (!value.IsEmpty())
+ {
+ if (value[0] == wxT('#'))
+ attr.SetBackgroundColour(HexStringToColour(value.Mid(1)));
+ else
+ attr.SetBackgroundColour(value);
+ }
+
+ value = node->GetPropVal(wxT("characterstyle"), wxEmptyString);
+ if (!value.IsEmpty())
+ attr.SetCharacterStyleName(value);
+
+ // Set paragraph attributes
+ if (isPara)
+ {
+ value = node->GetPropVal(wxT("alignment"), wxEmptyString);
+ if (!value.IsEmpty())
+ attr.SetAlignment((wxTextAttrAlignment) wxAtoi(value));
+
+ int leftSubIndent = 0;
+ int leftIndent = 0;
+ value = node->GetPropVal(wxT("leftindent"), wxEmptyString);
+ if (!value.IsEmpty())
+ leftIndent = wxAtoi(value);
+ value = node->GetPropVal(wxT("leftsubindent"), wxEmptyString);
+ if (!value.IsEmpty())
+ leftSubIndent = wxAtoi(value);
+ attr.SetLeftIndent(leftIndent, leftSubIndent);
+
+ value = node->GetPropVal(wxT("rightindent"), wxEmptyString);
+ if (!value.IsEmpty())
+ attr.SetRightIndent(wxAtoi(value));
+
+ value = node->GetPropVal(wxT("parspacingbefore"), wxEmptyString);
+ if (!value.IsEmpty())
+ attr.SetParagraphSpacingBefore(wxAtoi(value));
+
+ value = node->GetPropVal(wxT("parspacingafter"), wxEmptyString);
+ if (!value.IsEmpty())
+ attr.SetParagraphSpacingAfter(wxAtoi(value));
+
+ value = node->GetPropVal(wxT("linespacing"), wxEmptyString);
+ if (!value.IsEmpty())
+ attr.SetLineSpacing(wxAtoi(value));
+
+ value = node->GetPropVal(wxT("bulletstyle"), wxEmptyString);
+ if (!value.IsEmpty())
+ attr.SetBulletStyle(wxAtoi(value));
+
+ value = node->GetPropVal(wxT("bulletnumber"), wxEmptyString);
+ if (!value.IsEmpty())
+ attr.SetBulletNumber(wxAtoi(value));
+
+ value = node->GetPropVal(wxT("bulletsymbol"), wxEmptyString);
+ if (!value.IsEmpty())
+ attr.SetBulletSymbol(value[0]);
+
+ value = node->GetPropVal(wxT("parstyle"), wxEmptyString);
+ if (!value.IsEmpty())
+ attr.SetParagraphStyleName(value);
+ }
+
+ return true;
+}
+
+#endif
+
+IMPLEMENT_DYNAMIC_CLASS(wxRichTextHTMLHandler, wxRichTextFileHandler)
+
+/// Can we handle this filename (if using files)? By default, checks the extension.
+bool wxRichTextHTMLHandler::CanHandle(const wxString& filename) const
+{
+ wxString path, file, ext;
+ wxSplitPath(filename, & path, & file, & ext);
+
+ return (ext.Lower() == wxT("html") || ext.Lower() == wxT("htm"));
+}
+
+
+#if wxUSE_STREAMS
+bool wxRichTextHTMLHandler::LoadFile(wxRichTextBuffer *WXUNUSED(buffer), wxInputStream& WXUNUSED(stream))
+{
+ return false;
+}
+
+/*
+ * We need to output only _changes_ in character formatting.
+ */
+
+bool wxRichTextHTMLHandler::SaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream)
+{
+ buffer->Defragment();
+
+ wxTextOutputStream str(stream);
+
+ wxTextAttrEx currentParaStyle = buffer->GetAttributes();
+ wxTextAttrEx currentCharStyle = buffer->GetAttributes();
+
+ str << wxT("\n");
+
+ wxRichTextObjectList::compatibility_iterator node = buffer->GetChildren().GetFirst();
+ while (node)
+ {
+ wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph);
+ wxASSERT (para != NULL);
+
+ if (para)
+ {
+ OutputParagraphFormatting(currentParaStyle, para->GetAttributes(), stream, true);
+
+ wxRichTextObjectList::compatibility_iterator node2 = para->GetChildren().GetFirst();
+ while (node2)
+ {
+ wxRichTextObject* obj = node2->GetData();
+ wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText);
+ if (textObj && !textObj->IsEmpty())
+ {
+ OutputCharacterFormatting(currentCharStyle, obj->GetAttributes(), stream, true);
+
+ str << textObj->GetText();
+
+ OutputCharacterFormatting(currentCharStyle, obj->GetAttributes(), stream, false);
+ }
+
+ node2 = node2->GetNext();
+ }
+
+ OutputParagraphFormatting(currentParaStyle, para->GetAttributes(), stream, false);
+
+ str << wxT("\n");
+ }
+
+ node = node->GetNext();
+ }
+
+ str << wxT("\n");
+
+ return true;
+}
+
+/// Output character formatting
+void wxRichTextHTMLHandler::OutputCharacterFormatting(const wxTextAttrEx& WXUNUSED(currentStyle), const wxTextAttrEx& thisStyle, wxOutputStream& stream, bool start)
+{
+ wxTextOutputStream str(stream);
+
+ bool isBold = false;
+ bool isItalic = false;
+ bool isUnderline = false;
+ wxString faceName;
+
+ if (thisStyle.GetFont().Ok())
+ {
+ if (thisStyle.GetFont().GetWeight() == wxBOLD)
+ isBold = true;
+ if (thisStyle.GetFont().GetStyle() == wxITALIC)
+ isItalic = true;
+ if (thisStyle.GetFont().GetUnderlined())
+ isUnderline = true;
+
+ faceName = thisStyle.GetFont().GetFaceName();
+ }
+
+ if (start)
+ {
+ if (isBold)
+ str << wxT("");
+ if (isItalic)
+ str << wxT("");
+ if (isUnderline)
+ str << wxT("");
+ }
+ else
+ {
+ if (isUnderline)
+ str << wxT("");
+ if (isItalic)
+ str << wxT("");
+ if (isBold)
+ str << wxT("");
+ }
+}
+
+/// Output paragraph formatting
+void wxRichTextHTMLHandler::OutputParagraphFormatting(const wxTextAttrEx& WXUNUSED(currentStyle), const wxTextAttrEx& thisStyle, wxOutputStream& stream, bool start)
+{
+ // TODO: lists, indentation (using tables), fonts, right-align, ...
+
+ wxTextOutputStream str(stream);
+ bool isCentered = false;
+
+ if (thisStyle.GetAlignment() == wxTEXT_ALIGNMENT_CENTRE)
+ {
+ isCentered = true;
+ }
+
+ if (start)
+ {
+ if (isCentered)
+ str << wxT("
");
+ }
+ else
+ {
+ if (isCentered)
+ str << wxT("");
+ }
+}
+
+#endif
+
+#endif
+ // wxUSE_RICHTEXT
+
diff --git a/src/wxWindows.dsp b/src/wxWindows.dsp
index 863811fb88..e48dea16b9 100644
--- a/src/wxWindows.dsp
+++ b/src/wxWindows.dsp
@@ -1643,6 +1643,22 @@ SOURCE=.\msw\window.cpp
# PROP Default_Filter ""
# Begin Source File
+SOURCE=.\richtext\richtextbuffer.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\richtext\richtextctrl.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\richtext\richtextstyles.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\richtext\richtextxml.cpp
+# End Source File
+# Begin Source File
+
SOURCE=.\xml\xml.cpp
# End Source File
# Begin Source File
@@ -2719,6 +2735,22 @@ SOURCE=..\include\wx\renderer.h
# End Source File
# Begin Source File
+SOURCE=..\include\wx\richtext\richtextbuffer.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\wx\richtext\richtextctrl.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\wx\richtext\richtextstyles.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\wx\richtext\richtextxml.h
+# End Source File
+# Begin Source File
+
SOURCE=..\include\wx\sashwin.h
# End Source File
# Begin Source File