<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-14563877</id><updated>2012-01-04T04:33:23.386-08:00</updated><title type='text'>Trixul - Cross-Platform Development in C++</title><subtitle type='html'>Read along as I chronicle my efforts to develop the Trixul Cross-Platform GUI Toolkit. Trixul is a simple cross-platform toolkit that will be featured in my next book on the subject of cross-platform development in C++. 

Content posted to this blog is Copyright 2004 - 2006 Syd Logan, All Rights Reserved, and may not be duplicated or republished without the written permission of the author.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>56</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-14563877.post-8271339586209439411</id><published>2010-04-28T03:03:00.001-07:00</published><updated>2010-04-28T03:04:50.796-07:00</updated><title type='text'>This blog has moved</title><content type='html'>&lt;br /&gt;       This blog is now located at http://trixul.blogspot.com/.&lt;br /&gt;       You will be automatically redirected in 30 seconds, or you may click &lt;a href='http://trixul.blogspot.com/'&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;       For feed subscribers, please update your feed subscriptions to&lt;br /&gt;       http://trixul.blogspot.com/feeds/posts/default.&lt;br /&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-8271339586209439411?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://trixul.blogspot.com/' title='This blog has moved'/><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/8271339586209439411/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=8271339586209439411' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/8271339586209439411'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/8271339586209439411'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2010/04/this-blog-has-moved.html' title='This blog has moved'/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-8087150557226866380</id><published>2008-02-18T07:45:00.000-08:00</published><updated>2008-02-18T07:52:32.724-08:00</updated><title type='text'></title><content type='html'>I've ceased blogging about Trixul, moving focus from this blog to Trixul's home page and sourceforge as primary sources of information. See:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;http://www.trixul.com -- the home page for Trixul&lt;/li&gt;&lt;li&gt;http://sourceforge.net/news/?group_id=169691&lt;/li&gt;&lt;/ul&gt;Also, my book, &lt;span style="font-style: italic;"&gt;Developing Cross-Platform Applications in C++&lt;/span&gt;,  has a chapter devoted to Trixul. For more information, visit http://www.crossplatformbook.com&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-8087150557226866380?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/8087150557226866380/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=8087150557226866380' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/8087150557226866380'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/8087150557226866380'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2008/02/for-further-information-on-trixul-see.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-116986150416697968</id><published>2007-01-26T17:28:00.000-08:00</published><updated>2007-01-26T17:31:44.176-08:00</updated><title type='text'></title><content type='html'>I'm looking for persons who are experienced in C++ to work with me to flesh out the Trixul cross-platform GUI toolkit. GUIs in Trixul are crafted from JavaScript and XML, just like Mozilla's XUL. JavaScript can call C++ objects. This is all provided in a very lightweight codebase that is easy to read, and comes in under 10,000 lines of C++ source.&lt;br /&gt;&lt;br /&gt;Trixul natively supports Gtk+, Windows .NET Forms, and Cocoa. So, Gtk+ programmers, Windows .NET Forms programs, and Objective C programmers who know Cocoa are valuable to this project. If you know JavaScript, or XUL, you can help by crafting tests. If you know all of the above, well, I definitely want you on my team!&lt;br /&gt;&lt;br /&gt;If you are interested, or have questions, please sign up (if not already a member) at sourgeforge.net and contact me at slogan621 at users dot sourceforge dot net&lt;br /&gt;&lt;br /&gt;Thanks!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-116986150416697968?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/116986150416697968/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=116986150416697968' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/116986150416697968'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/116986150416697968'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2007/01/im-looking-for-persons-who-are.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-116961315031135481</id><published>2007-01-23T20:29:00.000-08:00</published><updated>2007-01-23T20:32:30.320-08:00</updated><title type='text'></title><content type='html'>Changes to the box layout algorithm and implementation were committed to CVS at sourceforge earlier this afternoon. &lt;br /&gt;&lt;br /&gt;Whilke I am sure there are some bugs to be discovered and fixed (I filed one just this afternoon), I believe the algorithm used is much more sound than its predecessor. During the past week I attempted two strategies, the last of which I finally checked in. I need to focus on writing about the design of Trixul at this point, so the bugs will have to wait...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-116961315031135481?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/116961315031135481/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=116961315031135481' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/116961315031135481'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/116961315031135481'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2007/01/changes-to-box-layout-algorithm-and.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-116892833878549485</id><published>2007-01-15T22:16:00.000-08:00</published><updated>2007-01-15T22:18:58.796-08:00</updated><title type='text'></title><content type='html'>I'm on vacation for 2 weeks to try and wrap up the writing of my book. This will include a rewrite of the algorithm used to do the box layout in the Trixul toolkit; in running side-by-side comparisons with Mozilla's Gecko engine, I noticed some problems in how unused space was distributed among spacers within a box. So, I wrote a simulator and came up with an algorithm that I am now fine tuning. Hopefully soon I will submit the changes to sourceforge.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-116892833878549485?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/116892833878549485/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=116892833878549485' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/116892833878549485'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/116892833878549485'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2007/01/im-on-vacation-for-2-weeks-to-try-and.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-115454060859278568</id><published>2006-08-02T10:40:00.000-07:00</published><updated>2006-08-02T10:43:28.613-07:00</updated><title type='text'></title><content type='html'>Trixul is now in the source forge CVS repository. You can get information about how to pull it by going to http://sourceforge.net/projects/trixul and clicking on the CVS link. You can also browse the source online by visiting http://trixul.cvs.sourceforge.net/trixul/trixul/&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-115454060859278568?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/115454060859278568/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=115454060859278568' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/115454060859278568'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/115454060859278568'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2006/08/trixul-is-now-in-source-forge-cvs.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-114891154168435439</id><published>2006-05-29T07:04:00.000-07:00</published><updated>2006-05-29T07:05:41.696-07:00</updated><title type='text'></title><content type='html'>I just submitted a request to host trixul on sourceforge under the project name "trixul". The source will be submitted under a BSD open source license.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-114891154168435439?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/114891154168435439/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=114891154168435439' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114891154168435439'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114891154168435439'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2006/05/i-just-submitted-request-to-host.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-114880737288373612</id><published>2006-05-28T02:06:00.000-07:00</published><updated>2006-05-28T17:58:10.356-07:00</updated><title type='text'></title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.trixul.com/blog/uploaded_images/checkwin-747310.png"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://www.trixul.com/blog/uploaded_images/checkwin-743606.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;And, finally, a few hours later, .NET checkboxes and radiobuttons. Suprisingly, .NET took a similar approach to Cocoa, coupling UI with management of radio groups by requiring developers to wrap radio buttons in a container widget. This necessitated the same approach - using my RadioGroupManager to support mutual inclusion among radio buttons in a group.&lt;br /&gt;&lt;br /&gt;For a while, I feared that the .NET implementation would be a deal-killer. The documentation stated that all radio buttons in the same form (i.e., with the same parent) would be mutually exclusive. Tests showed this to be true (you can see this in the above figure, where only one radio button on the page was selected). Clicking on buttons in radio group 1 cleared any selections in radio group 2, and vice-versa. The documentation then went on to declare that in order to define radio groups, you had to manage them in a GroupBox or Panel, which would have had huge implications on my layout engine. This was worse than Cocoa -- in Cocoa, if you didn't wrap them in a NSMatrix widget, no problem -- things were left alone. No true with .NET, mutual exclusion is built in, like it or not.&lt;br /&gt;&lt;br /&gt;Luckily, after playing around with things awhile, I discovered that there is a property on radio buttons that allows me to take them out of any management scheme, thereby allowing me to reuse RadioGroupManager exactly as it was designed for use with Cocoa radiobuttons. All I had to do was set the .NET RadioButton "AutoCheck" property to false when creating the RadioButton widget concrete instance.&lt;br /&gt;&lt;br /&gt;Phew, that was a close one :-) RadioButtons were, in the end, a very interesting exercise, one that really stress-tested my overall design.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-114880737288373612?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/114880737288373612/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=114880737288373612' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114880737288373612'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114880737288373612'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2006/05/and-finally-few-hours-later.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-114877686102967245</id><published>2006-05-27T17:33:00.000-07:00</published><updated>2006-05-27T17:41:01.046-07:00</updated><title type='text'></title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.trixul.com/blog/uploaded_images/radio-798701.png"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://www.trixul.com/blog/uploaded_images/radio-796399.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;Here's the Gtk+ screenshot. As it turns out, I needed to make use of RadioGroupManager, but in a different way. In Gtk+, the first radio button in the group is created with a NULL group, and subsequent radio buttons are added to the group by querying the radio group of that first radio button. Some minor changes to RadioGroupManager were made to support this, without breaking Cocoa. I also factored RadioGroupManager out of Cocoa, and removed all Cocoa-isms that it contained.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-114877686102967245?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/114877686102967245/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=114877686102967245' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114877686102967245'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114877686102967245'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2006/05/heres-gtk-screenshot.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-114871989260868546</id><published>2006-05-27T01:32:00.000-07:00</published><updated>2006-05-27T01:51:32.693-07:00</updated><title type='text'></title><content type='html'>The following JavaScript code is attached via onclick handlers to two buttons. The first button runs thru all the checkbuttons in the UI and changes their state; the other enables each radiobutton in turn. &lt;br /&gt;&lt;br /&gt;Notice how I overloaded the value property (and setvalue/getValue functions) to allow them, for checkboxes and radiobuttons, to take a boolean argument. &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;var radio = 1;&lt;br /&gt;var check = 1;&lt;br /&gt; &lt;br /&gt;function ToggleCheckItem()&lt;br /&gt;{&lt;br /&gt;    dump("Entered ToggleCheckItem\n");&lt;br /&gt;    var which = "CheckItem" + check;&lt;br /&gt;    var obj = document.getElementById(which);&lt;br /&gt;    try {&lt;br /&gt;        if (obj) {&lt;br /&gt;            dump("found item " + which + "\n");&lt;br /&gt;            try {&lt;br /&gt;                var value = obj.value;&lt;br /&gt;                if (value == true)&lt;br /&gt;                    value = false;&lt;br /&gt;                else&lt;br /&gt;                    value = true;&lt;br /&gt;                obj.setValue(value);&lt;br /&gt;            } catch (err) {}&lt;br /&gt;        }&lt;br /&gt;    } catch (err) {}&lt;br /&gt;&lt;br /&gt;    check = check + 1;&lt;br /&gt;    if (check &amp;gt; 8)&lt;br /&gt;        check = 1;&lt;br /&gt;    dump("Leaving ToggleCheckItem check is " + check + "\n");&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function SetNextRadio()&lt;br /&gt;{&lt;br /&gt;    dump("Entered SetNextRadio");&lt;br /&gt;    var which = "RadioButton" + radio;&lt;br /&gt;    var obj = document.getElementById(which);&lt;br /&gt;    try {&lt;br /&gt;        if (obj) {&lt;br /&gt;            dump("found item " + which + "\n");&lt;br /&gt;            try {&lt;br /&gt;                obj.value = true;&lt;br /&gt;            } catch (err) {}&lt;br /&gt;        }&lt;br /&gt;    } catch (err) {}&lt;br /&gt;&lt;br /&gt;    radio = radio + 1;&lt;br /&gt;    if (radio &amp;gt; 8)&lt;br /&gt;        radio = 1;&lt;br /&gt;    dump("Leaving SetNextRadio radio is " + radio + "\n");&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Element::SetValueHelper() is the function in the layout engine that responds to assignment of the value property of a JavaScript object. It queries the type of the control that the JavaScript object represents, stores the value in a variant type, and then calls the control's SetValue() function:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PRStatus&lt;br /&gt;Element::SetValueHelper(JSContext *cx, Control *control, jsval val, jsval *rval)&lt;br /&gt;{&lt;br /&gt;    if (!control)&lt;br /&gt;        return PR_FAILURE;&lt;br /&gt;    &lt;br /&gt;    PRStatus ret = PR_FAILURE;&lt;br /&gt;        &lt;br /&gt;    ContentType type = control-&amp;gt;GetContentType();&lt;br /&gt;    switch (type) {&lt;br /&gt;    case ContentTypeBoolean:&lt;br /&gt;    {           &lt;br /&gt;        JSBool b;&lt;br /&gt;        JS_ValueToBoolean(cx, val, &amp;b);&lt;br /&gt;        XPVariant v;&lt;br /&gt;        v.SetValue(static_cast&amp;lt;bool&amp;gt;(b));&lt;br /&gt;        if (control-&amp;gt;SetValue(v) == PR_SUCCESS)&lt;br /&gt;            ret = PR_SUCCESS;&lt;br /&gt;        break;&lt;br /&gt;    }   &lt;br /&gt;    case ContentTypeString:&lt;br /&gt;    {       &lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Control::SetValue() is implemented by each control. The RadioButton class and Checkbox class both implement it the same way (begging the question of whether or not this should be factored out), extracting the boolean checked/unchecked variable from the variant, then calling the concrete widget's SetChecked() method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PRStatus RadioButton::SetValue(const XPVariant &amp;v)&lt;br /&gt;{&lt;br /&gt;    bool value;&lt;br /&gt;    const_cast&amp;lt;XPVariant &amp;&amp;gt;(v).GetValue(value);&lt;br /&gt;    if (m_radiobutton)&lt;br /&gt;        return m_radiobutton-&amp;gt;SetChecked(value);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt; &lt;br /&gt;Finally, the CocoaRadioButtonImpl class implements SetChecked (as it is forced to by its inheritance of RadioButtonImpl, which declares SetChecked to be pure virtual):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PRStatus CocoaRadioButtonImpl::HandleCommand()&lt;br /&gt;{&lt;br /&gt;    if (m_radiobutton) {&lt;br /&gt;        int state = [m_radiobutton state];&lt;br /&gt;        RadioGroupManager *mgr = RadioGroupManager::GetManager(GetDocument());&lt;br /&gt;        if (mgr)&lt;br /&gt;            mgr-&amp;gt;SetState(m_group, this);&lt;br /&gt;    }&lt;br /&gt;    return PR_SUCCESS;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;PRStatus CocoaRadioButtonImpl::SetChecked(const bool checked)&lt;br /&gt;{&lt;br /&gt;    PRStatus ret = PR_FAILURE;&lt;br /&gt;    if (m_radiobutton) {&lt;br /&gt;        if (checked) {&lt;br /&gt;            [m_radiobutton setState: NSOnState];&lt;br /&gt;            HandleCommand(); // propogate the change&lt;br /&gt;        } else {&lt;br /&gt;            [m_radiobutton setState: NSOffState];&lt;br /&gt;        }&lt;br /&gt;        ret = PR_SUCCESS;&lt;br /&gt;    }   &lt;br /&gt;    return ret;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As you can see, we must call HandleCommand() if the state is set to true so that RadioGroupManager can adjust the state of all other radio buttons in the same radio group to the disabled state.&lt;br /&gt;&lt;br /&gt;Checkboxes are almost the same, but they do not need to propogate changes (checkboxes are mutually exclusive, in terms of state, to other checkboxes in the UI). &lt;br /&gt;&lt;br /&gt;Three languages (JavaScript, C++, Objective-C++) all conspiring together to implement an architecture that is simple, elegant, and powerful.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-114871989260868546?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/114871989260868546/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=114871989260868546' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114871989260868546'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114871989260868546'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2006/05/following-javascript-code-is-attached.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-114863985950199037</id><published>2006-05-26T03:11:00.000-07:00</published><updated>2006-05-27T00:44:15.443-07:00</updated><title type='text'></title><content type='html'>Let's look at the class, RadioGroupManager, that provides the functionality necessary to ensure that only one radio button in a group is selected:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#if !defined(__RADIOGROUPMANAGER_H__)&lt;br /&gt;#define __RADIOGROUPMANAGER_H__&lt;br /&gt;&lt;br /&gt;#include &amp;lt;map&amp;gt;&lt;br /&gt;#include &amp;lt;list&amp;gt;&lt;br /&gt;#include &amp;lt;string&amp;gt;&lt;br /&gt;&lt;br /&gt;#include "cocoaradiobuttonimpl.h"&lt;br /&gt;&lt;br /&gt;using namespace std;&lt;br /&gt;&lt;br /&gt;class Document;&lt;br /&gt;&lt;br /&gt;class RadioGroupManager&lt;br /&gt;{&lt;br /&gt;public:&lt;br /&gt;    PRStatus AddToGroup(const string &amp;group, CocoaRadioButtonImpl *impl);&lt;br /&gt;    PRStatus RemoveFromGroup(const string &amp;group,&lt;br /&gt;        CocoaRadioButtonImpl *impl);&lt;br /&gt;    PRStatus SetState(const string &amp;group, CocoaRadioButtonImpl *impl);&lt;br /&gt;    static PRStatus AddManager(const Document *doc);&lt;br /&gt;    static PRStatus RemoveManager(const Document *doc);&lt;br /&gt;    static RadioGroupManager *GetManager(const Document *doc);&lt;br /&gt;private:&lt;br /&gt;    map &amp;lt; string, list &amp;lt;CocoaRadioButtonImpl *&amp;gt; *&amp;gt; m_groups;&lt;br /&gt;    static map &amp;lt;const Document *, RadioGroupManager *&amp;gt; m_documentMap;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;#endif&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This class is private (and unique) to the Cocoa implementation. I know that Gtk+ does not require it, and very likely, I believe .NET will not require it either.&lt;br /&gt;&lt;br /&gt;The basic idea of the above class is to maintain, per document, a list of radiobutton widgets that belong to the same class, for each class specified in the document. When a window is created, the concrete implementation will register the document corresponding to the window by calling the static function AddManager(). AddManager creates an instance of RadioGroupManager, and places it in a static map (m_documentMap in the above listing). Similarly, when the window goes away, RemoveManager() is called by the Cocoa concrete window widget destructor to remove the entry from the map. We need to maintain one RadioButtonManager instance per document/window, since groups are scoped to the window in which they are defined (we might have 2 radiobutton groups with the same name; as long as they are in different window/document instances, this is ok.)&lt;br /&gt;&lt;br /&gt;As radiobutton widgets are instantiated, the concrete Cocoa widget implementation will add itself to a second map. This map is maintained privately by an instance of RadioGroupManager; which RadioGroupManager instance is determined by looking in the m_documentMap map using the document as the key. &lt;br /&gt;&lt;br /&gt;The key to this second map is the group attribute assigned to the radiobutton. The entry in the map corresponding to this key is an STL list; each item on that list is a pointer to the CocoaRadioButtonWidgetImpl class that implements that widget. &lt;br /&gt;&lt;br /&gt;When a Cocoa radio button is clicked, a callback is made into the CocoaRadioButtonImpl instance. When this occurs, the callback simply determines the group assigned to itself (stored as a member variable), then it determines the RadioGroupManager by indexing the m_documentMap map using its document pointer. Then, the list of CocoaRadioButtonImpl widgets found in the list maintained by RadioGroupManager is iterated; if the widget is not the one clicked, then a call is made to the NSButton widget instance to set its state to NSOffState, like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PRStatus&lt;br /&gt;RadioGroupManager::SetState(const string &amp;group,&lt;br /&gt;    CocoaRadioButtonImpl *impl)&lt;br /&gt;{&lt;br /&gt;    if (m_groups.find(group) != m_groups.end()) {&lt;br /&gt;        list &amp;lt;CocoaRadioButtonImpl *&amp;gt; *rbList;&lt;br /&gt;        rbList = m_groups[group];&lt;br /&gt;        if (rbList) {&lt;br /&gt;            list &amp;lt;CocoaRadioButtonImpl *&amp;gt;::iterator iter;&lt;br /&gt;            for (iter = rbList-&amp;gt;begin(); iter != rbList-&amp;gt;end(); ++iter) {&lt;br /&gt;                if ((*iter) != impl) &lt;br /&gt;                    (*iter)-&amp;gt;SetState(NSOffState);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    return PR_SUCCESS;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This sort of implementation/logic is probably at the heart of all radio button widget implementations, regardless of the oolkit or platform, including Cocoa. Where Cocoa falls down is in coupling the logic to a widget (NSMatrix) that impacts UI and layout. For Cocoa developers coding directly to Cocoa, or using XCode tools, NSMatrix is great. But, as I mentioned in the previous post, it is critical for some applications (such as mine) to decouple the GUI aspect of radiobutton management from the functional aspect. For me, it was critical to replace the Cocoa scheme with one of my own, since I had to have direct control of layout. Perhaps another advantage at doing things the way I did is now I could have radio buttons scattered throughout the window that belong to the same radio group. Not that this is generally a good thing to do in terms of usability -- usually, you want to have radio buttons that are in the same group physically adjacent in a window -- but it is now possible should a crazed UI designer insist on it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-114863985950199037?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/114863985950199037/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=114863985950199037' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114863985950199037'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114863985950199037'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2006/05/lets-look-at-class-radiogroupmanager.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-114837506271320497</id><published>2006-05-23T01:37:00.000-07:00</published><updated>2006-05-26T03:11:00.640-07:00</updated><title type='text'></title><content type='html'>After a few hours of work this weekend, scrolled lists are working now under Gtk+ and .NET, ported from Cocoa.&lt;br /&gt;&lt;br /&gt;I originally thought that this would be it; finishing scrolled lists, it would be time to put this thing up on sourceforge and see how it took off. But I felt Monday that I still needed to add two widgets; radio buttons, and checkboxes. So, tonight I fleshed out both of these widgets under Cocoa. &lt;br /&gt;&lt;br /&gt;As it turns out, both of these widgets are just versions of NSButton, with an appropriate style assigned to them at creation time (the default style gives the familiar push button widget). &lt;br /&gt;&lt;br /&gt;Checkboxes were easy to implement; all I needed to do was clone CocoaButtomImpl and set the appropriate style. Radio buttons were nearly the same, except for one major snafu. As many of you who have used radio buttons in various toolkits are aware, a radio button usually is associated somehow with a "radio group". Radio buttons that belong in a radio group are related by state -- that is, when one of the radio buttons becomes selected, the remaining radio buttons in the group become deselected. generally, a radio group is identified by some number or name. In my markup, I simply have a group="name" attribute that can be assigned to a radiobutton element. To place radiobuttons in the same group, one only needs to give each radiobutton the same group attribute, like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;box orient="vertical"&amp;gt;&lt;br /&gt;    &amp;lt;spacer/&amp;gt;&lt;br /&gt;    &amp;lt;radiobutton group="radiogroup1" label="Radio Button 1" id="RadioButton1" checked="true"/&amp;gt;&lt;br /&gt;    &amp;lt;spacer/&amp;gt;&lt;br /&gt;    &amp;lt;radiobutton group="radiogroup1" label="Radio Button 2" id="RadioButton2"/&amp;gt;&lt;br /&gt;    &amp;lt;spacer/&amp;gt;&lt;br /&gt;    &amp;lt;radiobutton group="radiogroup1" label="Radio Button 3" id="RadioButton3"/&amp;gt;&lt;br /&gt;    &amp;lt;spacer/&amp;gt;&lt;br /&gt;    &amp;lt;radiobutton group="radiogroup1" label="Radio Button 4" id="RadioButton4"/&amp;gt;&lt;br /&gt;    &amp;lt;spacer/&amp;gt;&lt;br /&gt;&amp;lt;/box&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The above results in a vertically stacked set of 4 radio buttons, all belonging to the group "radiogroup1".&lt;br /&gt;&lt;br /&gt;In some markup-based toolkits, one might see an additional tag, e.g., radiogroup, that wraps the buttons that belong to that group. I found that not to be necessary, and to incorrectly imply layout -- there is no reason that radio buttons related by group need to be physically adjacent in the UI (though it is clearly the case that visual proximity lends greatly to one's knowledge of the relationship that exists among members of the group.)&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.trixul.com/blog/uploaded_images/Picture 8-779299.png"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://www.trixul.com/blog/uploaded_images/Picture 8-777364.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;So, what was the snafu I mentioned? In Cocoa, to relate buttons, you are forced to use a view-based widget, NSMatrix, and make the radio buttons children of that widget. NSMatrix not only enforces layout semantics on the radio buttons, but it also implements the semantics I described earlier of ensuring that only one of the child radio buttons is active at any time.&lt;br /&gt;&lt;br /&gt;while that is fine and dandy for the majority of Cocoa users, it makes things tough if you are creating your own layout engine atop of Cocoa. The problem for me is that NSMatrix is incompatible with my layout scheme -- one critical problem (perhaps *the* critical problem) is that with NSMatrix in the way, users could not place spacer elements between adjacent radio buttons, nor could I compute my own layout of the radio buttons housed by the NSMatrix container.&lt;br /&gt;&lt;br /&gt;My only solution, then, is not to use NSMatrix, but, instead, to implement the "only one active" semantics associated with radio groups myself. To do this, I need two things: a callback that can handle when a radio button changes state, and a class to manage the radio groups that exist in a document. This class is called RadioGroupManager, and it maintains a map of all radiogroup names in the document to a list of all radio button elements that belong to that group. As radio buttons are created, the call into RadioGroupManager and add themselves to the map. When a given button changes its state (either programmatically or via user input) to active, it lets RadioGroupManager know about this state change. In response, RadioGroupManager iterates its list of all radio buttons that belong to the same group, and programmatically makes them inactive. This is essentially what NSMatrix must do each time as well. &lt;br /&gt;&lt;br /&gt;Had the designers of Cocoa decoupled the GUI aspect of NSMatrix from its radio button state management aspect, I could have used something (NSMatrixController??) provided by Cocoa. The coupling, as it stands, is a design flaw in my opinion (and my need to work around it is a clear example of why this is so).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-114837506271320497?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/114837506271320497/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=114837506271320497' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114837506271320497'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114837506271320497'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2006/05/after-few-hours-of-work-this-weekend.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-114811340931779247</id><published>2006-05-20T01:17:00.000-07:00</published><updated>2006-05-20T01:26:56.160-07:00</updated><title type='text'></title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.trixul.com/blog/uploaded_images/Picture 4-771215.png"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://www.trixul.com/blog/uploaded_images/Picture 4-769609.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;I have a basic implementation of scrolled lists up under Cocoa (based on NSTableView) that support the following: adding, appending, and removing strings to the list, querying selection count, and obtaining the nth selection. &lt;br /&gt;&lt;br /&gt;The XML markup for the above screen looks like the following:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;window name="main" title="GridList Test" main="true" width="600" height="300" x="100" y="100" position="center, mouse"&amp;gt;&lt;br /&gt;    &amp;lt;script type="text/javascript" src="resources/content/list.js"/&amp;gt;&lt;br /&gt;    &amp;lt;box orient="vertical"&amp;gt;&lt;br /&gt;        &amp;lt;spacer/&amp;gt;&lt;br /&gt;        &amp;lt;gridlist id="gridlisttest" width="100" height="100"/&amp;gt;&lt;br /&gt;        &amp;lt;spacer/&amp;gt;&lt;br /&gt;        &amp;lt;box orient="horizontal"&amp;gt;&lt;br /&gt;            &amp;lt;spacer/&amp;gt;&lt;br /&gt;            &amp;lt;button label="Add" onclick="return Add();"/&amp;gt;&lt;br /&gt;            &amp;lt;spacer/&amp;gt;&lt;br /&gt;            &amp;lt;button label="Append" onclick="return Append();"/&amp;gt;&lt;br /&gt;            &amp;lt;spacer/&amp;gt;&lt;br /&gt;            &amp;lt;button label="Selection Count" onclick="return GetSelectionCount();"/&amp;gt;&lt;br /&gt;            &amp;lt;spacer/&amp;gt;&lt;br /&gt;            &amp;lt;button label="Selections" onclick="return GetSelections();"/&amp;gt;&lt;br /&gt;            &amp;lt;spacer/&amp;gt;&lt;br /&gt;            &amp;lt;button label="Remove" onclick="return Remove();"/&amp;gt;&lt;br /&gt;            &amp;lt;spacer/&amp;gt;&lt;br /&gt;        &amp;lt;/box&amp;gt;&lt;br /&gt;    &amp;lt;/box&amp;gt;&lt;br /&gt;&amp;lt;/window&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;JavaScript code that supports the operations described above uses the DOM to locate the item (e.q., search based on ID), then calls functions implemented on the resulting object, as usual. Here is the JavaScript for the buttons shown in the screen shot:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;function Add()&lt;br /&gt;{&lt;br /&gt;    dump("Inside of list.js::Add\n");&lt;br /&gt;    var obj = document.getElementById("gridlisttest");&lt;br /&gt;    if (obj) {&lt;br /&gt;        obj.addItem("Mozart", 1);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function Append()&lt;br /&gt;{&lt;br /&gt;    dump("Inside of list.js::Append\n");&lt;br /&gt;    var obj = document.getElementById("gridlisttest");&lt;br /&gt;    if (obj) {&lt;br /&gt;        obj.appendItem("Amadeus");&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function Remove()&lt;br /&gt;{&lt;br /&gt;    dump("Inside of list.js::Remove\n");&lt;br /&gt;    var obj = document.getElementById("gridlisttest");&lt;br /&gt;    if (obj) {&lt;br /&gt;        obj.removeItemByPosition(1);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function GetSelectionCount()&lt;br /&gt;{&lt;br /&gt;    dump("Inside of list.js::Remove\n");&lt;br /&gt;    var obj = document.getElementById("gridlisttest");&lt;br /&gt;    if (obj) {&lt;br /&gt;        var count = obj.getSelectionCount();&lt;br /&gt;        dump("Selection count is " + count + "\n");&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function GetSelections()&lt;br /&gt;{&lt;br /&gt;    var obj = document.getElementById("gridlisttest");&lt;br /&gt;    if (obj) {&lt;br /&gt;        var i;&lt;br /&gt;        for (i = 0; i &amp;lt; obj.getSelectionCount(); i++) {&lt;br /&gt;            var sel = obj.getSelection(i);&lt;br /&gt;            dump("Selection " + i + " is " + sel + "\n");&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-114811340931779247?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/114811340931779247/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=114811340931779247' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114811340931779247'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114811340931779247'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2006/05/i-have-basic-implementation-of.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-114611052925288530</id><published>2006-04-26T21:01:00.000-07:00</published><updated>2006-04-28T13:42:11.680-07:00</updated><title type='text'></title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.trixul.com/blog/uploaded_images/winscrolledwin-717149.png"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://www.trixul.com/blog/uploaded_images/winscrolledwin-714591.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The port to Windows .NET of the scrolledwindow and scrolledview widgets took more effort than I had originally thought that it would (the patch file for the changes was well over 1000 lines when completed).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-114611052925288530?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/114611052925288530/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=114611052925288530' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114611052925288530'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114611052925288530'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2006/04/port-to-windows.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-114543867807708981</id><published>2006-04-19T02:23:00.000-07:00</published><updated>2006-04-19T02:30:08.370-07:00</updated><title type='text'></title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.trixul.com/blog/uploaded_images/Picture 10-733060.png"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://www.trixul.com/blog/uploaded_images/Picture 10-730915.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Here is a screen shot of the Gtk+/Linux port of the scrolledwindow and scrolledview widgets. The work took maybe 3 hours to complete. The Windows .NET port is next up.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-114543867807708981?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/114543867807708981/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=114543867807708981' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114543867807708981'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114543867807708981'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2006/04/here-is-screen-shot-of-gtklinux-port.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-114528282176874797</id><published>2006-04-17T06:53:00.000-07:00</published><updated>2006-04-17T07:10:53.703-07:00</updated><title type='text'></title><content type='html'>After some time implementing and debugging scrolled window support under Cocoa, I now have a solution. In the end, what I needed was two widgets. One which represented the actual scrolled window, and its scrollbars. This widget is "scrolledwindow". It has a width, a height, and two boolean attributes used to control the visibility of the scrollbars (one of these controls the horizontal scrollbar, the other the vertical).&lt;br /&gt;&lt;br /&gt;The other widget (scrolledview) represents the area being scrolled. It also has a width and height, which will typically be larger than the scrolled window (why else would you need scrollbars in a scrolledwindow widget if the child was smaller than you are?) It also has the interesting attribute that it implements my box layout paradigm, so, just like a box or a window, scrolledview can have arbitrary children, and support spacers too! To do this, I simply factored out the code in box that performed layout into a class called "boxlayout" and now window, box, and scrolledview inherit this base class.&lt;br /&gt;&lt;br /&gt;This is slightly different than the strategy I posted previously. Often this is how software development goes -- ideas are one thing, reality becomes clearer in the implementation.&lt;br /&gt;&lt;br /&gt;Here is a screenshot, and the portion of the code in XML markup that corresponds to it (the screenshot is of a scrolledwindow and scrolledview that have the same dimensions; the markup shows a more typical case, with scrolledview larger in size than the scrolledwindow).&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;scrolledwindow width="200" height="200" vertical="yes" horizontal="yes"&amp;gt;&lt;br /&gt;    &amp;lt;scrolledview width="500" height="500"&amp;gt;&lt;br /&gt;        &amp;lt;spacer/&amp;gt;&lt;br /&gt;        &amp;lt;button onclick="return Button1Click();" label="ScrolledWindow Button 1"/&amp;gt;&lt;br /&gt;        &amp;lt;spacer/&amp;gt;&lt;br /&gt;        &amp;lt;button onclick="return Button2Click();" label="ScrolledWindow Button 2"/&amp;gt;&lt;br /&gt;        &amp;lt;spacer/&amp;gt;&lt;br /&gt;    &amp;lt;/scrolledview&amp;gt;&lt;br /&gt;&amp;lt;/scrolledwindow&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.trixul.com/blog/uploaded_images/Picture 8-715052.png"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://www.trixul.com/blog/uploaded_images/Picture 8-774910.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-114528282176874797?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/114528282176874797/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=114528282176874797' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114528282176874797'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114528282176874797'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2006/04/after-some-time-implementing-and.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-114466154550515832</id><published>2006-04-10T02:20:00.000-07:00</published><updated>2006-04-10T02:47:40.723-07:00</updated><title type='text'></title><content type='html'>After playing around most of the weekend with the concept, here is what I plan to do for scrolledwindow widgets:&lt;br /&gt;&lt;br /&gt;-- scrolled window widgets are a mix of a container and control; much like the window tag, having both a concrete implementation (e.g., NSScrollView in Cocoa) and a non-concrete representation in the layout engine that manages the layout of children.&lt;br /&gt;&lt;br /&gt;-- scrolled windows will have the following attributes assignable in markup: &lt;br /&gt;&lt;br /&gt;    -- "width" and "height" will determine the size of the scrolled window control in a window. &lt;br /&gt;    -- "innerwidth" and "innerheight" will determine the scrollable view, and will represent the bounds into which the layout engine can place children. The scrollbars will be used to manipulate a view, of size "width" and "height" over this area. &lt;br /&gt;    -- Finally, the "vertical" and "horizontal" attributes will determine the presence of vertical and horizontal scrollbars. If set to "true", the corresponding scrollbar will be made available. Otherwise, if set to "false", the corrersponding scrollbar will not be visible.&lt;br /&gt;&lt;br /&gt;-- the scrolled window, like a window, will support a box layout with a vertical orientation. Thus, a scrolled window is just a vertical box with optional scrollbars and a viewing area that can be smaller that the layout area. Any widget (except windows), including spacers and embedded scrolled windows can be made children of a scrolled window.&lt;br /&gt;&lt;br /&gt;Here is some example markup:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;scrolledwindow width="200" height="200" &lt;br /&gt;vertical="true" horizontal="false" innerwidth="500" innerheight="500"&amp;gt;&lt;br /&gt;    &amp;lt;spacer/&amp;gt;&lt;br /&gt;    &amp;lt;button onclick="return Button1Click();" label="&amp;button1.label;"/&amp;gt;&lt;br /&gt;    &amp;lt;spacer/&amp;gt;&lt;br /&gt;    &amp;lt;button onclick="return Button2Click();" label="&amp;button2.label;"/&amp;gt;&lt;br /&gt;    &amp;lt;spacer/&amp;gt;&lt;br /&gt;&amp;lt;/scrolledwindow&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The above should result in a scrolled window that vertically manages two buttons in a 500x500 region. The viewable region is 200x200, and the view can only be scrolled in the vertical direction.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-114466154550515832?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/114466154550515832/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=114466154550515832' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114466154550515832'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114466154550515832'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2006/04/after-playing-around-most-of-weekend.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-114431115144050477</id><published>2006-04-06T01:02:00.000-07:00</published><updated>2006-04-08T21:46:21.366-07:00</updated><title type='text'></title><content type='html'>What's next, now that I have the spent the last 8 months writing the following, from scratch: &lt;br /&gt;&lt;br /&gt;-- XML UI language&lt;br /&gt;-- a set of simple widgets (buttons, text fields, static and editable, box widgets, menus, spacers, and windows)&lt;br /&gt;-- Mozilla JavaScript engine integration layer&lt;br /&gt;-- a component bridge between JavaScript and C++, utilizing my own IDL-like language, expressed in XML (but not COM)&lt;br /&gt;-- properties support (minimal DOM) for set/get value, enable and disable&lt;br /&gt;-- ability to create and destroy dialogs directly from JavaScript&lt;br /&gt;-- crude localization (no i18n support, but UI strings are kept in separate DTDs)&lt;br /&gt;-- the above all ported to MacOSX (Cocoa), Windows (.NET), and Linux (Gtk+)&lt;br /&gt;&lt;br /&gt;behind me (modulo any bugs I find)?&lt;br /&gt;&lt;br /&gt;Well, thinking on this, I'd like to see the following in the UI:&lt;br /&gt;&lt;br /&gt;-- scrolled window support, a "scrolled" tag that can be used as a container and can scroll its children&lt;br /&gt;-- simple list control&lt;br /&gt;-- a Gtk+-based print system (waiting for RedHat to come thru on a good CUPS-based implementation, they said by this May). Cocoa and Windows print support is already implemented.&lt;br /&gt;&lt;br /&gt;There are plenty of other controls I could add, like radio buttons, checkboxes, and so on. These are all needed to make the toolkit complete, but they can wait until I release to open source. For now, I need to get closure on this phase of the toolkit and return to writing manuscript (my deadline looms...)&lt;br /&gt;&lt;br /&gt;So, next weekend is a scrolled window container, and the following weekend is a list control. Then I am off to vacation (somewhere, anywhere!) and then back to focusing my time on writing.&lt;br /&gt;&lt;br /&gt;Stay tuned.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-114431115144050477?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/114431115144050477/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=114431115144050477' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114431115144050477'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114431115144050477'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2006/04/whats-next-now-that-i-have-spent-last.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-114405142195387465</id><published>2006-04-03T01:00:00.000-07:00</published><updated>2006-04-03T01:38:38.470-07:00</updated><title type='text'></title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.trixul.com/blog/uploaded_images/Picture 6-790569.png"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://www.trixul.com/blog/uploaded_images/Picture 6-788989.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;After a couple of weeks of laboring with issues related to the JavaScript engine, I have implemented support for "document.openDialog" and "document.close" functions. The document.openDialog() function takes a single argument, a URL to a XUL document. The document.close() function takes no arguments, and closes the current dialog. Both are implemented as functions callable from the document object in JavaScript.&lt;br /&gt;&lt;br /&gt;The screen shot shows a dialog (with the title "Test dialog") that is displayed when the user clicks the "World!" button in the main window (with the title "Hello World App"). The onclick handler for the "World!" button simply makes the following call to display the dialog:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;openDialog("/resources/content/dialog.xul");&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The source for dialog.xul is simple:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;!DOCTYPE window SYSTEM "dialog.dtd"&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;window name="main" title="&amp;dialog.title;" main="false" width="250" height="100" x="500" y="100"&amp;gt;&lt;br /&gt;    &amp;lt;script type="text/javascript" src="resources/content/dialog.js"/&amp;gt;&lt;br /&gt;    &amp;lt;box orient="vertical"&amp;gt;&lt;br /&gt;        &amp;lt;spacer/&amp;gt;&lt;br /&gt;        &amp;lt;box orient="horizontal"&amp;gt;&lt;br /&gt;            &amp;lt;spacer/&amp;gt;&lt;br /&gt;            &amp;lt;button onclick="return Button1Click();" label="&amp;button1.label;"/&amp;gt;&lt;br /&gt;            &amp;lt;spacer/&amp;gt;&lt;br /&gt;        &amp;lt;/box&gt;&lt;br /&gt;        &amp;lt;spacer/&amp;gt;&lt;br /&gt;    &amp;lt;/box&gt;&lt;br /&gt;&amp;lt;/window&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The above layout displays a 250x100 pixel dialog and centers the button in the middle of the dialog. I could have used any arbitrary layout or content, this is just one example. &lt;br /&gt;&lt;br /&gt;To close the window, I associate an onclick handler with the "Click me to dismiss" button, and call document.close() from it. This code is in dialog.js (see the script tag in the above code for how dialog.js is associated with the dialog). Here is the onclick handler:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;function Button1Click()&lt;br /&gt;{&lt;br /&gt;    dump("Inside of dialog::Button1Click\n");&lt;br /&gt;    document.close();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So, what was all the trouble I had with JavaScript in implementing the above? Well, it turns out that both dialogs implemented a function named Button1Click() in their respective JavaScript. Whenever the engine creates a document (both dialogs in the above example have a document object to represent them in the layout engine), I parse their scripts as a part of creating the layout document object, and prior to displaying the dialog. The problem I ran into was that the Button1Click() function associated with the "Hello World App" JavaScript was getting overridden by the Button1Click() function associated with the "Test Dialog" JavaScript. In the end, the scripts were being compiled and evaluated into a common JavaScript engine global object. The only way I found to overcome this problem was to spend numerous hours reading the netscape.public.mozilla.jseng newsgroup for clues. I didn't get a direct answer from doing this, but in the end, I did get some clues, and the trick I came up with involved the following:&lt;br /&gt;&lt;br /&gt;-- creating a superglobal JavaScript object&lt;br /&gt;-- for each document, creating a private JS object that I compiled and executed JavaScript into when processing scripts in response to an openDialog() call on the document&lt;br /&gt;-- adding listeners in the layout Document class for window raise/focus events. When the window is raised, switching the global document from the previously raised window's JavaScript object to the one of the newly raised window by making a call to JavaScript API function JS_SetGlobalObject().&lt;br /&gt;-- at the same time, recompiling the scripts associated with the newly focused document. This last step seemed unsavory at best, but it was necessary.&lt;br /&gt;&lt;br /&gt;Turns out, recompiling the JavaScript scripts at document raise time does not seem to affect global variables in the JavaScript. I'm guessing this is because they are rooted in the superglobal object.&lt;br /&gt;&lt;br /&gt;Someone needs to encourage Brendan Eich to write a book on JavaScript embedding. So many questions on the newsgroup were asked over and over again...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-114405142195387465?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/114405142195387465/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=114405142195387465' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114405142195387465'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114405142195387465'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2006/04/after-couple-of-weeks-of-laboring-with.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-114212620840955605</id><published>2006-03-11T17:10:00.000-08:00</published><updated>2006-03-11T18:08:13.380-08:00</updated><title type='text'></title><content type='html'>Those of you who are experienced JavaScript programmers, and who have read my last couple of posts, might be wondering why I have not implemented properties for setting and getting the value of a control, or for changing the enabled state of a control. After all, the syntax associated with the use of properties is arguably more elegant -- instead of calling functions implemented by the object, like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;var obj = document.getElementById("text1");&lt;br /&gt;if (obj) {&lt;br /&gt;    obj.enable();  // enable the text field&lt;br /&gt;    obj.setValue("Mozart"); // set the value to "Mozart"&lt;br /&gt;    var val = obj.getValue(); // get the value of the text field&lt;br /&gt;    obj.disable(); // disable the text field&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;one can achieve the same functionality by setting properties that are exposed by the object. The following code is semantically equivalent to the above, but uses properties instead of calling functions defined on the object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;var obj = document.getElementById("text1");&lt;br /&gt;if (obj) {&lt;br /&gt;    obj.enabled = true; // enable the text field&lt;br /&gt;    obj.value = "Mozart"; // set the value to "Mozart"&lt;br /&gt;    var val = obj.value; // get the value of the text field&lt;br /&gt;    obj.enabled = false; // disable the text field&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Well, turns out I did implement the above, and I'd like to briefly explain how it was done. In addition to implementing functions that can be called on an object, the Mozilla JavaScript engine also supports properties. To associate properties on an object, a JSPropertySpec vector can be used, in a way analogous to the use of the JSFunctionSpec to define functions on an object, to define properties and associate them with C++ callback functions.&lt;br /&gt;&lt;br /&gt;The following code declares a JSPropertySpec vector that defines two properties, enabled and value, and defines setters and getters for each:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;static JSPropertySpec element_properties[] = {&lt;br /&gt;    {"enabled", 0x01, 0, EnabledPropertyGetter,  EnabledPropertySetter},&lt;br /&gt;    {"value", 0x02, 0, ValuePropertyGetter,  ValuePropertySetter},&lt;br /&gt;    {0}&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The first field is the property name. The second field is a unique 8-bit value which is unused in my implementation. The third field is a bitmask that can be used to restrict access to the properties, see the online documentation at mozilla.org for more information. Finally, the last two fields contain the local C callback functions invoked when the JavaScript application reads and writes the properties, respectively. These callbacks are roughly analogous to the callbacks invoked when a function is called on an object. &lt;br /&gt;&lt;br /&gt;Here are the function implementations for both ValuePropertyGetter() and ValuePropertySetter(). In the case of ValuePropertySetter(), the value to which the control is to be set is passed via the vp argument. Similarly, the job of ValuePropertyGetter() is to retrieve the value of the control associated with the passed in object, and store its value in the vp argument. This, for the setter, vp should be treated read-only, and for the getter, vp should be considered write-only.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;static JSBool&lt;br /&gt;ValuePropertySetter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)&lt;br /&gt;{&lt;br /&gt;    JSBool ret = JS_FALSE;&lt;br /&gt;&lt;br /&gt;    Element *element = Element::FindElementByObj(obj);&lt;br /&gt;    if (element) {&lt;br /&gt;        Control *control = dynamic_cast&amp;lt;Control *&amp;gt;(element);&lt;br /&gt;        if (control &amp;&amp; element-&amp;gt;SetValueHelper(cx, control, *vp, vp) == PR_SUCCESS)&lt;br /&gt;            ret = JS_TRUE;&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    return ret;&lt;br /&gt;}   &lt;br /&gt;&lt;br /&gt;static JSBool&lt;br /&gt;ValuePropertyGetter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)&lt;br /&gt;{&lt;br /&gt;    JSBool ret = JS_FALSE;&lt;br /&gt;        &lt;br /&gt;    Element *element = Element::FindElementByObj(obj);&lt;br /&gt;    if (element) {&lt;br /&gt;        Control *control = dynamic_cast&amp;lt;Control *&amp;gt;(element);&lt;br /&gt;        if (control &amp;&amp; element-&amp;gt;GetValueHelper(cx, control, vp) == PR_SUCCESS)&lt;br /&gt;            ret = JS_TRUE;&lt;br /&gt;    } &lt;br /&gt;    return ret; &lt;br /&gt;}           &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Note that both of the functions above use GetValueHelper() and SetValueHelper() in the same way as they are used by DoGetValue() and DoSetValue() (which were both described in my previous post). After all, getting the value of the text field, regardless of how it is done -- by reading a property or via calling a function -- remains the same.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-114212620840955605?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/114212620840955605/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=114212620840955605' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114212620840955605'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114212620840955605'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2006/03/those-of-you-who-are-experienced.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-114210377136893255</id><published>2006-03-11T10:58:00.000-08:00</published><updated>2007-02-24T03:46:22.780-08:00</updated><title type='text'></title><content type='html'>This week, I continued work towards supporting DOM interfaces from JavaScript, adding support for getting and setting the value of controls (at this point, the only content-based controls I support are text and static text). The ability for user interface code to initialize, set, and retrieve the value of controls (e.g., text fields, radio buttons) is a requirement of any UI toolkit, and trixul is no exception. The prototypical case would be a program displaying a form to the user; the program must be able to load the form with any previously entered values. To do this, it must iterate each user interface object, and set its value. Similarly, when the user submits the form, the user interface code must iterate the controls in the form, obtain their values, and and then write the values somewhere (so that they can be acted upon by the application, or perhaps be persisted until the next time the user loads the data back into the form for editing).&lt;br /&gt;&lt;br /&gt;In a previous post, I described how DOM interfaces could be used to support function calls that can be used from JavaScript to enable or disable a control, for example:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;var button = document.getElementById("myButton");&lt;br /&gt;if (button &amp;&amp; someCondition)&lt;br /&gt;    button.enable();  // enable the button is someCondition is true&lt;br /&gt;else if (button)&lt;br /&gt;    button.disable();&lt;br /&gt;else&lt;br /&gt;    dump("Unable to locate the button 'myButton'\n");&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;and I also described how I implemented this functionality by exposing JS objects that mapped to concrete control/widget implementations in the layout engine.&lt;br /&gt;&lt;br /&gt;Implementing support for setting and getting the value controls follows a very similar strategy. What we want to support is code like the following, which gets and sets the value of a text field:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;var text = document.getElementById("myText");&lt;br /&gt;if (text) { &lt;br /&gt;    value = text.getValue();&lt;br /&gt;    dump("myText value is " + value + "\n");&lt;br /&gt;    // set value to the string "Hello";&lt;br /&gt;     text.setValue("Hello");&lt;br /&gt;} else&lt;br /&gt;    dump("Unable to locate the text field 'myText'\n");&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;When it comes to the implementation of the above, about the only difference between getValue/setValue and enable/disable is these functions either return a value (getvalue()) or require an argument (setValue()), meaning that getValue() must return a value of the appropriate type, and setValue() must correctly interpret the argument that it has been passed. In addition, both must interact with the concrete widget to retrieve its value, or set its value, respectively.&lt;br /&gt;&lt;br /&gt;When document.getElementById() is called, the JavaScript object returned is lazily instantiated; that is, the layout code will not create this object until JavaScript code attempts to retrieve it. The lifetime of the JavaScript object is determined by the scope of the JavaScript variable that stores its value, and once the scope is left, the JavaScript object becomes a candidate for being garbage collected. Multiple requests for a JavaScript object pertaining to a given DOM element will result in multiple JavaScript objects being created and returned, but this is ok. Why? The JavaScript object is nothing more than an interface that allows JavaScript code to make calls on the layout object; any state is maintained by the layout object, not by the JavaScript objects that represent it. &lt;br /&gt;&lt;br /&gt;In fact, the JavaScript object is the same object that is used to call enable() and disable(), all I needed to do was add entries to Element's JSFunctionSpec vector:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;static JSFunctionSpec element_functions[] = {&lt;br /&gt;    {"enable", DoEnable,  1},&lt;br /&gt;    {"disable", DoDisable,  1},&lt;br /&gt;    {"setValue", DoSetValue,  1},&lt;br /&gt;    {"getValue", DoGetValue,  1},&lt;br /&gt;    {0} &lt;br /&gt;};  &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and implement the functions DoSetValue() and DoGetValue(). &lt;br /&gt;&lt;br /&gt;The most interesting part of DoSetValue() and DoGetValue() is how they support the argument (DoSetValue()) and return value (DoGetValue()). When I call DoSetValue() against a JavaScript object that represents a text field, the argument needs to be interpreted as a string. If I am making the call against a radio button, it might be an integer or a boolean value to indicate its state. A progress bar value might be an integer in a specific range. Similarly, if I return the value of a progress bar, text field, or radio button, the types need to be appropriate for the control.&lt;br /&gt;&lt;br /&gt;Let's take a look at DoSetValue() and its helper function to see how this works. Here is the code for DoSetValue():&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;JS_STATIC_DLL_CALLBACK(JSBool)&lt;br /&gt;DoSetValue(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)&lt;br /&gt;{       &lt;br /&gt;    JSBool ret = JS_FALSE;&lt;br /&gt;&lt;br /&gt;    if (!argc)&lt;br /&gt;        return JS_TRUE;&lt;br /&gt;&lt;br /&gt;    Element *element = Element::FindElementByObj(obj);&lt;br /&gt;    if (element) {&lt;br /&gt;        Control *control = dynamic_cast&amp;lt;Control *&amp;gt;(element);&lt;br /&gt;        if (control &amp;&amp;&lt;br /&gt;            element-&amp;gt;SetValueHelper(cx, control, argv[0]) == PR_SUCCESS)&lt;br /&gt;            ret = JS_TRUE;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    return ret;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The function prototype is the same is it is for the callbacks supporting disable() and enable(). argv[0] will contain a JavaScript object that represents the value we wish to set the control to. This value, along with the control that maps to the object passed in, and the JavaScript context, are passed to the helper function SetValueHelper(). The job of SetValueHelper() is simple; it needs to convert the value passed in via argv[0] to a native C++ type, and then invoke a function in the concrete widget implementation that can interact with the platform-specific widget to change its value. Here is the body of SetValueHelper():&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PRStatus&lt;br /&gt;Element::SetValueHelper(JSContext *cx, Control *control, jsval val)&lt;br /&gt;{   &lt;br /&gt;    if (!control)&lt;br /&gt;        return PR_FAILURE;&lt;br /&gt;&lt;br /&gt;    PRStatus ret = PR_FAILURE;&lt;br /&gt;&lt;br /&gt;    ContentType type = control-&amp;gt;GetContentType();&lt;br /&gt;    switch (type) {&lt;br /&gt;    case ContentTypeString:&lt;br /&gt;        JSString *str;&lt;br /&gt;        str = JS_ValueToString(cx, val);&lt;br /&gt;        if (!str)&lt;br /&gt;            return ret;&lt;br /&gt;&lt;br /&gt;        char *bytes = JS_GetStringBytes(str);&lt;br /&gt;        if (bytes)&lt;br /&gt;            bytes = strdup(bytes);&lt;br /&gt;        if (bytes) {&lt;br /&gt;            XPVariant v;&lt;br /&gt;            v.SetValue(bytes);&lt;br /&gt;            if (control-&amp;gt;SetValue(v) == PR_SUCCESS)&lt;br /&gt;                ret = PR_SUCCESS;;&lt;br /&gt;            free(bytes);&lt;br /&gt;        }&lt;br /&gt;        break;&lt;br /&gt;    default:&lt;br /&gt;        break;&lt;br /&gt;    }&lt;br /&gt;    return ret;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Let's look at this code carefully. The first important line of code:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ContentType type = control-&amp;gt;GetContentType();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;queries the control object asking it what kind of content it manages. ContentType is an enum defined in the layout engine; each class deriving from Control, in its constructor, will call a function that registers its type (the setter and getter are implemented by the Control base class, which maintains the value in a private member variable. Text fields will register the type ContentTypeString. &lt;br /&gt;&lt;br /&gt;We then enter a switch statement based on this type. The code for handling ContentTypeText uses the JavaScript API function JS_ValueToString() to retrieve the JSObject value we were passed to a JSString, and we then call another JS API function, GetStringBytes(), to get the characters as a C string:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    switch (type) {&lt;br /&gt;    case ContentTypeString:&lt;br /&gt;        JSString *str;&lt;br /&gt;        str = JS_ValueToString(cx, val);&lt;br /&gt;        if (!str)&lt;br /&gt;            return ret;&lt;br /&gt;&lt;br /&gt;        char *bytes = JS_GetStringBytes(str);&lt;br /&gt;        if (bytes)&lt;br /&gt;            bytes = strdup(bytes);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;JS_ValueToString() is one of a handful of JavaScript API functions that can convert a JSValue to a type (e.g., JS_ValueToInt32() converts a JSValue to an integer). In a sense, a JSValue is a variant object, meaning a single object that can be used to represent values of varying types. JS_ValueToString() et al. allow application code to convert the value stored in the JSObject to whatever type is needed (assuming that the conversion makes sense).&lt;br /&gt;&lt;br /&gt;Now that the value of the JSObject is in hand, we only need to call the control to change the value of the widget. There are two ways to accomplish this. One would be to overload a setter function in Control for each possible type, e.g.:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PRStatus SetValue(const string &amp;value);&lt;br /&gt;PRStatus SetValue(const int &amp;value);&lt;br /&gt;PRStatus SetValue(const float &amp;value);&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Or, we could be a bit more formal about it and and use a variant. I decided to do just that, and I invented a simple variant type, XPVariant. It implements the same basic strategy as mentioned above (overloaded setters and getters), but encapsulates this and the representation of the value into a class that can be used outside of the scope of the problem at hand. The data is stored as a member variable in a union:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;private:&lt;br /&gt;    XPVarType m_type;&lt;br /&gt;    union {&lt;br /&gt;        char vChar;&lt;br /&gt;        PRUint8 vUint8;&lt;br /&gt;        PRInt8 vInt8;&lt;br /&gt;        PRUint16 vUint16;&lt;br /&gt;        PRInt16 vInt16;&lt;br /&gt;        PRUint32 vUint32;&lt;br /&gt;        PRInt32 vInt32;&lt;br /&gt;#ifdef HAVE_LONG_LONG&lt;br /&gt;        PRUint64 vUint64;&lt;br /&gt;        PRInt64 vInt64;&lt;br /&gt;#endif&lt;br /&gt;        PRFloat64 vFloat64;&lt;br /&gt;        PRBool vBool;&lt;br /&gt;    } m_val;&lt;br /&gt;    string m_vString;&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The types of the variables are mostly one-to-one with the types that are defined by NSPR. Strings are presented outside of the union as STL strings, however.&lt;br /&gt;&lt;br /&gt;The setters and getters are overloads of basically the same interface. Here are a few of them:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    PRStatus SetValue(const PRUint8 &amp;val);&lt;br /&gt;    PRStatus GetValue(PRUint8 &amp;val); &lt;br /&gt;    PRStatus SetValue(const PRInt8 &amp;val); &lt;br /&gt;    PRStatus GetValue(PRInt8 &amp;val); &lt;br /&gt;    PRStatus SetValue(const PRUint16 &amp;val); &lt;br /&gt;    PRStatus GetValue(PRUint16 &amp;val); &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The implementation of SetValue() and GetValue() for PRUint8 simply assign or read the appropriate value in the union:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PRStatus XPVariant::SetValue(const PRUint8 &amp;val)&lt;br /&gt;{&lt;br /&gt;    m_val.vUint8 = val;&lt;br /&gt;    m_type = XPVarTypeUint8;&lt;br /&gt;&lt;br /&gt;    return PR_SUCCESS;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;PRStatus XPVariant::GetValue(PRUint8 &amp;val)&lt;br /&gt;{&lt;br /&gt;    if (m_type == XPVarTypeUint8) {&lt;br /&gt;        val = m_val.vUint8;&lt;br /&gt;        return PR_SUCCESS;&lt;br /&gt;    } else&lt;br /&gt;        return PR_FAILURE;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;By storing the type (e.g., XPVarTypeUint8) in the object, we can make queries on the object to determine the type it is storing via a member function name GetType(). More sophistication can be achieved by implementing converters that, like those associated with JSObject, that can be used to convert from one to type to another. However, I don't see the need for implementing converters in this project, at this point.&lt;br /&gt;&lt;br /&gt;Continuing the discussion of SetValueHelper(), here is the code that uses the variant type to invoke the Control object interface to set the value:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;        if (bytes) {&lt;br /&gt;            XPVariant v;&lt;br /&gt;            v.SetValue(bytes);     // automatically makes the variant a string&lt;br /&gt;            if (control-&gt;SetValue(v) == PR_SUCCESS)&lt;br /&gt;                ret = PR_SUCCESS;;&lt;br /&gt;            free(bytes);&lt;br /&gt;        }&lt;br /&gt;        break;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Control::SetValue() then extracts the value from the variant, and calls the concrete implementation, which for Cocoa, is the following function, SetValue(), implemented in CocoaTextImpl:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PRStatus&lt;br /&gt;CocoaTextImpl::SetValue(const string &amp;v)&lt;br /&gt;{&lt;br /&gt;    if (m_text) {&lt;br /&gt;        NSString *nsvalue = [NSString stringWithCString: v.c_str()];&lt;br /&gt;        if (nsvalue)&lt;br /&gt;            [m_text setString: nsvalue];&lt;br /&gt;    }&lt;br /&gt;    return PR_SUCCESS;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Getting values is about as simple. As before, the JavaScript function calls us, this time in DoGetValue():&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;JS_STATIC_DLL_CALLBACK(JSBool)&lt;br /&gt;DoGetValue(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)&lt;br /&gt;{   &lt;br /&gt;    JSBool ret = JS_FALSE;&lt;br /&gt;        &lt;br /&gt;    Element *element = Element::FindElementByObj(obj);&lt;br /&gt;    if (element) {&lt;br /&gt;        Control *control = dynamic_cast&lt;Control *&gt;(element);&lt;br /&gt;        if (control &amp;&amp; element-&gt;GetValueHelper(cx, control, rval) == PR_SUCCESS)&lt;br /&gt;            ret = JS_TRUE; &lt;br /&gt;    }   &lt;br /&gt;    return ret; &lt;br /&gt;}           &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Once the control is located, GetValueHelper() is called. It's job is to call the control, and return the value of the control in its rval argument:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PRStatus&lt;br /&gt;Element::GetValueHelper(JSContext *cx, Control *control, jsval *rval)&lt;br /&gt;{&lt;br /&gt;    if (!control)&lt;br /&gt;        return PR_FAILURE;&lt;br /&gt;        &lt;br /&gt;    PRStatus ret = PR_FAILURE;&lt;br /&gt;    &lt;br /&gt;    ContentType type = control-&gt;GetContentType();&lt;br /&gt;    switch (type) {&lt;br /&gt;    case ContentTypeString:&lt;br /&gt;    {&lt;br /&gt;        XPVariant v;&lt;br /&gt;        if (control-&gt;GetValue(v) == PR_SUCCESS) {&lt;br /&gt;            string val;&lt;br /&gt;            if (v.GetValue(val) == PR_SUCCESS) {&lt;br /&gt;                JSString *str = ::JS_NewStringCopyN(cx, val.c_str(), val.size() + 1);                    &lt;br /&gt;                *rval = STRING_TO_JSVAL(str);&lt;br /&gt;                ret = PR_SUCCESS;&lt;br /&gt;            }   &lt;br /&gt;        }   &lt;br /&gt;        break;&lt;br /&gt;    }   &lt;br /&gt;    default:&lt;br /&gt;        break;&lt;br /&gt;    }&lt;br /&gt;    return ret;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The job of Control::GetValue() is to interact with the concrete widget to determine its current value, and place this in the XPVariant it is passed. Then, GetValueHelper() can convert the STL string returned in the case of a text field to a JSvalue, storing the result in rval. The JavaScript engine will then ensure that JavaScript code can assign the return value to a JavaScript variable.&lt;br /&gt;&lt;br /&gt;For completeness sake, below is the implementation of CocoaTextImpl::GetValue(), which retrieves the value of a text field and stores it in the passed STL string (Control, which calls this function, will take the result and assign it to the variant before returning):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PRStatus&lt;br /&gt;CocoaTextImpl::GetValue(string &amp;v)&lt;br /&gt;{&lt;br /&gt;    if (m_text) {&lt;br /&gt;        NSString *value = [m_text string];&lt;br /&gt;        if (value) {&lt;br /&gt;            v = [value cStringUsingEncoding: NSUTF8StringEncoding];&lt;br /&gt;            return PR_SUCCESS;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    return PR_FAILURE;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-114210377136893255?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/114210377136893255/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=114210377136893255' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114210377136893255'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114210377136893255'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2006/03/this-week-i-continued-work-towards.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-114102012559949735</id><published>2006-02-26T22:00:00.000-08:00</published><updated>2006-02-27T11:04:42.593-08:00</updated><title type='text'></title><content type='html'>In Cocoa, an NSMenu object can be set to allow clients to enable or disable menu items, or to have Cocoa itself enable or disable menu items based on whether or not the control that has focus (known in Cocoa as the firstResponder) can handle the action that selecting the menu item would invoke. In otherwords, if there is an object in the responder chain that can handle a cut: message, then the Cut menu item will be enabled, otherwise it will not. That is, as long as I map the menu item to that cut: action, like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[menuItem setAction:@selector(cut:)];&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This implies the following strategy. When creating the menu item concrete implementation, check the menu item class, and if it is cut, copy, paste, or clear, do the following:&lt;br /&gt;&lt;br /&gt;-- invoke the setAction method on the NSMenuItem object, passing the appropriate selector (as in the above code) &lt;br /&gt;-- call a method on the containing item (an NSMenu) to tell it that it should handle the enabling and the disabling of menu items (presumably, this will be the Edit menu, but it may not). Otherwise, call a method to tell Cocoa we are going to handle the enabling and disabling of the menu items. Code for this step looks like the following (in my implementation, that is):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;       if (menuImpl) {&lt;br /&gt;            NSMenu *parentMenu = menuImpl-&amp;gt;GetMenu();&lt;br /&gt;&lt;br /&gt;            if (parentMenu &amp;&amp; !setAction) {&lt;br /&gt;                     [parentMenu setAutoenablesItems: YES];&lt;br /&gt;                }&lt;br /&gt;                return PR_SUCCESS;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;setAction is a flag that was set if the menu item class was one corresponding to the selectors cut:, copy:, paste:, delete:, or selectAll:. setAutoenablesItems: YES tells the menu it needs to manage the enabling and disabling of the menu item. If this is invoked on the menu, any calls made from JavaScript to enable or disable menu items in this menu will silently fail.&lt;br /&gt;&lt;br /&gt;There is a bit of an issue regarding placing other items in a menu that uses setAutoenablesItems: in this way. There may be menu items that do not correspond to this paradigm, e.g., they have a selector that does not get handled by anyone, thus they would be perpetually disabled. The way to avoid this will be to keep all items out of menus like this. This strategy will work if the menu is restricted containing menu items corresponding to the above-mentioned selectors, as is the case with the Edit menu (at least, as I have implemented it in the sample application).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-114102012559949735?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/114102012559949735/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=114102012559949735' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114102012559949735'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114102012559949735'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2006/02/in-cocoa-nsmenu-object-can-be-set-to.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-114100391836812837</id><published>2006-02-26T17:23:00.000-08:00</published><updated>2006-02-26T20:59:47.876-08:00</updated><title type='text'></title><content type='html'>This weekend I tackled the issue of supporting the enabling and disabling of controls (e.g., menu items, buttons, text fields) from user code, once again, starting my work on Cocoa, and migrating it over to Gtk+ and Windows .NET. Most of the work was highly cross platform, but some of it (the menuitem code on Cocoa) was very non-portable (but, as usual, very powerful).&lt;br /&gt;&lt;br /&gt;In this entry, I'll describe the architecture of the overall solution. Notes on platform-specific issues that arose during the implementation will be described later in the week (notably, the implementation of enable and disable for Cocoa NSMenuItems under MacOS X).&lt;br /&gt;&lt;br /&gt;Typically, applications will enable and disable certain controls in the user interface based upon the state of the application. If there have been no changes to a document, for example, a text editor will have its Save and Save As... menu items disabled. If no text is selected in a text field that has the input focus, then Cut and Copy menu items will be disabled. If a user has not filled out all of the fields in a form, then perhaps a button labeled "Submit" will be disabled. This sort of functionality implies the following support: JavaScript (or less desirably, C++ component code) needs to be able to enable and disable controls, and it also needs to be able to query controls for their values (and, as a corollary, JavaScript code should also be able to set the value of a control).&lt;br /&gt;&lt;br /&gt;In &lt;a href="http://www.w3.org/TR/REC-DOM-Level-1/"&gt; the W3 DOM Level 1 Specification &lt;/a&gt;, the standard way to find an object is to search for it by name or ID. The result of this call is a DOM element, which can then by interacted with by the JavaScript code, e.g., by setting attributes on the element, or by calling functions that are implemented by the element. Here is how such code might look like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;var findText = document.getElementById("findText");&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The above code, taken from legacy Mozilla's JavaScript, shows how getElementById(), a W3 DOM standard function, can be used to find the DOM element with the id "findText". The result is stored in the variable findText, and then code like the following:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;var findTextValue = findText.value;&lt;br /&gt;dump("The value of the text field 'findText' is " + findTextValue + "\n");&lt;br /&gt;&lt;/pre&gt; &lt;br /&gt;can be called to retrieve the value associated with the text field referenced by findText, and print out its value.&lt;br /&gt;&lt;br /&gt;Assigning an id to an element is easily done by supplying an id="value" attribute in markup. For example, the following markup, also taken from Mozilla, illustrates the XUL used to define the textbox retrieved by calling getElementById() in the JavaScript code presented above, assigning the the id "findText":&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;textbox id="findText" type="timed" timeout="500" oncommand="doFind();"/&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In my design, I adopted the above paradigm based on identifying objects in markup with an id attribute, and finding objects in JavaScript by calling my own implementation of getElementById(). &lt;br /&gt;&lt;br /&gt;To do this, one needs to expose a JavaScript object that implements getElementById(). Since the goal is to find an element within an object, the natural choice was to extend layout's Document class to manage a "document" JavaScript object for each Document instance created. Each window displayed by the application has a Document instance representing it in the layout engine, and the "document" JavaScript object gives us access to that Document instance. And, all of the elements that we wish to locate are children of the document (elements are scoped to a particular instance of a document just like controls are scoped to a particular window in more traditional GUI toolkits), thus the Document class is well-suited to locating any item that we are interested in finding. &lt;br /&gt;&lt;br /&gt;When the Document class is instantiated by the layout engine, it will call a function named CreateJSObject() to create a JavaScript document object for that instance of Document, like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;document = new Document();&lt;br /&gt;if (document)&lt;br /&gt;    document-&gt;CreateJSObject();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The resulting JavaScript object is stored as a member variable in the Document instance, and, importantly, it is also stored in a static map that binds the JavaScript object to the Document instance (why this is done is explained later). Let's look at the code for CreateJSObject():&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;static JSFunctionSpec document_functions[] = {&lt;br /&gt;    {"getElementById", DoGetElementById,  1},&lt;br /&gt;    {0}&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;PRStatus&lt;br /&gt;Document::CreateJSObject()&lt;br /&gt;{&lt;br /&gt;    JSEngine *js = JSEngine::GetJSEngine();&lt;br /&gt;&lt;br /&gt;    if (!js)&lt;br /&gt;        return PR_FAILURE;&lt;br /&gt;&lt;br /&gt;    if (m_jsObj)&lt;br /&gt;        return PR_SUCCESS;&lt;br /&gt;&lt;br /&gt;    JSContext *ctx = js-&amp;gt;GetContext();&lt;br /&gt;    JSObject *obj = js-&amp;gt;GetGlobalObject();&lt;br /&gt;&lt;br /&gt;    JSClass document_class = {&lt;br /&gt;        "document", JSCLASS_NEW_RESOLVE,&lt;br /&gt;        JS_PropertyStub, JS_PropertyStub,&lt;br /&gt;        JS_PropertyStub, JS_PropertyStub,&lt;br /&gt;        JS_EnumerateStub, JS_ResolveStub,&lt;br /&gt;        JS_ConvertStub, JS_FinalizeStub&lt;br /&gt;    };&lt;br /&gt;&lt;br /&gt;    m_jsObj = JS_DefineObject(ctx, obj, "document", &amp;document_class, NULL, 0);&lt;br /&gt;&lt;br /&gt;    if (m_jsObj) {&lt;br /&gt;        JS_DefineFunctions(ctx, m_jsObj, document_functions);&lt;br /&gt;        m_objectMap.insert(pair&amp;lt;JSObject *, Document *&amp;gt;(m_jsObj, this));&lt;br /&gt;        return PR_SUCCESS;&lt;br /&gt;    }&lt;br /&gt;    return PR_FAILURE;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;JSObject *&lt;br /&gt;Document::GetJSObject()&lt;br /&gt;{   &lt;br /&gt;    return m_jsObj;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;map &amp;lt;JSObject *, Document *&amp;gt; Document::m_objectMap;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In short, the above code creates a JavaScript object named "document" that implements a function named "getElementById", which when invoked, invokes a standard JavaScript callback named DoGetElementById() which we must implement. It also adds the a JavaScript object, Document * pair to the map so that, given a JavaScript object, we can find the corresponding document. This will be needed so that, when we get called by the JavaScript engine in DoGetElementById(), we can find the Document instance that corresponds to the call.&lt;br /&gt;&lt;br /&gt;Next up is the code for DoGetElementById():&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;JS_STATIC_DLL_CALLBACK(JSBool)&lt;br /&gt;DoGetElementById(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)&lt;br /&gt;{&lt;br /&gt;    // find object to Document *mapping&lt;br /&gt;&lt;br /&gt;    JSBool ret = JS_FALSE;&lt;br /&gt;    *rval = NULL;&lt;br /&gt;&lt;br /&gt;    Document *document = Document::FindDocumentByObj(obj);&lt;br /&gt;    if (document) {&lt;br /&gt;        JSString *str;&lt;br /&gt;&lt;br /&gt;        str = JS_ValueToString(cx, argv[0]);&lt;br /&gt;        if (!str)&lt;br /&gt;            return JS_FALSE;&lt;br /&gt;&lt;br /&gt;        char *bytes = JS_GetStringBytes(str);&lt;br /&gt;        bytes = strdup(bytes);&lt;br /&gt;        if (bytes) {&lt;br /&gt;            Element *element = document-&amp;gt;GetElementById(NULL, bytes);&lt;br /&gt;            if (element) {&lt;br /&gt;                if (element-&gt;CreateJSObject() == PR_SUCCESS) {&lt;br /&gt;                    JSObject *jsobj = element-&amp;gt;GetJSObject();&lt;br /&gt;                    if (jsobj) {&lt;br /&gt;                        *rval = OBJECT_TO_JSVAL(jsobj);&lt;br /&gt;                        ret = JS_TRUE;&lt;br /&gt;                    }&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;            free(bytes);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    return ret;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Understanding the above code is key to understanding how we locate and return a JavaScript object representing the Document the user currently is interacting with, so let's look at this line by line.&lt;br /&gt;&lt;br /&gt;The line:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Document *document = Document::FindDocumentByObj(obj);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;looks up the passed-in JSObject * argument, obj, in the JSObject * to Document *map.  obj represents the document JavaScript object we created for the Document, and when the user references "document" from JavaScript, like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;document.getElementById(...);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;it is this JavaScript object (document) that will be passed into DoGetElementById() by the JavaScript engine, and can be used to find the Document object in the layout engine.&lt;br /&gt;&lt;br /&gt;The first argument to DoGetElementById() is a string which contains the id we need to lookup. With that id, we then call a helper function in Document, aptly named GetElementById(), which will iterate the entire document looking for the first element which has an id attribute with the value we seek. Once that element is found, we need to retrieve a JavaScript object from that element to return. Here is the code that gets the id argument, and calls on the document object to see if it can find the corresponding element. Remember that an Element in the layout engine represents some markup like a menu item, a button, or a text field.&lt;br /&gt;&lt;pre&gt; &lt;br /&gt;        char *bytes = JS_GetStringBytes(str);&lt;br /&gt;        bytes = strdup(bytes);&lt;br /&gt;        if (bytes) {&lt;br /&gt;            Element *element = document-&amp;gt;GetElementById(NULL, bytes);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Here is the (recursive) implementation of Document::GetElementById(). It eventually returns an Element *, or NULL if no such element exits in the document's DOM:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Element *Document::GetElementById(Element *root, const string&amp; id)&lt;br /&gt;{&lt;br /&gt;    if (!root)&lt;br /&gt;        root = m_root;&lt;br /&gt;&lt;br /&gt;    // if still NULL, bail&lt;br /&gt;&lt;br /&gt;    if (!root)&lt;br /&gt;        return NULL;&lt;br /&gt;&lt;br /&gt;    AnAttribute *attr;&lt;br /&gt;        &lt;br /&gt;    if (((attr = root-&amp;gt;GetAttributeByName("id")) &amp;&amp;&lt;br /&gt;        !PL_strcasecmp(id.c_str(), attr-&amp;gt;GetValue().c_str())))&lt;br /&gt;    {&lt;br /&gt;        return root;&lt;br /&gt;    } &lt;br /&gt;    &lt;br /&gt;    // if we get here, this node isn't it -- so check children&lt;br /&gt;        &lt;br /&gt;    list&amp;lt;Element *&amp;gt;::iterator itr;&lt;br /&gt;    Element *node;&lt;br /&gt;        &lt;br /&gt;    for (itr = root-&amp;gt;m_children.begin(); itr != root-&amp;gt;m_children.end(); ++itr) {&lt;br /&gt;        if ((node = GetElementById(*itr, id)))&lt;br /&gt;            return node;&lt;br /&gt;    }&lt;br /&gt;        &lt;br /&gt;    return NULL;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So, with an Element in hand, we still need to give a JSObject back to the JavaScript engine. The way to do this is, naturally enough, to ask the Element for a JSObject. It is pointless to create JSObjects for elements that the JavaScript code never will interact with -- we can lazily instantiate an object some point after we have located the Element in the above code, and store the object as a member variable of the Element for later use if the same instance of a Document requests it. So, returning back to the code in DoGetElementById() (found in Document.cpp), can see how it calls on Element to create a JSObject corresponding to the element, which it then returns back to the JavaScript engine, along with a return value of JS_TRUE to indicate that the element was found in the document, and that we were able to create (or read from cache) a JSObject to represent it.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;            Element *element = document-&amp;gt;GetElementById(NULL, bytes);&lt;br /&gt;            if (element) {&lt;br /&gt;                if (element-&amp;gt;CreateJSObject() == PR_SUCCESS) {&lt;br /&gt;                    JSObject *jsobj = element-&amp;gt;GetJSObject();&lt;br /&gt;                    if (jsobj) {&lt;br /&gt;                        *rval = OBJECT_TO_JSVAL(jsobj);&lt;br /&gt;                        ret = JS_TRUE;&lt;br /&gt;                    }&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;...&lt;br /&gt;      return ret;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The JavaScript object created and returned by an Element has a similar evolution to that of the object create and returned by a Document. It is added to a map so that it can be located from within the context of a JavaScript callback. The only real changes are that it has a different name "element" and implements different functions. Two of them, enable() and disable(), provide the implementations for enabling and disabling the native widget (button, menu item, text field) that the element wraps. These two functions, enable and disable, are very similar. The callbacks that implement them asks Element to search its map to find the corresponding Element * from the passed in JSObject, just like was done when JS callbacks were invoked on Document. If found, we simply involve Element's Disable() or Enable() function, which in turn calls Widget's Disable() or Enable() function, which calls (if we are taking about Button widgets) Button's Disable() or Enable() function, which, finally, invokes the platform implementation to do the actual disabling or enabling, as it were. Here is the source for the start of this chain, Element's DoEnable() function (DoDisable() is similar, and not shown):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;JS_STATIC_DLL_CALLBACK(JSBool)&lt;br /&gt;DoEnable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)&lt;br /&gt;{&lt;br /&gt;    JSBool ret = JS_FALSE;&lt;br /&gt;    Element *element = Element::FindElementByObj(obj);&lt;br /&gt;    if (element) {&lt;br /&gt;        Widget *widget = dynamic_cast&amp;lt;Widget *&amp;gt;(element);&lt;br /&gt;        if (widget) {&lt;br /&gt;            if (widget-&amp;gt;Enable() == PR_SUCCESS)&lt;br /&gt;              ret = JS_TRUE;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    return ret;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Element::FindElementByObj() looks up the Element in the static JSObj to Element map. If found, we dynamically cast the element to Widget and then invoke its Enable() function.&lt;br /&gt;&lt;br /&gt;Additional functions can be added to the Element JS Object for functionality that will be needed by the application, such as setting and getting the value of a control, or changing its label, in a very straightforward manner now that the above architecture is in place. I plan to do exactly that in the coming week.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-114100391836812837?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/114100391836812837/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=114100391836812837' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114100391836812837'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114100391836812837'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2006/02/this-weekend-i-tackled-issue-of.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-114039797217864595</id><published>2006-02-19T17:03:00.000-08:00</published><updated>2006-02-19T19:48:47.326-08:00</updated><title type='text'></title><content type='html'>This weekend's project goal was to design and implement an architecture to support file pickers (i.e., Open and Save As... dialogs).&lt;br /&gt;&lt;br /&gt;Unlike the widgets implemented to date, file pickers are not specified in XML markup, and they are not supported directly by the layout engine. Instead, file pickers come and go as the application needs them. Most of the time, this will be when the user selects the Open, Save, or Save As menu items in the File menu, but there is no strong reason to restrict an application from using them at other times.&lt;br /&gt;&lt;br /&gt;In Windows .NET, file pickers are implemented by two subclasses of FileDialog: OpenFileDialog, and SaveFileDialog. In Gtk+, there is a single class, GtkFileSelection, which can be tailored to act as a Save As dialog or an Open dialog. In Cocoa (MacOS X), the class NSOpenPanel implements Open dialogs, while NSSavePanel is used to implement Save As... dialogs.&lt;br /&gt;&lt;br /&gt;The following screen shots illustrate Save As... as it appears on Windows, Gtk+, and MacOS X Cocoa. The screen shots are directly from my layout engine, and were obtained by running the sample application and selecting File-&gt;Save As...&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/1570/737/1600/saveaswin.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/1570/737/1600/saveaswin.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/1570/737/1600/saveascocoa.png"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/1570/737/1600/saveascocoa.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/1570/737/1600/gtksaveas.png"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/1570/737/1600/gtksaveas.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Let's take a look at how the application uses the Save As... dialog, and then we'll go on to describe the implementation in some detail.&lt;br /&gt;&lt;br /&gt;In markup, the Save As... menu item is described as follows:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    &amp;lt;menubar&amp;gt;&lt;br /&gt;        &amp;lt;menu id="file-menu" label="File" shortcut="f"&amp;gt;&lt;br /&gt;            &amp;lt;menuitem class="separator"/&gt;&lt;br /&gt;            &amp;lt;menuitem label="&amp;amp;Open" shortcut="o" onclick="MenuFileOpen();"/&amp;gt;&lt;br /&gt;            &amp;lt;menuitem class="separator"/&amp;gt;&lt;br /&gt;            &amp;lt;menuitem label="&amp;amp;Close" shortcut="w" class="close" onclick="MenuFileClose();"/&amp;gt;&lt;br /&gt;            &amp;lt;menuitem label= "&amp;amp;Save" shortcut="s" class="save" onclick="MenuFileSave();"/&amp;gt;&lt;br /&gt;            &lt;span style="font-weight:bold;"&gt;&amp;lt;menuitem label= "Save &amp;amp;As..." shortcut="S" class="saveAs" onclick="MenuFileSaveAs();"/&amp;gt;&lt;/span&gt;&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The above markup defines a menuitem in the File menu with the label Save As..., and sets its onclick attribute to the string "MenuFileSaveAs();". When the user selects the Save As... menu item, control will pass into the user-supplied JavaScript function MenuFileSaveAs(), which is implemented as follows:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;function MenuFileSaveAs()&lt;br /&gt;{&lt;br /&gt;    var paths = filepicker.saveAsPicker();&lt;br /&gt;    dump("Inside of MenuFileOpen " + paths + "\n");&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The layout engine now supports a JavaScript object named filepicker. This object (at the moment) supports two simple functions: saveAsPicker() and openPicker(). saveAsPicker() is called to put up a Save As... dialog, and openPicker() is called to display an Open dialog, which is processed modally. Once the user dismisses the modal picker, both of these functions return a JavaScript string which contains a comma-separated list of paths selected by the user. The JavaScript code can either explode this CSV, and process it, or it can pass it down to a native (C++) component for handling. &lt;br /&gt;&lt;br /&gt;The filepicker object is at the core of the implementation of both the Open and Save As... functionality. It interacts with the platform factory objects to create a concrete, platform-specific implementation instance of a Save As... (or Open). The concrete implementation then goes on to post the dialog, await its dismissal, and then package up the selected paths are return them back to the JavaScript runtime.&lt;br /&gt;&lt;br /&gt;The filepicker class is instantiated as a singleton early in the lifetime of the layout engine. The job of this class is simple: create a JavaScript object named filepicker that implements both openPicker() and saveAsPicker(), register this object with the JavaScript runtime, and implement the openPicker() and saveAsPicker() function calls. To create and register the filepicker JavaScript object, the layout engine creates an instance of the filepicker C++ object, and then calls CreateJSObject():&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;static JSFunctionSpec filepicker_functions[] = {&lt;br /&gt;    {"openPicker", DoOpenPicker,  1},&lt;br /&gt;    {"saveAsPicker", DoSaveAsPicker,  1},&lt;br /&gt;    {0}&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;PRStatus&lt;br /&gt;FilePicker::CreateJSObject()&lt;br /&gt;{&lt;br /&gt;    JSEngine *js = JSEngine::GetJSEngine();&lt;br /&gt;&lt;br /&gt;    if (!js)&lt;br /&gt;        return PR_FAILURE;&lt;br /&gt;&lt;br /&gt;    JSContext *ctx = js-&amp;gt;GetContext();&lt;br /&gt;    JSObject *obj = js-&amp;gt;GetGlobalObject();&lt;br /&gt;&lt;br /&gt;    JSClass filepicker_class = {&lt;br /&gt;        "filepicker", JSCLASS_NEW_RESOLVE,&lt;br /&gt;        JS_PropertyStub,  JS_PropertyStub,&lt;br /&gt;        JS_PropertyStub,  JS_PropertyStub,&lt;br /&gt;        JS_EnumerateStub,  JS_ResolveStub,&lt;br /&gt;        JS_ConvertStub,   JS_FinalizeStub&lt;br /&gt;    };&lt;br /&gt;&lt;br /&gt;    m_jsObj = JS_DefineObject(ctx, obj, "filepicker",&lt;br /&gt;        &amp;filepicker_class, NULL, 0);&lt;br /&gt;&lt;br /&gt;    JS_DefineFunctions(ctx, m_jsObj, filepicker_functions);&lt;br /&gt;&lt;br /&gt;    return PR_SUCCESS;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The object itself is defined by calling JS_DefineObject(), and passing a data structure that defines the class and, in particular, the name by which it is made available to JavaScript functions. The functions that can be called off of this object are defined by calling JS_DefineFunctions(), which takes yet another data structure (filepicker_functions in the above listing) that maps function names to pointers to functions that implement them.&lt;br /&gt;&lt;br /&gt;So, what happens when the JavaScript Save As... menuitem handler function calls saveAsPicker()? The JavaScript engine will, because of the mapping we specified, will call DoSaveAsPicker(). Let's now take a look at this function:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;JS_STATIC_DLL_CALLBACK(JSBool)&lt;br /&gt;DoSaveAsPicker(JSContext *cx, JSObject *obj, uintN argc,&lt;br /&gt;    jsval *argv, jsval *rval)&lt;br /&gt;{&lt;br /&gt;    *rval = STRING_TO_JSVAL("");&lt;br /&gt;    SaveAsPickerImpl *saveAsPicker;&lt;br /&gt;    WidgetFactory *factory = GetWidgetFactory();&lt;br /&gt;    if (!factory)&lt;br /&gt;        return JS_FALSE;&lt;br /&gt;&lt;br /&gt;    saveAsPicker = factory-&amp;gt;MakeSaveAsPicker();&lt;br /&gt;    if (saveAsPicker &amp;&amp; saveAsPicker-&gt;Create() == PR_SUCCESS) {&lt;br /&gt;        string files;&lt;br /&gt;        if (saveAsPicker-&amp;gt;GetFile("", files) == PR_SUCCESS) {&lt;br /&gt;            JSString *str = ::JS_NewStringCopyN(cx, files.c_str(),&lt;br /&gt;                files.size() + 1);&lt;br /&gt;            *rval = STRING_TO_JSVAL(str);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    JSBool ret = JS_TRUE;&lt;br /&gt;&lt;br /&gt;    return ret;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The first major task of this function is to get an instance of the WidgetFactory. The WidgetFactory is the same that is used to get concrete implementations of all the widgets supported by the layout engine. We then ask it to make an instance of an abstract Save As... picker object by calling MakeSaveAsPicker(). Now we have an object of type SaveAsPickerImpl, and with is object we can create (by calling Create()) and display (by calling GetFile()) a platform-specific Save As... dialog. The concrete classes GtkSaveAsPickerImpl, CocoaSaveAsPickerImpl, and WindowsSaveAsPickerImpl all derive from SaveAsPickerImpl, and implement its interface, which is:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#if !defined(__SAVEASPICKERIMPL_H__)&lt;br /&gt;#define __SAVEASPICKERIMPL_H__&lt;br /&gt;&lt;br /&gt;// abstract class for saveas picker implementations&lt;br /&gt;&lt;br /&gt;#include "prtypes.h"&lt;br /&gt;&lt;br /&gt;#include &amp;lt;string&amp;gt;&lt;br /&gt;&lt;br /&gt;using namespace std;&lt;br /&gt;&lt;br /&gt;class SaveAsPickerImpl&lt;br /&gt;{&lt;br /&gt;public:&lt;br /&gt;    SaveAsPickerImpl() {};&lt;br /&gt;    virtual ~SaveAsPickerImpl() {};&lt;br /&gt;    virtual PRStatus Create() = 0;&lt;br /&gt;    virtual PRStatus GetFile(const string &amp;path, string &amp;files) = 0;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;#endif&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;When the abstract SaveAsPickerImpl call to GetFile() returns, we simply convert the STL string object that was passed as a reference to GetFile() to a JavaScript string, assign it to the rval argument, and then return.&lt;br /&gt;&lt;br /&gt;The concrete implementations of Create() and GetFile() were largely straightforward. All platforms support the ability to post such a dialog modally, though in Gtk+ it requires the program to enter a new main loop (more on that later). On Windows, getting the managed .NET string returned by the FileSaveDialog object converted to a C-based string was a pain, here is the code I never would have guessed, but eventually found after doing some googles and, as is usually the case, reading more wrong answers than correct ones. The nasty line is in bold in the following listing:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PRStatus WindowsSaveAsPickerImpl::Create(const string &amp;title)&lt;br /&gt;{&lt;br /&gt;    m_saveaspicker = __gc new SaveFileDialog();&lt;br /&gt;    if (m_saveaspicker) {&lt;br /&gt;        m_saveaspicker-&amp;gt;Title = title.c_str();&lt;br /&gt;        m_saveaspicker-&amp;gt;Multiselect = true;&lt;br /&gt;        return PR_SUCCESS;&lt;br /&gt;    }&lt;br /&gt;    return PR_FAILURE;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;PRStatus WindowsSaveAsPickerImpl::GetFile(const string &amp;path, string &amp;files)&lt;br /&gt;{&lt;br /&gt;    files = "";  // assume the user selects nothing&lt;br /&gt;&lt;br /&gt;    // show the dialog modally&lt;br /&gt;&lt;br /&gt;    if (m_saveaspicker &amp;&amp; m_saveaspicker-&gt;ShowDialog() == DialogResult::OK) {&lt;br /&gt;&lt;br /&gt;        // get the filenames selected, if any&lt;br /&gt;&lt;br /&gt;        String *filesret[] = m_saveaspicker-&amp;gt;FileNames;&lt;br /&gt;&lt;br /&gt;        // take the result and make a CSV list of the selected filenames&lt;br /&gt;&lt;br /&gt;        bool first = true;&lt;br /&gt;        for (int i = 0; i &amp;lt; m_saveaspicker-&amp;gt;FileNames-&amp;gt;Length; i++) {&lt;br /&gt;            int len = filesret[i]-&amp;gt;Length;&lt;br /&gt;            if (len) {&lt;br /&gt;                if (!first)&lt;br /&gt;                    files += ",";&lt;br /&gt;                first = false;&lt;br /&gt;                char *buf;&lt;br /&gt;                &lt;span style="font-weight:bold;"&gt;buf = reinterpret_cast&amp;lt;char *&amp;gt;(Runtime::InteropServices::Marshal::StringToHGlobalAnsi(filesret[i]).ToPointer());&lt;/span&gt;&lt;br /&gt;                files += buf;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    return PR_SUCCESS;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The above code should be fairly easy to follow. Create() calls __gc new FileSaveDialog() to create an instance of the .NET FileSaveDialog() class. GetFile() posts the dialog modally, gets the selected filenames, and returns the answer.&lt;br /&gt;&lt;br /&gt;The Cocoa implementation is nearly the same, performing the same functionality (and providing an infinitely easier way to convert a string from a native representation (in this case NSString) to C:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PRStatus CocoaSaveAsPickerImpl::Create(const string &amp;title)&lt;br /&gt;{&lt;br /&gt;    m_saveaspicker = [NSSavePanel savePanel];&lt;br /&gt;    if (m_saveaspicker)&lt;br /&gt;        return PR_SUCCESS;&lt;br /&gt;    return PR_FAILURE;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;PRStatus CocoaSaveAsPickerImpl::GetFile(const string &amp;path, string &amp;files)&lt;br /&gt;{&lt;br /&gt;    files = "";&lt;br /&gt;&lt;br /&gt;    // Set the options for how the get file dialog will appear&lt;br /&gt;&lt;br /&gt;    [m_saveaspicker setCanSelectHiddenExtension:YES];&lt;br /&gt;&lt;br /&gt;    // set up default directory&lt;br /&gt;&lt;br /&gt;    int result = [m_saveaspicker runModalForDirectory:nil file:nil];&lt;br /&gt;&lt;br /&gt;    if (result != NSFileHandlingPanelCancelButton) {&lt;br /&gt;&lt;br /&gt;        // append each chosen file to our list, creating a CSV&lt;br /&gt;&lt;br /&gt;        bool first = true;&lt;br /&gt;        for (unsigned int i = 0; i &amp;lt; [[m_saveaspicker filenames] count]; i ++) {&lt;br /&gt;            NSString *path = [[m_saveaspicker filenames] objectAtIndex:i];&lt;br /&gt;            if (path) {&lt;br /&gt;                if (!first)&lt;br /&gt;                    files += ",";&lt;br /&gt;                first = false;&lt;br /&gt;                char *buffer;&lt;br /&gt;                buffer = (char *) malloc([path length] + 1);&lt;br /&gt;                if (buffer) {&lt;br /&gt;                    [path getCString: buffer];&lt;br /&gt;                    files += buffer;&lt;br /&gt;                    free(buffer);&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    return PR_SUCCESS;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Finally, let's take a look at what was needed to implement the above under Gtk+. As I mentioned earlier, to go modal in Gtk+, one must introduce a main loop at the desired location. Main loops in Gtk+ can be embedded -- to exit the innermost main loop, one calls gtk_main_quit(). So, to support modality, we will enter a main loop, and when the user clicks either the Ok or Cancel buttons, we will exit the embedded loop to return control to the main application loop. The other major change is that instead of calling a modal function and retrieving the result as was done for Cocoa and .NET, Gtk+ forces us to register a callback function that will be invoked when the user clicks the Ok button in the GtkFileSelection dialog. This callback will then retrieve the selection. Two other signal handler callbacks are registered with the file selection widget, instructing the widget to destroy the dialog when either Ok or Cancel is clicked. So, in the end, if Ok is clicked, we will call two callbacks, one to retrieve the path selected by the user from the file selection widget, and another which destroys the window. Only the callback that destroys the window will be invoked if Cancel is clicked. Here is the code for the Create() function, which creates the GtkFileSelection dialog, and the callbacks that handle Ok and Cancel button presses:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;extern "C" {&lt;br /&gt;&lt;br /&gt;gint HandleSaveAsPickerOkThunk(GtkWidget *widget, gpointer callbackData)&lt;br /&gt;{&lt;br /&gt;    GtkSaveAsPickerImpl *pPickerImpl = (GtkSaveAsPickerImpl *) callbackData;&lt;br /&gt;    if (pPickerImpl) {&lt;br /&gt;        GtkWidget *w = pPickerImpl-&amp;gt;GetImpl();&lt;br /&gt;        if (w) {&lt;br /&gt;            pPickerImpl-&amp;gt;SetFilename(&lt;br /&gt;                gtk_file_selection_get_filename(GTK_FILE_SELECTION(w)));&lt;br /&gt;        }&lt;br /&gt;        gtk_main_quit();&lt;br /&gt;    }&lt;br /&gt;    return TRUE;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;gint HandleSaveAsPickerCancelThunk(GtkWidget *widget, gpointer callbackData)&lt;br /&gt;{&lt;br /&gt;    gtk_main_quit();&lt;br /&gt;    return TRUE;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;} // extern "C"&lt;br /&gt;&lt;br /&gt;PRStatus GtkSaveAsPickerImpl::Create(const string &amp;title)&lt;br /&gt;{&lt;br /&gt;    m_saveaspicker = gtk_file_selection_new(const_cast&amp;lt;char *&amp;gt;(title.c_str()));&lt;br /&gt;    if (m_saveaspicker) {&lt;br /&gt;&lt;br /&gt;        gtk_signal_connect(GTK_OBJECT(&lt;br /&gt;            GTK_FILE_SELECTION(m_saveaspicker)-&amp;gt;ok_button),&lt;br /&gt;            "clicked", GTK_SIGNAL_FUNC(HandleSaveAsPickerOkThunk), this);&lt;br /&gt;&lt;br /&gt;        gtk_signal_connect_object(GTK_OBJECT(&lt;br /&gt;            GTK_FILE_SELECTION(m_saveaspicker)-&amp;gt;ok_button),&lt;br /&gt;            "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy),&lt;br /&gt;            GTK_OBJECT(m_saveaspicker));&lt;br /&gt;&lt;br /&gt;        gtk_signal_connect(GTK_OBJECT(&lt;br /&gt;            GTK_FILE_SELECTION(m_saveaspicker)-&amp;gt;cancel_button),&lt;br /&gt;            "clicked", GTK_SIGNAL_FUNC(HandleSaveAsPickerCancelThunk), this);&lt;br /&gt;&lt;br /&gt;        gtk_signal_connect_object(GTK_OBJECT(&lt;br /&gt;            GTK_FILE_SELECTION(m_saveaspicker)-&amp;gt;cancel_button),&lt;br /&gt;            "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy),&lt;br /&gt;            GTK_OBJECT(m_saveaspicker));&lt;br /&gt;&lt;br /&gt;        return PR_SUCCESS;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The function GetFile() shows the GtkFileSelection dialog and then calls gtk_main() to cause it to display modally. When either callback calls gtk_main_quit(), gtk_main() returns, and the member variable set by HandleSaveAsPickerOkThunk() will contain the selections made by the user. GetFile() simply returns the value of the member variable to the caller by assigning to the STL string reference argument passed to GetFile(). Here is the code for GetFile():&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PRStatus GtkOpenPickerImpl::GetFile(const string &amp;path, string &amp;files)&lt;br /&gt;{&lt;br /&gt;    files = "";&lt;br /&gt;    if (m_openpicker) {&lt;br /&gt;        gtk_widget_show(m_openpicker);&lt;br /&gt;        gtk_main();&lt;br /&gt;    }&lt;br /&gt;    if (GetOkPressed() == true) {&lt;br /&gt;        files = m_filename;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Projects on deck for next weekend include developing basic support for message boxes, and then adding some interfaces to widget to allow widgets (menuitems in particular) to be made inactive or disabled from JavaScript. I also need to revisit printing, but after some conversations with engineers at RedHat, I will put that off for a few months since they have promised me they are planning to release by May a Gtk+ widget that supports (gasp) CUPS-based printing. I say it's about time!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-114039797217864595?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/114039797217864595/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=114039797217864595' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114039797217864595'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/114039797217864595'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2006/02/this-weekends-project-goal-was-to.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-113972242645222778</id><published>2006-02-11T21:25:00.000-08:00</published><updated>2006-02-11T22:09:30.066-08:00</updated><title type='text'></title><content type='html'>I pulled down the sources from Mozilla and grabbed the code I wrote for a Gtk+ dialog back in 1999 while I was at Netscape. Here is a screen shot of what the dialog looks like:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/1570/737/1600/print2.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/1570/737/1600/print2.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Looking more closely at the Firefox page setup and print dialogs, they have much of what I did (perhaps my very early work inspired some of this, who knows). I think the thing to do is not get too caught up in designing yet another layout, and clone what Firefox is doing (but make it a full-on Gtk+ widget, one that perhaps could be absorbed into Gtk+).&lt;br /&gt;&lt;br /&gt;I guess the next week or two will be spent working on the printing architecture, coding up a Gtk+ widget, and getting my story straight on Windows .NET too. Following that, I'll implement File Selection, which should be a whole lot less challenging.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-113972242645222778?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/113972242645222778/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=113972242645222778' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113972242645222778'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113972242645222778'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2006/02/i-pulled-down-sources-from-mozilla-and.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-113971020765745476</id><published>2006-02-11T17:29:00.000-08:00</published><updated>2006-02-11T18:15:27.090-08:00</updated><title type='text'></title><content type='html'>So, what exactly should be the architecture in the layout engine to support printing (as well as page setup)? On MacOS X, I already have something working. I simply handle the menuitem click for Print internally, and then tell the NSView for the window to print itself. Like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    case MenuItemClassPrint:&lt;br /&gt;        {&lt;br /&gt;            NSView *nsView;&lt;br /&gt;&lt;br /&gt;            // get the view of the containing window&lt;br /&gt;&lt;br /&gt;            WidgetImpl *top = GetRootWidget();&lt;br /&gt;&lt;br /&gt;            if (top) {&lt;br /&gt;                CocoaWindowImpl *winImpl = dynamic_cast&amp;lt;CocoaWindowImpl *&amp;gt;(top);            &lt;br /&gt;                if (winImpl) {&lt;br /&gt;                    nsView =  winImpl-&amp;gt;GetView();&lt;br /&gt;                    if (nsView)&lt;br /&gt;                        [[NSPrintOperation printOperationWithView:nsView] runOperation];&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        break;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;At least I am hoping it is that easy :-) Print setup on MacOS X is even easier -- displaying the print setup dialog is all that seems to be needed; the results are remembered by MacOS X and apply to subsequent print operations. In Windows .NET, it appears things are a bit more complicated. There is a PrintDialog object that will post a UI on behalf of the client, and will, should the user go thru with the print operations, callback on a class that inherits from System.Drawing.Printing.PrintController. The class inheriting from the PrintController interface implements methods allowing it to monitor printing process, and to draw the content being printed to a graphics device that represents the printer. I'm not really experienced with this yet -- I'll need to prototype to become more clear. I am not yet sure what methods support print setup, or if the result of the print setup needs to be maintained by the application or if it is system wide like on MacOS X.&lt;br /&gt;&lt;br /&gt;On Gtk+, it's all bare bones. When the user clicks print setup, I will need to display a dialog of my own creation. I will also need to store the results of that dialog in the application. Similarly, when print is clicked, I will need to refer back to these settings, and post my own print dialog. When the user clicks ok in the print dialog, I will need to send the Window a message asking it to print itself. &lt;br /&gt;&lt;br /&gt;This implies the following. First, there is no intrinsic need for the user to assign a JavaScript callback to either the page setup or print dialogs (I'll make an exception to this below, but the page setup and print functions can operate on their own). Second, it implies that I need to design some interface class called Printable that allows the toolkit to call methods on an object to, at a minimum, print itself. &lt;br /&gt;&lt;br /&gt;I mentioned that the client need not be called on JavaScript listeners when the page setup or print dialogs are invoked. In reality, it would be nice to callback a JavaScript function when printing is started, as each page is printed, when printing has completed, and should an error occur, some indication of the problem (an error code at least, a string to go along with it at most), so that the application can monitor the complete printing process and communicate it back to the user.&lt;br /&gt;&lt;br /&gt;A nice discovery today was that CUPS comes with a C-based library that allows me to do a wide variety of things, such a querying for the list of configured printers, as well as the default, communicate print settings, and initiate a print job. I'm sure that the backend to the Gtk+ widget I will need to create will use this library; had it existed back when I worked on the Netscape/Mozilla printing system, I am sure it would have been a part of the solution there too.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-113971020765745476?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/113971020765745476/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=113971020765745476' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113971020765745476'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113971020765745476'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2006/02/so-what-exactly-should-be-architecture.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-113968923543360744</id><published>2006-02-11T11:27:00.000-08:00</published><updated>2006-02-11T22:39:30.456-08:00</updated><title type='text'></title><content type='html'>Before I can claim victory with menus, there remains some non-trivial work related to supporting standard dialogs. By this, I mean the dialogs that are needed to support print, page setup, and file selection ("save as" and "open") functionality. &lt;br /&gt;&lt;br /&gt;Let's begin by discussing the layout of these dialogs. On the Macintosh, these dialogs are all provided by Cocoa in AppKit. I imagine that in .Net on Windows, a similar level of support holds true. Gtk+ provides a very simple file selection dialog (GtkFileSelection), but, unfortunately, print and page setup are missing, requiring the end user to come up with his or her own solution. This is a horrible state of affairs, and somewhat of a disappointment -- how could the Gtk+ community pursue projects like mono (bringing .NET APIs to Gtk+), and still not take care of this core issue? &lt;br /&gt;&lt;br /&gt;Focusing in on the print/page setup dialog problem, a google search indicates that various people are working on a solution. I even worked on the problem way back in 1999 for Netscape/Mozilla (see &lt;a href="http://lxr.mozilla.org/seamonkey/source/widget/src/gtk2/nsPrintdGTK.c" target="_blank"&gt;http://lxr.mozilla.org/seamonkey/source/widget/src/gtk2/nsPrintdGTK.c&lt;/a&gt;). In the end, the project (e.g., Mozilla, Open Office, GIMP) is almost always forced to come up with its own solution (see the following Figure) to the problem, or steal something from someone else (I'm using "steal" in the Open Source sense of the word, of course).&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/1570/737/1600/print.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/1570/737/1600/print.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Even support for Page Setup and Print is far from consistent among applications. Look at the GIMP print dialog, for example:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/1570/737/1600/gimpprint.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/1570/737/1600/gimpprint.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Here, GIMP doesn't implement a Page Setup dialog, but, instead, it rolls that functionality into the print dialog. While powerful, it is inconsistent in the sense that learning this dialog does nothing to help a user, say, learn how to perform a page setup/print sequence in Open Office.&lt;br /&gt;&lt;br /&gt;On top of the above problems, there is also the issue of how to communicate with the printer. The strategy that I employed back in the day at Netscape made use of Postscript and lpr(1), which, for decades, was &lt;span style="font-style:italic;"&gt;the&lt;/span&gt; method for printing. Nowadays, there are architectures like CUPS (which is compatible with lpr, according to the man pages). I'm sure that over the years, there has been more than one attempt to take a stab in the open source community of coming up with a generic printing architecture that serves the needs of everyone. Perhaps CUPS is it for configuration (i.e., page setup), and lpr is the way you perform the actual print.&lt;br /&gt;&lt;br /&gt;Interestingly, CUPS is the engine that sits behind printer configuration on MacOS X, making the decision to base a cross-platform architecture on CUPS/lpr a near slam dunk (well, except on Windows of course). This is even more true with the realization that CUPS is also available on Windows (but "available" doesn't mean "present", so sadly, one cannot rely upon it).&lt;br /&gt;&lt;br /&gt;So, this is what we have to work with:&lt;br /&gt;&lt;br /&gt;MacOS X/Cocoa Print Setup UI: Native&lt;br /&gt;MacOS X/Cocoa Print Setup Engine: Native with CUPS backend&lt;br /&gt;MacOS X/Cocoa Print UI: Native&lt;br /&gt;MacOS X/Cocoa Print Engine: CUPS/lpr&lt;br /&gt;&lt;br /&gt;Windows/.NET Print Setup UI: Native&lt;br /&gt;Windows/.NET Print Setup Engine: Native&lt;br /&gt;Windows/.NET Print UI: Native (perhaps CUPS if installed by the user)&lt;br /&gt;Windows/.NET Print Engine: Native&lt;br /&gt;&lt;br /&gt;Linux/Gtk+ Print Setup UI: Undefined/Varies by application&lt;br /&gt;Linux/Gtk+ Print Setup Engine: CUPS&lt;br /&gt;Linux/Gtk+ Print UI: Undefined/Varies by application&lt;br /&gt;Linux/Gtk+ Print Engine: CUPS/lpr&lt;br /&gt;&lt;br /&gt;After getting CUPS configured here at home (to support a crufty old HP LaserJet 4MP that I have connected to the parallel port of a Fedora Core 4 server, which is acting as a print server) and configuring a separate Linux laptop and MacOS X to use the printer to convince myself it works, I decided that, yes, CUPS needs to be a part of whatever solution I put together on Linux under Gtk+.&lt;br /&gt;&lt;br /&gt;Just for posterity, here is the cupsd.conf file on the print server:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;LogLevel info&lt;br /&gt;Port 631&lt;br /&gt;&amp;lt;Location /&amp;gt;&lt;br /&gt;Order Deny, Allow&lt;br /&gt;Deny From All&lt;br /&gt;Allow From 127.0.0.1&lt;br /&gt;Allow From 192.168.1.*&lt;br /&gt;&amp;lt;/Location&amp;gt;&lt;br /&gt;BrowseAddress 192.168.1.255&lt;br /&gt;&lt;br /&gt;&amp;lt;Location /printers/HPLaser&amp;gt;&lt;br /&gt;Order Deny,Allow&lt;br /&gt;Deny From All&lt;br /&gt;Allow From 127.0.0.1&lt;br /&gt;Allow From 192.168.1.*&lt;br /&gt;AuthType None&lt;br /&gt;&amp;lt;/Location&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And printers.conf:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;# Printer configuration file for CUPS v1.1.23&lt;br /&gt;# Written by cupsd on Sat 11 Feb 2006 04:13:22 PM PST&lt;br /&gt;&amp;lt;DefaultPrinter HPLaser&amp;gt;&lt;br /&gt;Info&lt;br /&gt;Location&lt;br /&gt;DeviceURI parallel:/dev/lp0&lt;br /&gt;State Idle&lt;br /&gt;Accepting Yes&lt;br /&gt;JobSheets none none&lt;br /&gt;QuotaPeriod 0&lt;br /&gt;PageLimit 0&lt;br /&gt;KLimit 0&lt;br /&gt;&amp;lt;/Printer&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-113968923543360744?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/113968923543360744/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=113968923543360744' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113968923543360744'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113968923543360744'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2006/02/before-i-can-claim-victory-with-menus.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-113939077057050954</id><published>2006-02-08T01:06:00.000-08:00</published><updated>2006-02-11T23:09:12.556-08:00</updated><title type='text'></title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/1570/737/1600/menus.png"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/1570/737/1600/menus.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The above is a screenshot after porting the concrete menu classes to Gtk+. &lt;br /&gt;&lt;br /&gt;It's interesting to compare the complexity of dealing with menuitem shortcuts in the Cocoa implementation with the Gtk+ implementation. First, let's look at the Cocoa implementation of the concrete menuitem widget function SetShortcut. It's only argument is an ASCII letter. As was described previously, a lowercase letter is used by Cocoa to indicate a clover-letter shortcut, while an uppercase letter is used to indicate a shift-clover-letter shortcut. Here is the code:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PRStatus CocoaMenuItemImpl::SetShortcut(const string&amp; shortcut)&lt;br /&gt;{&lt;br /&gt;    if (m_menuitem) {&lt;br /&gt;        NSString *key = [NSString stringWithCString: shortcut.c_str()];&lt;br /&gt;        if (key) {&lt;br /&gt;            [m_menuitem setKeyEquivalent: key];&lt;br /&gt;            return PR_SUCCESS;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    return PR_FAILURE;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The above code is trivial -- create an NSString from the STL string shortcut, and then pass the result to NSMenuItem setKeyEquivalent:.&lt;br /&gt;&lt;br /&gt;Now, let's look at the Gtk+ implementation:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PRStatus GtkMenuItemImpl::SetShortcut(const string&amp; shortcut)&lt;br /&gt;{&lt;br /&gt;    string accel;&lt;br /&gt;    guint accel_key;&lt;br /&gt;    GdkModifierType accel_mods;&lt;br /&gt;&lt;br /&gt;    accel = "&amp;lt;ctrl&amp;gt;" + shortcut;&lt;br /&gt;    gtk_accelerator_parse(accel.c_str(), &amp;accel_key, &amp;accel_mods);&lt;br /&gt;&lt;br /&gt;    if (!m_accelGroup) {&lt;br /&gt;        m_accelGroup = gtk_accel_group_new();&lt;br /&gt;        gtk_accel_group_ref(m_accelGroup);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    gtk_widget_add_accelerator(m_menuitem, "activate", m_accelGroup,&lt;br /&gt;        accel_key, accel_mods, GTK_ACCEL_VISIBLE);&lt;br /&gt;&lt;br /&gt;    return PR_SUCCESS;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Here, we must create a string that is of the form "&amp;lt;ctrl&amp;gt;letter", where letter is the shortcut specified in markup. This string is the parsed by a Gtk+ function named gtk_accelerator_parse(), which decomposes the string into the key and the X modifier flags that describe what modifier(s) (e.g., Alt, Ctrl) must be pressed, along with the key, to trigger a gtk_signal. The signal in question, ("activate") and the key and modifier(s) combination are mapped to the menuitem widget by calling gtk_widget_add_accelerator(). If needed, an accelerator group object is created prior to this mapping with a call to gtk_accel_group_new(). Effectively, what we are doing is telling the menuitem widget to active itself when the ctrl-key combination is pressed. The activate signal is mapped to a signal handler that is also invoked when the user selects the menuitem with the mouse.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-113939077057050954?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/113939077057050954/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=113939077057050954' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113939077057050954'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113939077057050954'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2006/02/above-is-screenshot-after-porting.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-113911565355719084</id><published>2006-02-04T20:55:00.000-08:00</published><updated>2006-02-05T08:58:17.666-08:00</updated><title type='text'></title><content type='html'>The menu implementation for Cocoa/MacOS X is largely complete. Best of all, I came up with a strategy that avoids #ifdefs in markup (something which is done in Mozilla's XUL that I find a bit unsavory). In this post, I will try and describe the Cocoa-based menu architecture. In following posts I will comment on changes needed to support Gtk+ on Linux and .NET Forms on Windows (once I know what those changes are, as the work to port this all to Gtk+ and .NET is pending).&lt;br /&gt;&lt;br /&gt;Let's start with a look at the XML markup used to describe menus.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    &amp;lt;menubar&amp;gt;&lt;br /&gt;        &amp;lt;menu id="file-menu" label="File" shortcut="f"&amp;gt;&lt;br /&gt;            &amp;lt;menuitem class="separator"/&amp;gt;&lt;br /&gt;            &amp;lt;menuitem label="Open" shortcut="o" onclick="MenuFileOpen();"/&amp;gt;&lt;br /&gt;            &amp;lt;menuitem class="separator"/&amp;gt;&lt;br /&gt;            &amp;lt;menuitem label="Close" shortcut="w" class="close" onclick="MenuFileClose();"/&amp;gt;&lt;br /&gt;            &amp;lt;menuitem label= "Save" shortcut="s" class="save" onclick="MenuFileSave();"/&amp;gt;&lt;br /&gt;            &amp;lt;menuitem label= "Save As..." shortcut="S" class="saveAs" onclick="MenuFileSaveAs();"/&amp;gt;&lt;br /&gt;            &amp;lt;menuitem class="separator"/&amp;gt;&lt;br /&gt;            &amp;lt;menuitem label="Page Setup..." shortcut="P" class="pageSetup" onclick="MenuFilePageSetup();"/&amp;gt;&lt;br /&gt;            &amp;lt;menuitem label="Print" shortcut="p" class="print" onclick="MenuFilePrint();"/&amp;gt;&lt;br /&gt;            &amp;lt;menuitem class="separator"/&amp;gt;&lt;br /&gt;            &amp;lt;menuitem label="Quit" shortcut= "q" class="quit" onclick="MenuFileQuit();"/&amp;gt;&lt;br /&gt;        &amp;lt;/menu&amp;gt;&lt;br /&gt;        &amp;lt;menu id="edit-menu" label="Edit" shortcut="e"&amp;gt;&lt;br /&gt;            &amp;lt;menuitem label="Cut" shortcut="x" class="cut" onclick="MenuEditCut();"/&amp;gt;&lt;br /&gt;            &amp;lt;menuitem label="Copy" shortcut="c" class="copy" onclick="MenuEditCopy();"/&amp;gt;&lt;br /&gt;            &amp;lt;menuitem label="Paste" shortcut="v" class="paste" onclick="MenuEditPaste();"/&amp;gt;&lt;br /&gt;            &amp;lt;menuitem label="Clear" class="clear" onclick="MenuEditClear();"/&gt;&lt;br /&gt;            &amp;lt;menuitem class="separator"/&amp;gt;&lt;br /&gt;            &amp;lt;menuitem label="Select All" shortcut="a" class="selectAll" onclick="MenuEditSelectAll();"/&amp;gt;&lt;br /&gt;            &amp;lt;menuitem label="Preferences..." shortcut="," class="preferences" onclick="MenuEditPreferences();"/&amp;gt;&lt;br /&gt;        &amp;lt;/menu&amp;gt;&lt;br /&gt;    &amp;lt;/menubar&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As you can see, there are three distinct tags - menubar, menu, and menuitem. The menubar tag is a simple container of menu tags. The menu tag describes each menu (e.g., File, Edit), and acts as a container of menuitems. Finally, menuitem describes clickable items in the containing menu.&lt;br /&gt;&lt;br /&gt;Menus and menuitems both implement label and shortcut attributes. On Windows, menu shortcuts are used to activate the corresponding menu, while on MacOS X/Cocoa, a shortcut assigned to the menu is ignored. Menuitem shortcuts are supported by all three platform toolkits. Under Cocoa, the shortcut is case-sensitive; a lowercase letter is used to implement clover-letter, while an uppercase letter implements shift-clover-letter. &lt;br /&gt;&lt;br /&gt;The onclick attribute assigns JavaScript code to a menuitem that will be executed when the menuitem is selected by the user.&lt;br /&gt;&lt;br /&gt;The class attribute assigns a class to a menuitem. This class tells the layout engine that the menuitem is special in the sense that toolkit specific behaviors are associated with the menu item. When present, the class causes the concrete menuitem implementation to invoke platform specific behavior. The following classes have been implemented so far:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;typedef enum&lt;br /&gt;{&lt;br /&gt;    MenuItemClassNone,&lt;br /&gt;    MenuItemClassSeparator,&lt;br /&gt;    MenuItemClassClose,&lt;br /&gt;    MenuItemClassSave,&lt;br /&gt;    MenuItemClassSaveAs,&lt;br /&gt;    MenuItemClassPageSetup,&lt;br /&gt;    MenuItemClassPrint,&lt;br /&gt;    MenuItemClassQuit,&lt;br /&gt;    MenuItemClassCut,&lt;br /&gt;    MenuItemClassCopy,&lt;br /&gt;    MenuItemClassPaste,&lt;br /&gt;    MenuItemClassClear,&lt;br /&gt;    MenuItemClassSelectAll,&lt;br /&gt;    MenuItemClassPreferences&lt;br /&gt;} MenuItemClass;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As you may notice, these menu items represent functionality closely coupled to behaviors that might be implemented by a toolkit, versus functionality that might be implemented by an application. In Cocoa, there are methods associated with AppKit classes for most of the above functionality. The following talks about the ones I have implemented so far:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;MenuItemClassSeparator&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In Cocoa, NSMenuItem has an attribute that allows one to identify itself as a separator. Doing so causes the appropriate rendering of the menu item as a separator. Separators are purely cosmetic, and cannot be selected by a user.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;MenuItemClassClose&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;NSWindow implements a close method that will cause the window to close.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;MenuItemClassPageSetup&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The tookit invokes the following code in response to selection of menuitems of this class. The settings made by the user in the NSPageLayout dialog have global effect on the application:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[[NSPageLayout pageLayout] runModal];&lt;br /&gt;&lt;/pre&gt;  &lt;br /&gt;&lt;span style="font-weight:bold;"&gt;MenuItemClassPrint&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Similarly, the Cocoa class NSPrintOperation is used to implement printing, as in the following code:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[[NSPrintOperation printOperationWithView:nsView] runOperation]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;printOperationWithView in the above code requires an NSView instance. To obtain one, I query the layout engine for an instance of the containing window, then query the Cocoa window implementation to determine the associated view instance. This is possible because each CocoaWidgetImpl maintains a copy of its associated view, and a method that can be used to retrieve it. Here is the code that accomplishes this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;NSView *nsView;&lt;br /&gt;            &lt;br /&gt;// get the view of the containing window&lt;br /&gt;            &lt;br /&gt;WidgetImpl *top = GetRootWidget();&lt;br /&gt;            &lt;br /&gt;if (top) {&lt;br /&gt;    CocoaWindowImpl *winImpl = dynamic_cast&amp;lt;CocoaWindowImpl *&amp;gt;(top);&lt;br /&gt;    if (winImpl) {&lt;br /&gt;        nsView = winImpl-&amp;gt;GetView();&lt;br /&gt;        if (nsView)                        &lt;br /&gt;            [[NSPrintOperation printOperationWithView:nsView] runOperation];               &lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt; &lt;br /&gt;&lt;span style="font-weight:bold;"&gt;MenuItemClassQuit&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Quit is implemented by passing control to the singleton concrete CocoaAppImpl object that manages the main loop of the application:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;CocoaAppImpl *appImpl = CocoaAppImpl::GetCocoaAppImplInstance();&lt;br /&gt;if (appImpl)&lt;br /&gt;{&lt;br /&gt;    ret = appImpl-&amp;gt;Shutdown();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Clipboard/Pasteboard Functions&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The next several menu items implement clipboard related functionality. Clipboard functionality in Cocoa is implemented by way of a Pasteboard server, but at least for now, I can rely on functionality built into NSTextView. Given an instance of NSTextView, all I need to do is invoke the appropriate method. Assuming the following declaration:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;NSTextView *m_text;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;the correspond calls are:&lt;br /&gt;&lt;pre&gt; &lt;br /&gt;[m_text cut:nil]; // MenuItemClassCut&lt;br /&gt;[m_text copy:nil]; // MenuItemClassCopy&lt;br /&gt;[m_text paste:nil]; // MenuItemClassPaste&lt;br /&gt;[m_text delete:nil]; // MenuItemClassClear&lt;br /&gt;[m_text selectAll:nil]; // MenuItemClassSelectAll    &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;However, there is the issue of *which* widget should be handing the clipboard operation. In a window containing several instances of NSTextView, one must identify the widget that currently has the input focus, or, in Cocoa terminology, is the "first responder". To determine this, one needs to once again get an instance of the containing Window widget, and then query it for this information. Here is the code that does this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;NSWindow *nsWindow = NULL;&lt;br /&gt;&lt;br /&gt;WidgetImpl *top = GetRootWidget();&lt;br /&gt;&lt;br /&gt;if (top) {&lt;br /&gt;    CocoaWindowImpl *winImpl = dynamic_cast&amp;lt;CocoaWindowImpl *&amp;gt;(top);&lt;br /&gt;    if (winImpl)&lt;br /&gt;        nsWindow = winImpl-&amp;gt;GetNSWindow();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;if (nsWindow)&lt;br /&gt;{&lt;br /&gt;    NSView *firstResponder = [nsWindow firstResponder];&lt;br /&gt;    if (firstResponder) {&lt;br /&gt;        ...&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Once we have the firstResponder, we can then notify it to perform the operation. But how is that done? We have two choices. First, we could iterate the DOM and query all widgets in the Window for their NSView instances, and compare these to the firstResponder view returned by the above code. But, doing that can be expensive. A better way is to associate a "delegate" with each abstract text widget, and then send a message to the delegate. The handler of that message can then interact with the actual Cocoa text widget to perform the operation. &lt;br /&gt;&lt;br /&gt;We can obtain the delegate assocated with a widget by invoking the delegate method on the firstResponder, and once we have the delegate, we can invoke any method that it implements. In the following code, I obtain the delegate from the firstResponder, and then invoke its onCut method (this code is used to handle the selection of the Cut menu item; similar code is used to handle Copy, Paste, Select All, and Clear:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;NSTextField *field = [firstResponder delegate];&lt;br /&gt;if (field) {&lt;br /&gt;    [field onCut];&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You are probably wondering now about the onCut function, and its implementation. In short, each CocoaTextWidgetImpl concrete class instance allocates an object, implemented in Objective-C, that acts as the widget's delegate.  This class, named PasteboardAction because it is used to handle pasteboard-specific functionality, is defined as:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#import &amp;lt;Foundation/NSObject.h&amp;gt;&lt;br /&gt;&lt;br /&gt;#import "pasteboardhandler.h"&lt;br /&gt;&lt;br /&gt;@interface PasteboardAction : NSObject&lt;br /&gt;{&lt;br /&gt;    PasteboardHandler *m_handler;&lt;br /&gt;}&lt;br /&gt;- (void) setHandler: (PasteboardHandler *) handler;&lt;br /&gt;- (void) onCut;&lt;br /&gt;- (void) onCopy;&lt;br /&gt;- (void) onPaste;&lt;br /&gt;- (void) onClear;&lt;br /&gt;- (void) onSelectAll;&lt;br /&gt;@end&lt;br /&gt;&lt;/pre&gt; &lt;br /&gt;PasteboardAction, as you can see, implements a member variable of type PasteboardHandler, which is a base class that is inherited by CocoaTextWidgetImpl. When one of the functions onCut, onCopy, etc. is invoked by a CocoaMenuItem widget instance, m_handler is used to call the associated CocoaTextWidgetImpl instance, which then invokes cut, copy, etc. on the NSTextView widget instance that it maintains. Here is the code that allocates the instance of PasteboardAction, and the code that makes that action a delegate of the NSTextView widget instance:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PasteboardAction *action;&lt;br /&gt;&lt;br /&gt;action = [PasteboardAction alloc];&lt;br /&gt;&lt;br /&gt;[action setHandler: this];&lt;br /&gt;&lt;br /&gt;// create the text&lt;br /&gt;&lt;br /&gt;m_text = [[NSTextView alloc] initWithFrame:graphicsRect];&lt;br /&gt;&lt;br /&gt;if (m_text) {&lt;br /&gt;&lt;br /&gt;    [m_text setDelegate: action];&lt;br /&gt;   &lt;br /&gt;    ...&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The implementation of onCut in PasteboardAction is:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;- (void) onCut&lt;br /&gt;{&lt;br /&gt;    m_handler-&amp;gt;Cut();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Cut(), which is implemented by CocoaTextWidgetImpl(), simply calls the cut method of its NSTextView instance:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;// pasteboardhandler implementation&lt;br /&gt;&lt;br /&gt;void CocoaTextImpl::Cut()&lt;br /&gt;{&lt;br /&gt;    if (m_text) &lt;br /&gt;        [m_text cut: nil];&lt;br /&gt;}              &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So, in summary, this is what happens when one of the selection-based menuitems is selected (in the following, the Copy menuitem will be used as the example). Each menuitem has a target that will be invoked when the menuitem is clicked. This target, which is an object, implements a method named onClick. The following is the code for onClick:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;- (void) onClick:(id) sender&lt;br /&gt;{&lt;br /&gt;    m_handler-&amp;gt;HandleCommand();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The handler is an instance of CocoaMenuItemImpl. CocoaMenuItemImpl::HandleCommand() implements a switch statement, with cases for each MenuItemClass. If the menuitem corresponds to a special menuitem class (in this case, MenuItemClassCopy), we find the firstResponder (the widget with the focus), and we invoke its onCopy method. The CocoaTextWidgetImpl widget that implements onCopy (via an inherited delegate class PasteboardAction) will then invoke CocoaTextWidgetImpl's Copy method, which in turn calls the native copy method on the NSTextView widget instance that it maintains. &lt;br /&gt;&lt;br /&gt;If that is a little hard to follow, the following stack should help:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;0  CocoaTextImpl::Copy (this=0x121f0d0) at cocoatextimpl.mm:176&lt;br /&gt;#1  0x003550e8 in -[PasteboardAction onCopy] (self=0x1272090, _cmd=0x357778) at cocoapasteboardaction.mm:21&lt;br /&gt;#2  0x00352e18 in CocoaMenuItemImpl::HandleCommand (this=0x121cfa0) at cocoamenuitemimpl.mm:297&lt;br /&gt;#3  0x00353a60 in -[MenuItemAction onClick:] (self=0x1259730, _cmd=0x3571a4, sender=0x1259740) at cocoamenuitemaction.mm:15&lt;br /&gt;#4  0x9372d270 in -[NSApplication sendAction:to:from:] ()&lt;br /&gt;#5  0x93787aa4 in -[NSMenu performActionForItemAtIndex:] ()&lt;br /&gt;#6  0x93787828 in -[NSCarbonMenuImpl performActionWithHighlightingForItemAtIndex:] ()&lt;br /&gt;#7  0x9368eb20 in _NSHandleCarbonMenuEvent ()&lt;br /&gt;#8  0x9368c484 in _DPSNextEvent ()&lt;br /&gt;#9  0x9368bdc8 in -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] ()&lt;br /&gt;#10 0x9368830c in -[NSApplication run] ()&lt;br /&gt;#11 0x0034eea4 in CocoaAppImpl::MainLoop (this=0x121da70) at cocoaappimpl.mm:29&lt;br /&gt;#12 0x0000b06c in App::MainLoop (this=0x121bab0) at app.cpp:28&lt;br /&gt;#13 0x000043ec in main (argc=3, argv=0xbffff8d0) at layout.cpp:378&lt;br /&gt;Current language:  auto; currently objective-c++&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;MenuItemClassPreferences&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The final menuitem class of interest is MenuItemClassPreferences. If the layout engine sees a menu with this menuitem class on Cocoa, it will create instances of CocoaMenuItemImpl, but the corresponding NSMenuItem instance will be the NSMenuItem instance associated with the Preferences... menu item previously created in the Apple menu (if you recall from an earlier post, CocoaMenubarImpl creates Apple and Window menu items in addition to the menus that are specified in markup. In Cocoa, the Preferences menuitem is always placed in the Apple menu. On Gtk+ or .NET, the Preferences... menu will not be overridden like this, and will be created as a normal menuitem below whichever menu the programmer specifies in markup (likely the Edit menu). This is the strategy I used to eliminate the need for the #ifdefs that are used in Mozilla's XUL to isolate platform-specific menuitems. Here is the code that detects the Preferences menuitem and overrides it by looking up the menuitem associated with the Apple menu preferences menuitem:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PRStatus CocoaMenuItemImpl::Create()&lt;br /&gt;{&lt;br /&gt;...&lt;br /&gt;    switch (menuItemClass)&lt;br /&gt;    {&lt;br /&gt;    case MenuItemClassQuit:&lt;br /&gt;        menuItem = menuBarImpl-&amp;gt;GetQuitMenuItem();&lt;br /&gt;        m_isOverride = true;&lt;br /&gt;        create = false;&lt;br /&gt;        add = false;&lt;br /&gt;        break;&lt;br /&gt;    case MenuItemClassPreferences:&lt;br /&gt;        menuItem = menuBarImpl-&amp;gt;GetPreferencesMenuItem();&lt;br /&gt;        m_isOverride = true;&lt;br /&gt;        create = false;&lt;br /&gt;        add = false;&lt;br /&gt;        break;&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;where GetPreferencesMenuItem() is implemented by CocoaMenubarImpl and simply returns the NSMenuItem object maintained by CocoaMenubarImpl as a member variable. Notice in the above code, a similar strategy is employed for the Quit menu item, which is also implemented in the Apple menu (versus the File menu in Gtk+ and .NET-based applications).&lt;br /&gt;&lt;br /&gt;Some todos include implementing Save and Save As..., which will likely require platform-specific file picker dialogs, and special handling in the toolkit. But first, I need to take the above and port it to .NET and Gtk+. Look for details in an upcoming post.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-113911565355719084?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/113911565355719084/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=113911565355719084' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113911565355719084'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113911565355719084'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2006/02/menu-implementation-for-cocoamacos-x.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-113859871016359080</id><published>2006-01-29T21:17:00.000-08:00</published><updated>2006-01-29T22:07:26.446-08:00</updated><title type='text'></title><content type='html'>Trixul is the name I came up with for the toolkit I have been working on during the past six months. Let me explain how this name came about.&lt;br /&gt;&lt;br /&gt;It started with a lifelong fascination for an animal with the scientific name triakis semifasciata -- the Leopard Shark, a harmless species that one commonly finds in the waters here off of San Diego. Long ago, I thought, if I ever had a company, or a product of some kind, I would use either the common name, or the scientific name, in some way or another.&lt;br /&gt;&lt;br /&gt;So, today, I decided at first that I would call the toolkit "triakis". But, a quick search found that someone already was using this name (http://www.triakis.com). &lt;br /&gt;&lt;br /&gt;Eventually, I converged on trixul, which is the first three letters of triakis, with XUL (e.g., mozilla's XUL XML UI language that inspired the development of my toolkit) tacked on the end.&lt;br /&gt;&lt;br /&gt;Trixul is pronounced "tricks-uh-l", which rhymes with "pixel". &lt;br /&gt;&lt;br /&gt;The prefix "tri" is convenient in that I am targeting three platforms, MacOS X, Linux, and Windows.&lt;br /&gt;&lt;br /&gt;I now own www.trixul.com, www.trixul.net, and www.trixel.org -- the availability of these domain names factored also into my decision. The three of these URLs will all point to the same place, and should be up in the next 48 or so hours.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-113859871016359080?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/113859871016359080/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=113859871016359080' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113859871016359080'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113859871016359080'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2006/01/trixul-is-name-i-came-up-with-for.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-113792129539127111</id><published>2006-01-22T01:08:00.000-08:00</published><updated>2006-02-11T20:25:08.266-08:00</updated><title type='text'></title><content type='html'>MacOS X menus are a bit less mystic to me after perusing the web and developer.apple.com for clues (and, of course, after writing globs of Objective-C++ code). In the end, it appears there are two classes of menu -- MacOS X-specific, and application specific -- with some blurring between the two mixed in. &lt;br /&gt;&lt;br /&gt;In terms of MacOS X-specific menus, applications need to worry about the following:&lt;br /&gt;&lt;br /&gt;-- creation of the menubar,&lt;br /&gt;-- creation of the Apple (application) menu, &lt;br /&gt;-- creation of the Window menu&lt;br /&gt;&lt;br /&gt;All MacOS X applications must have an Apple menu, and a Window menu. A number of interfaces/methods into NSApplication exist to support these two menus (as well as related submenus, as we shall see).&lt;br /&gt;&lt;br /&gt;The menubar is an instance of NSMenu. All one needs to do is allocate an instance of NSMenu, and then pass the result to NSApplication setMainMenu:, as in the following code:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    NSMenu *m_menuMain = [[NSMenu alloc] initWithTitle: @""];&lt;br /&gt;    [NSApp setMainMenu: m_menuMain];&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The menubar acts as a container for the application menus, including the Apple menu and the Window menu, discussed below.&lt;br /&gt;&lt;br /&gt;An Apple menu has the following core attributes:&lt;br /&gt;-- a name, which is assigned by MacOS X based on information present in the application bundle (Info.plist file),&lt;br /&gt;-- (optionally) an About... menu item,&lt;br /&gt;-- (optionally) a Preferences... menu item,&lt;br /&gt;-- a Services menu item, which parents a services menu,&lt;br /&gt;-- Hide, Hide Others, and Show All menu items (which cause the application windows to hide, the windows of all other applications to hide, and all windows (application and otherwise) to show, respectively,&lt;br /&gt;-- a Quit menu item, which causes the NSApplication object to fall out of its event processing loop (and the application to exit)&lt;br /&gt;&lt;br /&gt;To create the Apple menu, one does the following:&lt;br /&gt;-- create an instance of NSMenu to represent the Apple menu (here I will assume it is stored in a variable name menuApp). The title of the menu can be set to anything, since OS X will override it, as described above,&lt;br /&gt;-- add an instance of NSMenuItem to menuApp with the title "Preferences...",&lt;br /&gt;-- add a separator menuitem.&lt;br /&gt;So far, the code looks like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    NSMenu *menuApp = [[NSMenu alloc] initWithTitle: @"Apple Menu"];&lt;br /&gt;&lt;br /&gt;    [menuApp addItemWithTitle:@"Preferences..." action:nil keyEquivalent:@""];&lt;br /&gt;    [menuApp addItem: [NSMenuItem separatorItem]];&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Notice that the action: associated with the preferences menu item is nil. A preferences menuitem is not required. After all, if your application has no preferences, having that menu item is pointless. More on this later.&lt;br /&gt;&lt;br /&gt;The next step is the creation of the Services submenu. Most of the menu items in the Services menu, once it is added to the application, are filled in by MacOS X. At this stage, I know how to create one and add it to the Apple menu. More details on exactly what an application may want to add to this menu will likely emerge, but for now, here are the bare-minimum steps: &lt;br /&gt;-- create an instance of NSMenu to represent the services menu, and give it the title "Services"&lt;br /&gt;-- invoke the NSApplication instances setServicesMenu: method, passing the services menu instance as an argument.&lt;br /&gt;-- create an instance of NSMenuItem and give it a title "Services"&lt;br /&gt;-- invoke the above NSMenuItem's setSubMenu: method, passing the services menu instance as an argrument.&lt;br /&gt;-- add the menuItem to the apple menu.&lt;br /&gt;&lt;br /&gt;Here is the code for the above:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    NSMenu *menuServices = [[NSMenu alloc] initWithTitle: @"Services"];&lt;br /&gt;    [NSApp setServicesMenu:menuServices];&lt;br /&gt;&lt;br /&gt;    menuitem = [[NSMenuItem alloc] initWithTitle: @"Services"&lt;br /&gt;        action:nil keyEquivalent:@""];&lt;br /&gt;    [menuitem setSubmenu:menuServices];&lt;br /&gt;    [menuApp addItem: menuitem];&lt;br /&gt;    [menuitem release];&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Next we have the Hide, Hide Others, and Show All menu items. Steps follow a pattern that should now be evident:&lt;br /&gt;-- (optionally) add a separator menu item to group related menuitems,&lt;br /&gt;-- create an instance of NSMenuItem, using initWithTitle to set the title, action: to set the action that will be invoked when the menuitem is selected by the user, and set the key equivalent. Speaking of key equivalents, these come in two forms: using a lowercase letter corresponds to clover-key, while an uppercase key corresponds to shift clover-key,&lt;br /&gt;-- add the menuitem to the menu&lt;br /&gt;&lt;br /&gt;An additional step is to assign a target object to handle the menu item. This is done by invoking the menuitem's setTarget: method, and passing that object as the argument.&lt;br /&gt;&lt;br /&gt;With that, here is the code for the Hide, Hide Others, and Show All menu items. Notice the action arguments specify methods implemented by NSApplication, and setTarget: binds these to the NSApplication instance.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    [menuApp addItem: [NSMenuItem separatorItem]];&lt;br /&gt;    menuitem = [[NSMenuItem alloc] initWithTitle:@"Hide"&lt;br /&gt;        action:@selector(hide:) keyEquivalent:@""];&lt;br /&gt;    [menuitem setTarget: NSApp];&lt;br /&gt;    [menuApp addItem: menuitem];&lt;br /&gt;    [menuitem release];&lt;br /&gt;    menuitem = [[NSMenuItem alloc] initWithTitle:@"Hide Others"&lt;br /&gt;        action:@selector(hideOtherApplications:) keyEquivalent:@""];&lt;br /&gt;    [menuitem setTarget: NSApp];&lt;br /&gt;    [menuApp addItem: menuitem];&lt;br /&gt;    [menuitem release];&lt;br /&gt;    menuitem = [[NSMenuItem alloc] initWithTitle:@"Show All"&lt;br /&gt;        action:@selector(unhideAllApplications:) keyEquivalent:@""];&lt;br /&gt;    [menuitem setTarget: NSApp];&lt;br /&gt;    [menuApp addItem: menuitem];&lt;br /&gt;    [menuitem release];&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And, finally, here is the code for creating the Quit menuitem:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    [menuApp addItem: [NSMenuItem separatorItem]];&lt;br /&gt;    menuitem = [[NSMenuItem alloc] initWithTitle:@"Quit"&lt;br /&gt;        action:@selector(terminate:) keyEquivalent:@"q"];&lt;br /&gt;    [menuitem setTarget: NSApp];&lt;br /&gt;    [menuApp addItem: menuitem];&lt;br /&gt;    [menuitem release];&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Once we have the above code, all that remains is telling NSApplication that the above is our Apple menu, and then adding the menu to the menubar. The first step is done by making a call to an undocumented function, setAppleMenu:. Here is the code:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;   [NSApp setAppleMenu:menuApp];&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As of MacOS X 10.4 (Tiger), setAppleMenu: was removed from Cocoa header files, and this may lead you to a compiler error. To get around the compiler error, one can add the following code prior to usage:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;@interface NSApplication(NSWindowsMenu)&lt;br /&gt;- (void)setAppleMenu:(NSMenu *)aMenu;&lt;br /&gt;@end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This code may generate a warning, but should get you past the error (which is fatal).&lt;br /&gt;&lt;br /&gt;To add the menubar, I coded up the following routine, which is also used to add the Window menu to the menubar:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PRStatus&lt;br /&gt;CocoaMenubarImpl::AddToMenubar(NSMenu *menu)&lt;br /&gt;{&lt;br /&gt;    NSMenuItem *dummyItem = [[NSMenuItem alloc] initWithTitle:@""&lt;br /&gt;        action:nil keyEquivalent:@""];&lt;br /&gt;    [dummyItem setSubmenu:menu];&lt;br /&gt;    [m_menubar addItem:dummyItem];&lt;br /&gt;    [dummyItem release];&lt;br /&gt;    return PR_SUCCESS;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As strange as that code looks, it works. It effectively creates a dummy menuitem  adds that menuitem to the menubar, and then makes the menu you are adding a submenu of the menuitem that was added. This makes sense, since the menubar is a menu, the File menu, the Edit menu, and so on, are menuitems that have a submenu, which is the menu you are adding. &lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/1570/737/1600/applemenu.png"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/1570/737/1600/applemenu.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The Window menu is pretty much the same idea. The Window menu has two menu items, Minimize and Bring All to Front, which are bound to the performMiniaturize: and arrangeInFront: actions of the NSApplication instance, respectively. The Window menu is bound to the NSApplication object using NSApplication's setWindowsMenu: method. As with the Apple menu, AddToMenubar() is called to add the Window menu to the menubar. Here is the code for your perusal: &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PRStatus&lt;br /&gt;CocoaMenubarImpl::CreateWindowMenu()&lt;br /&gt;{&lt;br /&gt;    NSMenu *menuWindows = [[NSMenu alloc] initWithTitle: @"Window"];&lt;br /&gt;&lt;br /&gt;    [menuWindows addItemWithTitle:@"Minimize"&lt;br /&gt;        action:@selector(performMiniaturize:) keyEquivalent:@""];&lt;br /&gt;    [menuWindows addItem: [NSMenuItem separatorItem]];&lt;br /&gt;    [menuWindows addItemWithTitle:@"Bring All to Front"&lt;br /&gt;        action:@selector(arrangeInFront:) keyEquivalent:@""];&lt;br /&gt;&lt;br /&gt;    [NSApp setWindowsMenu:menuWindows];&lt;br /&gt;    AddToMenubar(menuWindows);&lt;br /&gt;    [menuWindows release];&lt;br /&gt;    return PR_SUCCESS;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/1570/737/1600/windowmenu.png"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/1570/737/1600/windowmenu.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;With the above knowledge, I modified the Cocoa concrete menubar implementation CocoaMenubarImpl so that it creates a menubar that, by default, contains an Apple menu and a Window menu. Here is the complete code for this implementation (some of which has already been presented):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PRStatus CocoaMenubarImpl::Create()&lt;br /&gt;{&lt;br /&gt;    m_menubar = [[NSMenu alloc] initWithTitle: @""];&lt;br /&gt;    if (m_menubar) {&lt;br /&gt;        [NSApp setMainMenu: m_menubar];&lt;br /&gt;        CreateAppleMenu();&lt;br /&gt;        CreateWindowMenu();&lt;br /&gt;        return PR_SUCCESS;&lt;br /&gt;    }&lt;br /&gt;    return PR_FAILURE;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;PRStatus&lt;br /&gt;CocoaMenubarImpl::AddToMenubar(NSMenu *menu)&lt;br /&gt;{&lt;br /&gt;    NSMenuItem *dummyItem = [[NSMenuItem alloc] initWithTitle:@""&lt;br /&gt;        action:nil keyEquivalent:@""];&lt;br /&gt;    [dummyItem setSubmenu:menu];&lt;br /&gt;    [m_menubar addItem:dummyItem];&lt;br /&gt;    [dummyItem release];&lt;br /&gt;    return PR_SUCCESS;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;PRStatus&lt;br /&gt;CocoaMenubarImpl::CreateAppleMenu()&lt;br /&gt;{&lt;br /&gt;    NSMenuItem *menuitem;&lt;br /&gt;    // Create the application (Apple) menu.&lt;br /&gt;    NSMenu *menuApp = [[NSMenu alloc] initWithTitle: @"Apple Menu"];&lt;br /&gt;&lt;br /&gt;    NSMenu *menuServices = [[NSMenu alloc] initWithTitle: @"Services"];&lt;br /&gt;    [NSApp setServicesMenu:menuServices];&lt;br /&gt;&lt;br /&gt;    [menuApp addItemWithTitle:@"Preferences..." action:nil keyEquivalent:@""];&lt;br /&gt;    [menuApp addItem: [NSMenuItem separatorItem]];&lt;br /&gt;    menuitem = [[NSMenuItem alloc] initWithTitle: @"Services"&lt;br /&gt;        action:nil keyEquivalent:@""];&lt;br /&gt;    [menuitem setSubmenu:menuServices];&lt;br /&gt;    [menuApp addItem: menuitem];&lt;br /&gt;    [menuitem release];&lt;br /&gt;    [menuApp addItem: [NSMenuItem separatorItem]];&lt;br /&gt;    menuitem = [[NSMenuItem alloc] initWithTitle:@"Hide"&lt;br /&gt;        action:@selector(hide:) keyEquivalent:@""];&lt;br /&gt;    [menuitem setTarget: NSApp];&lt;br /&gt;    [menuApp addItem: menuitem];&lt;br /&gt;    [menuitem release];&lt;br /&gt;    menuitem = [[NSMenuItem alloc] initWithTitle:@"Hide Others"&lt;br /&gt;        action:@selector(hideOtherApplications:) keyEquivalent:@""];&lt;br /&gt;    [menuitem setTarget: NSApp];&lt;br /&gt;    [menuApp addItem: menuitem];&lt;br /&gt;    [menuitem release];&lt;br /&gt;    menuitem = [[NSMenuItem alloc] initWithTitle:@"Show All"&lt;br /&gt;        action:@selector(unhideAllApplications:) keyEquivalent:@""];&lt;br /&gt;    [menuitem setTarget: NSApp];&lt;br /&gt;    [menuApp addItem: menuitem];&lt;br /&gt;    [menuitem release];&lt;br /&gt;    [menuApp addItem: [NSMenuItem separatorItem]];&lt;br /&gt;    menuitem = [[NSMenuItem alloc] initWithTitle:@"Quit"&lt;br /&gt;        action:@selector(terminate:) keyEquivalent:@"q"];&lt;br /&gt;    [menuitem setTarget: NSApp];&lt;br /&gt;    [menuApp addItem: menuitem];&lt;br /&gt;    [menuitem release];&lt;br /&gt;&lt;br /&gt;    [NSApp setAppleMenu:menuApp];&lt;br /&gt;    AddToMenubar(menuApp);&lt;br /&gt;    [menuApp release];&lt;br /&gt;    return PR_SUCCESS;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;PRStatus&lt;br /&gt;CocoaMenubarImpl::CreateWindowMenu()&lt;br /&gt;{   &lt;br /&gt;    NSMenu *menuWindows = [[NSMenu alloc] initWithTitle: @"Window"];&lt;br /&gt;&lt;br /&gt;    [menuWindows addItemWithTitle:@"Minimize"&lt;br /&gt;        action:@selector(performMiniaturize:) keyEquivalent:@""];&lt;br /&gt;    [menuWindows addItem: [NSMenuItem separatorItem]];&lt;br /&gt;    [menuWindows addItemWithTitle:@"Bring All to Front"&lt;br /&gt;        action:@selector(arrangeInFront:) keyEquivalent:@""];&lt;br /&gt;&lt;br /&gt;    [NSApp setWindowsMenu:menuWindows];&lt;br /&gt;    AddToMenubar(menuWindows);&lt;br /&gt;    [menuWindows release];&lt;br /&gt;    return PR_SUCCESS;&lt;br /&gt;}  &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;By adding a &amp;lt;menubar&amp;gt; tag to the XUL markup, I now get a functional menubar with the desired Apple and Window menu at runtime. As can be seen in the earlier screenshots, the Apple menu is given the name of the executable (layout is the name of the binary that currently implements the runtime engine), and the MacOS X runtime has added the window named "Hello World App" to the Window menu. All of the menu items in the Window menu are functional without any additional code.&lt;br /&gt;&lt;br /&gt;This isn't the end of the story, by any means -- I still need to experiment with the implementation of File and Edit menus, which most applications have. These menus are not MacOS X-specific, and represent the second class of menus identified at the beginning of this article. Also, adding the Window menu at the time the menubar is created may not work -- I may need to do it after all the user menus have been added, since the menus are listed in the order they are added to the menubar, and the Window menu usually follows the application-specific class of menus. And I haven't yet investigated the Help menu, how to handle selection of the Preferences menuitem, or have looked into adding an About... menu item to the Apple menu.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-113792129539127111?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/113792129539127111/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=113792129539127111' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113792129539127111'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113792129539127111'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2006/01/macos-x-menus-are-bit-less-mystic-to.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-113727891480910147</id><published>2006-01-14T13:57:00.000-08:00</published><updated>2006-01-22T03:28:22.660-08:00</updated><title type='text'></title><content type='html'>Menus represent more of an implementation challenge than perhaps any of the other widgets that have been implemented in the layout engine to this point. While there are many attributes shared by menu implementations on the various platforms we are looking at, there are significant differences as well, some of which have an effect on the architecture of the solution, and an impact on the how a developer specifies menus in markup.&lt;br /&gt;&lt;br /&gt;Let's start our discussion of cross-platform menus by listing the cross-platform and platform-specific attributes of menus.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Cross Platform Menu Attributes&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Menus have a name. The name of the menu identifies the general functionality that is provided by the menu.&lt;br /&gt;&lt;br /&gt;Menus (naturally enough) contain menu items. Menu items, like menus, have names that are used to identify the functionality provided by the menu item. Examples of menu items are Open, Close, Copy, and Paste.  &lt;br /&gt;&lt;br /&gt;Applications invariably provide a File menu that contains some of the following menu items: New, used to create new documents, or content; Open, used to open existing documents located on persistent storage; Close, used to close the currently open document; Save, used to save the currently open document to persistent storage; Save As, used to select a file and save the currently open document to that file; Exit or Quit, used to close all open documents, and terminate the application; and Print (and possibly Print Setup), which can be used to configure and print content displayed in the currently active document. The File menu is always displayed as the leftmost menu in the menubar (except on MacOS X, see below).&lt;br /&gt;&lt;br /&gt;Applications may, depending on their requirements, provide an Edit menu that contains some or all of the following menu items: Cut, used to remove selected content from the currently open document, and place it in the clipboard; Copy, to copy selected content from the currently open document, and place it in the clipboard; Paste, to paste the contents of the clipboard into the currently active document; and Select All, which selects all of the content managed by whatever widget currently has the input focus; Find and Find Next, which are used to find content in the current document.&lt;br /&gt;&lt;br /&gt;Menus are organized horizontally in a container object known as a menu bar. Menu items are organized vertically in a menu.&lt;br /&gt;&lt;br /&gt;Menus may, depending on their content, contain separators. A separator is a special instance of a menu item that, instead of displaying a label, displays a horizontal line. Unlike other menu items, a separator cannot be selected. Separators are used to define (and separate) related groups of menu items.&lt;br /&gt;&lt;br /&gt;Menu items may be associated with what is known (depending on who is describing them) as either a shortcut, a keyboard equivalent, or an accelerator (I'll refer to them here as shortcuts). A shortcut consists of one or more keys, usually prefixed by a special trigger key (e.g., ctrl) that is shared by all shortcuts. Whenever the keys associated with a shortcut are pressed by the user, the associated menu item is activated. For many, use of shortcuts is much more efficient than moving the mouse to a menu, opening the menu, locating and clicking on the menu item. Invariably, shortcuts are displayed in the menu along with the title of the associated menu item; doing so documents the shortcuts that are supported by the application. Shortcuts are never associated with separators.&lt;br /&gt;&lt;br /&gt;Menu items can be active (enabled) or inactive (disabled). Inactive menu items are displayed in a dimmed font in menus, to communicate their inactive state to the user.&lt;br /&gt;&lt;br /&gt;Menu items are associated with a command. When the menu item is selected by the user, the command is executed.&lt;br /&gt;&lt;br /&gt;Now that we have looked at the plaform-independent attributes of menus, let's look at those attributes which are platform-specific.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Platform-Specific Menu Attributes&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In Gtk+, menus can be torn away from the menu bar. Once torn away, the menu displays in its own window. There are some cases where tear-away menus are effective, but these cases can (in my opinion) be better satisfied by displaying the functionality of the menu in a floating toolbox/toolbar window.&lt;br /&gt;&lt;br /&gt;On both Linux (Gtk+) and Windows, shortcuts are prefixed by the ctrl key. On MacOS X, the apple/clover key is used to prefix shortcuts. &lt;br /&gt;&lt;br /&gt;Each toolkit/platform supports some for of help system, dictating what menu items are included in a Help menu, what shortcuts are used to access these menu items, and what actions result when menu items are selected by the user.&lt;br /&gt;&lt;br /&gt;The same can be said about printing; each system implements a different mechanism for supporting printing and print setup.&lt;br /&gt;&lt;br /&gt;MacOS X displays menus in a single menubar located on the desktop, while menubars in Linux and Windows are associated with windows. Since only one window can have the focus on either of these platforms, the difference is largely irrelevant.&lt;br /&gt;&lt;br /&gt;What is relevant on MacOS is the "application" menu, which displays to the right of the system-managed Apple menu, and to the left of the application's File menu. The name assigned to the application menu is the name of the application, e.g., Firefox is the name of the application menu defined by the Firefox web browser. The MacOS X application menu has, by convention, content that conflicts with content typically found in the File, Edit, and Help menus associated with Linux and Windows applications:&lt;br /&gt;&lt;br /&gt;-- The first menu item in the application menu is About (application name). This menu item would be found typically in the Help menu on the other platforms.&lt;br /&gt;&lt;br /&gt;-- The preferences menu, normally found in the Edit menu in Linux and Windows applications, is located in the application menu on MacOS X.&lt;br /&gt;&lt;br /&gt;-- The application menu contains a Quit menu item, which conflicts with the Quit (or Exit) menu item that would be located in the File menu on Linux or Windows.&lt;br /&gt;&lt;br /&gt;MacOS X also requires applications to provide a Window menu. The Window menu contains two items at the top of the menu -- Minimize and Zoom -- which correspond the the minimize and zoom controls displayed at the extreme left edge of the window title bar. The other items in the Window menu can be used to select the application's active window from among windows already being displayed by the application, or predefined windows not yet visible (depending upon the design of the application).&lt;br /&gt;&lt;br /&gt;Some of these differences, as I implied, may have an impact on markup used to define the menus. As a starting point, let's take a look at how Mozilla/Firefox defines the menus for the browser application. The markup I am going to discuss here can be found at http://lxr.mozilla.org/seamonkey/source/browser/base/content/browser-menubar.inc -- the line numbers may differ depending on changes made by Mozilla contributors. Below, I will copy and paste relevant sections of the content discussed, which should minimize the impact of such changes.&lt;br /&gt;&lt;br /&gt;First off, the above file is actually an include; it is included by browser.xul (which defines the markup for the browser application window). &lt;br /&gt;&lt;br /&gt;The following is the markup from browser-menubar.inc for the Edit menu:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt; 83             &amp;lt;menu id="edit-menu" label="&amp;editMenu.label;"&lt;br /&gt; 84                   accesskey="&amp;editMenu.accesskey;"&amp;gt;&lt;br /&gt; 85               &amp;lt;menupopup id="menu_EditPopup"&amp;gt;&lt;br /&gt; 86                 &amp;lt;menuitem label="&amp;undoCmd.label;"&lt;br /&gt; 87                           key="key_undo"&lt;br /&gt; 88                           accesskey="&amp;undoCmd.accesskey;"&lt;br /&gt; 89                           command="cmd_undo"/&amp;gt;&lt;br /&gt; 90                 &amp;lt;menuitem label="&amp;redoCmd.label;" &lt;br /&gt; 91                           key="key_redo"&lt;br /&gt; 92                           accesskey="&amp;redoCmd.accesskey;"&lt;br /&gt; 93                           command="cmd_redo"/&amp;gt;&lt;br /&gt; 94                 &amp;lt;menuseparator/&amp;gt; &lt;br /&gt; 95                 &amp;lt;menuitem label="&amp;cutCmd.label;" &lt;br /&gt; 96                           key="key_cut"&lt;br /&gt; 97                           accesskey="&amp;cutCmd.accesskey;" &lt;br /&gt; 98                           command="cmd_cut"/&amp;gt; &lt;br /&gt; 99                 &amp;lt;menuitem label="&amp;copyCmd.label;"&lt;br /&gt;100                           key="key_copy"&lt;br /&gt;101                           accesskey="&amp;copyCmd.accesskey;"&lt;br /&gt;102                           command="cmd_copy"/&amp;gt;&lt;br /&gt;103                 &amp;lt;menuitem label="&amp;pasteCmd.label;"&lt;br /&gt;104                           key="key_paste"&lt;br /&gt;105                           accesskey="&amp;pasteCmd.accesskey;"&lt;br /&gt;106                           command="cmd_paste"/&amp;gt;&lt;br /&gt;107                 &amp;lt;menuitem label="&amp;deleteCmd.label;"&lt;br /&gt;108                           key="key_delete"&lt;br /&gt;109                           accesskey="&amp;deleteCmd.accesskey;"&lt;br /&gt;110                           command="cmd_delete"/&amp;gt;&lt;br /&gt;111                 &amp;lt;menuseparator/&amp;gt;&lt;br /&gt;112                 &amp;lt;menuitem label="&amp;selectAllCmd.label;"&lt;br /&gt;113                           key="key_selectAll"&lt;br /&gt;114                           accesskey="&amp;selectAllCmd.accesskey;"&lt;br /&gt;115                           command="cmd_selectAll"/&amp;gt;&lt;br /&gt;116                 &amp;lt;menuseparator/&amp;gt;&lt;br /&gt;117                 &amp;lt;menuitem id="menu_find" label="&amp;findOnCmd.label;" accesskey="&amp;findOnCmd.accesskey;" key="key_find" command="cmd_find"/&amp;gt;&lt;br /&gt;118                 &amp;lt;menuitem label="&amp;findAgainCmd.label;" accesskey="&amp;findAgainCmd.accesskey;" key="key_findAgain" command="cmd_findAgain"/&amp;gt;&lt;br /&gt;119                 &amp;lt;menuseparator hidden="true" id="textfieldDirection-separator"/&amp;gt;&lt;br /&gt;120                 &amp;lt;menuitem id="textfieldDirection-swap"&lt;br /&gt;121                           command="cmd_switchTextDirection"&lt;br /&gt;122                           key="key_switchTextDirection"&lt;br /&gt;123                           label="&amp;bidiSwitchTextDirectionItem.label;"&lt;br /&gt;124                           accesskey="&amp;bidiSwitchTextDirectionItem.accesskey;"&lt;br /&gt;125                           hidden="true"/&amp;gt;&lt;br /&gt;126 #ifdef XP_UNIX&lt;br /&gt;127 #ifndef XP_MACOSX&lt;br /&gt;128                 &amp;lt;menuseparator/&amp;gt;&lt;br /&gt;129                 &amp;lt;menuitem id="menu_preferences"&lt;br /&gt;130                           label="&amp;preferencesCmdUnix.label;"&lt;br /&gt;131                           accesskey="&amp;preferencesCmdUnix.accesskey;"&lt;br /&gt;132                           oncommand="openPreferences();"/&amp;gt;&lt;br /&gt;133 #endif&lt;br /&gt;134 #endif&lt;br /&gt;135               &amp;lt;/menupopup&amp;gt;&lt;br /&gt;136             &amp;lt;/menu&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As you can see, the layout is very intuitive. On line 83, the menu element defines the Edit menu, and assigns it a name. menuitem tags are, naturally enough, children of the menu element, and define the items that are contained in the menu. menuitem tags have the same basic attributes as a menu, in addition to a command attribute that defines the code that is to be executed if the user selects that menuitem.&lt;br /&gt;&lt;br /&gt;Also, as you can see, the code (starting around line 126) contains #ifdefs. These #ifdefs are used to add a Preferences menu item to the Edit menu on Unix systems (except for MacOS X, which the Mozilla build system also defines the XP_UNIX #define at compile time, since MacOS X, internally, is Unix-based). On MacOS X, preferences are located in the application menu, as was described earlier. On Windows, preferences are accessible in the Tools menu, using the Options menu item (all platforms have a Tools menu, but only the Windows version of Firefox provides an Options menu item in the Tools menu).&lt;br /&gt;&lt;br /&gt;So, we see in Mozilla one way that menus can be made platform-specific -- use #ifdefs in the markup. Perhaps the low frequency of use -- very few other places in markup need this particular strategy -- makes the use of #ifdefs acceptable. Yet, when abused, #ifdefs are never a very good idea. On the otherhand, perhaps there is another way to do this that avoids #ifdefs, and I will spend a good bit of time looking for another way as I try and come up with a menu implementation for my layout engine.&lt;br /&gt;&lt;br /&gt;As you can see, entities are used to define commandkey attributes (shortcuts). This implies that there are dtd files that are platform-specific. Using lxr to identify the definition of one of these entities, &amp;printCmd.commandkey;, we see the following from browser.dtd:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt; 24 &amp;lt;!ENTITY printCmd.commandkey "p"&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Thus, there is some logic (perhaps outside of the XML parser) that, when adding items to the menu, concatenates the commandkey specified in markup with its prefix character (e.g., ctrl on Unix, or clover on MacOS X). I'll use that same strategy in the concrete menu classes that do the work of building the menus from markup.&lt;br /&gt;&lt;br /&gt;Finally, there is the issue of how to deal with platform-specific behavior (the print and help commands, and so forth). This must be handled by providing platform-specific handlers that can be called from Javascript. Where this functionality lives is an issue that must still be considered, and there are two options. If the functionality is toolkit-specific, versus application-dependent, then a helper class (or a library of classes) registered as an object with the Javascript engine, and provided by the toolkit, would be an appropriate solution. Otherwise, platform-specific functionality can be provided by the application/component developer. The component library, of course, can abstract platform dependencies in the same ways that the layout engine does (e.g., by using factories).&lt;br /&gt;&lt;br /&gt;As to supporting the MacOS X differences such as the application menu, and the Window menu, these are issues that will need to be considered in the design. Hopefully I will have some details in my next post.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-113727891480910147?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/113727891480910147/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=113727891480910147' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113727891480910147'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113727891480910147'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2006/01/menus-represent-more-of-implementation.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-113616855541971674</id><published>2006-01-01T18:06:00.000-08:00</published><updated>2006-01-02T02:32:39.073-08:00</updated><title type='text'></title><content type='html'>Getting the resize handling code implemented for Windows turned out to be rather time consuming. Writing the code was easy enough. It was getting it all to link properly that drove me mad. I spent numerous hours this New Year's weekend fighting what appeared to be a linker error in VC++ 2003.NET -- errors related to undefined .ctor and member functions at link time in my mixed-mode (managed/unmanaged) DLL. &lt;br /&gt;&lt;br /&gt;The problem manifested itself once I added the managed helper class that was designed to receive Form Resize events. On doing so, the linker started to complain that the Button Click event helper class that had previously linked without problems had unresolved references. This was happening even though both were of these classes were coded to the exact same pattern, both were being built and linked into the DLL in exactly the same manner, and both were used in the same way in their respective contexts.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/1570/737/1600/winone.png"&gt;&lt;img style="margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/1570/737/1600/winone.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;In the end, I was forced to put the implementation of these helper classes into a single source file in order resolve the linker issue. When I migrate to .NET 2005, I'll likely split this file into separate compilation units, as keeping all of the handlers required for all of the widgets in the same file might become difficult to maintain. But for now, I am past the problem. Time to move on :-) Here's the code:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;// Button Clicks&lt;br /&gt;&lt;br /&gt;#include "buttoncallbackhelper.h"&lt;br /&gt;#include "windowsbuttonimpl.h"&lt;br /&gt;&lt;br /&gt;#pragma managed&lt;br /&gt;&lt;br /&gt;ButtonCallbackHelper::ButtonCallbackHelper(WindowsButtonImpl *impl)&lt;br /&gt;{&lt;br /&gt;    m_impl = impl;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void ButtonCallbackHelper::OnButtonClick(Object *sender, System::EventArgs* e)&lt;br /&gt;{&lt;br /&gt;    if (m_impl)&lt;br /&gt;        m_impl-&amp;gt;HandleCommand();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// Window resize&lt;br /&gt;&lt;br /&gt;#include "windowresizehelper.h"&lt;br /&gt;#include "windowswindowimpl.h"&lt;br /&gt;&lt;br /&gt;WindowResizeHelper::WindowResizeHelper(WindowsWindowImpl *impl)&lt;br /&gt;{&lt;br /&gt;    m_impl = impl;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void WindowResizeHelper::OnResizeHandler(Object *sender, System::EventArgs* e)&lt;br /&gt;{&lt;br /&gt;    if (m_impl &amp;&amp; sender) {&lt;br /&gt;        Control *control = dynamic_cast&amp;lt;Control *&amp;gt;(sender);&lt;br /&gt;        if (control) {&lt;br /&gt;            Rectangle r = control-&amp;gt;ClientRectangle;&lt;br /&gt;            int x, y, width, height;&lt;br /&gt;&lt;br /&gt;            x = r.Left;&lt;br /&gt;            y = r.Top;&lt;br /&gt;            width = r.Right - r.Left;&lt;br /&gt;            height = (r.Bottom - r.Top);&lt;br /&gt;            m_impl-&amp;gt;HandleResize(x, y, width, height);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As you can see, handling a button click is simple -- all we need to know is that it occured, and pass it up to the containing WindowsButtonImpl instance for handling. In the case of handling a window resize, it is a bit more complicated -- dynamic_cast the Object that generated the event to an instance of Control, retrieve the client rectangle, compute the x, y, width, and height values from this result, and then pass them along to the WindowsWindowImpl instance associated with the Control, which sends them on to whatever listeners in the layout engine have registered for the event.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/1570/737/1600/wintwo.png"&gt;&lt;img style="margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/1570/737/1600/wintwo.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Because the callback handlers needed to be coded as managed, I implemented these helper classes to maintain a pointer to the parent, non-managed abstract class. In the case of the Button helper class, the abstract class is WindowsButtonImpl. The constructor for the helper class takes that pointer as an argument. When the Click callback is triggered, it uses the member variable to find the parent class and calls  it with the event, and it is then percolated up to the layout engine.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/1570/737/1600/winthree.png"&gt;&lt;img style="margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/1570/737/1600/winthree.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The final touch needed was to add __declspec(dllexport) prefixes to the generated component header and wrapper functions -- in testing component execution, it was clear that the JavaScript engine was not finding the component interfaces because they were not being exported. A simple #define EXPORT that resolves to __declspec(dllexport) on Windows and to nothing elsewhere, plus some code changes to the component wrapper generator was all that was required.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-113616855541971674?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/113616855541971674/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=113616855541971674' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113616855541971674'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113616855541971674'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2006/01/getting-resize-handling-code.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-113591854893775839</id><published>2005-12-29T20:54:00.000-08:00</published><updated>2007-02-03T16:31:06.116-08:00</updated><title type='text'></title><content type='html'>I didn't expect it to take 8 hours straight to wire up a button press handler, but sure enough, it did! Here's how I was able to get a managed .NET Form to call my Javascript via listeners located in unmanaged code.&lt;br /&gt;&lt;br /&gt;As is typical in GUI toolkits, the application registers a callback handler with the object or widget that generates the event. The list of events that the .NET Button class supports can be found &lt;a href="http://msdn2.microsoft.com/en-us/library/system.windows.forms.button_members.aspx#"&gt;by clicking here&lt;/a&gt; (scroll down to the bottom of the page, and then up a few clicks to find the section labeled "Public Events"). Of interest to me, of course, was hooking into the Button object's "Click" event.&lt;br /&gt;&lt;br /&gt;To register a callback function with the Button object, I used the following code in WindowsButtonImpl::Create():&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;m_button-&amp;gt;add_Click(new EventHandler( &lt;br /&gt;    static_cast&amp;lt;ButtonCallbackHelper*&amp;gt;(m_button),&lt;br /&gt;    &amp;ButtonCallbackHelper::OnButtonClick));&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Perhaps the most difficult part of the above was getting the first argument to EventHandler's constructor correct. To understand the problem, let's start with a look at the general pattern for registering a callback, which is:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;obj1-&amp;gt;add_EventName(new EventHandler(obj2, Method));&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;where:&lt;br /&gt;&lt;br /&gt;-- obj1 is the .NET Form Control object that generates the event (e.g., Button).&lt;br /&gt;-- EventName is the name of the event&lt;br /&gt;-- obj2 is the object that will be notified by obj1 when EventName fires&lt;br /&gt;-- Method is the name of the class member in obj2 that implements the callback, prefixed with an &amp; operator.&lt;br /&gt;&lt;br /&gt;My first attempt was to make the callback function a member of WindowsButtonImpl, which is compiled with /clr, but which is not managed because the class declaration is not prefixed with __gc, like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;__gc class WindowsButtonImpl&lt;br /&gt;{&lt;br /&gt;    ...&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;My code, which omits the __gc prefix, caused the compiler to point out that the first argument to EventHandler must be a pointer to a managed object (i.e., it must be an object belonging to a class that was declared with the __gc modifier). The first obvious solution -- making WindowsButtonImpl a managed class -- was not possible, as doing so would require the class that embeds instances of WindowsButtonImpl to take on the gcroot template pattern that I described in the last post, bringing platform specific code above the level of the Windows-platform concrete classes. The right solution, I determined, was to create a class that inherits from .NET's Button, and instantiate it instead of Button as the concrete button widget implementation. That class would be declared using the gcroot template (just as was done with Button), and it would be extended to include a callback function that would be used to handle the Button Click events. That class, ButtonCallbackHelper, is defined simply as follows:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#include "windowsbuttonimpl.h"&lt;br /&gt;&lt;br /&gt;__gc class ButtonCallbackHelper : public Button&lt;br /&gt;{&lt;br /&gt;public:&lt;br /&gt;    void OnButtonClick(Object *sender, System::EventArgs* e);&lt;br /&gt;}; &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;OnButtonClick is the name I gave the callback function. The first argument to this function specifies the object that generated the event, which I imagine can be dynamic_cast as needed (for example, to Button), although I didn't need to do this in my handler. The second argument contains data specific to the event. This argument is also something that I didn't need to look at in the handler, but for some events, it is critical. For example, it might contain information about what key was pressed in the case of an event sent to report a low-level keyboard press or release event. Button Click, however, is a very abstract event, and is independent of the mechanism used to cause the Click to fire -- it button click could have been triggered by a mouse press/release, or just as likely, by the user navigating the focus to the Button object with the Tab key and pressing Enter -- and so an application could care less about the details surrounding the Click event. Just knowing it fired is all we need.&lt;br /&gt;&lt;br /&gt;The next stumbling block was getting the EventHandler CTOR to accept an instance of ButtonCallbackHelper as its first argument. As required, ButtonCallbackHelper had to be declared within the WindowsButtonImpl class using the gcroot template paradigm I described in the previous post. However, the EventHandler CTOR refuses to take an object declared this way. To get past this problem required a static_cast to ButtonCallbackHelper *, as is evident in the code presented at the start of this blog entry.&lt;br /&gt;&lt;br /&gt;The next step was to arrange for the managed object to call unmanaged code in WindowsButtonImpl from within the Click callback function. In order to do this, we need to store in the ButtonCallbackHandler object a pointer or a reference to the WindowsButtonImpl object that contains that ButtonCallbackHandler instance. Then, in the callback function, we can issue a method of the WindowsButtonImpl, which knows how to publish the occurance of the button press back to the layout engine, which in turn will invoke the Javascript handler that is supposed to ultimately handle the event. &lt;br /&gt;&lt;br /&gt;To do this, I coded a constructor for ButtonCallbackHandler that takes a pointer to the containing WindowsButtonImpl class. Here is the modified ButtonCallbackHelper definition:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#include "windowsbuttonimpl.h"&lt;br /&gt;&lt;br /&gt;__gc class ButtonCallbackHelper : public Button&lt;br /&gt;{&lt;br /&gt;public:&lt;br /&gt;    ButtonCallbackHelper(WindowsButtonImpl *impl);&lt;br /&gt;    void OnButtonClick(Object *sender, System::EventArgs* e);&lt;br /&gt;private:&lt;br /&gt;    WindowsButtonImpl *m_impl;&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And, here is the code in the WindowsButtonImpl::Create function that creates the ButtonCallbackHelper instance that it manages, and passes itself to the constructor:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PRStatus WindowsButtonImpl::Create()&lt;br /&gt;{   &lt;br /&gt;    m_button = __gc new ButtonCallbackHelper(this);&lt;br /&gt;    if (m_button) {&lt;br /&gt;        ...&lt;br /&gt;        return PR_SUCCESS;&lt;br /&gt;    }&lt;br /&gt;    return PR_FAILURE;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Notice that __gc is placed before the "new" keyword. This is because, once again, ButtonCallbackHelper must be implemented by managed code (for two reasons -- the callback must be implemented in managed code, and we are subclassing Button, which is implemented as managed code).&lt;br /&gt;&lt;br /&gt;Let's take a look at the implementation of ButtonCallbackHelper:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#include "buttoncallbackhelper.h"&lt;br /&gt;&lt;br /&gt;ButtonCallbackHelper::ButtonCallbackHelper(WindowsButtonImpl *impl)&lt;br /&gt;{&lt;br /&gt;    m_impl = impl;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void ButtonCallbackHelper::OnButtonClick(Object *sender, System::EventArgs* e)&lt;br /&gt;{&lt;br /&gt;    if (m_impl)&lt;br /&gt;        m_impl-&amp;gt;HandleCommand();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As you can see, ButtonCallbackHelper's constructor stores the pointer to a WindowsButtonImpl object that was passed as an argument. This pointer allows OnButtonClick to call WindowsButtonImpl's HandleCommand() method. Since WindowButtonImpl is implemented as non-managed code, this call represents the transition from managed code to unmanaged code. From then on, the code behaves exactly as it does on all of the other platforms, with HandleCommand() calling a function that is implemented in the layout engine, designed to iterate and call all of the listeners that have been registered against the abstract widget.&lt;br /&gt;&lt;br /&gt;For those of you who are so inclined, here is a call stack that illustrates the entire process at runtime of responding to the Button Click event, including the invocation of the Javascript function that is assigned in markup to handle the event:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  js3250.dll!JS_CallFunction(JSContext * cx=0x003f8628, JSObject * obj=0x003f9fc8, JSFunction * fun=0x072e64e0, unsigned int argc=0x00000000, long * argv=0x00000000, long * rval=0x0012e9bc)  Line 4116 + 0x22 C&lt;br /&gt;  layout.exe!Button::ButtonPressed()  Line 124 + 0x1a C++&lt;br /&gt;  layout.exe!ButtonPressSubject::NotifyButtonPress() Line 14 C++&lt;br /&gt;  layout.exe!WindowsButtonImpl::HandleCommand() Line 21 C++&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The code above represents those portions of the call stack that are executed within unmanaged code. The code below this line is all executed as managed code. The top of the managed portion of the stack (00146e38, see below) is likely some function that is used to transition from managed to unmanaged. I suspect that it is a part of the gcroot template.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  00146e38() &lt;br /&gt;  layout.exe!ButtonCallbackHelper.OnButtonClick(System.Object sender = 0x0012f138, System.EventArgs e = 0x0012f148) Line 15 C++&lt;br /&gt;  system.windows.forms.dll!System.Windows.Forms.Control.OnClick(System.EventArgs e) + 0x54 bytes &lt;br /&gt;  system.windows.forms.dll!System.Windows.Forms.Button.OnClick(System.EventArgs e) + 0x2b bytes &lt;br /&gt;  system.windows.forms.dll!System.Windows.Forms.Button.OnMouseUp(System.Windows.Forms.MouseEventArgs mevent) + 0xd6 bytes &lt;br /&gt;  system.windows.forms.dll!System.Windows.Forms.Control.WmMouseUp(System.Windows.Forms.Message m, System.Windows.Forms.MouseButtons button, int clicks) + 0x1c3 bytes &lt;br /&gt;  system.windows.forms.dll!System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message m) + 0x468 bytes &lt;br /&gt;  system.windows.forms.dll!System.Windows.Forms.ButtonBase.WndProc(System.Windows.Forms.Message m) + 0xdb bytes &lt;br /&gt;  system.windows.forms.dll!System.Windows.Forms.Button.WndProc(System.Windows.Forms.Message m) + 0x5d bytes &lt;br /&gt;  system.windows.forms.dll!ControlNativeWindow.OnMessage(System.Windows.Forms.Message m) + 0xb bytes &lt;br /&gt;  system.windows.forms.dll!ControlNativeWindow.WndProc(System.Windows.Forms.Message m) + 0xbc bytes &lt;br /&gt;  system.windows.forms.dll!System.Windows.Forms.NativeWindow.Callback(int hWnd, int msg, int wparam, int lparam) + 0x30 bytes &lt;br /&gt;  0018c752() &lt;br /&gt;  user32.dll!77d43a50()  &lt;br /&gt;  user32.dll!77d43b1f()  &lt;br /&gt;  user32.dll!77d43d79()  &lt;br /&gt;  user32.dll!77d43c7d()  &lt;br /&gt;  system.windows.forms.dll!System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods+IMsoComponentManager.FPushMessageLoop(int dwComponentID, int reason, int pvLoopData) + 0x382 bytes &lt;br /&gt;  system.windows.forms.dll!ThreadContext.RunMessageLoopInner(int reason, System.Windows.Forms.ApplicationContext context) + 0x15f bytes &lt;br /&gt;  system.windows.forms.dll!ThreadContext.RunMessageLoop(int reason, System.Windows.Forms.ApplicationContext context) + 0x45 bytes &lt;br /&gt;  system.windows.forms.dll!System.Windows.Forms.Application.Run() + 0x31 bytes &lt;br /&gt;  layout.exe!WindowsAppImpl::MainLoop() Line 36 C++&lt;br /&gt;  00146e38() &lt;br /&gt;  layout.exe!App::MainLoop()  Line 24 C++&lt;br /&gt;  layout.exe!main(int argc=0x00000003, char * * argv=0x003f5d28)  Line 365 C++&lt;br /&gt;  layout.exe!mainCRTStartup()  Line 398 + 0x11 C&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Well, there you have it -- managed code calling an unmanaged callback function via a wrapper class (after some trial and error). Next stop is to do the same thing for Window resize events, so that I can reflow my layout as the user resizes the window. I suspect that the very same pattern that I applied here for handling Button clicks will also apply to handling Form resizes. But I'm one step ahead already, since I already have a managed wrapper class that inherits fron Form. Details, and code, to follow, so stay tuned.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-113591854893775839?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/113591854893775839/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=113591854893775839' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113591854893775839'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113591854893775839'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2005/12/i-didnt-expect-it-to-take-8-hours.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-113575921294183954</id><published>2005-12-27T23:47:00.000-08:00</published><updated>2005-12-28T01:17:39.493-08:00</updated><title type='text'></title><content type='html'>I have to say, I'm pretty darned impressed with the way .NET Forms is architected. In stark contrast to MFC or Win32, one can code nearly on intuition. Class names are logical -- Button, TextBox, Label -- and methods are predictable; and once you learn the basics of one widget class (or, more precisely, a widget class and its inheritance chain), things become easy. Very, very easy. Passing instances of managed class pointers around posed no problem; other than declaring the pointer gcroot to embed it in a non-managed class (see details in my previous post).&lt;br /&gt;&lt;br /&gt;And best of all, coding the concrete implementations for ButtonImpl, TextImpl, and StaticTextImpl left me with a lot of duplicate, factorable code that I will no doubt be able push up a level to WindowsWidgetImpl, just like I was able to do with Gtk+. All of the controls are positioned, shown, and hidden the same way. Many of the interfaces I am calling against the concrete classes are implemented by the Form's Control class, which Button, Label, and TextBox inherit from, meaning all I should need to do is dynamic_cast my pointers from TextBox, Button, and Label to Control *, and then call a parent class of my design that will handle the actual call into the framework. &lt;br /&gt;&lt;br /&gt;One (initially) disappointing cross-platform difference did emerge -- I can't make TextBox controls in .NET so they are not selectable. However, on reflection, that is a minor issue. To be honest, who really would disable the ability to select the text displayed by a text widget, anyway? &lt;br /&gt;&lt;br /&gt;I really am very pleased that I was able to get so far so quick. The .NET Framework is really nicely done. My initial impression now is that there is no way I would have gotten as far as I did in the past few hours had I chosen to code against Win32 or MFC, or had the .NET Forms framework been badly designed. Kudos to Microsoft on doing the interfaces right this time.&lt;br /&gt;&lt;br /&gt;Here's the complete implementation of WindowsWidgetStaticText:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#include "windowswindowimpl.h"&lt;br /&gt;#include "windowsstatictextimpl.h"&lt;br /&gt;&lt;br /&gt;WindowsStaticTextImpl::WindowsStaticTextImpl()&lt;br /&gt;{&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;WindowsStaticTextImpl::~WindowsStaticTextImpl()&lt;br /&gt;{&lt;br /&gt;    // destroy the text&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;PRStatus &lt;br /&gt;WindowsStaticTextImpl::Create()&lt;br /&gt;{&lt;br /&gt;    m_text = __gc new Label();&lt;br /&gt;    if (m_text) {&lt;br /&gt;       if (!m_formParent) {&lt;br /&gt;           WidgetImpl *top = GetRootWidget();&lt;br /&gt;           if (top) {&lt;br /&gt;               SetFormParent(top);&lt;br /&gt;           }&lt;br /&gt;       }&lt;br /&gt;&lt;br /&gt;       if (m_formParent) {&lt;br /&gt;           m_formParent-&amp;gt;Controls-&amp;gt;Add(m_text);&lt;br /&gt;           return PR_SUCCESS;&lt;br /&gt;       }&lt;br /&gt;    }&lt;br /&gt;    return PR_FAILURE;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;PRStatus&lt;br /&gt;WindowsStaticTextImpl::SetString(const string&amp; value)&lt;br /&gt;{&lt;br /&gt;    if (m_text)&lt;br /&gt;        m_text-&amp;gt;Text = value.c_str();&lt;br /&gt;    return PR_SUCCESS;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;PRStatus&lt;br /&gt;WindowsStaticTextImpl::Show()&lt;br /&gt;{&lt;br /&gt;    if (m_text)&lt;br /&gt;        m_text-&amp;gt;Show();&lt;br /&gt;    return ShowImpl();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;PRStatus&lt;br /&gt;WindowsStaticTextImpl::Hide()&lt;br /&gt;{&lt;br /&gt;    if (m_text)&lt;br /&gt;        m_text-&amp;gt;Hide();&lt;br /&gt;    return ShowImpl();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;PRStatus&lt;br /&gt;WindowsStaticTextImpl::SetGeometry(const int &amp;x, const int &amp;y,&lt;br /&gt;        const int &amp;width, const int &amp;height, const char &amp;mask)&lt;br /&gt;{&lt;br /&gt;    if (m_text) {&lt;br /&gt;        Size size;&lt;br /&gt;        Point point;&lt;br /&gt;&lt;br /&gt;        size = m_text-&amp;gt;ClientSize;&lt;br /&gt;        point = m_text-&amp;gt;Location;&lt;br /&gt;&lt;br /&gt;        if (mask &amp; GEOM_X)&lt;br /&gt;            point.X = x;&lt;br /&gt;        if (mask &amp; GEOM_Y)&lt;br /&gt;            point.Y = y;&lt;br /&gt;        if (mask &amp; GEOM_WIDTH)&lt;br /&gt;            size.Width = width;&lt;br /&gt;        if (mask &amp; GEOM_HEIGHT)&lt;br /&gt;            size.Height = height;&lt;br /&gt;&lt;br /&gt;        m_text-&amp;gt;ClientSize=size;&lt;br /&gt;        m_text-&amp;gt;Location=point;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    return PR_SUCCESS;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;PRStatus&lt;br /&gt;WindowsStaticTextImpl::GetGeometryRequest(int &amp;x, int &amp;y, int &amp;width,&lt;br /&gt;    int &amp;height)&lt;br /&gt;{&lt;br /&gt;    Size size, request(0, 0);&lt;br /&gt;&lt;br /&gt;    if (m_text) {&lt;br /&gt;        size = m_text-&amp;gt;ClientSize;&lt;br /&gt;        width = size.get_Width();&lt;br /&gt;        height = m_text-&amp;gt;PreferredHeight;&lt;br /&gt;    }&lt;br /&gt;    return PR_SUCCESS;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And, here's the screenshot I promised (but didn't expect to see so soon):&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/1570/737/1600/windows.png"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/1570/737/1600/windows.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Next steps: handle resizing (I need to learn how to handle window resize events in .NET) and button presses (ditto for the learning curve), plus a bit of work to wire up the components, using the abstract mechanisms already being used by the Cocoa and Gtk+ implementations. So, I'm not completely done. But, as you can see, a lot has been accomplished in about 4 or 5 hours of work!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-113575921294183954?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/113575921294183954/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=113575921294183954' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113575921294183954'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113575921294183954'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2005/12/i-have-to-say-im-pretty-darned.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-113565658648264287</id><published>2005-12-26T19:46:00.000-08:00</published><updated>2005-12-27T11:40:13.536-08:00</updated><title type='text'></title><content type='html'>While some people were out shopping for bargains, or returning gifts -- today is the day after Christmas, and that's what many people do -- I spent all day (literally) trying to work out a way to merge managed CLR .NET Forms code with the layout engine, which is unmanaged C++ code. For an introduction to .NET, the CLR, and managed code, check out &lt;a href="http://en.wikipedia.org/wiki/Microsoft_.NET"&gt;the following link at wikipedia.org&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;C++, as I understand it, is the only programming language designed by Microsoft to allow programmers to write code which combines unmanaged source with managed sources. Clearly, Microsoft was aware, given the enormous amount of code written in C++, that .NET (or at least, the CLR) would die a rapid death if some way to combine managed and unmanaged code did not exist (Apple did a similar thing I suppose by supporting Carbon, to allow legacy Mac code to port to what was otherwise a Cocoa-based world). C# and Visual Basic are perhaps the most commonly used languages supported by the CLR, other than C++, but neither allows programmers to mix both managed and unmanaged sources together.&lt;br /&gt;&lt;br /&gt;There were a few thorny issues that I ran into while trying to get the layout code up and running under .NET. Perhaps the most difficult of these was a known bug in Visual Studio .NET 2003 that I ran into that, luckily for me, some kind soul at Microsoft had documented well enough for me to work around (see http://support.microsoft.com/?id=814472 if you care to read about the details, and if you are developing DLLs that mix managed and unmanaged code, as I am, you really must take a look). &lt;br /&gt;&lt;br /&gt;Here is where I stand now with things after maybe 15 hours of hacking away on Windows:&lt;br /&gt;&lt;br /&gt;-- both jsengine and layout are completely ported, and the job (except for wrangling with the toolchain) was trivial. I can parse (using the same expat-based code) a XUL document, and I call and execute JS from it. The DOM works nicely, and my DOM dump tool shows the structure for a complicated XUL document just as it should be shown.&lt;br /&gt;&lt;br /&gt;-- I have coded stub routines for all of the concrete Windows .NET Form classes. &lt;br /&gt;&lt;br /&gt;-- in one of the stubs, the concrete WindowImpl class, I instantiate a managed class that I am able to embed in the non-managed wrapper -- again, thanks to some code I found via google. Let's look at this in more detail, because it is important to understand how my non-CLR code can create and interact with CLR-based managed code, a requirement if I am going to make use of .NET Forms. Below is the code, first the header, and followed by an explanation:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#if !defined(__WINDOWSWINDOWIMPL__H__)&lt;br /&gt;#define __WINDOWSWINDOWIMPL__H__&lt;br /&gt;&lt;br /&gt;#include "../windowimpl.h"&lt;br /&gt;&lt;br /&gt;#include "stdafx.h"&lt;br /&gt;&lt;br /&gt;// for discussion of gcroot.h, see &lt;br /&gt;// http://www.ondotnet.com/pub/a/dotnet/2003/03/03/mcppp2.html?page=2&lt;br /&gt;&lt;br /&gt;#include &amp;lt;gcroot.h&amp;gt;&lt;br /&gt;&lt;br /&gt;#using &amp;lt;mscorlib.dll&amp;gt;&lt;br /&gt;#using &amp;lt;System.dll&amp;gt;&lt;br /&gt;#using &amp;lt;System.Drawing.dll&amp;gt;&lt;br /&gt;#using &amp;lt;System.Windows.Forms.dll&amp;gt;&lt;br /&gt;&lt;br /&gt;using namespace System;&lt;br /&gt;using namespace System::ComponentModel;&lt;br /&gt;using namespace System::Drawing;&lt;br /&gt;using namespace System::Windows::Forms;&lt;br /&gt;&lt;br /&gt;#include "windowswidgetimpl.h"&lt;br /&gt;&lt;br /&gt;__gc class FormImpl : public Form&lt;br /&gt;{&lt;br /&gt;public:&lt;br /&gt;    FormImpl() {};&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;class WindowsWindowImpl : public WindowImpl, public WindowsWidgetImpl&lt;br /&gt;{&lt;br /&gt;public:&lt;br /&gt;    WindowsWindowImpl();&lt;br /&gt;    virtual ~WindowsWindowImpl();&lt;br /&gt;    virtual PRStatus Create();&lt;br /&gt;    virtual PRStatus Show();&lt;br /&gt;    virtual PRStatus Hide();&lt;br /&gt;    virtual PRStatus SetTitle(const string&amp; title);&lt;br /&gt;    virtual PRStatus HandleCommand() {return PR_SUCCESS;};&lt;br /&gt;    virtual PRStatus SetGeometry(const int &amp;x, const int &amp;y, &lt;br /&gt;        const int &amp;width, const int &amp;height, const char &amp;mask) {&lt;br /&gt;        return SetGeometryImpl(x, y, width, height, mask); };&lt;br /&gt;    virtual PRStatus GetGeometryRequest(int &amp;x, int &amp;y, &lt;br /&gt;        int &amp;width, int &amp;height) {&lt;br /&gt;        return GetGeometryRequestImpl(x, y, width, height); };&lt;br /&gt;private:&lt;br /&gt;&lt;br /&gt;    // see comment on gcroot, above&lt;br /&gt;&lt;br /&gt;    gcroot&amp;lt;FormImpl *&amp;gt; m_form;&lt;br /&gt;};&lt;br /&gt;#endif&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In the above, the following lines:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#include "stdafx.h"&lt;br /&gt;&lt;br /&gt;// for discussion of gcroot.h, see &lt;br /&gt;// http://www.ondotnet.com/pub/a/dotnet/2003/03/03/mcppp2.html?page=2&lt;br /&gt;&lt;br /&gt;#include &amp;lt;gcroot.h&amp;gt;&lt;br /&gt;&lt;br /&gt;#using &amp;lt;mscorlib.dll&amp;gt;&lt;br /&gt;#using &amp;lt;System.dll&amp;gt;&lt;br /&gt;#using &amp;lt;System.Drawing.dll&amp;gt;&lt;br /&gt;#using &amp;lt;System.Windows.Forms.dll&amp;gt;&lt;br /&gt;&lt;br /&gt;using namespace System;&lt;br /&gt;using namespace System::ComponentModel;&lt;br /&gt;using namespace System::Drawing;&lt;br /&gt;using namespace System::Windows::Forms;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;bring in (what I believe are) required headers and such for using .NET forms code. The source, compiled with /clr, will fail to compile unless this code is included. The gcroot include itself is actually optional for pure managed code, but if you want to embed a managed class pointer in a non-managed class, you will need to include it, and you must declare the member variable holding the pointer to the managed class like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;private:&lt;br /&gt;&lt;br /&gt;    // see comment on gcroot, above&lt;br /&gt;&lt;br /&gt;    gcroot&amp;lt;FormImpl *&amp;gt; m_form;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This template class gcroot does some magic that I have not yet been forced to understand. But without it, the compiler will complain loudly that you cannot embed a managed object in an unmanaged container. I guess the only other way out, had this solution not existed (and I thought about doing this before I stumbed upon the gcroot paradigm), would have been to maintain a map of managed FormImpl classes and WindowImpl classes, and do lookups as needed to map one to the other in the layout engine. Thankfully, I was able to avoid going down that path completely. I suspect I will need to use the same solution to embed, for example, a managed .NET Form Button object inside of my unmanaged ButtonImpl class. Should be straightforward enough.&lt;br /&gt;&lt;br /&gt;And now the source for the WindowImpl Create() function, which shows how I instantiate an instance of FormImpl and stuff it into the m_form member variable:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PRStatus WindowsWindowImpl::Create()&lt;br /&gt;{&lt;br /&gt;    m_form = __gc new FormImpl();&lt;br /&gt;    if (m_form)&lt;br /&gt;        return PR_SUCCESS;&lt;br /&gt;    return PR_FAILURE;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That was simple enough. To set the title of the Window, I merely need to set the Form's text attribute using code that is very familiar I am sure to .NET Forms programmers:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PRStatus WindowsWindowImpl::SetTitle(const string&amp; title)&lt;br /&gt;{&lt;br /&gt;    if (m_form)&lt;br /&gt;        m_form-&amp;gt;Text = title.c_str();&lt;br /&gt;    return PR_SUCCESS;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So, things are looking great. Not as fast as going to Gtk+ from Cocoa, but not bad for a couple of days of work. The secret in the speed comes from designing abstractions that were able to accomodate the requirements of each platform toolkit (and a little luck). If all goes well, I should have the rest of the concrete .NET classes coded up shortly and be able to post my first Windows screenshot.&lt;br /&gt;&lt;br /&gt;One final thing. Porting to Visual C++ identified some rather subtle, but horrible errors in my code (some uninitialized member variables, for example) that led to crashes that I had yet to see from gcc(1)-generated code. I'm not the first one to say this, but it warrants repeating -- the more compilers you toss your code too, the more likely your code isn't going to blow up in your users faces, because the more likely it will be that errors will be caught. On Windows, that other compiler could be Borland, gcc (via cygwin), CodeWarrior, or Visual Studio, and I am sure several others exist if you look hard enough. On Unix, there are fewer choices, but you can certainly take the cross platform code over to MacOS X and Windows and give it a workout there. The more cross-platform your code is, the more able you will be to take advantage of this tactic for identifying weaknesses in your code.&lt;br /&gt;&lt;br /&gt;Just do it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-113565658648264287?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/113565658648264287/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=113565658648264287' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113565658648264287'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113565658648264287'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2005/12/while-some-people-were-out-shopping.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-113522237441502926</id><published>2005-12-21T19:07:00.000-08:00</published><updated>2005-12-25T11:24:59.873-08:00</updated><title type='text'></title><content type='html'>A while back, I surveyed POSIX.1, Cygwin, MacOS X, and Win32 APIs, with the idea of determining how well the POSIX API was supported by each platform (clearly, POSIX itself supported POSIX very nicely :-). To this survey, I added an enumeration of equivalent NSPR 4.3 functionality. NSPR 4.3, in some cases, added APIs that provided functionality that already existed on all of the above platforms. This was primarily because at the time NSPR came into being, Macintosh was still pre-MacOS X, and if there ever was a platform that was not POSIX, it was MacOS prior to darwin and the release of MacOS X. An example of the utter defiance shown by Apple to the Unix way was its networking API. MacTCP, as it was called, was about as far away as one could get from a BSD sockets-like interface (without leaving the solar system, that is). To cite one example, the typical way to send data over a TCP connection required one to fill out a ParmBlk (a struct, essentially) in order to describe the parameters of the send (including a pointer to the data), and then call a function name PBControlAsync() to make the send happen:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;typedef struct TCPSendPB {&lt;br /&gt;    byte           ulpTimeoutValue;   /* upper-layer protocol&lt;br /&gt;                                         timeout */&lt;br /&gt;    byte           ulpTimeoutAction;  /* upper-layer protocol&lt;br /&gt;                                         timeout action */&lt;br /&gt;    byte           validityFlags;     /* validity flags for&lt;br /&gt;                                         options */&lt;br /&gt;    Boolean        pushFlag;          /* true if data should be sent&lt;br /&gt;                                         immediately */&lt;br /&gt;    Boolean        urgentFlag;        /* identifies the data as&lt;br /&gt;                                         important */&lt;br /&gt;    Ptr            wdsPtr;            /* pointer to write data&lt;br /&gt;                                         structure */&lt;br /&gt;    unsigned long  sendFree;&lt;br /&gt;    unsigned short sendLength;&lt;br /&gt;    Ptr            userDataPtr;&lt;br /&gt;}TCPSendPB;&lt;br /&gt;&lt;br /&gt;TCPSendPB pb;&lt;br /&gt;struct TCPSendPB *sendpb = &amp;pb.csParam.send;&lt;br /&gt;pb.csCode = TCPSend;/* send TCP data */&lt;br /&gt;sendpb-&gt;ulpTimeoutValue = (int) ttmo_write;&lt;br /&gt;sendpb-&gt;ulpTimeoutAction = 0;&lt;br /&gt;sendpb-&gt;validityFlags = timeoutValue|timeoutAction;&lt;br /&gt;sendpb-&gt;pushFlag = T; /* send the data now */&lt;br /&gt;sendpb-&gt;urgentFlag = NIL; /* non-urgent data */&lt;br /&gt;sendpb-&gt;wdsPtr = (Ptr) &amp;wds;&lt;br /&gt;sendpb-&gt;userDataPtr = NIL;&lt;br /&gt;PBControlAsync ((ParmBlkPtr) &amp;pb);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Try porting that to Linux! TCP opens, reads, and writes all followed about the same pattern. As, for that matter, did many of the other APIs implemented throughout MacOS toolbox. To port BSD or POSIX sockets-based code to classic Mac, one had to wrap MacTCP, or just give up. I did exactly that when I worked on a port of Apple's X server application (named &lt;a href="http://en.wikipedia.org/wiki/MacX"&gt;MacX&lt;/a&gt;, not to be confused with MacOS X). No, I didn't give up, but I did write an abstraction layer. The MIT X11 Server code used BSD sockets; it was much better to adhere to its requirements than it would have been to modify the core X11 sources.&lt;br /&gt;&lt;br /&gt;But I am starting to digress. Another good reason for coding to NSPR over any other API was that as NSPR embraces further platforms (which was a distinct possibility since Mozilla was based on it), you pick up support on that platform for free.&lt;br /&gt;&lt;br /&gt;The following is a link to the document that I produced which surveys the aforementioned APIs: &lt;a href="http://www.sydlogan.com/posix.html"&gt;POSIX.1 API Support&lt;/a&gt;. Looking at it, you  should come away with an appreciation of just how big POSIX.1 is, and how well it is covered by the platforms we concern ourselves with in this book.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-113522237441502926?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/113522237441502926/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=113522237441502926' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113522237441502926'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113522237441502926'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2005/12/while-back-i-surveyed-posix.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-113498151486567660</id><published>2005-12-19T00:37:00.000-08:00</published><updated>2005-12-27T13:05:59.776-08:00</updated><title type='text'></title><content type='html'>Most of the past weekend, and much of tonight was spent working on a port to Windows XP. The goals at this stage were:&lt;br /&gt;&lt;br /&gt;-- get the cross platform portions of the layout engine building&lt;br /&gt;-- build Windows versions of the mozilla JavaScript library, libexpat, and NSPR downloaded or built, and get them checked into CVS&lt;br /&gt;-- create Windows-specific versions of the project makefiles&lt;br /&gt;&lt;br /&gt;To get the layout engine porting started, the first thing that I needed to do was get Windows-based makefiles written. In addition, I have a few bash scripts that were used to setup the runtime environment (their primary function is to create a distribution directory appropriate for the platform, and to copy libraries and headers from their location in the development tree this location before invoking make). When going from MacOS X to Linux, porting the bash scripts was trivial, since both platforms use a GNU toolchain. One of the major differnces between MacOS X and Linux was caused by the layout of the dist directory (in MacOS X there is a fixed disk layout that is required by all applications (see Chapter XXX), while there are no hard and fast rules on Linux). Using the output of uname in my bash scripts allowed me to isolate differences. At the top of each bash script is the following:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#!/bin/sh&lt;br /&gt;platform=`uname`;&lt;br /&gt;if [ `echo $platform | grep -i "Linux"` ]&lt;br /&gt;then&lt;br /&gt;platform="linux"&lt;br /&gt;fi&lt;br /&gt;if [ `echo $platform | grep -i "Darwin"` ]&lt;br /&gt;then&lt;br /&gt;platform="macosx"&lt;br /&gt;fi&lt;br /&gt;if [ `echo $platform | grep -i "NT"` ]&lt;br /&gt;then&lt;br /&gt;platform="windows"&lt;br /&gt;fi&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;With the platform variable set, all I need to do is perform tests of it within the script, for example:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;if [ $platform = "macosx" ]&lt;br /&gt;then&lt;br /&gt;# code specific to MacOS X&lt;br /&gt;fi&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As far as makefiles go, at the moment, I have separate makefiles for each platform, one called Makefile.macosx and the other called Makefile.linux; I just made a copy of the MacOS X makefile to come up with the linux version, and tweeked the linux version slightly to deal with changes in flags passed to g++ that were required. Later I will create a more unified makefile, but for now, it's not high on my list of priorities. &lt;br /&gt;&lt;br /&gt;To preserve my investment in the development of these makefiles and bash scripts, I installed cygwin on my Windows machine, which provides ports of bash and GNU make. The alternative would be to create a makefile compatible with Microsoft's nmake, and convert the bash scripts to DOS bat files (or, even better, install perl). But cygwin is easy enough to download (http://www.cygwin.com) and install.&lt;br /&gt;&lt;br /&gt;As far as the compile/link portion of the toolchain goes, I made the decision to go with Microsoft Windows .NET. Starting with the Linux makefile, it was a relatively simple matter of text substitution -- I replaced "g++" in the Windows makefile with "cl", "link", and "lib", I replaced all occurances of .o with .obj, and changed the pathnames from which the compile read include files and libraries, and to which the compiler generated its output. The primary reason for going with .NET is my need to port the concrete widget implementation to a native Microsoft GUI toolkit (e.g, Win32). If this were a console based app, I might consider just using g++ (although cygwin does require a license if you are building a commercial application, something to keep in mind). &lt;br /&gt;&lt;br /&gt;Microsoft provides a free version of their command line-based compiler for download (see http://msdn.microsoft.com/visualc/vctoolkit2003/). Or you can use Visual Studio .NET, which I recommend if you plan to do any debugging of your code (there might be freely available debuggers out there, perhaps even gdb(1) works just fine, but I have my head in the sand on that issue). I've done plenty of Windows programming in my life, and Microsoft tools were always available wherever I worked, and I suspect that they are available where you work, too. &lt;br /&gt;&lt;br /&gt;Regardless of your choice of compiler, the main point is this - GNU make is decoupled from the actual compiler/linker that is used. Don't think for a moment that because you are using a Microsoft toolchain that you are forced to use nmake. It's far better to install cygwin, and use what you developed for MacOS X and Linux to build on Windows. Perhaps the most notable example of the use of building with the GNU make/Visual C++ combination is mozilla.org, which uses this strategy (developed by Chris Seawood) to build all their major apps (mozilla, firefox, thunderbird) on the Windows platform.&lt;br /&gt;&lt;br /&gt;So, with the toolchain in place, the makefiles ported to Windows and the Microsoft toolchain, and the bash scripts updated to support Windows, it was time to type:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;C:\ sh build&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and see what happened.&lt;br /&gt;&lt;br /&gt;For the most part, everything built unchanged, with the following exceptions. &lt;br /&gt;&lt;br /&gt;The first problem was getopt(3) is not portable to Win32. Fortunately, NSPR provides a portable solution -- PL_CreateOptState(), PL_GetNextOpt(), and PL_DestroyOptState() -- and it was straightforward replacing one with the other.&lt;br /&gt;&lt;br /&gt;The next problem was my use of basename(3) and dirname(3). Neither of these functions, as it turns out, are implemented by the Win32 api, nor are they implemented by NSPR. To fix this, I spent an hour or two developing and testing my own version of these functions. You can find an implementation of both of these functions by clicking &lt;a href="http://www.sydlogan.com/plbasename.h"&gt;here for the header&lt;/a&gt; and &lt;a href="http://www.sydlogan.com/plbasename.c"&gt;here for the source&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The next problem I faced was getcwd(3). Like both basename(3) and dirname(3), this function is the same on Linux and MacOS X since it is defined by POSIX, but on Windows, its name is _getcwd(), with a leading underscore (there are a few functions in Win32 like this, see &lt;a href="http://www.sydlogan.com/posix.html"&gt;my page on POSIX.1 interfaces on Win32, MacOS X, and Linux&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;There are at least three ways to deal with this difference that come to mind:&lt;br /&gt;&lt;br /&gt;-- use a macro to hide the difference in syntax. Fortunately, getcwd() and _getcwd() are the same except for the leading underscore, leading one to do something like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#if defined(XP_WIN)&lt;br /&gt;#define GETCWD _getcwd&lt;br /&gt;#else&lt;br /&gt;#define GETCWD getcwd&lt;br /&gt;#endif&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;However, Win32 requires you to include &amp;lt;direct.h&amp;gt;, while getcwd on Linux and MacOS X requires &amp;lt;stdlib.h&amp;gt;. So, there would need to be another #ifdef introduced into the sources referencing getcwd() to bring in the right header. Another reason against using is a macro is that, in general, macros are ugly -- they are hard to read, and can be hard to deal with in a debugger. And I tend to avoid using them. So, this isn't the way I ultimately went.&lt;br /&gt;&lt;br /&gt;-- wrap getcwd() with a factory. The idea is to define an abstract class that implements getcwd(), and use a factory class to get a pointer to the corresponding, platform-specific concrete class, much like the layout engine uses a factory to wrap Cocoa, Gtk+, and Windows-based concrete GUI classes. &lt;br /&gt;&lt;br /&gt;-- isolate getcwd() in a source file of its own, and use #ifdefs to in this source file to isolate the platform specific code. This is essentially the approach taken by    NSPR.&lt;br /&gt;&lt;br /&gt;A factory that just serves up just getcwd() is overkill, in my opinion. Were the factory to abstract a larger collection of APIs (much like NSPR), it would probably be worth doing. The overhead for supporting just one function, however, is in my opinion too great. So I decided to implement a function PL_getcwd(), using #ifdefs to separate the platform-specific code. Click &lt;a href="http://www.sydlogan.com/plgetcwd.h"&gt;here for the header&lt;/a&gt;, and &lt;a href="http://www.sydlogan.com/plgetcwd.c"&gt;here for the source&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Having ported the core layout engine code, my next step is to ponder the various options I have in front of me with respect to chosing a native GUI toolkit for the Windows platform. On MacOS X, this was a no-brainer -- Cocoa is the way to go. On Linux, the choice was a little more difficult - Gtk+ vs Qt. In reality, I could have gone either way, but since I have more Gtk+ experience, it was a natural choice to go with Gtk+ (though someday, after this book ships I imagine, I need to get busy learning Qt - I have a Zaurus Qtopia-based Linux PDA that is just waiting for me to write applications for it). On Windows, the only real choices are Win32, MFC, and .NET Framework. Win32 is clearly sufficient for the job (Mozilla's cross platform toolkit is based upon it), but the API is not modern -- choosing it would be equivalent to going with Carbon (MacOS Toolbox) on MacOS X, or Xlib on Linux. MFC is C++ based, but it is well know that it only provides little more than a wrapper above Win32. MFC shows signs of age as well. And both MFC and Win32 require the use of res files to describe controls and window/dialog layout, which is undesirable. &lt;br /&gt;&lt;br /&gt;Microsoft, fortunately, has recognized the need to upgrade their GUI platform, and so the best option for this project is probably going to be to use their .NET Framework. In .NET there are classes for each of the GUI controls and containers that I have already running on the other platforms, and the syntax looks like it will be a whole lot cleaner to use than anything Microsoft has managed to put out in the past 20 years, at least from what I have seen so far. The only concerns I have are with the .NET CLR (Common Language Runtime), and .NET's permutations on the Standard C++ language. I'm hoping that I can compile native, vs. managed, and I am also hoping I can do this work in standard C++. Although, I suppose having neither would not necessarily be a deal killer. &lt;br /&gt;&lt;br /&gt;To learn more about the .NET framework, the next few weeks will be spent writing "hello world" apps in .NET Framework to get an idea of just what I am signing up for. Then it will be a few hours I suspect of coding to bring the Windows port into existence. Stay tuned...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-113498151486567660?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/113498151486567660/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=113498151486567660' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113498151486567660'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113498151486567660'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2005/12/most-of-past-weekend-and-much-of.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-113486289401155365</id><published>2005-12-17T15:40:00.000-08:00</published><updated>2005-12-18T16:48:53.396-08:00</updated><title type='text'></title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/1570/737/1600/class%20diagram.1.jpg"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/1570/737/320/class%20diagram.1.jpg" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;To the left is a class diagram that illustrates the relationships between the abstract widget interface classes, and the concrete widget implementation classes (only Button is illustrated in this document to keep things simple). Let's zoom in and take a closer look at the components that make up this class diagram.&lt;br /&gt;&lt;br /&gt;At the top of the following figure, we have two classes, Element and Widget. Element represents a DOM entity in the layout engine. As attributes, Elements maintain a parent, a list of child Elements, an associated Document, and a type. In addition, the Element class provides setters and getters allowing the layout engine to access these attributes. Element might have easily had been named Object; the abstract class Object in the Gtk+ class hierarchy is roughly analogous to Element, for example.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/1570/737/1600/elementwidget.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/1570/737/400/elementwidget.jpg" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Inheriting from Element is Widget. A Widget, also represented in the class diagram above, is a base class that represents all things that are related to widgets. Widgets can be shown, hidden, drawn, and created. They can be containers of other widgets (examples include boxes, grids, and so forth), or they can be controls that manage no children, but represent some functionality (menu items, text fields, and buttons, to name but a few). Widgets have some geometry (an x, y position, and a width and height), and several of the interfaces defined by Widget are used by the layout to retrieve the desired geometry of the widget (GetGeometryRequest()) and tell the widget what its final geometry is once it has been computed by the layout engine (much of this was described in an earlier post to this blog). &lt;br /&gt;&lt;br /&gt;So, as mentioned, widgets can be containers. Or they can be controls. Button widgets, which we are currently focusing on, are obviously controls, because they do not manage children, and represent some user interface object that a user directly interacts with. But for a moment, take a look at the following Figure, which illustrates the Widget-&gt;Container-&gt;Box and Widget-&gt;Control inheritance class diagram.&lt;br /&gt; &lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/1570/737/1600/widgetcontrol.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/1570/737/400/widgetcontrol.jpg" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;As you can see, the Container class abstracts things that Widgets managing a set of children must provide. This includes the ability to compute a layout given some region allocated it by a parent (ComputeLayout()), as well as iterate its children and command them to be created, drawn, shown, and hidden. &lt;br /&gt;&lt;br /&gt;Returning to the portion of the class diagram that involves buttons, the next Figure shows the relationship between a Control, the abstract Button class, and the abstract ButtonImpl class.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/1570/737/1600/controlbutton.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/1570/737/400/controlbutton.jpg" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;It's important to note that ButtonImpl does not inherit from Button, rather, a Button manages an instance of a ButtonImpl (in Design Pattern terminology, one calls this relationship between classes a bridge (see GoF, around page 151)). Implementing this as a bridge allows the ButtonImpl hierarchy to exist independently of Buttons; should radical changes in how a button is implemented becomes necessary, I can make those changes without impacting the Widget-&gt;Control-&gt;Button class hierarchy, and vice versa. Bridges also play nicely with the factory concept, allowing me to isolate what a factory provides in an independent class hierarchy. The hook, once again, is in the Button class, which maintains the link between abstraction and implementation, calling whatever ButtonImpl-derived class the widget factory decided to provide.&lt;br /&gt;&lt;br /&gt;The final Figure of this post wraps up our look at the class hierarchy, focusing on the classes GtkWidgetImpl, CocoaWidgetImpl, GtkButtonImpl, and CocoaButtonImpl.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/1570/737/1600/concrete.1.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/1570/737/400/concrete.1.jpg" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;There are a few things that are important to notice in the above class diagram. First of all, GtkButtonImpl and CocoaButtonImpl implement the same interface (inheriting that interface from WidgetImpl, which abstracts all of the things a concrete widget must be, and from ButtonImpl, which isolates the interfaces that are specific to a button and don't belong in a WidgetImpl class, namely SetLabel()). &lt;br /&gt;&lt;br /&gt;Second, and perhaps more suprisingly, both GtkButtonImpl and CocoaButtonImpl also inherit from two mixin classes - GtkWidgetImpl and CocoaWidgetImpl - respectively. What, exactly, is the purpose of these two classes? Well, GtkWidgetImpl and CocoaWidgetImpl implement abstractions that are specific to each platform, but which are common for all concrete widgets that are created on that platform. Be they buttons, menus, text fields, images, or whatever you might envision supporting in the toolkit. Originally, these classes did not exist in my design, but I found that I was duplicating code in all of the CocoaWidgetImpl-derived classes, that this code being duplicated was not portable enough to implement in WidgetImpl, and so I decided to create mixin classes that contained factorizations of the duplicated code. In Gtk+, for example, all widgets must be parented by an instance of GtkFixed, which is the immediate child of GtkWindow in the instance hierarchy and gives the concrete widgets (controls like GtkButton) a place to add themselves and allows for the layout engine to control their positions as it computes the layout of a window. Hence the need for an interface named SetFixedParent(), which allows the widget to store a pointer to the GtkFixed widget instance by querying the abstract WidgetImpl object that is passed as an argument for its concrete implementation pointer. Here is the code that does that:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PRStatus GtkWidgetImpl::SetFixedParent(WidgetImpl *top)&lt;br /&gt;{&lt;br /&gt;    if (top) {&lt;br /&gt;        GtkWidgetImpl *widgetImpl = dynamic_cast&lt;GtkWidgetImpl *&gt;(top);&lt;br /&gt;&lt;br /&gt;        if (widgetImpl) {&lt;br /&gt;            GtkWidget *w = widgetImpl-&gt;GetImpl();&lt;br /&gt;            if (w) {&lt;br /&gt;                m_fixedParent = w;&lt;br /&gt;                return PR_SUCCESS;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    return PR_FAILURE;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In the case of Cocoa, we are required to implement a completely different strategy for parenting a widget. Essentially, this is done by obtaining the NSView pointer associated with the parent widget, and telling the NSButton that is being instantiated, within its Create() function, what this view parent is:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;        m_view = (NSView *) m_button;&lt;br /&gt;&lt;br /&gt;        WidgetImpl *parentImpl = GetParent();&lt;br /&gt;&lt;br /&gt;        if (parentImpl) {&lt;br /&gt;&lt;br /&gt;            NSView *parentView = dynamic_cast&lt;CocoaWidgetImpl *&gt;(parentImpl)-&gt;GetView();&lt;br /&gt;&lt;br /&gt;            // add the button to the parent view&lt;br /&gt;&lt;br /&gt;            if (parentView) {&lt;br /&gt;                [parentView addSubview: m_view];&lt;br /&gt;                return PR_SUCCESS;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To support this, CocoaWidgetImpl implements a pair of functions, GetView() and SetView(), which allow an instance of class inheriting CocoaWidgetImpl to store and retrieve this view pointer, as shown in the above code.&lt;br /&gt;&lt;br /&gt;As another example of the factorizations present in CocoaWidgetImpl and GtkWidgetImpl, compare the two functions CocoaWidgetImpl::SetGeometry() and GtkWidgetImpl::SetGeometry(). Again, remember that these factorizations exist to support all concrete widgets implemented for a specific platform. First up, CocoaWidgetImpl::SetGeometry():&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PRStatus CocoaWidgetImpl::SetGeometry(const int &amp;x, const int &amp;y,&lt;br /&gt;    const int &amp;width, const int &amp;height, const char &amp;mask)&lt;br /&gt;{&lt;br /&gt;    if (m_view) {&lt;br /&gt;        NSRect graphicsRect;&lt;br /&gt;        if (mask == GEOM_ALL)&lt;br /&gt;            graphicsRect = NSMakeRect(x, y, width, height);&lt;br /&gt;        else {&lt;br /&gt;            graphicsRect = [m_view frame];&lt;br /&gt;            if (mask &amp; GEOM_X)&lt;br /&gt;                graphicsRect.origin.x = x;&lt;br /&gt;            if (mask &amp; GEOM_Y)&lt;br /&gt;                graphicsRect.origin.y = y;&lt;br /&gt;            if (mask &amp; GEOM_WIDTH)&lt;br /&gt;                graphicsRect.size.width = width;&lt;br /&gt;            if (mask &amp; GEOM_HEIGHT)&lt;br /&gt;                graphicsRect.size.height = height;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        [m_view setFrame: graphicsRect];&lt;br /&gt;        return PR_SUCCESS;&lt;br /&gt;    }&lt;br /&gt;    return PR_FAILURE;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The above function essentially takes the geometry (x, y, width, height) passed to it and, based on the mask specified by the mask argument, invokes Objective-C code to modify the geometry of the NSView inherited by the (NSButton) widget. &lt;br /&gt;&lt;br /&gt;Now let's look at the GtkWidgetImpl implementation of the same function:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PRStatus GtkWidgetImpl::SetGeometryImpl(const int &amp;x, const int &amp;y,&lt;br /&gt;    const int &amp;width, const int &amp;height, const char &amp;mask)&lt;br /&gt;{&lt;br /&gt;    if (m_widget) {&lt;br /&gt;        // get the current values&lt;br /&gt;&lt;br /&gt;        GtkArg arg;&lt;br /&gt;&lt;br /&gt;        if (mask &amp; GEOM_X) {&lt;br /&gt;            arg.name = "GtkWidget::x";&lt;br /&gt;            arg.type = GTK_TYPE_INT;&lt;br /&gt;            GTK_VALUE_INT(arg) = x;&lt;br /&gt;            gtk_object_arg_set(GTK_OBJECT(m_widget), &amp;arg, NULL);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        if (mask &amp; GEOM_Y) {&lt;br /&gt;            arg.name = "GtkWidget::y";&lt;br /&gt;            arg.type = GTK_TYPE_INT;&lt;br /&gt;            GTK_VALUE_INT(arg) = y;&lt;br /&gt;            gtk_object_arg_set(GTK_OBJECT(m_widget), &amp;arg, NULL);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        if (mask &amp; GEOM_WIDTH) {&lt;br /&gt;            arg.name = "GtkWidget::width";&lt;br /&gt;            arg.type = GTK_TYPE_INT;&lt;br /&gt;            GTK_VALUE_INT(arg) = width;&lt;br /&gt;            gtk_object_arg_set(GTK_OBJECT(m_widget), &amp;arg, NULL);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        if (mask &amp; GEOM_HEIGHT) {&lt;br /&gt;            arg.name = "GtkWidget::height";&lt;br /&gt;            arg.type = GTK_TYPE_INT;&lt;br /&gt;            GTK_VALUE_INT(arg) = height;&lt;br /&gt;            gtk_object_arg_set(GTK_OBJECT(m_widget), &amp;arg, NULL);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        return PR_SUCCESS;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    return PR_FAILURE;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;While the structure of both of these implementations is the same, notice that the mechanisms used are very specific to Gtk+ (namely, C calls to gtk_object_arg_set() for each geometry attribute that is to be modified). Such is the nature of the code found at this level; this is code which is best designed and implemented by platform-savvy developers who are given the basic API via the abstract classes (WidgetImpl, ButtonImpl) to work with, and who are free to create helper classes like CocoaWidgetImpl to do those things that are necessary for only that particular platform.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-113486289401155365?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/113486289401155365/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=113486289401155365' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113486289401155365'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113486289401155365'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2005/12/to-left-is-class-diagram-that.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-113472323080337077</id><published>2005-12-15T23:54:00.000-08:00</published><updated>2005-12-17T17:03:24.090-08:00</updated><title type='text'></title><content type='html'>The port from Cocoa to Gtk+ was pretty straightforward; as I mentioned in a previous post, I was able to pull it off in 10 or so hours of work. &lt;br /&gt;&lt;br /&gt;The first step in performing the port was to create a directory to hold the Gtk+ specific sources. This was a simple matter of making a copy of the cocoa sources, i.e.,:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$ cd layout&lt;br /&gt;$ cp -r cocoa gtk&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The include files that were copied largely remained the same. This was to be expected, and reflects the fact that the code located in these directories provides concrete implementations of abstract classes defined by the layout engine, and as such, must adhere to interfaces defined by the abstract classes. One example is the widget factory code, which provides interfaces on all platforms for creating widgets (buttons, windows, text fields). Let's start by looking at WidgetFactory's definition:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#if !defined(__WIDGETFACTORY__H__)&lt;br /&gt;#define __WIDGETFACTORY__H__&lt;br /&gt;&lt;br /&gt;// abstract class for widget creation factory. For each widget&lt;br /&gt;// we can create, there is a MakeFoo function that returns a&lt;br /&gt;// FooImpl. In each platform widget directory, there is a class&lt;br /&gt;// that inherits from WidgetFactory. Its MakeFoo function&lt;br /&gt;// instantiates a concrete class, e.g., GtkFooImpl, that inherits&lt;br /&gt;// from FooImpl, and represents Foo on that platform.&lt;br /&gt;&lt;br /&gt;// The layout Foo abstract class has a reference to an object that&lt;br /&gt;// belongs to the class FooImpl, which it creates in its constructor.&lt;br /&gt;// Calls to the Foo class are delegated to that object. Likewise,&lt;br /&gt;// the Foo class will be a listener to events that are defined in&lt;br /&gt;// FooImpl and are implemented by the concrete class that inherits&lt;br /&gt;// from FooImpl.&lt;br /&gt;&lt;br /&gt;#include "textimpl.h"&lt;br /&gt;#include "statictextimpl.h"&lt;br /&gt;#include "buttonimpl.h"&lt;br /&gt;#include "windowimpl.h"&lt;br /&gt;#include "boximpl.h"&lt;br /&gt;#include "spacerimpl.h"&lt;br /&gt;#include "appimpl.h"&lt;br /&gt;#include "prtypes.h"&lt;br /&gt;&lt;br /&gt;class WidgetFactory {&lt;br /&gt;public:&lt;br /&gt;    virtual ~WidgetFactory() {};&lt;br /&gt;    virtual AppImpl* MakeApp() = 0;&lt;br /&gt;    virtual ButtonImpl* MakeButton() = 0;&lt;br /&gt;    virtual StaticTextImpl* MakeStaticText() = 0;&lt;br /&gt;    virtual TextImpl* MakeText() = 0;&lt;br /&gt;    virtual WindowImpl* MakeWindow() = 0;&lt;br /&gt;    virtual BoxImpl* MakeBox() = 0;&lt;br /&gt;    virtual SpacerImpl* MakeSpacer() = 0;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;WidgetFactory *GetWidgetFactory();&lt;br /&gt;&lt;br /&gt;#endif&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Notice that all of the functions in WidgetFactory (except for the CTOR and DTOR) are declared pure virtual, forcing implementation by the concrete classes that inherit from WidgetFactory. The only exception to this is the function GetWidgetFactory(), which will be discussed below.&lt;br /&gt;&lt;br /&gt;To gain an understanding of the role of factories in this implementation, let's now look at gtkfactory.h, which provides the concrete, platform-specific implementation of WidgetFactory for Gtk+:&lt;br /&gt;&lt;pre&gt; &lt;br /&gt;&lt;br /&gt;#if !defined(__GTK_FACTORY__H__)&lt;br /&gt;#define __GTK_FACTORY__H__&lt;br /&gt;&lt;br /&gt;#include "../widgetfactory.h"&lt;br /&gt;#include "../textimpl.h"&lt;br /&gt;#include "../statictextimpl.h"&lt;br /&gt;#include "../buttonimpl.h"&lt;br /&gt;#include "../boximpl.h"&lt;br /&gt;#include "../spacerimpl.h"&lt;br /&gt;#include "../windowimpl.h"&lt;br /&gt;#include "../appimpl.h"&lt;br /&gt;#include "prtypes.h"&lt;br /&gt;&lt;br /&gt;class GtkFactory : public WidgetFactory&lt;br /&gt;{&lt;br /&gt;public:&lt;br /&gt;    GtkFactory();&lt;br /&gt;    virtual ~GtkFactory();&lt;br /&gt;    virtual TextImpl *MakeText();&lt;br /&gt;    virtual StaticTextImpl *MakeStaticText();&lt;br /&gt;    virtual ButtonImpl *MakeButton();&lt;br /&gt;    virtual BoxImpl *MakeBox();&lt;br /&gt;    virtual SpacerImpl *MakeSpacer();&lt;br /&gt;    virtual WindowImpl *MakeWindow();&lt;br /&gt;    virtual AppImpl *MakeApp();&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;#endif&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As you can see, Gtk+ is a no-frills implementation of the WidgetFactory interface. The Cocoa implementation is mostly identical except for the names of the class, the CTOR, and the DTOR (e.g., class CocoaFactory, CocoaFactory(), and ~CocoaFactory(), respectively). &lt;br /&gt;&lt;br /&gt;Next, let's take a look at the layout code that instantiates the concrete factory classes GtkWidgetFactory and CocoaWidgetFactory:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class WidgetFactory;&lt;br /&gt;&lt;br /&gt;#if defined(HAVE_MACOS)&lt;br /&gt;#include "cocoa/cocoafactory.h"&lt;br /&gt;#endif&lt;br /&gt;#if defined(HAVE_GTK)&lt;br /&gt;#include "gtk/gtkfactory.h"&lt;br /&gt;#endif&lt;br /&gt;&lt;br /&gt;WidgetFactory *GetWidgetFactory()&lt;br /&gt;{&lt;br /&gt;    static WidgetFactory *widgetFactory = 0;&lt;br /&gt;&lt;br /&gt;    if (!widgetFactory)&lt;br /&gt;#if defined(HAVE_MACOS)&lt;br /&gt;        widgetFactory = new CocoaFactory;&lt;br /&gt;#endif&lt;br /&gt;#if defined(HAVE_GTK)&lt;br /&gt;        widgetFactory = new GtkFactory;&lt;br /&gt;#endif&lt;br /&gt;    return widgetFactory;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The code above supports the creation of an application singleton that provides the concrete implemenation of the abstract WidgetFactory interface used by the layout engine to create buttons and other widgets. The #ifdef's in the code are a reasonable way to ensure that the correct platform implementation is returned by GetWidgetFactory(). Had I named the concrete classes the same, however, these #ifdefs probably would not be needed. Whenever the concrete classes differ in some way in the class definition, then the above pattern would be more appropriate. &lt;br /&gt;&lt;br /&gt;So, given a factory for generating platform-specific widgets, how does one use it? Let's focus on the creation of a button widget. Say that we have some XML markup that includes a &amp;lt;button&amp;gt; tag. When the layout engine parses this XML, it creates a button object, as in the following code taken from layout's parser code:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;static void&lt;br /&gt;StartElement(void *userData, const char *name, const char **atts)&lt;br /&gt;{&lt;br /&gt;    Document *document = (Document *) userData;&lt;br /&gt;    Element *element = NULL;&lt;br /&gt;    Script *script = NULL;&lt;br /&gt;    int i;  &lt;br /&gt;    &lt;br /&gt;    if (!PL_strcasecmp(name, "button")) {&lt;br /&gt;        element = new Button();&lt;br /&gt;        element-&gt;SetType(TYPE_BUTTON);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The Button class implements the generic, platform independent functionality that is needed by the layout engine, and maintains a pointer to the platform-specific class that was provided by the widget factory, calling it as needed from its member functions:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class Button : public Control, public ButtonPressObserver&lt;br /&gt;{&lt;br /&gt;public:&lt;br /&gt;    Button();&lt;br /&gt;    ~Button();&lt;br /&gt;    PRStatus Create();&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;private:&lt;br /&gt;    ButtonImpl *m_button;&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The Button object that is created exists in a DOM maintained by the layout engine. At some point, when the document that was parsed is displayed (e.g., the application loads or opens a window containing the DOM), the DOM iterated, and the Create() function for each element in the DOM is called by the layout engine. Create() in Button is actually the implementation of an interface that is declared in the abstract class Widget, from which Button directly inherits via the Control base class. In any event, Button's Create() is called. It's implementation accesses the concrete implementation of Create() using a pointer to the abstract widget implementation (class ButtonImpl), as follows:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PRStatus Button::Create()&lt;br /&gt;{&lt;br /&gt;    PRStatus status;&lt;br /&gt;&lt;br /&gt;    status = m_button-&gt;Create();&lt;br /&gt;    if (status == PR_SUCCESS) {&lt;br /&gt;        status = SetLabel(GetLabel());&lt;br /&gt;    }&lt;br /&gt;    return status;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now here is where it gets a little tricky. m_button is an instance of ButtonImpl, which inherits from WidgetImpl. WidgetImpl declares Create() as pure virtual:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class WidgetImpl&lt;br /&gt;{&lt;br /&gt;public:&lt;br /&gt;    WidgetImpl() : m_parent(NULL) {};&lt;br /&gt;    virtual ~WidgetImpl() {};&lt;br /&gt;    virtual PRStatus Create() = 0;&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The connection to the concrete classes implemented for Cocoa and Gtk+ is made via the classes that are created by the widget factory. Here again is GtkButton's definition:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#if !defined(__GTKBUTTONIMPL__H__)&lt;br /&gt;#define __GTKBUTTONIMPL__H__&lt;br /&gt;&lt;br /&gt;#include "buttonimpl.h"&lt;br /&gt;#include &lt;gtk/gtk.h&gt;&lt;br /&gt;&lt;br /&gt;#include "gtkwidgetimpl.h"&lt;br /&gt;&lt;br /&gt;#include "commandhandler.h"&lt;br /&gt;&lt;br /&gt;class GtkButtonImpl : public ButtonImpl, public GtkWidgetImpl, public CommandHandler&lt;br /&gt;{&lt;br /&gt;public:&lt;br /&gt;    GtkButtonImpl();&lt;br /&gt;    virtual ~GtkButtonImpl();&lt;br /&gt;    virtual PRStatus Create();&lt;br /&gt;    virtual PRStatus Show();&lt;br /&gt;    virtual PRStatus Hide();&lt;br /&gt;    virtual PRStatus SetLabel(const string&amp; label);&lt;br /&gt;    virtual PRStatus HandleCommand();&lt;br /&gt;    virtual PRStatus SetGeometry(const int &amp;x, const int &amp;y,&lt;br /&gt;        const int &amp;width, const int &amp;height, const char &amp;mask) {&lt;br /&gt;        return SetGeometryImpl(x, y, width, height, mask); };&lt;br /&gt;    virtual PRStatus GetGeometryRequest(int &amp;x, int &amp;y,&lt;br /&gt;        int &amp;width, int &amp;height) {&lt;br /&gt;        return GetGeometryRequestImpl(x, y, width, height); };&lt;br /&gt;private:&lt;br /&gt;};&lt;br /&gt;#endif&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Notice how GtkButtonImpl includes the header buttonimpl.h, and inherits ButtonImpl. This is the hook we are looking for that ties ButtonImpl to GtkButtonImpl; we needed to call a class that implements ButtonImpl, and GtkButtonImpl fills the need.&lt;br /&gt;&lt;br /&gt;One final detail needs to be described. That is, how the factory itself is invoked in order to initialize the m_button member of the abstract Button class. This is done from within the &lt;br /&gt;Button CTOR, as follows:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Button::Button()&lt;br /&gt;{&lt;br /&gt;    WidgetFactory *factory = GetWidgetFactory();&lt;br /&gt;&lt;br /&gt;    if (factory)&lt;br /&gt;        m_button = factory-&gt;MakeButton();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Simply put, an instance of Button gets a pointer to the singleton WidgetFactory instance, and calls its MakeButton() function, and assigns the result to the m_button member variable. Once this has been done, any calls made via m_button will find their way into the concrete class (GtkButtonImpl, or CocoaButtonImpl). &lt;br /&gt;&lt;br /&gt;Abstract class Button's remaining interfaces (e.g., SetLabel()) use a pattern that is similar to Create(). That is, they check to make sure m_button is not null, and then invoke the concrete implementation. For example, a call to Button's SetLabel() function made by the layout engine is handled as follows:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PRStatus Button::SetLabel(string&amp; label)&lt;br /&gt;{&lt;br /&gt;    if (m_button)&lt;br /&gt;        m_button-&gt;SetLabel(label);&lt;br /&gt;    return PR_SUCCESS;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt; &lt;br /&gt;Now that you (hopefully) understand the relationship between the layout engines abstract widget classes, the platform-specific concrete classes, and the factory that is used to tie the two together, let's take a closer look at some of concrete code that is associated with Button's implementation. As you might expect, the interfaces are the same, but the implementations are vastly different. We'll start first with the header files that define the concrete classes, and then look more closely at the Create() function. Here is the header for Cocoa's CocoaButtonImpl class:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#if !defined(__COCOABUTTONIMPL__H__)&lt;br /&gt;#define __COCOABUTTONIMPL__H__&lt;br /&gt;&lt;br /&gt;#include "../buttonimpl.h"&lt;br /&gt;#import &lt;Cocoa/Cocoa.h&gt;&lt;br /&gt;&lt;br /&gt;#include "cocoawidgetimpl.h"&lt;br /&gt;&lt;br /&gt;#include "commandhandler.h"&lt;br /&gt;&lt;br /&gt;class CocoaButtonImpl : public ButtonImpl, public CocoaWidgetImpl, public CommandHandler&lt;br /&gt;{&lt;br /&gt;public:&lt;br /&gt;    CocoaButtonImpl();&lt;br /&gt;    virtual ~CocoaButtonImpl();&lt;br /&gt;    virtual PRStatus Create();&lt;br /&gt;    virtual PRStatus Show();&lt;br /&gt;    virtual PRStatus Hide();&lt;br /&gt;    virtual PRStatus SetLabel(const string&amp; label);&lt;br /&gt;    virtual PRStatus HandleCommand();&lt;br /&gt;    virtual PRStatus GetGeometryRequest(int &amp;x, int &amp;y, int &amp;width, int &amp;height);&lt;br /&gt;    virtual PRStatus SetGeometry(const int &amp;x, const int &amp;y,&lt;br /&gt;        const int &amp;width, const int &amp;height, const char &amp;mask);&lt;br /&gt;private:&lt;br /&gt;    NSButton *m_button;&lt;br /&gt;};&lt;br /&gt;#endif&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And for Gtk+:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#if !defined(__GTKBUTTONIMPL__H__)&lt;br /&gt;#define __GTKBUTTONIMPL__H__&lt;br /&gt;&lt;br /&gt;#include "../buttonimpl.h"&lt;br /&gt;#include &lt;gtk/gtk.h&gt;&lt;br /&gt;&lt;br /&gt;#include "gtkwidgetimpl.h"&lt;br /&gt;&lt;br /&gt;#include "commandhandler.h"&lt;br /&gt;&lt;br /&gt;class GtkButtonImpl : public ButtonImpl, public GtkWidgetImpl, public CommandHandler&lt;br /&gt;{&lt;br /&gt;public:&lt;br /&gt;    GtkButtonImpl();&lt;br /&gt;    virtual ~GtkButtonImpl();&lt;br /&gt;    virtual PRStatus Create();&lt;br /&gt;    virtual PRStatus Show();&lt;br /&gt;    virtual PRStatus Hide();&lt;br /&gt;    virtual PRStatus SetLabel(const string&amp; label);&lt;br /&gt;    virtual PRStatus HandleCommand();&lt;br /&gt;    virtual PRStatus SetGeometry(const int &amp;x, const int &amp;y,&lt;br /&gt;        const int &amp;width, const int &amp;height, const char &amp;mask) {&lt;br /&gt;        return SetGeometryImpl(x, y, width, height, mask); };&lt;br /&gt;    virtual PRStatus GetGeometryRequest(int &amp;x, int &amp;y,&lt;br /&gt;        int &amp;width, int &amp;height) {&lt;br /&gt;        return GetGeometryRequestImpl(x, y, width, height); };&lt;br /&gt;private:&lt;br /&gt;};&lt;br /&gt;#endif&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Notice, other than I decided to implement the bodies of a couple of the member functions directly in the Gtk+ header, that these two concrete implementations are essentially the same, except for the names assigned to the class (and CTOR, etc.). This should be no suprise, since these classes are intended to implement abstract interfaces. &lt;br /&gt;&lt;br /&gt;Now let's take a look at Create(), starting first with Gtk+:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PRStatus GtkButtonImpl::Create()&lt;br /&gt;{&lt;br /&gt;    m_widget = gtk_button_new();&lt;br /&gt;    if (m_widget) {&lt;br /&gt;&lt;br /&gt;        if (!m_fixedParent) {&lt;br /&gt;            WidgetImpl *top = GetRootWidget();&lt;br /&gt;            if (top) {&lt;br /&gt;                SetFixedParent(top);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        if (m_fixedParent) {&lt;br /&gt;            gtk_fixed_put(GTK_FIXED(m_fixedParent), m_widget, 0, 0);&lt;br /&gt;            gtk_signal_connect(GTK_OBJECT(m_widget), "clicked",&lt;br /&gt;                GTK_SIGNAL_FUNC(HandleCommandThunk), this);&lt;br /&gt;            return PR_SUCCESS;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    return PR_FAILURE;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And now for Cocoa:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PRStatus CocoaButtonImpl::Create()&lt;br /&gt;{&lt;br /&gt;    NSRect graphicsRect = NSMakeRect(1.0, 1.0, 1.0, 1.0);&lt;br /&gt;    ButtonAction *action;&lt;br /&gt;&lt;br /&gt;    action = [ButtonAction alloc];&lt;br /&gt;&lt;br /&gt;    [action setHandler: this];&lt;br /&gt;    // create the button&lt;br /&gt;&lt;br /&gt;    m_button = [[NSButton alloc] initWithFrame:graphicsRect];&lt;br /&gt;&lt;br /&gt;    if (m_button) {&lt;br /&gt;&lt;br /&gt;        [m_button setBezelStyle: NSRoundedBezelStyle];&lt;br /&gt;        [m_button setTarget:action];&lt;br /&gt;        [m_button setAction:@selector(onClick:)];&lt;br /&gt;        m_view = (NSView *) m_button;&lt;br /&gt;&lt;br /&gt;        WidgetImpl *parentImpl = GetParent();&lt;br /&gt;&lt;br /&gt;        if (parentImpl) {&lt;br /&gt;&lt;br /&gt;            NSView *parentView = dynamic_cast&lt;CocoaWidgetImpl *&gt;(parentImpl)-&gt;GetView();&lt;br /&gt;&lt;br /&gt;            // add the button to the parent view&lt;br /&gt;&lt;br /&gt;            if (parentView) {&lt;br /&gt;                [parentView addSubview: m_view];&lt;br /&gt;                return PR_SUCCESS;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    return PR_FAILURE;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Other than the function prototype (i.e., arguments and return value), there are huge differences evident in the implementation of these two classes. Most obvious is the fact that the Gtk+ code is written in C++, while the Cocoa code is written in Objective-C++. Beyond that, the other major differences are in how the Cocoa button is parented (made a subview of the view maintained by the button's parent in the DOM) and how Gtk+ is parented (made a child of an instance of the GtkFixed widget, which is in turn parented by the toplevel window within which the button exists). The point is that beyond the requirement that the interface to and return value from Create() adhere to the conventions forced upon it by layout, there are absolutely no rules telling you what you can or cannot do when it comes to implementing that interface; you are free to do whatever is needed.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-113472323080337077?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/113472323080337077/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=113472323080337077' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113472323080337077'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113472323080337077'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2005/12/port-from-cocoa-to-gtk-was-pretty.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-113447898412283855</id><published>2005-12-13T05:00:00.000-08:00</published><updated>2005-12-13T05:15:41.186-08:00</updated><title type='text'></title><content type='html'>After maybe 10 hours of work, I now have a port of my current progress to Gtk+. This time included creating Makefiles, and writing all of the concrete Gtk+ classes from scratch. This is a very important milestone because it illustrates just how portable the core architecture is. Compared to the amount of effort that it took getting the first implementation of where I am at this point running on Cocoa, 10 hours is practically nothing, and is exactly the desired result for constructing a toolkit in this manner. This doesn't mean Cocoa or MacOS X was more difficult to develop for, it's just that by isolating the platform specifics as I have, my job in going to a new platform is dramatically easier (I expect the same amount of effort in going to Win32, though I will have to do a little more research into my choice of concrete toolkits -- Win32? Something else more .NET-ish? My guess is Win32 is the right choice, but we shall see. I'll supply more details regarding what I had to do to get a Gtk+ port in a later post. But for now, here is a screen shot:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/1570/737/1600/gtk.png"&gt;&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/1570/737/320/gtk.jpg" border="0" alt="" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-113447898412283855?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/113447898412283855/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=113447898412283855' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113447898412283855'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113447898412283855'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2005/12/after-maybe-10-hours-of-work-i-now.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-113367806017471444</id><published>2005-12-03T22:32:00.000-08:00</published><updated>2005-12-04T18:53:01.860-08:00</updated><title type='text'></title><content type='html'>After a couple of weeks of (part-time) work, spacers have been added as a widget and incorporated into the layout engine. The following screen shots show examples of a complex layout with spacers after various resizes (note, the vertical orientation of these windows is inverted, due to Cocoa -- window origins are at the lower left corner, not the upper left corner as in many other toolkits. This is something the layout engine needs to deal with and I had yanked support for it while debugging to simplify things):&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/1570/737/1600/Picture%202.png"&gt;&lt;img style=" margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/1570/737/320/Picture%202.jpg" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/1570/737/1600/Picture%203.png"&gt;&lt;img style=" margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/1570/737/320/Picture%203.jpg" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/1570/737/1600/Picture%204.png"&gt;&lt;img style=" margin:0 10px 10px 0;cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/1570/737/320/Picture%204.jpg" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The widget hierarchy for this window is:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;root&lt;br /&gt;window&lt;br /&gt;  button&lt;br /&gt;  spacer&lt;br /&gt;  vertical box&lt;br /&gt;    spacer&lt;br /&gt;    text&lt;br /&gt;    spacer&lt;br /&gt;    text&lt;br /&gt;    spacer&lt;br /&gt;    text&lt;br /&gt;    spacer&lt;br /&gt;    horizontal box&lt;br /&gt;      spacer&lt;br /&gt;      button&lt;br /&gt;      spacer&lt;br /&gt;      button&lt;br /&gt;      spacer&lt;br /&gt;      vertical box&lt;br /&gt;        spacer&lt;br /&gt;        button&lt;br /&gt;        spacer&lt;br /&gt;        button&lt;br /&gt;        spacer&lt;br /&gt;      spacer&lt;br /&gt;    spacer&lt;br /&gt;  spacer&lt;br /&gt;  vertical box&lt;br /&gt;    spacer&lt;br /&gt;    text&lt;br /&gt;    spacer&lt;br /&gt;    text&lt;br /&gt;    spacer&lt;br /&gt;    text&lt;br /&gt;    spacer&lt;br /&gt;    horizontal box&lt;br /&gt;      spacer&lt;br /&gt;      button&lt;br /&gt;      spacer&lt;br /&gt;      button&lt;br /&gt;      spacer&lt;br /&gt;      vertical box&lt;br /&gt;        spacer&lt;br /&gt;        button&lt;br /&gt;        spacer&lt;br /&gt;        button&lt;br /&gt;        spacer&lt;br /&gt;      spacer&lt;br /&gt;    spacer&lt;br /&gt;  spacer&lt;br /&gt;  button&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In short, the window contains two vertical box children, each which contains an identical set of child widgets. These widgets include a few text fields, and a horizontal box. the horizontal box contains three children of its own: two buttons, and a vertical box which itself contains two child buttons.&lt;br /&gt;&lt;br /&gt;Between all these widgets are spacers.&lt;br /&gt;&lt;br /&gt;As the window is resized (including when it is first displayed), an event is triggered in the concrete widget's window class. This event is being listened to by an abstract window class. The abstract window class then invokes a function that it implements named ComputeLayout(), passing to it the width and height of the window. This size, plus the layout of the document below the window, and the size needs of the widgets it contains, will determine the ultimate layout of the window. This layout is computed, recursively, by calls to ComputeLayout() (for container widgets), and to GetGeometryRequest(), which is implemented by each widget that exists in the widget hierarchy of the window.&lt;br /&gt;&lt;br /&gt;Leaf widgets (e.g., those that do not contain other children), simply report their absolute size requirement from within GetGeometryRequest(). For example, the abstract text widget's GetGeometryRequest() function invokes the concrete text widget's implementation of GetGeometryRequest(), which will use the size of the font, and the size of the text being displayed, to determine the size request that is returned.&lt;br /&gt;&lt;br /&gt;It is in the ComputeLayout() functions implemented by Box:: and Window:: that all of the important work is done. Let's take a closer look at these functions, starting with the simpler of the two, Window::ComputeLayout(), since it only lays its children out vertically. The Box::ComputeLayout() function must layout vertically or horizontally, depending upon the orientation of the box.&lt;br /&gt;&lt;br /&gt;Here is the source for Window::ComputeLayout():&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PRStatus Window::ComputeLayout(const int &amp;x, const int &amp;y, const int &amp;width,&lt;br /&gt;    const int &amp;height)&lt;br /&gt;{&lt;br /&gt;&lt;br /&gt;    list&lt;Element *&gt;::iterator itr;&lt;br /&gt;    int widthacc = 0, heightacc = 0;&lt;br /&gt;&lt;br /&gt;    int childx, childy;&lt;br /&gt;    int childwidth, childheight, childwidth2, childheight2;&lt;br /&gt;    int spacerdenom = 0;&lt;br /&gt;    int winx, winy, winwidth, winheight;&lt;br /&gt;    int vboxcount;&lt;br /&gt;&lt;br /&gt;    GetGeometry(winx, winy, winwidth, winheight);&lt;br /&gt;&lt;br /&gt;    // figure out what size the children want to be&lt;br /&gt;&lt;br /&gt;    GetGeometryRequest(childx, childy, childwidth, childheight, true, false);&lt;br /&gt;&lt;br /&gt;    // figure out the spacer relative sizes&lt;br /&gt;&lt;br /&gt;    for (itr = m_children.begin(); itr != m_children.end(); ++itr) {&lt;br /&gt;        Widget *w = reinterpret_cast&lt;Widget *&gt;(*itr);&lt;br /&gt;        if (w) {&lt;br /&gt;            if (w-&gt;IsSpacer()) {&lt;br /&gt;                Spacer *spacer = reinterpret_cast&lt;Spacer *&gt;(w);&lt;br /&gt;                spacerdenom += spacer-&gt;GetSpacerValue();&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // allocate the spacers, dividing up the remaining width and height&lt;br /&gt;&lt;br /&gt;    int remainingwidth = winwidth - childwidth;&lt;br /&gt;    int remainingheight = winheight - childheight;&lt;br /&gt;&lt;br /&gt;    if (remainingheight &lt; 0)&lt;br /&gt;        remainingheight = 0;&lt;br /&gt;    if (remainingwidth &lt; 0)&lt;br /&gt;        remainingwidth = 0;&lt;br /&gt;&lt;br /&gt;    for (itr = m_children.begin(); itr != m_children.end(); ++itr) {&lt;br /&gt;        Widget *w = reinterpret_cast&lt;Widget *&gt;(*itr);&lt;br /&gt;        if (w) {&lt;br /&gt;            if (w-&gt;IsSpacer()) {&lt;br /&gt;                Spacer *spacer = reinterpret_cast&lt;Spacer *&gt;(w);&lt;br /&gt;                int spacerval = spacer-&gt;GetSpacerValue();&lt;br /&gt;                int spacerwidth = (int)(remainingwidth * ((double)spacerval/spacerdenom));&lt;br /&gt;                int spacerheight = (int)(remainingheight * ((double)spacerval/spacerdenom));&lt;br /&gt;                w-&gt;SetGeometry(0, 0, spacerwidth, spacerheight, GEOM_ALL);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    GetGeometryRequest(childx, childy, childwidth2, childheight2, false, true);&lt;br /&gt;    GetSiblingVboxCount(vboxcount); // share equally among all vertical children&lt;br /&gt;&lt;br /&gt;    int remainingwidth2 = (winwidth - childwidth2) / vboxcount;&lt;br /&gt;    int remainingheight2 = (winheight - childheight2) / vboxcount;&lt;br /&gt;&lt;br /&gt;    if (remainingheight2 &lt; 0)&lt;br /&gt;        remainingheight2 = 0;&lt;br /&gt;    if (remainingwidth2 &lt; 0)&lt;br /&gt;        remainingwidth2 = 0;&lt;br /&gt;&lt;br /&gt;    for (itr = m_children.begin(); itr != m_children.end(); ++itr) {&lt;br /&gt;        Widget *w = reinterpret_cast&lt;Widget *&gt;(*itr);&lt;br /&gt;        if (w) {&lt;br /&gt;            int tmpx, tmpy;&lt;br /&gt;            w-&gt;GetGeometryRequest(childx, childy, childwidth, childheight, true, false);&lt;br /&gt;            if (w-&gt;IsContainer()) {&lt;br /&gt;                tmpx = 0;&lt;br /&gt;                tmpy = heightacc;&lt;br /&gt;&lt;br /&gt;                Container *c = reinterpret_cast&lt;Container *&gt;(w);&lt;br /&gt;                bool isBox = (w-&gt;GetType() == TYPE_BOX);&lt;br /&gt;                bool isVbox = false;&lt;br /&gt;                if (isBox) {&lt;br /&gt;                    Box::BoxOrientation orient;&lt;br /&gt;                    Box *box;&lt;br /&gt;                    box = reinterpret_cast&lt;Box *&gt;(c);&lt;br /&gt;                    if (box) {&lt;br /&gt;                        box-&gt;GetOrientation(orient);&lt;br /&gt;                        if (orient == Box::Vertical)&lt;br /&gt;                            isVbox = true;&lt;br /&gt;                    }&lt;br /&gt;                }&lt;br /&gt;                c-&gt;ComputeLayout(tmpx, tmpy, winwidth, remainingheight2);&lt;br /&gt;                w-&gt;GetGeometry(childx, childy, childwidth, childheight);&lt;br /&gt;                if (childwidth &gt; widthacc)&lt;br /&gt;                    widthacc = childwidth;&lt;br /&gt;                heightacc += childheight;&lt;br /&gt;            }&lt;br /&gt;            else {&lt;br /&gt;&lt;br /&gt;                w-&gt;SetGeometry(0, heightacc,&lt;br /&gt;                    childwidth, childheight, GEOM_ALL);&lt;br /&gt;                if (childwidth &gt; widthacc)&lt;br /&gt;                    widthacc = childwidth;&lt;br /&gt;                heightacc += childheight;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    return PR_SUCCESS;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Breaking this down a bit, the function does the following:&lt;br /&gt;&lt;br /&gt;-- Get the size of the window. This was stored in the Window class instance when the window was created (or when it was resized, depending on what triggered ComputeLayout() to be invoked. This is done with a call to GetGeometry(), which every widget (container or not) implements.&lt;br /&gt;&lt;br /&gt;-- Determine the intrinsic size requirements of the window's children, including any embedded boxes it contains. This computation omits the size requirements of spacers, which are not known at this time (and are computed later).&lt;br /&gt;&lt;br /&gt;-- For all spacers that are immediate children of the window (if there are any), compute a weight relative to the other spacers that are also immediate children. This weight begins, in markup, as an integer valued attribute of the spacer. The sum of these attributes is computed. Then, a percentage is computed for each spacer by dividing the value of its weight attribute by this sum. An example will help understand this better. Assume that the window contains two button, and three spacers, arranged as follows:&lt;br /&gt;&lt;br /&gt;spacer weight = "1"&lt;br /&gt;button&lt;br /&gt;spacer weight = "2"&lt;br /&gt;button&lt;br /&gt;spacer weight = "3"&lt;br /&gt;&lt;br /&gt;The sum of the spacer's weights is 6. The percentage amount of vertical space allocated to each spacer is:&lt;br /&gt;&lt;br /&gt;spacer 0.167&lt;br /&gt;button&lt;br /&gt;spacer 0.333&lt;br /&gt;button&lt;br /&gt;spacer 0.5&lt;br /&gt;&lt;br /&gt;The first spacer this gets 1/6th of the whatever vertical space is not used by the buttons in the window, the second spacer gets a third of this space, and finally, the last spacer gets half of the remaining space.&lt;br /&gt;&lt;br /&gt;-- The remaining width and remaining height values are computed by subtracting the child widget size requests from the width and height of the window (which were passed as arguments to ComputeLayout()).&lt;br /&gt;&lt;br /&gt;-- The spacers are assigned their vertical heights.&lt;br /&gt;&lt;br /&gt;-- Now that the spacers have been assigned their sizes, and now that we know the size requirements of the window's other children, we can iterate the entire list of children (spacers, child containers, buttons -- whatever) and generate a layout. &lt;br /&gt;&lt;br /&gt;If the child widget is not a container, then this is relatively straightforward: call the widget's GetGeometryRequest() function to get its desired width and height, and then call the widget's SetGeometry() member. Each widget is given its requested width and height, and its x coordinate is set to zero. The y coordinate is set to the accumulated height of all children that came before it in the window. &lt;br /&gt;&lt;br /&gt;If, however, the child is a container (e.g., a box), we call its ComputeLayout() function, which performs the same algorithm being described here to layout its children. We pass to ComputeLayout() zero as the x coordinate, the accumulated height as the y coordinate, and restrict its width to the width of the window, and its height to a subset of the vertical space remaining in the window. Once ComputeLayout() returns, we query the geometry of the container by calling its GetGeometry() member function, and add its height to the accumulated y coordinate we are maintaining. &lt;br /&gt;&lt;br /&gt;Once all the children have been layed out, the window's geometry is set with a call to its SetGeometry() member function. &lt;br /&gt;&lt;br /&gt;To help make better sense of all of this, here is the basic algorithm:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;heightacc = 0;&lt;br /&gt;widthacc = 0;&lt;br /&gt;while (there are children) {&lt;br /&gt;    GetGeometryRequest(x, y, widthrequest, heightrequest);&lt;br /&gt;    if (child is a container) {&lt;br /&gt;        ComputeLayout(0, heightacc, windowwidth, remainingheight);&lt;br /&gt;        GetGeometry(x, y, width, height);&lt;br /&gt;        heightacc += height;&lt;br /&gt;        widthacc += width;&lt;br /&gt;    } else {&lt;br /&gt;        SetGeometry(0, heightacc, widthrequest, heightrequest);&lt;br /&gt;        heightacc += heightrequest;&lt;br /&gt;        widthacc += widthrequest;&lt;br /&gt;    }&lt;br /&gt;} &lt;br /&gt;&lt;br /&gt;// x and y in the following call are the original coordinates for the window&lt;br /&gt;&lt;br /&gt;SetGeometry(x, y, widthacc, heightacc);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;One attribute of above algorithm is that the first vertically oriented container widget (window or vertical box) in the window's widget hierarchy that contains a spacer will allocate all of the vertical space that is not allocated to widgets. This is an attribute of the topdown nature by which calls to ComputeLayout() are made to containers in the window. A bottom up visitation would, naturally enough, reverse this, with the lowest-level container containing spacers using all the remaining space in the window. There is probably some modification to either approach that might distribute the remaining space more equitably among containers at all levels of the widget hierarchy of the window. However, this is an exercise for the reader :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-113367806017471444?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/113367806017471444/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=113367806017471444' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113367806017471444'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113367806017471444'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2005/12/after-couple-of-weeks-of-part-time.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-113193715725460715</id><published>2005-11-13T17:55:00.000-08:00</published><updated>2005-11-13T21:52:14.466-08:00</updated><title type='text'></title><content type='html'>I now have basic box support working in the layout engine on MacOS X.&lt;br /&gt;&lt;br /&gt;The following XML illustrates a layout that is based on boxes:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;#60;window name="main" title="&amp;hello.title;" main="true" width="320" height="200" x="100" y="100" position="center, mouse"&amp;#62;&lt;br /&gt;    &amp;#60;script type="text/javascript" src="resources/content/simple.js"/&amp;#62;&lt;br /&gt;    &amp;#60;box orient="vertical"&amp;#62;&lt;br /&gt;        &amp;#60;text editable="true" selectable="true" string="&amp;text1.value;" width="100" height="40"/&amp;#62;&lt;br /&gt;        &amp;#60;text editable="false" selectable="false" string="&amp;text2.value;" width="100" height="30"/&amp;#62;&lt;br /&gt;        &amp;#60;statictext string="&amp;statictext.value;" width="100" height="40"/&amp;#62;&lt;br /&gt;        &amp;#60;box orient="horizontal"&amp;#62;&lt;br /&gt;            &amp;#60;button onclick="return Button1Click();" label="&amp;button1.label;"/&amp;#62;&lt;br /&gt;            &amp;#60;button onclick="return Button2Click();" label="&amp;button2.label;"/&amp;#62;&lt;br /&gt;        &amp;#60;/box&amp;#62;&lt;br /&gt;&amp;#60;/window&amp;#62;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;There are two boxes in this UI, one vertical, and one horizontal. Actually, if you count the window tag, there are three boxes (the window tag has layout semantics similar to a vertical box). The vertical box has 4 children, from top to bottom: an editable text field, a non-editable text field, a static text field, and a horizontal box. The horizontal box has two children, both which are buttons. The resulting UI looks like the following:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/1570/737/1600/Picture%201.0.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://photos1.blogger.com/blogger/1570/737/400/Picture%201.jpg" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The basic strategy to implement boxes was to create a class named box that inherits from Container:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#if !defined(__CONTAINER__H__)&lt;br /&gt;#define __CONTAINER__H__&lt;br /&gt;&lt;br /&gt;#include "widget.h"&lt;br /&gt;&lt;br /&gt;class Container : public Widget&lt;br /&gt;{&lt;br /&gt;public:&lt;br /&gt;    virtual ~Container() {};&lt;br /&gt;    virtual PRStatus CreateChildren();&lt;br /&gt;    virtual PRStatus DrawChildren();&lt;br /&gt;    virtual PRStatus ShowChildren();&lt;br /&gt;    virtual PRStatus HideChildren();&lt;br /&gt;private:&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;#endif&lt;br /&gt;&lt;br /&gt;#if !defined(__BOX__H__)&lt;br /&gt;#define __BOX__H__&lt;br /&gt;&lt;br /&gt;#include "container.h"&lt;br /&gt;#include "boximpl.h"&lt;br /&gt;#include "widgetfactory.h"&lt;br /&gt;&lt;br /&gt;class  Box : public Container&lt;br /&gt;{&lt;br /&gt;public:&lt;br /&gt;&lt;br /&gt;    typedef enum {&lt;br /&gt;        Unknown,&lt;br /&gt;        Horizontal,&lt;br /&gt;        Vertical&lt;br /&gt;    } BoxOrientation;&lt;br /&gt;&lt;br /&gt;    Box();&lt;br /&gt;    virtual ~Box();&lt;br /&gt;    PRStatus Draw();&lt;br /&gt;    PRStatus Create();&lt;br /&gt;    PRStatus Show();&lt;br /&gt;    PRStatus Hide();&lt;br /&gt;    PRStatus ComputeLayout(const int &amp;base,&lt;br /&gt;        const int &amp;offsetx, const int &amp;offsety,&lt;br /&gt;        int &amp;x, int &amp;y, int &amp;width, int &amp;height);&lt;br /&gt;    PRStatus GetGeometry(int &amp;x, int &amp;y, int &amp;width, int &amp;height);&lt;br /&gt;    PRStatus SetGeometry(const int &amp;x, const int &amp;y,&lt;br /&gt;        const int &amp;width, const int &amp;height, const char &amp;mask);&lt;br /&gt;    ElementType GetType() {return TYPE_BOX;};&lt;br /&gt;    bool IsContainer() {return true;};&lt;br /&gt;    WidgetImpl *GetImpl() {return m_box;};&lt;br /&gt;&lt;br /&gt;    PRStatus GetOrientation(BoxOrientation &amp;orient);&lt;br /&gt;    PRStatus SetOrientation(const BoxOrientation orient);&lt;br /&gt;&lt;br /&gt;private:&lt;br /&gt;    BoxImpl *m_box;&lt;br /&gt;    BoxOrientation m_orient;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;#endif&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Containers are what you might expect: in addition to being widgets, they have children (themselves widgets) that need to be created, shown, hidden, and drawn. In addition, boxes must compute a layout, and they have an orientation - vertical boxes arrange their children from top to bottom, while horizontal boxes arrange their children from left to right.&lt;br /&gt;&lt;br /&gt;Almost every UI toolkit involves the following steps to create and show a UI:&lt;br /&gt;&lt;br /&gt;-- create the widgets, assigning each a size and location within the window&lt;br /&gt;-- make the widgets visible&lt;br /&gt;&lt;br /&gt;With a static layout engine, the XML describing the layout specifies the x and y coordinates of each widget, and when the UI is created, the coordinates that were specified in markup for each widget are used to set their locations before they are made visible on the screen. &lt;br /&gt;&lt;br /&gt;On the other hand, an engine supporting dynamic layout creates widgets without regard to their final locations; at the point of creation, all that is important is that the internal representation of the widget elements preserves 1) the parent/child relationships that were specified in markup and 2) the relative ordering of the children with containers (e.g., vertically and horizontally). Once the widgets are created, the layout engine then computes the layout of the widgets in the window in a top-down, somewhat recursive fashion. Once the layout is computed, the layout engine then instructs each widget to show itself (this also happens in a top-down, recursive fashion). It is the computation of the layout that is the most interesting aspect of the layout engine's work. Let's take a look at how it is done in the simplest of cases.&lt;br /&gt;&lt;br /&gt;Each widget has a size requirement. A static text field, for example, must be large enough to display its label. The process of computing a layout includes querying  each widget to determine how much real estate is needed in order for that widget to display itself completely. This computation is done in a topdown fashion for all widgets in the window, including containers. In the layout above, the window widget calls the outermost vertical box widget, asking it to compute its layout. The vertical box widget, in turn, asks each of its children to compute their layouts. In the case of the text widgets and the static text widget, computing a layout is done by determining its basic size (the height of its font determines the vertical space requirement, while the width of the font mulitplied by the number of characters in its label determines its horizontal space requirement). In the case of the horizontal box, its layout is computed by calling each of its button children to determine their size needs (which in turn are a function of the width and height of their labels), and then using this information to compute the size of the horizontal box, the height of which is the greater height of the two buttons, and the width of which is the sum of the widths that were computed by each of the buttons.&lt;br /&gt;&lt;br /&gt;Once the above sizes are computed, widgets are then told by containers to reposition themselves based on the computed layout. When all the widgets in the window have been given their new sizes, the layout engine then iterates the widget hierarchy once more, and tells each widget to display itself.&lt;br /&gt;&lt;br /&gt;Should the user resize a window containing a dynamic layout, the same process may repeat itself. Depending on the location of the window origin, recomputing a layout may be needed. Under Cocoa, the origin of a window is at the lower left hand corner of the window as viewed by the user. Because of this, changing the height of a window requires a recomputation of the location of each widget because the layout is specified in markup as though the origin was actually located at the upper left hand corner of the window. But even if that were not the case, other characteristics of a layout may require recomputation in both the horizontal and vertical directions. One example would be a container that is designed to give each child an equal (or homogeneous) amount of space. As the amount of space changes in a container, the amount of space available to each widget must also change, forcing a recomputation of the layout. At this point, the layout engine does not support this sort of mode; recomputation is done only to ensure that the widgets position relative to 0,0 is consistent when the height of the window changes.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-113193715725460715?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/113193715725460715/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=113193715725460715' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113193715725460715'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113193715725460715'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2005/11/i-now-have-basic-box-support-working.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-113005334190015933</id><published>2005-10-23T00:21:00.001-07:00</published><updated>2005-11-13T17:54:44.120-08:00</updated><title type='text'></title><content type='html'>It's been a while since I posted here. So, time to get caught up on what I have been doing in the recent past, and plan to do in the not too distant future.&lt;br /&gt;&lt;br /&gt;Progress has been slow, about all I have done in the past 8 weeks is implement static text widgets, recode the editable text fields, and do some refactoring to support container widgets more correctly.&lt;br /&gt;&lt;br /&gt;I'm currently implementing box and spacer widgets. Up to now, each widget/control I support has had to specify its own geometry (x, y, width, height) in markup. This can be a workable solution, but it has two problems. &lt;br /&gt;&lt;br /&gt;First, it makes things harder on developers to come up with a nicely layed out user interface. By requiring developers to specify the x, y coordinates and dimensions of the elements that make up their UIs, much trial and error is usually involved in coming up with a suitable layout for a window or dialog. &lt;br /&gt;&lt;br /&gt;Second, and more importantly, statically defined UIs are not adaptive. You might be asking, "adaptive to what?" Good question. For one, to changing requirements. Add a button, or any other control to a dialog, and you, as the developer, will likely have to go back to your code and reassign new positions and dimensions to existing controls to maintain a decent looking user interface. What a pain.&lt;br /&gt;&lt;br /&gt;Layouts often need to be adaptive to runtime state changes, too, and this is what really makes dynamic layout a must-have in modern UIs. Examples: font sizes may change, requiring controls to use more (or less) real estate in a window, effecting the layout of other controls. Or, due to program state, controls may be hidden or removed completely (or, conversely, show or added). Or, the user may simply want to resize a window, and it is desireable for the UI to make best use of the available space.&lt;br /&gt;&lt;br /&gt;Layout widgets like boxes, spacers, and grids are missing from the core legacy toolkits on the platforms we care about in this book: Win32, Xlib, and legacy MacOS Toolbox all pre-date the concept. MFC and Cocoa (if you are writing the code by hand), which are higher level and sit above their respective core GUI toolkits, also do not support dynamic layouts. Toolkits that do support dynamic layout include Qt, Gtk+, wxWidgets (and there are surely others). &lt;br /&gt;&lt;br /&gt;There are a couple of ways a layout engine can implement dynamic layout. If the layout engine's widgets are layered above a native toolkit that implements a dynamic layout, then concrete platform implementations can be used. This is a good choice for the Linux version of my layout engine, because I am layering above Gtk+. On MacOS X and Windows, however, it makes more sense to implement the semantics of boxes, grids, and spacers locally. Of course, once a toolkit has the generic support, there is little need to rely on concrete toolkit implementations to fall back on. This means in my case, all of the container objects will be implemented by me.&lt;br /&gt;&lt;br /&gt;It would probably help for me to describe what boxes, grids, and spacers are. In a nutshell, a box is a container widget that arranges its children either vertically (vertical boxes, or vboxes) or horizontally (horizontal boxes, or hboxes). As children are added, they are packed into the space owned by the box, how they are packed (left to right, for example) depends on what kind of box they are. One commonly sees horizontal boxes used to aggregate buttons (e.g., Ok, Cancel) at the bottom of a dialog. A wide variety of window layouts can be achieved simply thru the use of boxes (in concert with spacers, described below). &lt;br /&gt;&lt;br /&gt;Grids, on the other hand, are very similar to tables in HTML, resembling a spreadsheet, if you will. A grid consists of rows and columns of cells. Each cell houses a child widget, and each child is typically given an equal amount of space within which to display itself. Grids are important, no doubt, but less commonly used  that are boxes.&lt;br /&gt;&lt;br /&gt;The final widget, the spacer, provides a very important role. Spacers are fillers that exist between widgets in a box. As the size of the box expands (or contracts), spacers change their size in order to fill in the space that remains. As an example, consider a dialog with a horizontal box that contains two buttons, Ok and Cancel. The UI designer of the project would like the two buttons arranged such that they always are always centered in the dialog regardless of the dialog size. By adding a spacer to the left of the Ok button, and another to the right of the Cancel button, and by instructing each spacer to take up half of the space that remains in the box, we can achieve this effect. &lt;br /&gt;&lt;br /&gt;A drawing would make this more clear perhaps. And I will provide one (or more) at a later time. &lt;br /&gt;&lt;br /&gt;More will be posted when I get a decent implementation of boxes and spacers implemented in my toolkit (I'll probably leave grids as an exercise to the reader).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-113005334190015933?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/113005334190015933/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=113005334190015933' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113005334190015933'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/113005334190015933'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2005/10/its-been-while-since-i-posted-here_23.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-112599102428113303</id><published>2005-09-06T00:09:00.000-07:00</published><updated>2005-09-06T01:27:13.783-07:00</updated><title type='text'></title><content type='html'>Some interesting output:&lt;br /&gt;&lt;br /&gt;GetComponent: found a component object!&lt;br /&gt;94981D9E-FC99-11D9-8BDE-F66BAD1E3F3A&lt;br /&gt;Button1Click got the component&lt;br /&gt;Able to create a JS function for Hello!!!&lt;br /&gt;*****Created the JS object for GUUID 2ABDF00B-025E-11DA-BFFE-000A27942344&lt;br /&gt;2ABDF00B-025E-11DA-BFFE-000A27942344&lt;br /&gt;Button1Click got the object&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;******Hello from HelloClass C++ &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;JavaScript is now calling into my C++ component. Yay!&lt;br /&gt;&lt;br /&gt;However, I ran into some JavaScript engine crashes when trying to call a function not exported from the component; this illuminated the need to make changes to how I was allocating certain JS engine data structures and so forth. Thanks to Brendan Eich and John Bandhauer for pointing out the issues. Here was the original post, and their responses:&lt;br /&gt;&lt;br /&gt;Me:&lt;br /&gt;&lt;br /&gt;I'm working on code that, like xpconnect, bridges JavaScript code and and C++ code found in a shared library component. In doing so, JavaScript code makes a call to obtain an object for a C++ class it is interested in using, and then calls functions on it. For example:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;componentmgr.getComponent("94981D9E-FC99-11D9-8BDE-F66BAD1E3F3A");&lt;br /&gt;function Button1Click()&lt;br /&gt;{&lt;br /&gt;    dump("Inside of Button1Click\n");&lt;br /&gt;    dump(170 + "\n");&lt;br /&gt;&lt;br /&gt;    var component = componentmgr.getComponent("94981D9E-FC99-11D9-8BDE-F66BAD1E3F3A");&lt;br /&gt;    if (component) {&lt;br /&gt;        dump("Button1Click got the component\n");&lt;br /&gt;        var anobject = component.getObject("2ABDF00B-025E-11DA-BFFE-000A27942344");&lt;br /&gt;&lt;br /&gt;        if (anobject) {&lt;br /&gt;            dump("Button1Click got the object\n");&lt;br /&gt;            anobject.Hello("Syd"); // a function that does exist&lt;br /&gt;            anobject.Foobar();     // a function that doesn't exist, crash&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;To handle the request by the above code for a class object, a JavaScript object is created and populated with pointers to extern "C" functions that wrap the object, using JS_DefineFunction(). This JavaScript object is then returned to the caller. Here's the relevant code:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    JSClass objectClass = {&lt;br /&gt;        name.c_str(), JSCLASS_NEW_RESOLVE,&lt;br /&gt;        JS_PropertyStub,  JS_PropertyStub,&lt;br /&gt;        JS_PropertyStub,  JS_PropertyStub,&lt;br /&gt;        JS_EnumerateStub,  JS_ResolveStub,&lt;br /&gt;        JS_ConvertStub,   JS_FinalizeStub&lt;br /&gt;    };&lt;br /&gt;&lt;br /&gt;    m_jsObj = JS_DefineObject(ctx, obj, name.c_str(),&lt;br /&gt;        &amp;objectClass, NULL, 0);&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;    list &lt;Arg *&gt; args = func-&gt;GetArgumentList();&lt;br /&gt;    int size = args.size();&lt;br /&gt;&lt;br /&gt;    // query the component for the proxy lib entry point&lt;br /&gt;&lt;br /&gt;    JSNative fptr = (JSNative) lib-&gt;FindSymbol(name2);&lt;br /&gt;&lt;br /&gt;    if (fptr) {&lt;br /&gt;        JSFunction *func = JS_DefineFunction(ctx, m_jsObj,&lt;br /&gt;            fnName.c_str(),&lt;br /&gt;            fptr,  // JSNative&lt;br /&gt;            size, 0);&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Here is the problem: if the JavaScript function calls functions that were explicitly added to the object via JS_DefineFunction(), things work great. However, if I call a function that was not added this way, the JavaScript engine crashes hard:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#0  0xa30a353c in gNotifyRef ()&lt;br /&gt;#1  0x00672854 in js_LookupPropertyWithFlags (cx=0x3e2950, obj=0x3e5a80, id=4678032, flags=1, objp=0xbfffdbc0, propp=0xbfffdbc4) at jsobj.c:2542&lt;br /&gt;#2  0x0067235c in js_LookupProperty (cx=0x3e2950, obj=0x3e5a80, id=4678032, objp=0xbfffdbc0, propp=0xbfffdbc4) at jsobj.c:2447&lt;br /&gt;#3  0x0067378c in js_GetProperty (cx=0x3e2950, obj=0x3e5a80, id=4678032, vp=0xbfffdd1c) at jsobj.c:2732&lt;br /&gt;#4  0x00655874 in js_Interpret (cx=0x3e2950, pc=0x47921b "5", result=0xbfffe3b0) at jsinterp.c:3290&lt;br /&gt;#5  0x00644fac in js_Invoke (cx=0x3e2950, argc=0, flags=2) at jsinterp.c:1193&lt;br /&gt;#6  0x00645414 in js_InternalInvoke (cx=0x3e2950, obj=0x3e3fc0, fval=4086384, flags=0, argc=0, argv=0x0, rval=0xbfffe664) at jsinterp.c:1270&lt;br /&gt;#7  0x005fd0fc in JS_CallFunction (cx=0x3e2950, obj=0x3e3fc0, fun=0x44a7a0, argc=0, argv=0x0, rval=0xbfffe664) at jsapi.c:3867&lt;br /&gt;#8  0x000066f8 in Button::ButtonPressed() (this=0x4650d0) at button.cpp:129&lt;br /&gt;#9  0x002cebf0 in ButtonPressSubject::NotifyButtonPress() (this=0x465108) at ../subject.cpp:13&lt;br /&gt;#10 0x002cd4d0 in CocoaButtonImpl::HandleCommand() (this=0x465100) at cocoabuttonimpl.mm:55&lt;br /&gt;#11 0x002ce28c in -[ButtonAction onClick:] (self=0x546050, _cmd=0x2d9f84, sender=0x5463b0) at cocoabuttonaction.mm:15&lt;br /&gt;#12 0x930f9f5c in -[NSApplication sendAction:to:from:] ()&lt;br /&gt;#13 0x9311a718 in -[NSControl sendAction:to:] ()&lt;br /&gt;#14 0x9315eea4 in -[NSCell _sendActionFrom:] ()&lt;br /&gt;#15 0x930f3968 in -[NSCell trackMouse:inRect:ofView:untilMouseUp:] ()&lt;br /&gt;#16 0x9315eac0 in -[NSButtonCell trackMouse:inRect:ofView:untilMouseUp:] ()&lt;br /&gt;#17 0x9312e6b4 in -[NSControl mouseDown:] ()&lt;br /&gt;#18 0x930c1698 in -[NSWindow sendEvent:] ()&lt;br /&gt;#19 0x930a94ec in -[NSApplication sendEvent:] ()&lt;br /&gt;#20 0x930b2418 in -[NSApplication run] ()&lt;br /&gt;#21 0x002ce1b4 in CocoaAppImpl::MainLoop() (this=0x45fb50) at cocoaappimpl.mm:28&lt;br /&gt;#22 0x00008200 in App::MainLoop() (this=0x45fad0) at app.cpp:23&lt;br /&gt;#23 0x00003bec in main (argc=0, argv=0xbffffb90) at layout.cpp:305&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I'm wondering, is there something that needs to be done to protect calling applications from tubing the JavaScript engine in this way? I'm still getting my feet wet with JavaScript engine embedding, though I have made great progress, this is stumping me a bit. &lt;br /&gt;&lt;br /&gt;Brendan's response:&lt;br /&gt;&lt;br /&gt;Hey Syd,&lt;br /&gt;&lt;br /&gt;Simplest theory first: if this is how the actual code looks, including objectClass being local to the calling function or method, then that won't work.  The JSClass must live as long as any instance of it, and typically the way this is done is by making the JSClass struct static.&lt;br /&gt;&lt;br /&gt;/be &lt;br /&gt;&lt;br /&gt;jband's response:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;&gt;     JSClass objectClass = {&lt;br /&gt;&gt;         name.c_str(), JSCLASS_NEW_RESOLVE,&lt;br /&gt;&gt;         JS_PropertyStub,  JS_PropertyStub,&lt;br /&gt;&gt;         JS_PropertyStub,  JS_PropertyStub,&lt;br /&gt;&gt;         JS_EnumerateStub,  JS_ResolveStub,&lt;br /&gt;&gt;         JS_ConvertStub,   JS_FinalizeStub&lt;br /&gt;&gt;     };&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Hi Syd,&lt;br /&gt;&lt;br /&gt;I'm not even going to ask why you'd want to do this. I guess this makes for an interesting book project. But, I'm still a believer that XPConnect was the right way to go :-)&lt;br /&gt;&lt;br /&gt;I'd say one mistake is in using JSCLASS_NEW_RESOLVE and passing JS_ResolveStub. I believe that JSCLASS_NEW_RESOLVE tells the engine that the resolve callback is of type JSNewResolveOp, but you are passing in a callback of type JSResolveOp. That would be bad.&lt;br /&gt;&lt;br /&gt;I'd also have to wonder if the name string you are passing is static (name.c_str()) - the engine doesn't make a copy of it. As Brendan pointed out, same story for the entire JSClass structure.&lt;br /&gt;&lt;br /&gt;You are either calling the wrong address if the JSClass* is not pointing to data that doesn't change, or you are screwing up the stack on return from JS_ResolveStub.&lt;br /&gt;&lt;br /&gt;BTW, it is good to say explicitly which js version you are using when asking for help so that folks can correlate line numbers in stack dumps with actual source lines. &lt;br /&gt;&lt;br /&gt;John.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-112599102428113303?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/112599102428113303/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=112599102428113303' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/112599102428113303'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/112599102428113303'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2005/09/some-interesting-output-getcomponent.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-112590610058043473</id><published>2005-09-05T00:35:00.000-07:00</published><updated>2005-09-05T13:41:33.413-07:00</updated><title type='text'></title><content type='html'>Sometimes, you just have to throw it all away, and start from scratch.&lt;br /&gt;&lt;br /&gt;Unfortunately, my design didn't hold water when it came to two (closely related) things:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;   &lt;li&gt;Stuffing the address of a C++ virtual member function into a JavaScript object, which wants nothing to do with C++ virtual member functions.&lt;/li&gt;   &lt;li&gt;Difficulty coming up with a scheme for passing "this" to the proxy class directly from JavaScript. This was the drop-dead issue; I might have been able to figure out a way to make the compiler allow me to specify a native function as a pointer to a C++ member, but getting a "this" pointer for the proxy class on the stack when the wrapper function is called would surely have been difficult, if not impossible.&lt;/li&gt; &lt;/ul&gt; So, it was time to step back, think a bit, and re-design. &lt;br /&gt;&lt;br /&gt;It didn't take long to see there was a better way. The new design, instead of generating a C++ proxy class for each component class, generates extern "C" wrapper functions that can be directly added to the JavaScript object via JS_DefineFunction(). These wrapper functions, when called, simply lookup a pointer to the corresponding component class instance, and call it. &lt;br /&gt;&lt;br /&gt;Each wrapper function name is generated so that it contains the name of the class, and the name of the member function. This ensures that each function is uniquely named (at least, within the component). As an example, below are the proxy functions that are generated for the HelloClass class directly from the SIL file:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;extern "C" JSBool&lt;br /&gt;HelloClass_Get_age(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);&lt;br /&gt;&lt;br /&gt;extern "C" JSBool&lt;br /&gt;HelloClass_Set_age(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);&lt;br /&gt;&lt;br /&gt;extern "C" JSBool&lt;br /&gt;HelloClass_Hello(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The above functions correspond to, and wrap, the Get_age(), Set_age(), and Hello() member functions of the HelloClass class implemented by the component.&lt;br /&gt;&lt;br /&gt;Let's now take a look at the function NewHelloClassProxy(), and talk about what it does:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;static map&amp;lt;JSObject *, HelloClass *&amp;gt; objHelloClassMap;&lt;br /&gt;&lt;br /&gt;extern "C" PRStatus&lt;br /&gt;NewHelloClassProxy(JSObject *obj)&lt;br /&gt;{&lt;br /&gt;    HelloClass *ptr = new HelloClass();&lt;br /&gt;    if (!ptr)&lt;br /&gt;        return PR_FAILURE;&lt;br /&gt;    AddHelloClassClass(obj, ptr);&lt;br /&gt;    return PR_SUCCESS;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;When the JavaScript application requests a HelloClass object, we create a JavaScript object (as before). We then call the above function, NewHelloClassProxy(), and pass to it the pointer to the JavaScript object that was just created. NewHelloClassProxy() then creates an instance of HelloClass, and adds it to an STL map that holds JavaScript object/HelloClass instance pairs. Given a JavaScript object, we can find the corresponding pointer to a HelloClass instance.&lt;br /&gt;&lt;br /&gt;When NewHelloClassProxy() returns, the layout engine stuffs the address of the extern "C" wrapper functions into the JavaScript object. When these functions are invoked by the JavaScript engine, the JavaScript object will be passed as an argument. We can take this JavaScript object, look it up in the map, and then use the resulting HelloClass instance pointer to invoke the wrapped component class instance method.&lt;br /&gt;&lt;br /&gt;Here is an example of how the wrapper function HelloClass_get_age() locates the appropriate HelloClass instance, using the JavaScript object it is passed, and invokes the component class instance method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;extern "C" JSBool&lt;br /&gt;HelloClass_Get_age(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)&lt;br /&gt;{&lt;br /&gt;    PRInt32 arg_0;&lt;br /&gt;    HelloClass *ptr = GetHelloClassClass(obj);&lt;br /&gt;    if (!ptr || ptr-&gt;Get_age(arg_0) != PR_SUCCESS)&lt;br /&gt;        return JS_FALSE;&lt;br /&gt;    *rval = JSVAL_TO_INT(arg_0);&lt;br /&gt;    return JS_TRUE;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The implementation of GetHelloClassClass() is straightforward; it simply looks up the JavaScript object in the map and returns the HelloClass instance pointer:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;static HelloClass *&lt;br /&gt;GetHelloClassClass(JSObject *obj)&lt;br /&gt;{&lt;br /&gt;    return objHelloClassMap[obj];&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ok, so, this should do it. I think that I said this once before, but all that should remain is to wire the above support into the layout code to handle object requests from JavaScript, as descriobed. If all goes well (and it should this time, I hope), I'll be calling C++ from JavaScript soon enough.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-112590610058043473?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/112590610058043473/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=112590610058043473' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/112590610058043473'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/112590610058043473'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2005/09/sometimes-you-just-have-to-throw-it.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-112575278460096633</id><published>2005-09-03T05:56:00.000-07:00</published><updated>2005-09-03T06:29:08.683-07:00</updated><title type='text'></title><content type='html'>The lookup code generated for the proxy class is based on the strategy outlined in the previous post. First, some code snippets, followed by a discussion:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class HelloClass;&lt;br /&gt;class HelloClassProxy;&lt;br /&gt;&lt;br /&gt;typedef JSBool(HelloClassProxy::*JSSIG_HelloClass)(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);&lt;br /&gt;&lt;br /&gt;class HelloClassProxy {&lt;br /&gt;public:&lt;br /&gt;   HelloClassProxy();&lt;br /&gt;   ~HelloClassProxy();&lt;br /&gt;   JSSIG_HelloClass&lt;br /&gt;   GetEntryPoint(const string&amp; name);&lt;br /&gt;   JSBool&lt;br /&gt;   Get_age(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);&lt;br /&gt;   JSBool&lt;br /&gt;   Set_age(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);&lt;br /&gt;   JSBool&lt;br /&gt;   Hello(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);&lt;br /&gt;private:&lt;br /&gt;   HelloClass *m_impl;&lt;br /&gt;}; &lt;br /&gt;&lt;br /&gt;extern "C" HelloClassProxy *NewHelloClassProxy();&lt;br /&gt;extern "C" void DeleteHelloClassProxy(HelloClassProxy *ptr);&lt;br /&gt;extern "C" JSSIG_HelloClass HelloClassProxyLookup(HelloClassProxy *ptr, const string&amp; name);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The typedef JSSIG_HelloClass represents a pointer to a HelloClassProxy member function with the prototype mandated by the mozilla JavaScript engine. For each class in the component, a similar typedef is generated by the layout engine.&lt;br /&gt;&lt;br /&gt;GetEntryPoint() is a new member function that returns a pointer to a HelloClassProxy member function that matches the name passed in. Here is the code that is now being generated for that particular function by iterating the DOM for functions and properties implemented by HelloClassProxy:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;JSSIG_HelloClass&lt;br /&gt;HelloClassProxy::GetEntryPoint(const string&amp; name)&lt;br /&gt;{&lt;br /&gt;   if (name == "Hello")&lt;br /&gt;       return &amp;amp;HelloClassProxy::Hello;&lt;br /&gt;   if (name == "Get_age")&lt;br /&gt;       return &amp;HelloClassProxy::Get_age;&lt;br /&gt;   if (name == "Set_age")&lt;br /&gt;       return &amp;HelloClassProxy::Set_age;&lt;br /&gt;   return NULL;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As you can see, all this function does is perform a simple check for a matching string, and return a pointer to a member function if a match is found. Notice the interesting way by which a pointer to a member function is obtained; this takes some getting used to if you and an old-timer C programmer (like I was). Yes, I could have put the names and functions into an STL map, but this code was more straightforward to implement in the layout code generator.&lt;br /&gt;&lt;br /&gt;The final function generated by the layout engine to support lookups is a simple extern "C" function that allows the layout engine to call into the component and invoke GetEntryPoint() thru the class object that it holds:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;extern "C" JSSIG_HelloClass&lt;br /&gt;HelloClassProxyLookup(HelloClassProxy *ptr, const string&amp; name)&lt;br /&gt;{&lt;br /&gt;   if (!ptr)&lt;br /&gt;       return NULL;&lt;br /&gt;   return ptr-&gt;GetEntryPoint(name);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That's pretty much it. This should be all that is needed for the layout engine to populate the JavaScript object with entry points into the proxy classes. The next step is to add code to the layout engine that does just that.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-112575278460096633?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/112575278460096633/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=112575278460096633' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/112575278460096633'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/112575278460096633'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2005/09/lookup-code-generated-for-proxy-class.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-112574051474139444</id><published>2005-09-03T02:30:00.000-07:00</published><updated>2005-09-03T06:25:30.246-07:00</updated><title type='text'></title><content type='html'>Creating the public functions to allocate and deallocate proxy objects was straightforward enough; simply generate into the proxy header and source files code like the following:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;extern "C" HelloClassProxy *&lt;br /&gt;NewHelloClassProxy()&lt;br /&gt;{&lt;br /&gt;    HelloClassProxy *ptr = new HelloClassProxy();&lt;br /&gt;    return ptr;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;extern "C" void&lt;br /&gt;DeleteHelloClassProxy(HelloClassProxy *ptr)&lt;br /&gt;{&lt;br /&gt;    if (ptr)&lt;br /&gt;        delete ptr;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;HelloClassProxy::HelloClassProxy()&lt;br /&gt;{&lt;br /&gt;    m_impl = new HelloClass();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;HelloClassProxy::~HelloClassProxy()&lt;br /&gt;{&lt;br /&gt;    if (m_impl)&lt;br /&gt;        delete m_impl;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;At runtime, the layout engine, when it goes to create the JavaScript object that represents an instance of the proxy class in response to a query by the user JavaScript code to obtain an instance of an object, constructs a string that consists of a concatenation of the strings "New", the class name (e.g., HelloClass), and "Proxy". It then calls the Library class object to query the shared library for a function with that name (in this case, NewHelloClassProxy). If it is returned a non-NULL value, it then can call that function to allocate a proxy object. Notice that allocating the proxy object also causes the concrete object (HelloClass) wrapped by the proxy to be instantiated (see the source for the HelloClassProxy constructor, above). Here's a snippet of that code taken from the layout engine:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;PRStatus&lt;br /&gt;Object::CreateJSObject()&lt;br /&gt;{&lt;br /&gt;    PRStatus ret = PR_SUCCESS;&lt;br /&gt;    JSEngine *js = JSEngine::GetJSEngine();&lt;br /&gt;&lt;br /&gt;    if (!js)&lt;br /&gt;        return PR_FAILURE;&lt;br /&gt;&lt;br /&gt;    Component *comp = GetComponent();&lt;br /&gt;&lt;br /&gt;    if (!comp)&lt;br /&gt;        return PR_FAILURE;&lt;br /&gt;&lt;br /&gt;    Library *lib = comp-&gt;GetLibrary();&lt;br /&gt;&lt;br /&gt;    if (!lib)&lt;br /&gt;        return PR_FAILURE;&lt;br /&gt;&lt;br /&gt;    JSContext *ctx = js-&gt;GetContext();&lt;br /&gt;    JSObject *obj = js-&gt;GetGlobalObject();&lt;br /&gt;&lt;br /&gt;    string name = GetAttributeByName(string("name"))-&gt;GetValue();&lt;br /&gt;&lt;br /&gt;    // create an instance of the proxy class&lt;br /&gt;&lt;br /&gt;    string creator;&lt;br /&gt;&lt;br /&gt;    creator = "New" + name + "Proxy";&lt;br /&gt;&lt;br /&gt;    void *fptr = lib-&gt;FindSymbol(creator);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;However, we have a problem, Houston. Once the layout engine has an instance of the proxy object (in fptr, above), it needs to create entry points in the JavaScript object for each method implemented by the proxy object. These entry points are what the JavaScript engine uses to map calls made by the user's JavaScript into C/C++ calls. For the HelloClassProxy class, this includes the methods Hello(), Get_age(), and Set_age(). With an instance of the object in hand, you say, the layout code can obtain the addresses of these objects, so what is the problem? The problem is that the best we can do (in my current design that is) is obtain effectively a void * when calling the shared library implementation of NewHelloClassProxy(). The layout engine cannot allocate a pointer to a HelloClassProxy object; to do otherwise would require the layout engine to anticipate the types of all proxy objects it is ever going to be expected to interface to. But with only a void *, I can't possible obtain addresses of functions in the object's vtable. &lt;br /&gt;&lt;br /&gt;But there is a solution. We can generate into the proxy source an addition extern "C" interface, perhaps call it HelloClassProxyLookup(), that takes a string and a void * representing the proxy object, and then returns a pointer to the method in the object that pertains to that name. This pointer can then be stuffed into the JavaScript object. The code in the layout engine that generates the proxy source (and the lookup function) will be modified generate the code needed to support this functionality.&lt;br /&gt;&lt;br /&gt;Another thing I need to concern myself with is passing "this" to the proxy functions from JavaScript. Currently, I am not accounting for this required argument.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-112574051474139444?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/112574051474139444/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=112574051474139444' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/112574051474139444'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/112574051474139444'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2005/09/creating-public-functions-to-allocate.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-112539327526221048</id><published>2005-08-30T01:54:00.000-07:00</published><updated>2005-09-02T22:24:16.636-07:00</updated><title type='text'></title><content type='html'>One month has passed, with plenty of coding since the last time I blogged, so I figured it is time for an update on the implementation of the component scheme that was outlined in my last post. To date, I have completed (but not fully debugged) all of the software that generates proxy header and source files from the SIL file, as well as a header file that is used to define the class that is interfaced to by the proxy. I've also fleshed out most of the code that is called by JavaScript to get an instance of a component, and with that instance, locate and create JavaScript objects that the component exports. I also have implemented a class that thinly wraps NSPR's portable dlsym implementation (PRLibrary, etc.).&lt;br /&gt;&lt;br /&gt;Here is some JavaScript that illustrates how a callback (again, a button click handler) obtains a component object, and from that component, obtains an object representing a class implemented by the component, and, finally, calls a function implemented by that class using the object:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;function Button1Click()&lt;br /&gt;{&lt;br /&gt;    var component = componentmgr.getComponent("94981D9E-FC99-11D9-8BDE-F66BAD1E3F3A");&lt;br /&gt;    if (component) {&lt;br /&gt;        var anobject = component.getObject("2ABDF00B-025E-11DA-BFFE-000A27942344");&lt;br /&gt;&lt;br /&gt;        if (anobject) {&lt;br /&gt;            anobject.Hello("Syd"); // a function that does exist&lt;br /&gt;            anobject.Foobar();     // a function that doesn't exist&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In the above, &lt;span style="font-weight: bold;"&gt;componentmgr&lt;/span&gt; is a pre-defined JavaScript object that is made available to all SIL-based applications. This object provides JavaScipt code an interface directly to the layout engine's component manager singleton. Each component (and each component class) is uniquely identified by a uuid (&lt;span style="font-weight: bold;"&gt;$ man 1 uuidgen&lt;/span&gt; if you are not sure what a uuid is). Thus, because every component has a unique ID, and every class has a unique ID, unambiguous references to components and objects can be made. This is a simple and effective scheme. Once an object has been located and is available to JavaScript, all that remains for the code to do is make direct calls to the object's public functions (e.g., Hello() and Foobar() in the above example) to perform the work needed.&lt;br /&gt;&lt;br /&gt;Some details needs to be finished up before this is all working correctly.&lt;br /&gt;&lt;ul&gt;   &lt;li&gt;Classes in components need to export extern "C" methods that can be used to create and destroy instances, and component objects need to call these functions to create and destroy class instances. &lt;/li&gt;   &lt;li&gt;I need to determine some method by which objects are destroyed as the JavaScript scope within which an object has been declared/obtained has exited. Not sure what support the mozilla JavaScript engine provides in this regard, but it must be there, and I will find it :-)&lt;/li&gt; &lt;/ul&gt;Component developers basically need to implement a shared library that contains sources for both the proxy class and the implementation class. My original scheme called for two libraries, one with proxy code, and the other with implementation, but by placing both in the same library, things are much simpler (the proxy doesn't have to dlload/dlsym for implementation code being the major simplification).&lt;br /&gt;&lt;br /&gt;Some code would be nice, you say? The first three files (of the four reproduced below) are generated directly from the SIL file.&lt;br /&gt;&lt;br /&gt;First, the proxy header (simple_proxy.h):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;/* This is generated source code. Do not edit */&lt;br /&gt;&lt;br /&gt;#if !defined(__94981D9E_FC99_11D9_8BDE_F66BAD1E3F3A_PROXY_H__)&lt;br /&gt;#define __94981D9E_FC99_11D9_8BDE_F66BAD1E3F3A_PROXY_H__&lt;br /&gt;&lt;br /&gt;#include "prtypes.h"&lt;br /&gt;#include "jsapi.h"&lt;br /&gt;&lt;br /&gt;class HelloClass;&lt;br /&gt;&lt;br /&gt;class HelloClassProxy {&lt;br /&gt;public:&lt;br /&gt;  HelloClassProxy();&lt;br /&gt;  ~HelloClassProxy();&lt;br /&gt;  JSBool&lt;br /&gt;  Get_age(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);&lt;br /&gt;  JSBool&lt;br /&gt;  Set_age(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);&lt;br /&gt;  JSBool&lt;br /&gt;  Hello(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);&lt;br /&gt;private:&lt;br /&gt;  HelloClass *m_impl;&lt;br /&gt;};&lt;br /&gt;#endif&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The above contains JSBool functions that can be called directly from JavaScript via the internal JavaScript object that is created when the client calls the component manager's getObject() function. Notice this object has a member variable called m_impl. The type of this member variable is a pointer to the actual C++ class implemented by the component developer (i.e., the function that does all the real work). Hence, m_impl is the bridge between the proxy and the functionality in the object that the JavaScript application is interested in calling. m_impl is initialized when the proxy object is created.&lt;br /&gt;&lt;br /&gt;Next up, the proxy source (simple_proxy.cpp). Its' main job is to marshal arguments that are passed by the JavaScript application to the component object, invoke the component object funtion, and then pass back any return values and status to the JavaScript caller.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;/* This is generated source code. Do not edit */&lt;br /&gt;&lt;br /&gt;#include "simple_proxy.h"&lt;br /&gt;#include "simple.h"&lt;br /&gt;#include "prtypes.h"&lt;br /&gt;#include "jsstr.h"&lt;br /&gt;&lt;br /&gt;HelloClassProxy::HelloClassProxy()&lt;br /&gt;{&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;HelloClassProxy::~HelloClassProxy()&lt;br /&gt;{&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;JSBool&lt;br /&gt;HelloClassProxy::Hello(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)&lt;br /&gt;{&lt;br /&gt;  if (!m_impl)&lt;br /&gt;      return JS_FALSE;&lt;br /&gt;  JSString *str_0 = JSVAL_TO_STRING(argv[0]);&lt;br /&gt;  string arg_0(JS_GetStringBytes(str_0));&lt;br /&gt;  PRInt32 ret;&lt;br /&gt;  ret = m_impl-&gt;Hello(arg_0);&lt;br /&gt;  *rval = INT_TO_JSVAL(ret);&lt;br /&gt;  return JS_TRUE;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;JSBool&lt;br /&gt;HelloClassProxy::Get_age(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)&lt;br /&gt;{&lt;br /&gt;  PRInt32 arg_0;&lt;br /&gt;  if (!m_impl || m_impl-&gt;Get_age(arg_0) != PR_SUCCESS)&lt;br /&gt;      return JS_FALSE;&lt;br /&gt;  *rval = JSVAL_TO_INT(arg_0);&lt;br /&gt;  return JS_TRUE;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;JSBool&lt;br /&gt;HelloClassProxy::Set_age(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)&lt;br /&gt;{&lt;br /&gt;  PRInt32 arg_0 = JSVAL_TO_INT(argv[0]);&lt;br /&gt;  if (!m_impl || m_impl-&gt;Set_age(arg_0) != PR_SUCCESS)&lt;br /&gt;      return JS_FALSE;&lt;br /&gt;  return JS_TRUE;&lt;br /&gt;}&lt;br /&gt;#endif&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Next, the class header (simple.h). This file, also generated from the SIL file, defines the class that the component programmer must implement:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;/* This is generated source code. Do not edit */&lt;br /&gt;&lt;br /&gt;#if !defined(__94981D9E_FC99_11D9_8BDE_F66BAD1E3F3A_COMP_H__)&lt;br /&gt;#define __94981D9E_FC99_11D9_8BDE_F66BAD1E3F3A_COMP_H__&lt;br /&gt;&lt;br /&gt;#include "prtypes.h"&lt;br /&gt;#include &amp;lt;string&amp;gt;&lt;br /&gt;using namespace std;&lt;br /&gt;&lt;br /&gt;class HelloClass {&lt;br /&gt;public:&lt;br /&gt;  HelloClass();&lt;br /&gt;  ~HelloClass();&lt;br /&gt;  PRStatus Get_age(PRInt32&amp; val) {val = m_age; return PR_SUCCESS;};&lt;br /&gt;  PRStatus Set_age(PRInt32&amp; val) {m_age = val; return PR_SUCCESS;};&lt;br /&gt;  PRInt32 Hello(string&amp; Hello);&lt;br /&gt;private:&lt;br /&gt;  PRInt32 m_age;&lt;br /&gt;};&lt;br /&gt;#endif&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The final source code (simple.cpp), which is not generated, but is rather created by the component developer, implements the HelloClass class that was defined in simple.h (which was generated from the SIL file). Internally, this class is instantiated and called by the HelloClassProxy proxy class. Here is the source, which could be just about anything the developer desires (as long as it adheres to the contract specified by the SIL file):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#include "simple.h"&lt;br /&gt;&lt;br /&gt;HelloClass::HelloClass()&lt;br /&gt;{&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;HelloClass::~HelloClass()&lt;br /&gt;{&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;PRInt32&lt;br /&gt;HelloClass::Hello(string&amp; Hello)&lt;br /&gt;{&lt;br /&gt;  printf("******Hello from HelloClass C++\n");&lt;br /&gt;  return 1;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-112539327526221048?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/112539327526221048/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=112539327526221048' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/112539327526221048'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/112539327526221048'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2005/08/one-month-has-passed-with-plenty-of.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-112272012746801599</id><published>2005-07-30T03:23:00.000-07:00</published><updated>2005-08-30T04:27:28.296-07:00</updated><title type='text'></title><content type='html'>XPConnect is the name assigned by mozilla.org to infrastructure that allows JavaScript to interface with XPCOM-based components. XPCOM is mozilla's component architecture, similar to (and based upon) Microsoft COM. In a nutshell, functions like the onclick() handler that I described previously can only do so much given the support provided by the JavaScript engine. To make them truly powerful, they need to be able to instantiate and call external objects created in a language like C++.&lt;br /&gt;&lt;br /&gt;For example, I might want to, from my onclick() handler, read the value of a text field in my UI, and store this value somewhere in an external database. JavaScript alone does not provide database functionality, and it is not realistic, nor is it practical to build into JavaScript all the functionality that an application might need, even if this functionality could be anticipated (and it cannot). To talk to a database, JavaScript code would need to call external, C++ code that knows how to interface with the database. The way to extend JavaScript to interface with this external code is some mechanism like Mozilla's XPConnect that acts as a bridge between the two bodies of code at runtime.&lt;br /&gt;&lt;br /&gt;As we saw with Dump(), JavaScript can be made to call C++ functions. Say that we have a component that implements the following C++ class:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class Foo {&lt;br /&gt;public:&lt;br /&gt;    Foo();&lt;br /&gt;    ~Foo();&lt;br /&gt;    void SomeFunc(int a, string&amp; b);&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;and we want to call that function from JavaScript. Recall the C++ function prototype for Dump():&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;JS_STATIC_DLL_CALLBACK(JSBool)&lt;br /&gt;Dump(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In the mozilla JavaScript engine, all functions invoked from JavaScript must adhere to this function prototype. So, the function prototype for SomeFunc() in my class would be the same:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;JS_STATIC_DLL_CALLBACK(JSBool)&lt;br /&gt;SomeFunc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;But, clearly, I don't want to force component authors to implement classes that have member functions corresponding to this function prototype.&lt;br /&gt;&lt;br /&gt;One solution for mapping calls from JavaScript to native methods would be to create, for each function that we might want to be able to call in the component from JavaScript, a wrapper or proxy function. The job of the wrapper would then be to extract the arguments that were passed in, convert each them from its JavaScript type to a corresponding C++ type, invoke the C++ function, obtain the result upon the return of the C++ function, convert this result from its C++ type to a corresponding JavaScript type, and then return control back to JavaScript.&lt;br /&gt;&lt;br /&gt;I spent a lot of time over the past few evenings studying this problem in search of a reasonably powerful, yet easily describable solution. Part of that study involved taking a look at XPConnect, which, as I mentioned above, is the mozilla technology that provides the bridge between JavaScript and objects in externally written C++ components. XPConnect uses IDL files (xpidl) and typeinfo files to dynamically bind C++ components to JavaScript (and in the other direction as well; JS objects can be called from C++ if desired).&lt;br /&gt;&lt;br /&gt;While using XPConnect is straightforward, describing it is far too involved for me to cover in this book. As I did with the layout engine, I have opted to implement a simpler (if perhaps less functional) scheme of my own design, one that is inspired by XPConnect.&lt;br /&gt;&lt;br /&gt;The scheme that I envision, and plan to implement in the coming few weeks, builds on top of the SIL files previously described in a way that is somewhat analogous to the use of xpidl files by XPCOM/XPConnect, but is easier to describe, and thus more suitable for use in my book.&lt;br /&gt;&lt;br /&gt;Here is the basic plan:&lt;br /&gt;&lt;ul&gt;   &lt;li&gt;component authors create a shared library or DLL that implements classes of their choosing&lt;/li&gt;   &lt;li&gt;the component, and the classes implemented by that component that are to be called from JavaScript, are described in a SIL file&lt;/li&gt;   &lt;li&gt;a special tool reads the SIL file and creates source code and a header for each class that wraps the classes in that component. Each method that is described in the SIL file will have a corresponding function that wraps the actual function implemented by the component. The wrappers marshal arguments and return values; the function prototype for each of these wrappers will be:&lt;/li&gt; &lt;/ul&gt;STATIC_DLL_CALLBACK(JSBool)&lt;br /&gt;WrapperFunc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)&lt;br /&gt;&lt;ul&gt;   &lt;li&gt;Component developers will deliver the component shared library, the wrapper shared library, and the SIL file for each component. &lt;/li&gt;   &lt;li&gt;All the components will be stored in a components directory accessible to the application. The layout engine will, at start up, iterate this directory, and process each SIL file it encounters. &lt;/li&gt;   &lt;li&gt;For each class defined by a component and instantiated by JavaScript, the layout engine will instantiate an instance of the corresponding wrapper class (and the class it wraps), and a table of JS function objects will be created to map the names of functions callable from JavaScript to methods implemented in the wrapper. &lt;/li&gt; &lt;/ul&gt;More details will emerge as I flesh out the implementation, I am sure.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-112272012746801599?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/112272012746801599/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=112272012746801599' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/112272012746801599'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/112272012746801599'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2005/07/xpconnect-is-name-assigned-by-mozilla.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-112229508765945968</id><published>2005-07-25T05:31:00.000-07:00</published><updated>2005-07-25T05:38:07.663-07:00</updated><title type='text'></title><content type='html'>This morning, I implemented in C++ a simple component manager that iterates a directory looking for SIL files, and for each one found, creates an in-memory data structure to represent each component and the classes that the component implements. &lt;br /&gt;&lt;br /&gt;The next step will be to iterate the in-memory structures and create JavaScript objects that implement each component and class represented, and register these objects with the JavaScript engine. &lt;br /&gt;&lt;br /&gt;In addition, some kind of marshalling code within the layout manager will be needed to dispatch calls made from JavaScript to the components (not to mention, some code to load each DLL at runtime will be required). This is going to take some thought...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-112229508765945968?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/112229508765945968/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=112229508765945968' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/112229508765945968'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/112229508765945968'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2005/07/this-morning-i-implemented-in-c-simple.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-112228863075646514</id><published>2005-07-25T03:42:00.000-07:00</published><updated>2005-08-30T04:28:30.283-07:00</updated><title type='text'></title><content type='html'>Tonight I fleshed out the basics for implementing a simple component manager. What is a component manager? Well, In order for callbacks (like onclick) in JavaScript to be effective, they need to be able to do something more meaningful than dump strings to stderr. To gain more power, they need to interface to external code written in a more powerful language, like C or C++. Components are one way to package code that can be called from JavaScript.&lt;br /&gt;&lt;br /&gt;For this project, components and their interfaces are described in a XML file with a sil extension (which stands for Simple Interface Language).&lt;br /&gt;&lt;br /&gt;Example:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;&amp;#60;component name="simple" id="94981D9E-FC99-11D9-8BDE-F66BAD1E3F3A"&amp;#62;&lt;br /&gt;    &amp;#60;class name="HelloClass"&amp;#62;&lt;br /&gt;        &amp;#60;function name="Hello"&amp;#62;&lt;br /&gt;            &amp;#60;arg name="MyName" type="string"/&amp;#62;&lt;br /&gt;            &amp;#60;return type="number"/&amp;#62;&lt;br /&gt;        &amp;#60;/function&amp;#62;&lt;br /&gt;        &amp;#60;property name="age" type="number"/&amp;#62;&lt;br /&gt;    &amp;#60;/class&amp;#62;&lt;br /&gt;    &amp;#60;class name="HelloClass2"&amp;#62;&lt;br /&gt;        &amp;#60;function name="Hello"&amp;#62;&lt;br /&gt;            &amp;#60;arg name="MyName" type="string"/&amp;#62;&lt;br /&gt;            &amp;#60;return type="number"/&amp;#62;&lt;br /&gt;        &amp;#60;/function&amp;#62;&lt;br /&gt;        &amp;#60;property name="age" type="number"/&amp;#62;&lt;br /&gt;    &amp;#60;/class&amp;#62;&lt;br /&gt;&amp;#60;/component&amp;#62;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Allowed argument and return types are string, number, and bool, corresponding to the basic types supported by JavaScript. Properties are defined with a property tag. The also have a name and type.&lt;br /&gt;&lt;br /&gt;Each component is implemented as a single DLL/shared lib that implements the above classes, and methods to create and destroy objects belonging to the classes implemented by the component.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-112228863075646514?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/112228863075646514/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=112228863075646514' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/112228863075646514'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/112228863075646514'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2005/07/tonight-i-fleshed-out-basics-for.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-112224960566951695</id><published>2005-07-24T16:27:00.000-07:00</published><updated>2005-08-30T07:48:01.676-07:00</updated><title type='text'></title><content type='html'>Turns out, the best place to add my "dump" function was to the global object. Here's how I did it.&lt;br /&gt;&lt;br /&gt;First, I created a C function named Dump, which is essentially a copy of the function of the same name found in js/src/xpconnect/shell/xpcshell.cpp. The function prototype for Dump() is:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;JS_STATIC_DLL_CALLBACK(JSBool)&lt;br /&gt;Dump(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This is pretty much boilerplate for any functions that are invoked from JS to an object created by you in C or C++. The implementation of this function is straightforward; take the character string passed and display it to stderr.&lt;br /&gt;&lt;br /&gt;To tell the JS engine about this function, we need to fill out a simple array:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;static JSFunctionSpec glob_functions[] = {&lt;br /&gt;  {"dump",            Dump,           1},&lt;br /&gt;  {0}&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;JSFunctionSpec is simply a mapping from the name of a function as it is called from JavaScript (here, "dump") to the name of the function as it is implemented in C or C++ (in this case, "Dump"). The final field of the JSFunctionSpec struct is the number of arguments passed, which in this case is 1.&lt;br /&gt;&lt;br /&gt;Notice we don't have to specify the type of the argument. JavaScript implements 3 fundamental types - boolean, numbers (with no distinction between integers and floats), and strings. However, in JavaScript, you cannot declare the type of a variable. Instead, JavaScript is able (like any other compiler) to determine the type of a variable based on lexical properties alone, e.g.,&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;var myVar;&lt;br /&gt;&lt;br /&gt;myVar = 3;     // var is an integer&lt;br /&gt;mVar = "3";  // var is now a string&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The JS engine allows embedding C/C++ applications to convert JS variables to whatever type is necessary via a set of conversion functions. In the C/C++ implementation of Dump(), I call one of these conversion functions to retrive a string that can be displayed to stderr:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  String *str;&lt;br /&gt;&lt;br /&gt;  str = JS_ValueToString(cx, argv[0]);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Then, I extract from the JSString object a C-style string, which I make a copy of and sent to fputs() for display:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  char *bytes = JS_GetStringBytes(str);&lt;br /&gt;  bytes = strdup(bytes);&lt;br /&gt;&lt;br /&gt;  fputs(bytes, stderr);&lt;br /&gt;  free(bytes);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Instead, had I wanted the argument to dump() to be evaluated as a signed 32-bit int, I would have called:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;extern JS_PUBLIC_API(JSBool)&lt;br /&gt;JS_ValueToECMAInt32(JSContext *cx, jsval v, int32 *ip);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So, no matter what type of variable the caller passes to dump() in JavaScript:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;dump(3);&lt;br /&gt;dump("3");&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I can retrieve a character string and display it properly. Here is the complete source for Dump() in C:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;JS_STATIC_DLL_CALLBACK(JSBool)&lt;br /&gt;Dump(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)&lt;br /&gt;{&lt;br /&gt;  JSString *str;&lt;br /&gt;  if (!argc)&lt;br /&gt;      return JS_TRUE;&lt;br /&gt;&lt;br /&gt;  str = JS_ValueToString(cx, argv[0]);&lt;br /&gt;  if (!str)&lt;br /&gt;      return JS_FALSE;&lt;br /&gt;&lt;br /&gt;  char *bytes = JS_GetStringBytes(str);&lt;br /&gt;  bytes = strdup(bytes);&lt;br /&gt;&lt;br /&gt;  fputs(bytes, stderr);&lt;br /&gt;  free(bytes);&lt;br /&gt;  return JS_TRUE;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now that I have defined the function in C/C++, all that remains is to call the JavaScript engine and tell it about the functions I have defined, associating them with the global object. This is done by calling JS_DefineFunctions():&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  JS_DefineFunctions(cx, m_glob, glob_functions);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That's all there is to it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-112224960566951695?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/112224960566951695/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=112224960566951695' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/112224960566951695'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/112224960566951695'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2005/07/turns-out-best-place-to-add-my-dump.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-112191707391876288</id><published>2005-07-20T20:31:00.000-07:00</published><updated>2005-08-30T08:06:09.846-07:00</updated><title type='text'></title><content type='html'>Thanks to a few sentences worth of help from robert ginda on #mozilla, and tons of walking thru mozilla breakpoints to see how XUL wires up command handlers and executes them, clicking a button now results in the following output:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;2005-07-20 20:30:14.653 layout[22453] Help! I've been clicked!&lt;br /&gt; Button::ButtonPressed success&lt;br /&gt; Button::ButtonPressed. return is a number&lt;br /&gt; Button::ButtonPressed. return is an int&lt;br /&gt; Button::ButtonPressed. return is a primitive&lt;br /&gt; Button::ButtonPressed return is 17&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The JS is:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;function Button1Click()&lt;br /&gt;{&lt;br /&gt;    return 17;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So, what's the big deal? My layout engine is calling onclick handlers for button elements, that's what!&lt;br /&gt;&lt;br /&gt;So, as a next step, I need to see if I can create an object that interfaces to C++ and provides logging services. Dumping output to stderr should be good enough. That way, I can write something like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;function Button1Click()&lt;br /&gt;{&lt;br /&gt;    logger.print("Button1Click: value is %d\n", 17);&lt;br /&gt;    return 17;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Fun stuff :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-112191707391876288?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/112191707391876288/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=112191707391876288' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/112191707391876288'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/112191707391876288'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2005/07/thanks-to-few-sentences-worth-of-help.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-112160831921729804</id><published>2005-07-17T06:50:00.000-07:00</published><updated>2005-07-17T07:22:08.586-07:00</updated><title type='text'></title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/1570/737/1600/hello.jpg"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://photos1.blogger.com/blogger/1570/737/320/hello.jpg" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;Here's the screen shot I was promising. See the previous post for the XML markup that implements this applet. The operating system is MacOS X 10.2.x&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-112160831921729804?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/112160831921729804/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=112160831921729804' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/112160831921729804'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/112160831921729804'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2005/07/heres-screen-shot-i-was-promising.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-14563877.post-112160690837903799</id><published>2005-07-17T06:23:00.001-07:00</published><updated>2006-12-28T21:13:42.550-08:00</updated><title type='text'></title><content type='html'>Some of you may know me from my previous book, Gtk+ Programming in C, and to a lesser degree, Developing Imaging Applications with XIElib. Both were published by Prentice Hall. You can find out more about either book at http://www.sydlogan.com, and purchase copies at amazon.com or other booksellers (the XIElib title is no longer being published, and I doubt there is really a market for it anymore. But copies can be found if you really want one. In fact, if you *really* want a copy and are having a hard time finding one, drop me a line, I've got one or two I would be we willing to sell).&lt;br /&gt;&lt;br /&gt;I'm even less well known for having written articles related to the two books above for various magazines during the mid to late 1990s. The most recent was a widget development article I did for C/C++ Users Journal back in 2002.&lt;br /&gt;&lt;br /&gt;So, enough with the past. On to the present. The topic of my current effort is cross platform development in C++.&lt;br /&gt;&lt;br /&gt;Basically, I will be writing about techniques that I learned during my tenure at Netscape during the development of the Mozilla/Netscape 6-7 codebase. I learned a lot working on the Netscape browser, and I plan to cover a wide variety of topics, including how to deal with source code management, build systems, makefiles, GUI toolkits, standards -- in a nutshell, everything that one must consider to do large scale cross-platform development.&lt;br /&gt;&lt;br /&gt;So far, about 300 pages have been written and submitted to my editor for an initial review, so I am well into the project. My focus now is more on coding and less on writing -- I'm currently in the thick of developing a simple XML-based cross platform GUI layout manager and toolkit that embeds the mozilla JavaScript engine. If this sounds a lot to you like mozilla's XUL, then you are correct, except for one big difference -- I am writing all of this completely from scratch. Mozilla is just too gnarly to describe; by writing my own, I hope to keep the code *very* small, and in the end, this should make it easier to describe, while allowing me to get the important points regarding the design of such a system across to the reader.&lt;br /&gt;&lt;br /&gt;XML markup isn't the only GUI discussed in the book; it includes perhaps the best available tutorial for the wxWidgets toolkit (http://www.wxwidgets.org).&lt;br /&gt;&lt;br /&gt;Returning out attention back to the layout engine, I made great strides this weekend in getting Mozilla's JavaScript engine wired into my layout engine. Right now, the layout engine supports a very small set of widgets: editable text, static text, window, and button are it for now. I have a DOM up and running that represents the document and its attributes, and a concrete widget factory and implementations for the above widgets based on Cocoa running on MacOS X, written in Objective-C++. The XML parsing is done, at least for the elements that are supported so far. All of this in about 1800 lines of code (like I said, a far cry from XUL and mozilla's layout engine, which is orders of magnitude larger). Though I am sure my code will increase quite a bit in size, of course.&lt;br /&gt;&lt;br /&gt;Here's a nice stack trace showing some of the excitement that happens when a button is pressed:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(gdb) b Button::ButtonPressed&lt;br /&gt;Breakpoint 1 at 0x663c: file button.cpp, line 87.&lt;br /&gt;(gdb) c&lt;br /&gt;Continuing.&lt;br /&gt;2005-07-17 05:36:37.431 layout[8564] Help! I've been clicked!&lt;br /&gt;&lt;br /&gt;Breakpoint 1, Button::ButtonPressed() (this=0x3b5b70) at button.cpp:87&lt;br /&gt;87          string val;&lt;br /&gt;(gdb) n&lt;br /&gt;89              attribute = GetAttributeByName(string("onclick"));&lt;br /&gt;(gdb) n&lt;br /&gt;90              if (attribute)  {&lt;br /&gt;(gdb) n&lt;br /&gt;91                      val = attribute-&gt;GetValue();&lt;br /&gt;(gdb) n&lt;br /&gt;93      }&lt;br /&gt;(gdb) print val&lt;br /&gt;$1 = {&lt;br /&gt;static npos = 4294967295,&lt;br /&gt;_M_dataplus = {&lt;br /&gt;&lt;allocator&gt;&lt;char&gt;&gt; = {&lt;no&gt;},&lt;br /&gt;members of _Alloc_hider:&lt;br /&gt;_M_p = 0x32db5c "Button1Click();"&lt;br /&gt;},&lt;br /&gt;static _S_empty_rep_storage = {0, 0, 0, 0}&lt;br /&gt;}&lt;br /&gt;(gdb) where&lt;br /&gt;#0  Button::ButtonPressed() (this=0x3b5b70) at button.cpp:93&lt;br /&gt;#1  0x00229bf0 in ButtonPressSubject::NotifyButtonPress() (this=0x3b5ba8) at ../subject.cpp:13&lt;br /&gt;#2  0x002284d0 in CocoaButtonImpl::HandleCommand() (this=0x3b5ba0) at cocoabuttonimpl.mm:55&lt;br /&gt;#3  0x0022928c in -[ButtonAction onClick:] (self=0x4956c0, _cmd=0x234f84, sender=0x495a20) at cocoabuttonaction.mm:15&lt;br /&gt;#4  0x930f9f5c in -[NSApplication sendAction:to:from:] ()&lt;br /&gt;#5  0x9311a718 in -[NSControl sendAction:to:] ()&lt;br /&gt;#6  0x9315eea4 in -[NSCell _sendActionFrom:] ()&lt;br /&gt;#7  0x930f3968 in -[NSCell trackMouse:inRect:ofView:untilMouseUp:] ()&lt;br /&gt;#8  0x9315eac0 in -[NSButtonCell trackMouse:inRect:ofView:untilMouseUp:] ()&lt;br /&gt;#9  0x9312e6b4 in -[NSControl mouseDown:] ()&lt;br /&gt;#10 0x930c1698 in -[NSWindow sendEvent:] ()&lt;br /&gt;#11 0x930a94ec in -[NSApplication sendEvent:] ()&lt;br /&gt;#12 0x930b2418 in -[NSApplication run] ()&lt;br /&gt;#13 0x002291b4 in CocoaAppImpl::MainLoop() (this=0x3a6780) at cocoaappimpl.mm:28&lt;br /&gt;#14 0x00008648 in App::MainLoop() (this=0x3a3250) at app.cpp:23&lt;br /&gt;#15 0x000039a4 in main (argc=2, argv=0xbffffb8c) at layout.cpp:259&lt;br /&gt;(gdb)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Objective-C++ is just plain cool; look at the jump from #3 in the stack, which is from a pure Objective-C object, to the code in #2, which is implemented in a C++ class. It's stuff like this that makes my life easier.&lt;br /&gt;&lt;br /&gt;As you can see, I grab the JS code associated with the button from a DOM attribute that was parsed from the XML (onclick="code"), and all that remains is calling the JS engine to execute the code (here, the code to execute is Button1Click(), take a look at the dump of val made in gdb).&lt;br /&gt;&lt;br /&gt;Here is the XML for the UI:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;?xml version="1.0"?&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;!--&lt;br /&gt;Copyright (C) 2004-2005 Syd Logan. All Rights Reserved.&lt;br /&gt;--&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;!DOCTYPE window SYSTEM "simple.dtd"&amp;gt;&lt;br /&gt;&amp;lt;window name="main" title="&amp;hello.title;" main="true" width="320" height="200" x="100" y="100" position="center, mouse" libraries="main,utils"&amp;gt;   &lt;br /&gt;    &amp;lt;script type="text/javascript" src="resources/content/simple.js"/&amp;gt;   &lt;br /&gt;    &amp;lt;text editable="true" selectable="true" string="&amp;text1.value;" width="100" height="40" x="10" y="70"/&amp;gt;   &lt;br /&gt;    &amp;lt;text editable="false" selectable="false" string="&amp;text2.value;" width="100" height="40" x="10" y="50"/&amp;gt;   &lt;br /&gt;    &amp;lt;button onclick="Button1Click();" label="&amp;button1.label;" width="100" height="40" x="10" y="10"/&amp;gt;   &lt;br /&gt;    &amp;lt;button onclick="Button2Click();" label="&amp;button2.label;" width="100" height="40" x="100" y="10"/&amp;gt;&lt;br /&gt;&amp;lt;/window&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The window described by this document displays static and editable text fields, and two buttons. The labels come from simple.dtd (not shown here), and the JS code associated with the onclick attributes of the buttons are implemented in the JS code that is pointed to by the script element. This is all very similar to XUL.&lt;br /&gt;&lt;br /&gt;If I can figure out how to add images to this blog, I'll post a screen shot showing what the above XML code looks like when implemented by my layout engine.&lt;br /&gt;&lt;br /&gt;One last thing -- notice that at this point that absolute position attributes (x, y) must be specified for each of the widgets to describe the layout of the window. My layout engine, at this point, implements only a static layout manager, thus this requirement for the XML to supply fixed positions. Of course, this stinks -- any decent modern GUI toolkit provides "box" or "grid" layout managers, both which support a far more powerful way of describing layouts. Don't worry -- I will certainly implement one (box) or both (box and grid) for this project.&lt;br /&gt;&lt;br /&gt;But first things first; I need to get the JS engine embedding work done, and wire the button and editable text widgets to their handlers (this is in addition to a few other places where JS is required, e.g., onload handlers that must fire when a document has been loaded). Stay tuned.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/14563877-112160690837903799?l=trixul.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://trixul.blogspot.com/feeds/112160690837903799/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=14563877&amp;postID=112160690837903799' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/112160690837903799'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/14563877/posts/default/112160690837903799'/><link rel='alternate' type='text/html' href='http://trixul.blogspot.com/2005/07/some-of-you-may-know-me-from-my.html' title=''/><author><name>Syd</name><uri>http://www.blogger.com/profile/00427089024677391384</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://3.bp.blogspot.com/_ClkTjB9nrHo/SQgRIBSs2mI/AAAAAAAAAAM/3iueJ_tMgSM/S220/me.jpg'/></author><thr:total>0</thr:total></entry></feed>
