From d6949858764602ead48c553327153a215393f77a Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson Date: Fri, 17 Feb 2012 08:32:35 +0100 Subject: [observer] Use wxGC for drawing --- lib/observer/src/observer_app_wx.erl | 85 ++++++++++++----------- lib/observer/src/observer_perf_wx.erl | 125 ++++++++++++++++++---------------- 2 files changed, 111 insertions(+), 99 deletions(-) (limited to 'lib/observer/src') diff --git a/lib/observer/src/observer_app_wx.erl b/lib/observer/src/observer_app_wx.erl index 7b4f7c4ce5..cb5f8c1259 100644 --- a/lib/observer/src/observer_app_wx.erl +++ b/lib/observer/src/observer_app_wx.erl @@ -63,6 +63,8 @@ -define(ID_TRACE_TREE_PIDS, 106). -define(ID_TRACE_TREE_NAMES, 107). +-define(wxGC, wxGraphicsContext). + start_link(Notebook, Parent) -> wx_object:start_link(?MODULE, [Notebook, Parent], []). @@ -100,22 +102,22 @@ init([Notebook, Parent]) -> wxPanel:connect(DrawingArea, left_dclick), wxPanel:connect(DrawingArea, right_down), - DefFont = wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT), + %% DefFont = wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT), + DefFont = wxFont:new(12,?wxFONTFAMILY_DECORATIVE,?wxFONTSTYLE_NORMAL,?wxFONTWEIGHT_NORMAL), SelCol = wxSystemSettings:getColour(?wxSYS_COLOUR_HIGHLIGHT), + GreyBrush = wxBrush:new({230,230,240}), SelBrush = wxBrush:new(SelCol), LinkPen = wxPen:new(SelCol, [{width, 2}]), - %% GC = wxGraphicsContext:create(DrawingArea), - %% _Font = wxGraphicsContext:createFont(GC, DefFont), process_flag(trap_exit, true), {Panel, #state{parent=Parent, panel =Panel, apps_w=Apps, app_w =DrawingArea, - paint=#paint{font= DefFont, - pen= ?wxBLACK_PEN, - brush=?wxLIGHT_GREY_BRUSH, - sel= SelBrush, - links=LinkPen + paint=#paint{font = DefFont, + pen = wxPen:new({80,80,80}, [{width, 2}]), + brush= GreyBrush, + sel = SelBrush, + links= LinkPen } }}. @@ -219,9 +221,13 @@ handle_sync_event(#wx{event = #wxPaint{}},_, #state{app_w=DA, app=App, sel=Sel, paint=Paint}) -> %% PaintDC must be created in a callback to work on windows. DC = wxPaintDC:new(DA), - wxScrolledWindow:doPrepareDC(DA,DC), + GC = ?wxGC:create(DC), + %% Argh must handle scrolling when using ?wxGC + {Sx,Sy} = wxScrolledWindow:calcScrolledPosition(DA, {0,0}), + ?wxGC:translate(GC, Sx,Sy), %% Nothing is drawn until wxPaintDC is destroyed. - draw(DC, App, Sel, Paint), + draw(GC, App, Sel, Paint), + ?wxGC:destroy(GC), wxPaintDC:destroy(DC), ok. %%%%%%%%%% @@ -250,12 +256,18 @@ handle_info(not_active, State = #state{appmon=AppMon, current=Prev}) -> {noreply, State}; handle_info({delivery, Pid, app_ctrl, _, Apps0}, - State = #state{appmon=Pid, apps_w=LBox}) -> + State = #state{appmon=Pid, apps_w=LBox, current=Curr0}) -> Apps = [atom_to_list(App) || {_, App, {_, _, _}} <- Apps0], wxListBox:clear(LBox), wxListBox:appendStrings(LBox, [App || App <- lists:sort(Apps)]), - {noreply, State}; - + case Apps of + [App|_] when Curr0 =:= undefined -> + Curr = list_to_atom(App), + appmon_info:app(Pid, Curr, true, []), + {noreply, State#state{current=Curr}}; + _ -> + {noreply, State} + end; handle_info({delivery, _Pid, app, _Curr, {[], [], [], []}}, State = #state{panel=Panel}) -> wxWindow:refresh(Panel), @@ -264,7 +276,10 @@ handle_info({delivery, _Pid, app, _Curr, {[], [], [], []}}, handle_info({delivery, Pid, app, Curr, AppData}, State = #state{panel=Panel, appmon=Pid, current=Curr, app_w=AppWin, paint=#paint{font=Font}}) -> - App = build_tree(AppData, {AppWin,Font}), + GC = ?wxGC:create(AppWin), + ?wxGC:setFont(GC, Font, {0,0,0}), + App = build_tree(AppData, {GC,Font}), + ?wxGC:destroy(GC), setup_scrollbar(AppWin, App), wxWindow:refresh(Panel), wxWindow:layout(Panel), @@ -432,15 +447,15 @@ middle([{#box{y=Y0},_}|List], _) -> {#box{y=Y1},_} = lists:last(List), (Y0+Y1) div 2. -box(Str0, N2P, {Win,Font}) -> +box(Str0, N2P, {Win,_Font}) -> Pid = gb_trees:get(Str0, N2P), Str = if hd(Str0) =:= $< -> lists:append(io_lib:format("~w", [Pid])); true -> Str0 end, - {TW,TH, _, _} = wxWindow:getTextExtent(Win, Str, [{theFont, Font}]), + {TW,TH, _, _} = ?wxGC:getTextExtent(Win, Str), Data = #str{text=Str, x=?BX_HE, y=?BY_HE, pid=Pid}, %% Add pid - #box{w=TW+?BX_E, h=TH+?BY_E, s1=Data}. + #box{w=round(TW)+?BX_E, h=round(TH)+?BY_E, s1=Data}. box_to_pid(#box{s1=#str{pid=Pid}}) -> Pid. box_to_reg(#box{s1=#str{text=[$<|_], pid=Pid}}) -> Pid; @@ -458,39 +473,30 @@ draw(_DC, undefined, _, _) -> ok; draw(DC, #app{dim={_W,_H}, ptree=Tree, links=Links}, Sel, #paint{font=Font, pen=Pen, brush=Brush, links=LPen, sel=SelBrush}) -> - %% Canvas = wxGraphicsContext:create(DC), - %% Pen = wxGraphicsContext:createPen(Canvas, ?wxBLACK_PEN), - %% wxGraphicsContext:setPen(Canvas, Pen), - %% Brush = wxGraphicsContext:createBrush(Canvas, ?wxLIGHT_GREY_BRUSH), - %% wxGraphicsContext:setBrush(Canvas, Brush), - %% Font = wxGraphicsContext:createFont(Canvas, wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT)), - %% wxGraphicsContext:setFont(Canvas, Font), - %% draw_tree(Tree, Canvas). - wxDC:setPen(DC, LPen), + ?wxGC:setPen(DC, LPen), [draw_xlink(Link, DC) || Link <- Links], - wxDC:setPen(DC, Pen), - %% wxDC:drawRectangle(DC, {2,2}, {W-2,H-2}), %% DEBUG - wxDC:setBrush(DC, Brush), - wxDC:setFont(DC, Font), + ?wxGC:setPen(DC, Pen), + %% ?wxGC:drawRectangle(DC, 2,2, _W-2,_H-2), %% DEBUG + ?wxGC:setBrush(DC, Brush), + ?wxGC:setFont(DC, Font, {0,0,0}), draw_tree(Tree, root, DC), case Sel of undefined -> ok; {#box{x=X,y=Y,w=W,h=H,s1=Str1}, _} -> - wxDC:setBrush(DC, SelBrush), - wxDC:drawRoundedRectangle(DC, {X-1,Y-1}, {W+2,H+2}, 8.0), + ?wxGC:setBrush(DC, SelBrush), + ?wxGC:drawRoundedRectangle(DC, X-1,Y-1, W+2,H+2, 8.0), draw_str(DC, Str1, X, Y) end. draw_tree({Box=#box{x=X,y=Y,w=W,h=H,s1=Str1}, Chs}, Parent, DC) -> - %%wxGraphicsContext:drawRoundedRectangle(DC, float(X), float(Y), float(W), float(H), 8.0), - wxDC:drawRoundedRectangle(DC, {X,Y}, {W,H}, 8.0), + ?wxGC:drawRoundedRectangle(DC, X,Y, W,H, 8.0), draw_str(DC, Str1, X, Y), Dot = case Chs of [] -> ok; [{#box{x=CX0},_}|_] -> CY = Y+(H div 2), CX = CX0-(?BB_X div 2), - wxDC:drawLine(DC, {X+W, CY}, {CX, CY}), + ?wxGC:strokeLine(DC, X+W, CY, CX, CY), {CX, CY} end, draw_link(Parent, Box, DC), @@ -500,9 +506,9 @@ draw_link({CX,CY}, #box{x=X,y=Y0,h=H}, DC) -> Y = Y0+(H div 2), case Y =:= CY of true -> - wxDC:drawLine(DC, {CX, CY}, {X, CY}); + ?wxGC:strokeLine(DC, CX, CY, X, CY); false -> - wxDC:drawLines(DC, [{CX, CY}, {CX, Y}, {X,Y}]) + ?wxGC:strokeLines(DC, [{CX, CY}, {CX, Y}, {X,Y}]) end; draw_link(_, _, _) -> ok. @@ -521,9 +527,8 @@ draw_xlink(X0, Y00, X1, Y11, BH, DC) -> {Y0,Y1} = if Y00 < Y11 -> {Y00+BH-6, Y11+6}; true -> {Y00+6, Y11+BH-6} end, - wxDC:drawLines(DC, [{X0,Y0}, {X0+5,Y0}, {X1-5,Y1}, {X1,Y1}]). + ?wxGC:strokeLines(DC, [{X0,Y0}, {X0+5,Y0}, {X1-5,Y1}, {X1,Y1}]). draw_str(DC, #str{x=Sx,y=Sy, text=Text}, X, Y) -> - %%wxGraphicsContext:drawText(DC, Text, float(Sx+X), float(Sy+Y)); - wxDC:drawText(DC, Text, {X+Sx,Y+Sy}); + ?wxGC:drawText(DC, Text, X+Sx,Y+Sy); draw_str(_, _, _, _) -> ok. diff --git a/lib/observer/src/observer_perf_wx.erl b/lib/observer/src/observer_perf_wx.erl index 8f4e317d99..96e433cbf2 100644 --- a/lib/observer/src/observer_perf_wx.erl +++ b/lib/observer/src/observer_perf_wx.erl @@ -39,6 +39,8 @@ appmon }). +-define(wxGC, wxGraphicsContext). + -record(paint, {font, small, pen, pen2, pens}). -define(RQ_W, 1). @@ -74,11 +76,12 @@ init([Notebook, Parent]) -> wxPanel:connect(IO, paint, [callback]), wxPanel:connect(MEM, paint, [callback]), - DefFont = wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT), - DefSize = wxFont:getPointSize(DefFont), - DefFamily = wxFont:getFamily(DefFont), - Font = wxFont:new(DefSize, DefFamily, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_BOLD), - SmallFont = wxFont:new(10, DefFamily, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_NORMAL), + % DefFont = wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT), + %% DefSize = wxFont:getPointSize(DefFont), + %% DefFamily = wxFont:getFamily(DefFont), + %% Font = wxFont:new(DefSize, DefFamily, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_BOLD), + Font = wxFont:new(12,?wxFONTFAMILY_DECORATIVE,?wxFONTSTYLE_NORMAL,?wxFONTWEIGHT_BOLD), + SmallFont = wxFont:new(10, ?wxFONTFAMILY_DECORATIVE, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_NORMAL), BlackPen = wxPen:new({0,0,0}, [{width, 2}]), Pens = [wxPen:new(Col, [{width, 2}]) || Col <- tuple_to_list(colors())], process_flag(trap_exit, true), @@ -118,12 +121,14 @@ handle_sync_event(#wx{obj=Panel, event = #wxPaint{}},_, Panel =:= element(?IO_W, Windows) -> ?IO_W end, DC = wxPaintDC:new(Panel), + GC = ?wxGC:create(DC), %% Nothing is drawn until wxPaintDC is destroyed. try - draw(Offset, Id, DC, Panel, Paint, Data, Active) + draw(Offset, Id, GC, Panel, Paint, Data, Active) catch _:Err -> io:format("Internal error ~p ~p~n",[Err, erlang:get_stacktrace()]) end, + ?wxGC:destroy(GC), wxPaintDC:destroy(DC), ok. %%%%%%%%%% @@ -250,21 +255,22 @@ calc_delta([], []) -> []. draw(Offset, Id, DC, Panel, Paint=#paint{pens=Pens, small=Small}, Data, Active) -> %% This can be optimized a lot by collecting data once %% and draw to memory and then blit memory and only draw new entries in new memory - %% area. + %% area. Hmm now rewritten to use ?wxGC I don't now if it is feasable. {Len, Max0, Hs} = collect_data(Id, Data), Max = calc_max(Max0), NoGraphs = try tuple_size(hd(Hs)) catch _:_ -> 0 end, Size = wxWindow:getClientSize(Panel), {X0,Y0,WS,HS} = draw_borders(Id, NoGraphs, DC, Size, Max, Paint), + Last = 60*WS+X0-1, Start = max(61-Len, 0)*WS+X0 - Offset*WS, case Hs of [] -> ignore; [_] -> ignore; _ -> Draw = fun(N) -> - Lines = make_lines(Hs, Start, N, {X0,round(Max*HS)}, Y0, WS, HS), - wxDC:setPen(DC, element(1+ ((N-1) rem tuple_size(Pens)), Pens)), - wxDC:drawLines(DC, Lines), + Lines = make_lines(Hs, Start, N, {X0,Max*HS,Last}, Y0, WS, HS), + ?wxGC:setPen(DC, element(1+ ((N-1) rem tuple_size(Pens)), Pens)), + ?wxGC:strokeLines(DC, Lines), N+1 end, [Draw(I) || I <- lists:seq(NoGraphs, 1, -1)] @@ -272,9 +278,8 @@ draw(Offset, Id, DC, Panel, Paint=#paint{pens=Pens, small=Small}, Data, Active) case Active of false -> NotActive = "Service not available", - wxDC:setTextForeground(DC, {0,0,0}), - wxDC:setFont(DC, Small), - wxDC:drawText(DC, NotActive, {X0 + 100, element(2,Size) div 2}); + ?wxGC:setFont(DC, Small, {0,0,0}), + ?wxGC:drawText(DC, NotActive, X0 + 100, element(2,Size) div 2); true -> ignore end, @@ -284,30 +289,30 @@ make_lines(Ds = [Data|_], PX, N, Clip, ZeroY, WS, HS) -> Y = element(N,Data), make_lines(Ds, PX, N, Clip, ZeroY, WS, HS, Y, []). -make_lines([D1 | Ds = [D2|Rest]], PX, N, Clip={Cx,Cy}, ZeroY, WS, HS, Y0, Acc0) -> +make_lines([D1 | Ds = [D2|Rest]], PX, N, Clip={Cx,Cy, _}, ZeroY, WS, HS, Y0, Acc0) -> Y1 = element(N,D1), Y2 = element(N,D2), Y3 = case Rest of [D3|_] -> element(N,D3); [] -> Y2 end, - This = {max(Cx, round(PX)),ZeroY-min(Cy,round(Y1*HS))}, - Acc = make_splines(Y0,Y1,Y2,Y3,PX,Clip,ZeroY,WS,HS,[This|Acc0]), + This = {max(Cx, PX),ZeroY-min(Cy,Y1*HS)}, + Acc = if (abs(Y1-Y2) * HS) < 3.0 -> [This|Acc0]; + WS < 3.0 -> [This|Acc0]; + PX < Cx -> + make_splines(Y0,Y1,Y2,Y3,PX,Clip,ZeroY,WS,HS,Acc0); + true -> + make_splines(Y0,Y1,Y2,Y3,PX,Clip,ZeroY,WS,HS,[This|Acc0]) + end, make_lines(Ds, PX+WS, N, Clip, ZeroY, WS, HS, Y1, Acc); -make_lines([D1], PX, N, _Clip, ZeroY, _WS, HS, _Y0, Acc) -> +make_lines([D1], _PX, N, {_,Cy,Last}, ZeroY, _WS, HS, _Y0, Acc) -> Y1 = element(N,D1), - [{round(PX),ZeroY-round(Y1*HS)}|Acc]. - -make_splines(_Y0,Y1,Y2,_Y3, _PX, _Clip,_ZeroY, _WS,HS, Acc) - when (abs(Y1-Y2) * HS) < 3.0 -> - Acc; -make_splines(_Y0,_Y1,_Y2,_Y3, _PX, _Clip,_ZeroY, WS,_HS, Acc) - when WS < 3.0 -> - Acc; + [{Last,ZeroY-min(Cy, Y1*HS)}|Acc]. + make_splines(Y00,Y10,Y20,Y30,PX,Clip,ZeroY,WS,HS,Acc) -> Y1 = Y10*HS, Y2 = Y20*HS, - Steps = round(min(abs(Y1-Y2), WS)), + Steps = min(abs(Y1-Y2), WS), if Steps > 2 -> Y0 = Y00*HS, Y3 = Y30*HS, @@ -318,15 +323,15 @@ make_splines(Y00,Y10,Y20,Y30,PX,Clip,ZeroY,WS,HS,Acc) -> Acc end. -splines(N, XD, XD0, Tan, Y1,Y2, PX0, Clip={Cx,Cy},ZeroY, WS, Acc) when N > 0 -> +splines(N, XD, XD0, Tan, Y1,Y2, PX0, Clip={Cx,Cy,_},ZeroY, WS, Acc) when N > 0 -> PX = PX0+WS, Delta = XD+XD0, if PX < Cx -> splines(N-1, Delta, XD0, Tan, Y1, Y2, PX, Clip,ZeroY, WS, Acc); true -> - Y = min(Cy, max(0,round(spline(Delta, Tan, Y1,Y2)))), + Y = min(Cy, max(0,spline(Delta, Tan, Y1,Y2))), splines(N-1, Delta, XD0, Tan, Y1, Y2, PX, Clip,ZeroY, WS, - [{round(PX), ZeroY-Y}|Acc]) + [{PX, ZeroY-Y}|Acc]) end; splines(_N, _XD, _XD0, _Tan, _Y1,_Y2, _PX, _Clip,_ZeroY, _WS, Acc) -> Acc. @@ -357,8 +362,10 @@ draw_borders(Type, NoGraphs, DC, {W,H}, Max, Str1 = observer_lib:to_str(MaxUnit), Str2 = observer_lib:to_str(MaxUnit div 2), Str3 = observer_lib:to_str(0), - {TW,TH} = wxDC:getTextExtent(DC, Str1), - {SpaceW, _} = wxDC:getTextExtent(DC, " "), + + ?wxGC:setFont(DC, Font, {0,0,0}), + {TW,TH,_,_} = ?wxGC:getTextExtent(DC, Str1), + {SpaceW, _,_,_} = ?wxGC:getTextExtent(DC, "W"), GraphX0 = ?BW+TW+?BW, GraphX1 = W-?BW*4, @@ -366,60 +373,60 @@ draw_borders(Type, NoGraphs, DC, {W,H}, Max, MaxTextY = ?BH+TH+?BH, BottomTextY = H-?BH-TH, SecondsY = BottomTextY - ?BH - TH, - GraphY0 = MaxTextY + (TH div 2), + GraphY0 = MaxTextY + (TH / 2), GraphY1 = SecondsY - ?BH, GraphW = GraphX1-GraphX0-1, GraphH = GraphY1-GraphY0-1, - GraphY25 = GraphY0 + (GraphY1 - GraphY0) div 4, - GraphY50 = GraphY0 + (GraphY1 - GraphY0) div 2, - GraphY75 = GraphY0 + 3*(GraphY1 - GraphY0) div 4, + GraphY25 = GraphY0 + (GraphY1 - GraphY0) / 4, + GraphY50 = GraphY0 + (GraphY1 - GraphY0) / 2, + GraphY75 = GraphY0 + 3*(GraphY1 - GraphY0) / 4, ScaleW = GraphW / 60, ScaleH = GraphH / Max, - wxDC:setFont(DC, Small), + ?wxGC:setFont(DC, Small, {0,0,0}), Align = fun(Str, Y) -> - {StrW, _} = wxDC:getTextExtent(DC, Str), - wxDC:drawText(DC, Str, {GraphX0 - StrW - ?BW, Y}) + {StrW, _, _, _} = ?wxGC:getTextExtent(DC, Str), + ?wxGC:drawText(DC, Str, GraphX0 - StrW - ?BW, Y) end, Align(Str1, MaxTextY), - Align(Str2, GraphY50 - (TH div 2)), - Align(Str3, GraphY1 - (TH div 2) + 1), + Align(Str2, GraphY50 - (TH / 2)), + Align(Str3, GraphY1 - (TH / 2) + 1), - wxDC:setPen(DC, Pen), + ?wxGC:setPen(DC, Pen), DrawSecs = fun(Secs, Pos) -> Str = [observer_lib:to_str(Secs)|" s"], - X = round(GraphX0+Pos), - wxDC:drawText(DC, Str, {X-SpaceW, SecondsY}), - wxDC:drawLine(DC, {X, GraphY0}, {X, GraphY1+5}), + X = GraphX0+Pos, + ?wxGC:drawText(DC, Str, X-SpaceW, SecondsY), + ?wxGC:strokeLine(DC, X, GraphY0, X, GraphY1+5), Pos + 10*ScaleW end, lists:foldl(DrawSecs, 0, lists:seq(60,0, -10)), - wxDC:drawLine(DC, {GraphX0-3, GraphY25}, {GraphX1, GraphY25}), - wxDC:drawLine(DC, {GraphX0-3, GraphY50}, {GraphX1, GraphY50}), - wxDC:drawLine(DC, {GraphX0-3, GraphY75}, {GraphX1, GraphY75}), + ?wxGC:strokeLine(DC, GraphX0-3, GraphY25, GraphX1, GraphY25), + ?wxGC:strokeLine(DC, GraphX0-3, GraphY50, GraphX1, GraphY50), + ?wxGC:strokeLine(DC, GraphX0-3, GraphY75, GraphX1, GraphY75), - wxDC:setPen(DC, Pen2), - wxDC:drawLines(DC, [{GraphX0, GraphY0-1}, {GraphX0, GraphY1+1}, - {GraphX1, GraphY1+1}, {GraphX1, GraphY0-1}, - {GraphX0, GraphY0-1}]), + ?wxGC:setPen(DC, Pen2), + ?wxGC:strokeLines(DC, [{GraphX0, GraphY0-1}, {GraphX0, GraphY1+1}, + {GraphX1, GraphY1+1}, {GraphX1, GraphY0-1}, + {GraphX0, GraphY0-1}]), - wxDC:setFont(DC, Font), + ?wxGC:setFont(DC, Font, {0,0,0}), case Type of - ?RQ_W -> wxDC:drawText(DC, "Scheduler Utilization (%) ", {TopTextX,?BH}); - ?MEM_W -> wxDC:drawText(DC, "Memory Usage " ++ Unit, {TopTextX,?BH}); - ?IO_W -> wxDC:drawText(DC, "IO Usage " ++ Unit, {TopTextX,?BH}) + ?RQ_W -> ?wxGC:drawText(DC, "Scheduler Utilization (%) ", TopTextX,?BH); + ?MEM_W -> ?wxGC:drawText(DC, "Memory Usage " ++ Unit, TopTextX,?BH); + ?IO_W -> ?wxGC:drawText(DC, "IO Usage " ++ Unit, TopTextX,?BH) end, Text = fun(X,Y, Str, PenId) -> if PenId == 0 -> - wxDC:setTextForeground(DC, {0,0,0}); + ?wxGC:setFont(DC, Font, {0,0,0}); PenId > 0 -> Id = 1 + ((PenId-1) rem tuple_size(colors())), - wxDC:setTextForeground(DC, element(Id, colors())) + ?wxGC:setFont(DC, Font, element(Id, colors())) end, - wxDC:drawText(DC, Str, {X, Y}), - {StrW, _} = wxDC:getTextExtent(DC, Str), + ?wxGC:drawText(DC, Str, X, Y), + {StrW, _, _, _} = ?wxGC:getTextExtent(DC, Str), StrW + X + SpaceW end, case Type of -- cgit v1.2.3