root/trunk/import/htmlayout/htmlayout.d

Revision 18, 18.2 kB (checked in by bobef, 5 months ago)

Updated HTMLayout wrapper to 0.7 (3.2.2.13)

Line 
1 //wrapper for Andrew Fedoniouk's excellent HTMLayout (3.2.2.13) - http://www.terrainformatica.com/
2
3 //todo: make the event struct wrappers (i.e. HMouse, HKey, etc) return HElement everywhere, not HELEMENT
4 //todo: wrap HTMLayoutElementSetExpando, HTMLayoutElementGetExpando
5 //todo: complete HJson templates {fromAA,toAA,fromArray,toArray}; HJson.toString should work with arrays and maps maybe
6 //todo: wrap HRange propperly
7 //todo: handleEvent(size) followed by handleEvent(draw) makes draw to not work, but vice versa works
8 //todo: port Andrew's hpp behaviors (or get some ideas from them)
9 //todo: flowerd.skin: msgBox, SkinnedWindow.icon, dropped files (and other dropped stuff) handler, HSkinLite
10 //todo: add scripting support
11 //todo: add mootools style animator class
12
13 /*
14
15 changelog:
16
17 0.7
18     JSON_VALUE.toString is not including the terminating null character for strings anymore
19     added shortcuts for all HElement states (most of which are only getters; i.e. readOnly, disabled, checked, etc.)
20     made HJson a separate class instead of alias of JSON_VALUE
21     upadated to HTMLayout 3.2.2.13 (new HDataArrived members)
22     added HJson.V_BYTES
23     renamed HLayout.elementByUID to HLayout.getUID
24     added HElement.location
25     added HElement.isInside
26     added flowerd.sharedlib, which can now load HTMLayout from memory
27     added HBehavior.reset and HBehavior.resetAll . This is intended to be used if HTMLayout is unloaded and loaded again in the lifetime of the application
28
29 0.6
30     renamed flowerd.flowerd to flowerd.dfl
31     added flowerd.dwt
32     added htmlayout.behavior.translatable
33     removed HElement.setAttribute and HElement.setStyle and HElement.setState (these was because of flowerscript, which ain't gonna be)
34     removed the Object parameter from all event handler delegates and put 'param' member in the appropriate struct instead
35     added HDialogCreated struct
36     changed the param of DOCUMENT_LOADED and DOCUMENT_COMPLETE notification to HReady instead of int
37     renamed HElement.byId to HElement.getId and HElement.byTag to HElement.getTag
38     renamed HElement.type to HElement.tag
39     renamed HEventHandler.tag to HEventHandler.htag
40     made this syntax possible: HElement.style["display"]="none";
41     added HElement.handleStatic, HElement.unhandleStatic that will not erase the event handlers for the element when the HElement object is destoyed. I.e. the event handler will be GC proof. handleStatic will take precedence before handleEvent
42     added HElement.opIndex, HElement.opIndexAssign which is working with the element's attrinutes
43     added HELement.opCall, HScrollBar.opCall, HEditBox.opCall
44     modified HLite.render to make use of the x and y parameters
45     updated to HTMLayout 3.2.2.8 (drag and drop)
46     updated to HTMLayout 3.2.2.5 (graphin)
47     removed the dependency of DFL for Win32 headers, now using tango.sys.win32.Types
48     fixed few potential bugs similar to the one in 0.5
49     some small code cleanups
50
51 0.5
52     fixed bug in HJson.opCall(char[])
53
54 0.4:
55     fixed HElement.showPopup
56     added HElement.showAsPopup, HElement.showAsPopupAt
57     added additional parameter to HElement.addClass and HElement.delClass
58
59 0.3:
60     added HJson.parse, JSon.toBool
61     added missing alias for UI_STATE_CHANGED
62     added HElement.hasClass, HElement.scrollPos(POINT), HElement.viewSize
63     renamed HElement.setCapture to capture
64
65 0.2:
66     wrapped all event structures. MOUSE_PARAMS becomes HMouse, etc. (params is omitted)
67     aliased all METHOD_PARAMS derivatives like HXcall, HTextValue, etc. (params is omitted)
68     wrapped JSON_VALUE (aliased as HJson; only partially tested)
69     HElement is not increasing the ref count of the element by default anymore. call HElement.keep explicitly if you need it
70     HLayoutBase,HLayout and HLite are now handling the events just like HElement (with handleEvent)
71     added HElement.click, HElement.xcall
72     added HEditBox, HScrollBar
73
74 0.1:
75     first release
76
77 */
78
79 module htmlayout.htmlayout;
80
81 public import htmlayout.capi;
82 public import htmlayout.events;
83 public import htmlayout.element;
84 public import htmlayout.graphin;
85 import tango.stdc.stringz;
86 import tango.text.convert.Utf;
87 import tango.io.File;
88 import tango.io.FileSystem;
89 import tango.io.FilePath;
90 import tango.sys.win32.Types;
91
92 extern(Windows) BOOL OffsetViewportOrgEx(
93   HDC hdc,         // handle to device context
94   int nXOffset,    // horizontal offset
95   int nYOffset,    // vertical offset
96   LPPOINT lpPoint  // original origin
97 );
98
99 template EVENT_HANDLER_STRING0(char[] RETURNTYPE,char[] NAME,char[] TYPE,char[] EVENT)
100 {
101     const EVENT_HANDLER_STRING0 =
102     RETURNTYPE~" on"~NAME~"("~(TYPE.length?TYPE~" e":"")~") {return dg"~NAME~".call("~(TYPE.length?"e":"")~");}"~
103     "protected alias Handlers!("~RETURNTYPE~(TYPE.length?","~TYPE:"")~") dg"~NAME~"_t;"~
104     "protected dg"~NAME~"_t mdg"~NAME~";"~
105     "protected dg"~NAME~"_t dg"~NAME~"(){if(mdg"~NAME~" is null) mdg"~NAME~"=new dg"~NAME~"_t; return mdg"~NAME~";}"~
106     "bool handleEvent("~RETURNTYPE~" delegate("~(TYPE.length?TYPE:"")~") dg,Object param=null){return dg"~NAME~".add(dg,param);}"~
107     "bool unhandleEvent("~RETURNTYPE~" delegate("~(TYPE.length?TYPE:"")~") dg){return dg"~NAME~".del(dg);}";
108 }
109
110 abstract class HLayoutBase : HEventHandler
111 {
112     this(char[] mediatype="screen")
113     {
114         if(!mediaType(mediatype)) throw new Exception("Error setting media type");
115     }
116
117     HElement rootElement(){lastResult=HPR_INVALID_STATE;return null;}
118     HElement findElement(INT x, INT y){lastResult=HPR_INVALID_STATE;return null;}
119     bool mediaType(char[] mediatype){return false;}
120     bool loadHTML(char[] url,char[] data){return false;}
121     bool loadHTML(char[] file)
122     {
123         char[] buf;
124         scope(exit) delete buf;
125         try (buf=cast(char[])File(file).read());
126         catch(Object o)
127         {
128             lastResult=HPRESULT.HPR_FILE_NOT_FOUND;
129             return false;
130         }
131         return loadHTML(FileSystem.toAbsolute(FilePath(file)).toString,buf);
132     }
133     bool masterCSS(char[] data,bool append=true)
134     {
135         if(append) return cast(bool)HTMLayoutAppendMasterCSS(cast(LPCBYTE)data.ptr,data.length);
136         else return cast(bool)HTMLayoutSetMasterCSS(cast(LPCBYTE)data.ptr,data.length);
137     }
138     bool renderBitmap(HBITMAP hbmp, INT x, INT y, INT w, INT h) {return false;}
139     bool traverseUIEvent(UINT evt,PVOID eventCtlStruct,out bool bProcessed){return false;}
140     bool declareElement(char[] type,UINT elementModel) {return cast(bool)HTMLayoutDeclareElementType(toStringz(type),elementModel);}
141
142     HPRESULT lastResult;
143     char[] lastResultMsg()
144     {
145         if(lastResult==HPR_OK) return "HPR_OK";
146         else if(lastResult==HPR_INVALID_HANDLE) return "HPR_INVALID_HANDLE";
147         else if(lastResult==HPR_INVALID_FORMAT) return "HPR_INVALID_FORMAT";
148         else if(lastResult==HPR_FILE_NOT_FOUND) return "HPR_FILE_NOT_FOUND";
149         else if(lastResult==HPR_INVALID_PARAMETER) return "HPR_INVALID_PARAMETER";
150         else if(lastResult==HPR_INVALID_STATE) return "HPR_INVALID_STATE";
151         else return null;
152     }
153
154     mixin(EVENT_HANDLER_STRING0!("bool","LoadData","HLoadData","HLN_LOAD_DATA"));
155     mixin(EVENT_HANDLER_STRING0!("bool","DataLoaded","HDataLoaded","HLN_DATA_LOADED"));
156     mixin(EVENT_HANDLER_STRING0!("bool","CreateControl","HCreateControl","HLN_CREATE_CONTROL"));
157     mixin(EVENT_HANDLER_STRING0!("bool","ControlCreated","HControlCreated","HLN_CONTROL_CREATED"));
158     mixin(EVENT_HANDLER_STRING0!("bool","DestroyControl","HDestroyControl","HLN_DESTROY_CONTROL"));
159     mixin(EVENT_HANDLER_STRING0!("bool","AttachBehavior","HAttachBehavior","HLN_ATTACH_BEHAVIOR"));
160     mixin(EVENT_HANDLER_STRING0!("bool","BehaviorChanged","HBehaviorChanged","HLN_BEHAVIOR_CHANGED"));
161     mixin(EVENT_HANDLER_STRING0!("bool","Ready","HReady","HLN_DOCUMENT_COMPLETE+HLN_DOCUMENT_LOADED"));
162
163 protected:
164
165     UINT mBaseCallback(LPNMHDR hdr)
166     {
167         if(hdr.code==HLN_CREATE_CONTROL) return onCreateControl(HCreateControl(cast(LPNMHL_CREATE_CONTROL)hdr));
168         else if(hdr.code==HLN_CONTROL_CREATED) return onControlCreated(HControlCreated(cast(LPNMHL_CREATE_CONTROL)hdr));
169         else if(hdr.code==HLN_LOAD_DATA) return onLoadData(HLoadData(cast(LPNMHL_LOAD_DATA)hdr));
170         else if(hdr.code==HLN_DATA_LOADED) return onDataLoaded(HDataLoaded(cast(LPNMHL_DATA_LOADED)hdr));
171         else if(hdr.code==HLN_DESTROY_CONTROL) return onDestroyControl(HDestroyControl(cast(LPNMHL_DESTROY_CONTROL)hdr));
172         else if(hdr.code==HLN_ATTACH_BEHAVIOR)
173         {
174             auto a=HAttachBehavior(cast(LPNMHL_ATTACH_BEHAVIOR)hdr);
175             if(onAttachBehavior(a)) return true;
176             else
177             {
178                 auto b=HBehavior.find(a.name);
179                 if(b)
180                 {
181                     a.elementProc=b.callback;
182                     a.elementTag=b.htag;
183                     a.elementEvents=b.events;
184                     return true;
185                 }
186                 else return false;
187             }
188         }
189         else if(hdr.code==HLN_BEHAVIOR_CHANGED) return onBehaviorChanged(HBehaviorChanged(cast(LPNMHL_BEHAVIOR_CHANGED)hdr));
190         else if(hdr.code==HLN_DOCUMENT_LOADED || hdr.code==HLN_DOCUMENT_COMPLETE) return onReady(HReady(hdr.code));
191         else
192         {
193             //tango.io.Stdout.Stdout(cast(int)hdr.code).newline.flush;
194             return false;
195         }
196     }
197
198 protected:
199 }
200
201 class HLite : HLayoutBase
202 {
203     this(char[] mediatype="screen")
204     {
205         mHandle=HTMLiteCreateInstance();
206         mHandles[mHandle]=this;
207         mHandles.rehash;
208         if(HTMLiteSetCallback(mHandle,&mCallback)!=HPR_OK) throw new Exception("Error setting HTMLite callback");
209         super(mediatype);
210     }
211     ~this()
212     {
213         mHandles.remove(mHandle);
214         HTMLiteDestroyInstance(mHandle);
215     }
216
217     HElement rootElement()
218     {
219         HELEMENT hret;
220         if((lastResult=HTMLiteGetRootElement(mHandle,&hret))==HPR_OK) return new HElement(hret);
221         else return null;
222     }
223
224     HElement findElement(INT x, INT y)
225     {
226         HELEMENT hret;
227         if((lastResult=HTMLiteFindElement(mHandle,x,y,&hret))==HPR_OK) return new HElement(hret);
228         else return null;
229     }
230
231     bool loadHTML(char[] url,char[] data){return (lastResult=HTMLiteLoadHtmlFromMemory(mHandle,toString16z(.toString16(url)),cast(LPCBYTE)toStringz(data),data.length))==HPR_OK;}
232     bool loadHTML(char[] file){return HTMLiteLoadHtmlFromFile(mHandle,toString16z(.toString16(FileSystem.toAbsolute(FilePath(file)).toString)))==HPR_OK;}
233     bool mediaType(char[] mediatype){return (lastResult=HTMLiteSetMediaType(mHandle,toStringz(mediatype)))==HPR_OK;}
234
235     HPRESULT measure(INT viewWidth, INT viewHeight) {return HTMLiteMeasure (mHandle,viewWidth,viewHeight);}
236     HPRESULT render(HDC hdc, INT x, INT y, INT w, INT h)
237     {
238         POINT p;
239         OffsetViewportOrgEx(hdc,x,y,&p);
240         scope(exit) OffsetViewportOrgEx(hdc,p.x,p.y,null);
241         return HTMLiteRender (mHandle,hdc,x,y,w,h);     
242     }
243
244     bool renderBitmap(HBITMAP hbmp, INT x, INT y, INT w, INT h) {return (lastResult=HTMLiteRenderOnBitmap (mHandle,hbmp,x,y,w,h))==HPR_OK;}
245     HPRESULT dataReady(char[] url,void[] data){return HTMLiteSetDataReady (mHandle,toStringz(url),cast(LPCBYTE)data.ptr,data.length);}
246     INT minWidth()
247     {
248         INT ret;
249         if((lastResult=HTMLiteGetDocumentMinWidth(mHandle,&ret))==HPR_OK) return ret;
250         else return -1;
251     }
252     INT minHeight()
253     {
254         INT ret;
255         if((lastResult=HTMLiteGetDocumentMinHeight(mHandle,&ret))==HPR_OK) return ret;
256         else return -1;
257     }
258
259     bool traverseUIEvent(UINT evt,PVOID eventCtlStruct,out bool bProcessed)
260     {
261         BOOL bOutProcessed;
262         auto ret=cast(bool)HTMLiteTraverseUIEvent(mHandle,evt,eventCtlStruct,&bOutProcessed);
263         bProcessed=cast(bool)bOutProcessed;
264         return ret;
265     }
266
267     bool advanceFocus(UINT where)
268     {
269         BOOL res;
270         if((lastResult=HTMLiteAdvanceFocus(mHandle,where,&res))==HPR_OK) return cast(bool)res;
271         else return false;
272     }
273
274     mixin(EVENT_HANDLER_STRING0!("bool","RefreshArea","HRefreshArea","HLN_REFRESH_AREA"));
275     mixin(EVENT_HANDLER_STRING0!("bool","SetTimer","HSetTimer","HLN_SET_TIMER"));
276     mixin(EVENT_HANDLER_STRING0!("bool","SetCursor","HSetCursor","HLN_SET_CURSOR"));
277     mixin(EVENT_HANDLER_STRING0!("bool","UpdateUI","","HLN_UPDATE_UI"));
278
279 protected:
280
281     extern(Windows) static UINT mCallback(HTMLITE hLite,LPNMHDR hdr)
282     {
283         auto lite=hLite in mHandles;
284         if(lite is null) throw new Exception("Error handling event");
285
286         if(hdr.code==HLN_REFRESH_AREA) return lite.onRefreshArea(HRefreshArea(cast(LPNMHL_REFRESH_AREA)hdr));
287         else if(hdr.code==HLN_SET_TIMER) return lite.onSetTimer(HSetTimer(cast(LPNMHL_SET_TIMER)hdr));
288         else if(hdr.code==HLN_SET_CURSOR) return lite.onSetCursor(HSetCursor(cast(LPNMHL_SET_CURSOR)hdr));
289         else if(hdr.code==HLN_UPDATE_UI) return lite.onUpdateUI();
290         else return lite.mBaseCallback(hdr);
291     }
292
293     static HLite[HTMLITE] mHandles;
294     HTMLITE mHandle;
295 }
296
297 class HLayout : HLayoutBase
298 {
299     this(HWND handle,char[] mediatype="screen")
300     {
301         mHandle=handle;
302         mHandles[mHandle]=this;
303         mHandles.rehash;
304         HTMLayoutSetCallback(mHandle,&mCallback,mHandle);
305         super(mediatype);
306     }
307
308     static char[] className8() {return fromStringz(HTMLayoutClassNameA());}
309     static wchar[] className16() {return fromString16z(HTMLayoutClassNameW());}
310     bool dataReady(char[] uri,void[] data) {return cast(bool)HTMLayoutDataReady (mHandle, toString16z(toString16(uri)), cast(LPBYTE)data.ptr, data.length);}
311     bool dataReadyAsync(char[] uri,void[] data, UINT dataType) {return cast(bool)HTMLayoutDataReadyAsync (mHandle, toString16z(toString16(uri)), cast(LPBYTE)data.ptr, data.length,dataType);}
312     LRESULT defProc(UINT msg, WPARAM wParam, LPARAM lParam) {return HTMLayoutProc (mHandle, msg, wParam, lParam);}
313     LRESULT defProcND(UINT msg, WPARAM wParam, LPARAM lParam, out bool pbHandled)
314     {
315         BOOL res;
316         auto ret=HTMLayoutProcND (mHandle, msg, wParam, lParam, &res);
317         pbHandled=cast(bool)res;
318         return ret;
319     }
320     UINT minWidth() {return HTMLayoutGetMinWidth(mHandle);}
321     UINT minHeight(UINT width) {return HTMLayoutGetMinHeight(mHandle,width);}
322     bool loadHTML(char[] url,char[] data)
323     {
324         if(url is null) return cast(bool)HTMLayoutLoadHtml(mHandle,cast(LPCBYTE)(toStringz(data)),data.length);
325         else return cast(bool)HTMLayoutLoadHtmlEx(mHandle,cast(LPCBYTE)(toStringz(data)),data.length,toString16z(.toString16(url)));
326     }
327     bool loadHTML(char[] file){return cast(bool)HTMLayoutLoadFile(mHandle,toString16z(.toString16(FileSystem.toAbsolute(FilePath(file)).toString)));}
328     bool mediaType(char[] mediatype){return true;return cast(bool)HTMLayoutSetMediaType(mHandle,toString16z(toString16(mediatype)));}
329
330     void mode(int HTMLayoutMode) {return HTMLayoutSetMode(mHandle,HTMLayoutMode);}
331     bool selectionExist() {return cast(bool)HTMLayoutSelectionExist(mHandle);}
332     char[] selectedHTML()
333     {
334         UINT pSize;
335         auto ret=HTMLayoutGetSelectedHTML(mHandle,&pSize);
336         return (cast(char*)ret)[0..pSize];
337     }
338     bool clipboardCopy() {return cast(bool)HTMLayoutClipboardCopy(mHandle);}
339     HElement rootElement()
340     {
341         HELEMENT hret;
342         if((lastResult=HTMLayoutGetRootElement(mHandle,&hret))==HLDOM_OK) return new HElement(hret);
343         else return null;
344     }
345
346     HElement findElement(INT x, INT y)
347     {
348         HELEMENT hret;
349         if((lastResult=HTMLayoutFindElement(mHandle,POINT(x,y),&hret))==HLDOM_OK) return new HElement(hret);
350         else return null;
351     }
352
353     HElement focusElement()
354     {
355         HELEMENT hret;
356         if((lastResult=HTMLayoutGetFocusElement(mHandle,&hret))==HLDOM_OK) return new HElement(hret);
357         else return null;
358     }
359
360     HElement getUID(UINT uid)
361     {
362         HELEMENT hret;
363         if((lastResult=HTMLayoutGetElementByUID(mHandle,uid,&hret))==HLDOM_OK) return new HElement(hret);
364         else return null;
365     }
366
367     bool traverseUIEvent(UINT evt,PVOID eventCtlStruct,out bool bProcessed)
368     {
369         BOOL bOutProcessed;
370         auto ret=cast(bool)HTMLayoutTraverseUIEvent(evt,eventCtlStruct,&bOutProcessed);
371         bProcessed=cast(bool)bOutProcessed;
372         return ret;
373     }
374
375     bool renderBitmap(HBITMAP hbmp, INT x, INT y, INT w, INT h) {return cast(bool)HTMLayoutRender(mHandle,hbmp,RECT(x,y,w,h));}
376
377     UINT enumResources(bool delegate(char[] uri,char[] type,char[] imageData) dg)
378     {
379         synchronized
380         {
381             mEnumCallback=dg;
382             return HTMLayoutEnumResources(mHandle,&mEnumResoureCallback);
383         }
384     }
385
386     bool css(char[] data,char[] baseurl,char[] mediatype){return cast(bool)HTMLayoutSetCSS(mHandle,cast(LPCBYTE)data.ptr,data.length,toString16z(toString16(baseurl)), toString16z(toString16(mediatype)));}
387     bool headers(char[] headers){return cast(bool)HTMLayoutSetHttpHeaders(mHandle,headers.ptr,headers.length);}
388     bool setOption(UINT option,UINT value){return cast(bool)HTMLayoutSetOption(mHandle,option,value );}
389
390     int dialog(POINT position, INT alignment, UINT style, UINT styleEx, LPHTMLAYOUT_NOTIFY notificationCallback, LPELEMENT_EVENT_PROC eventsCallback, LPVOID callbackParam, char[] html){INT_PTR ret=HTMLayoutDialog (mHandle, position, alignment, style, styleEx, notificationCallback, eventsCallback, callbackParam, cast(LPCBYTE) html.ptr, html.length); if(ret) return ret; else return -1;}
391
392     bool _handleEvent(UINT subscription){return (lastResult=HTMLayoutWindowAttachEventHandler(mHandle,callback,htag,subscription))==HLDOM_OK;}
393     bool _unhandleEvents(){return (lastResult=HTMLayoutWindowDetachEventHandler(mHandle,callback,htag))==HLDOM_OK;}
394
395     HLDOM_RESULT lastResult;
396     char[] lastResultMsg()
397     {
398         if(lastResult==HLDOM_OK) return "HLDOM_OK - function completed successfully";
399         else if(lastResult==HLDOM_INVALID_HWND) return "HLDOM_INVALID_HWND - invalid HWND";
400         else if(lastResult==HLDOM_INVALID_HANDLE) return "HLDOM_INVALID_HANDLE - invalid HELEMENT";
401         else if(lastResult==HLDOM_PASSIVE_HANDLE) return "HLDOM_PASSIVE_HANDLE - attempt to use HELEMENT which is not marked by HTMLayout_UseElement()";
402         else if(lastResult==HLDOM_INVALID_PARAMETER) return "HLDOM_INVALID_PARAMETER - parameter is invalid, e.g. pointer is null";
403         else if(lastResult==HLDOM_OPERATION_FAILED) return "HLDOM_OPERATION_FAILED";
404         else return null;
405     }
406
407     mixin(EVENT_HANDLER_STRING0!("bool","DialogCreated","HDialogCreated","HLN_DIALOG_CREATED"));
408     mixin(EVENT_HANDLER_STRING0!("bool","DialogCloseRQ","HDialogCloseRQ","HLN_DIALOG_CLOSE_RQ"));
409
410 protected:
411
412     static bool delegate(char[] uri,char[] type,char[] imageData) mEnumCallback;
413     extern(Windows) static BOOL mEnumResoureCallback(LPCWSTR resourceUri, LPCSTR resourceType, LPCBYTE imageData, DWORD imageDataSize){return mEnumCallback(.toString(fromString16z(resourceUri)),fromStringz(resourceType),(cast(char*)imageData)[0..imageDataSize]);}
414
415     extern(Windows) static LRESULT mCallback(UINT uMsg, WPARAM wParam, LPARAM lParam, LPVOID vParam)
416     {
417         LPNMHDR  hdr = cast(LPNMHDR)lParam;
418         auto lay=cast(HWND)vParam in mHandles;
419         if(lay is null) throw new Exception("Error handling event");
420
421         if(hdr.code==HLN_DIALOG_CREATED) return lay.onDialogCreated(HDialogCreated(hdr));
422         else if(hdr.code==HLN_DIALOG_CLOSE_RQ) return lay.onDialogCloseRQ(HDialogCloseRQ(cast(LPNMHL_DIALOG_CLOSE_RQ)hdr));
423         else return lay.mBaseCallback(hdr);
424     }
425
426     static HLayout[HWND] mHandles;
427     HWND mHandle;
428 }
Note: See TracBrowser for help on using the browser.