Browse Source

Merge branch 'master' into helios-sunit

Nicolas Petton 11 years ago
parent
commit
e2c59b16c9

+ 23 - 8
css/helios.css

@@ -536,6 +536,7 @@ body[id="helios"] .key_helper .command strong {
 body[id="helios"] .key_helper #binding-helper-main {
   display: block;
   padding: 5px;
+  background: #eee;
 }
 body[id="helios"] .key_helper .label {
   padding: 1px 4px;
@@ -625,16 +626,13 @@ body[id="helios"] #helper {
 }
 body[id="helios"] #overlay {
   z-index: 2000;
-  background: rgba(112, 66, 20, 0.1);
+  background: transparent;
   position: fixed;
   top: 0;
   left: 0;
   right: 0;
   bottom: 0;
 }
-body[id="helios"] #overlay.light {
-  background: rgba(50, 50, 50, 0.1);
-}
 body[id="helios"] .confirmation,
 body[id="helios"] .dialog {
   z-index: 2001;
@@ -650,10 +648,10 @@ body[id="helios"] .dialog {
   left: 50%;
   margin-left: -135px;
   box-shadow: 0 0 6px #333;
-  transition: top .5s;
-  -webkit-transition: top .5s;
-  -moz-transition: top .5s;
-  -o-transition: top .5s;
+  transition: top .2s;
+  -webkit-transition: top .2s;
+  -moz-transition: top .2s;
+  -o-transition: top .2s;
 }
 body[id="helios"] .confirmation .hl_widget .form-actions,
 body[id="helios"] .dialog .hl_widget .form-actions {
@@ -816,3 +814,20 @@ body[id="helios"] .transcript textarea {
   padding: 0;
   border: 0;
 }
+body[id="helios"] .hl_debugger .tool_container {
+  top: 53px;
+}
+body[id="helios"] .hl_debugger .head {
+  position: absolute;
+  top: 23px;
+  width: 100%;
+  background: #0088cc url(/images/debugger.png) 10px center no-repeat;
+  border-bottom: 1px solid white;
+}
+body[id="helios"] .hl_debugger .head h2 {
+  font-size: 14px;
+  line-height: 30px;
+  padding: 0 30px;
+  margin: 0;
+  color: white;
+}

+ 30 - 9
css/helios.less

@@ -617,6 +617,7 @@ body[id="helios"] {
 		#binding-helper-main {
 			display: block;
 			padding: 5px;
+			background: #eee;
 		}
 
 		.label {
@@ -725,16 +726,12 @@ body[id="helios"] {
 
 	#overlay {
 		z-index: 2000;
-		background: rgba(112, 66, 20, 0.1);
+		background: transparent;
 		position: fixed;
 		top: 0;
 		left: 0;
 		right: 0;
 		bottom: 0;
-
-		&.light {
-			background: rgba(50, 50, 50, 0.1);
-		}
 	}
 
 	.confirmation, .dialog {
@@ -751,10 +748,10 @@ body[id="helios"] {
 		left: 50%;
 		margin-left: -135px;
 		box-shadow: 0 0 6px #333;
-		transition: top .5s;
-		-webkit-transition: top .5s;
-		-moz-transition: top .5s;
-		-o-transition: top .5s;
+		transition: top .2s;
+		-webkit-transition: top .2s;
+		-moz-transition: top .2s;
+		-o-transition: top .2s;
 
 		.hl_widget {
 
@@ -933,4 +930,28 @@ body[id="helios"] {
 		padding: 0;
 		border: 0;
 	}
+
+	.hl_debugger {
+		
+		.tool_container {
+			top: 53px;
+		}
+
+		.head {
+			position: absolute;
+			top: 23px;
+			width: 100%;
+			background: #08c url(/images/debugger.png) 10px center no-repeat;
+			border-bottom: 1px solid white;
+
+			h2 {
+				font-size: 14px;
+				line-height: 30px;
+				padding: 0 30px;
+				margin: 0;
+				color: white;
+			}
+		}
+	}
+
 }

+ 0 - 24
src/Compiler-Core.js

@@ -687,30 +687,6 @@ globals.Compiler.klass);
 
 smalltalk.addClass('DoIt', globals.Object, [], 'Compiler-Core');
 globals.DoIt.comment="`DoIt` is the class used to compile and evaluate expressions. See `Compiler >> evaluateExpression:`.";
-smalltalk.addMethod(
-smalltalk.method({
-selector: "foo",
-protocol: 'tests',
-fn: function (){
-var self=this;
-function $Array(){return globals.Array||(typeof Array=="undefined"?nil:Array)}
-return smalltalk.withContext(function($ctx1) { 
-var $2,$3,$1;
-$2=_st($Array())._new();
-_st($2)._add_((3));
-$ctx1.sendIdx["add:"]=1;
-_st($2)._add_((4));
-$3=_st($2)._yourself();
-$1=$3;
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"foo",{},globals.DoIt)})},
-args: [],
-source: "foo ^ Array new add: 3; add: 4; yourself",
-messageSends: ["add:", "new", "yourself"],
-referencedClasses: ["Array"]
-}),
-globals.DoIt);
-
 
 
 smalltalk.addClass('NodeVisitor', globals.Object, [], 'Compiler-Core');

+ 0 - 5
src/Compiler-Core.st

@@ -223,11 +223,6 @@ Object subclass: #DoIt
 !DoIt commentStamp!
 `DoIt` is the class used to compile and evaluate expressions. See `Compiler >> evaluateExpression:`.!
 
-!DoIt methodsFor: 'tests'!
-
-foo ^ Array new add: 3; add: 4; yourself
-! !
-
 Object subclass: #NodeVisitor
 	instanceVariableNames: ''
 	package: 'Compiler-Core'!

+ 67 - 3
src/Helios-Browser.js

@@ -1865,7 +1865,7 @@ return self._renderItemLabel_level_on_(aClass,anInteger,html);
 }, function($ctx3) {$ctx3.fillBlock({},$ctx2,2)})}));
 $5=_st($4)._onClick_((function(){
 return smalltalk.withContext(function($ctx3) {
-return self._activateListItem_(_st(li)._asJQuery());
+return self._reactivateListItem_(_st(li)._asJQuery());
 }, function($ctx3) {$ctx3.fillBlock({},$ctx2,3)})}));
 return $5;
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}));
@@ -1876,8 +1876,8 @@ return self._renderItem_level_on_(each,_st(anInteger).__plus((1)),html);
 }, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,4)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"renderItem:level:on:",{aClass:aClass,anInteger:anInteger,html:html,li:li},globals.HLClassesListWidget)})},
 args: ["aClass", "anInteger", "html"],
-source: "renderItem: aClass level: anInteger on: html\x0a\x09| li |\x0a    \x0a\x09li := html li.\x0a\x09li asJQuery data: 'item' put: aClass.\x0a    li\x0a\x09\x09class: (self listCssClassForItem: aClass);\x0a\x09\x09with: [ \x0a        \x09html a\x0a            \x09with: [ \x0a            \x09\x09(html tag: 'i') class: (self cssClassForItem: aClass).\x0a  \x09\x09\x09\x09\x09self renderItemLabel: aClass level: anInteger on: html ];\x0a\x09\x09\x09\x09onClick: [\x0a                  \x09self activateListItem: li asJQuery ] ].\x0a                    \x0a    (self getChildrenOf: aClass) do: [ :each |\x0a    \x09self renderItem: each level: anInteger + 1 on: html ]",
-messageSends: ["li", "data:put:", "asJQuery", "class:", "listCssClassForItem:", "with:", "a", "tag:", "cssClassForItem:", "renderItemLabel:level:on:", "onClick:", "activateListItem:", "do:", "getChildrenOf:", "renderItem:level:on:", "+"],
+source: "renderItem: aClass level: anInteger on: html\x0a\x09| li |\x0a    \x0a\x09li := html li.\x0a\x09li asJQuery data: 'item' put: aClass.\x0a    li\x0a\x09\x09class: (self listCssClassForItem: aClass);\x0a\x09\x09with: [ \x0a        \x09html a\x0a            \x09with: [ \x0a            \x09\x09(html tag: 'i') class: (self cssClassForItem: aClass).\x0a  \x09\x09\x09\x09\x09self renderItemLabel: aClass level: anInteger on: html ];\x0a\x09\x09\x09\x09onClick: [\x0a                  \x09self reactivateListItem: li asJQuery ] ].\x0a                    \x0a    (self getChildrenOf: aClass) do: [ :each |\x0a    \x09self renderItem: each level: anInteger + 1 on: html ]",
+messageSends: ["li", "data:put:", "asJQuery", "class:", "listCssClassForItem:", "with:", "a", "tag:", "cssClassForItem:", "renderItemLabel:level:on:", "onClick:", "reactivateListItem:", "do:", "getChildrenOf:", "renderItem:level:on:", "+"],
 referencedClasses: []
 }),
 globals.HLClassesListWidget);
@@ -1962,6 +1962,22 @@ referencedClasses: []
 }),
 globals.HLClassesListWidget);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "reselectItem:",
+protocol: 'actions',
+fn: function (anItem){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+_st(self._model())._forceSelectedClass_(anItem);
+return self}, function($ctx1) {$ctx1.fill(self,"reselectItem:",{anItem:anItem},globals.HLClassesListWidget)})},
+args: ["anItem"],
+source: "reselectItem: anItem\x0a\x09self model forceSelectedClass: anItem",
+messageSends: ["forceSelectedClass:", "model"],
+referencedClasses: []
+}),
+globals.HLClassesListWidget);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "selectItem:",
@@ -3178,6 +3194,22 @@ referencedClasses: []
 }),
 globals.HLMethodsListWidget);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "reselectItem:",
+protocol: 'actions',
+fn: function (aSelector){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+_st(self._model())._forceSelectedMethod_(self._methodForSelector_(aSelector));
+return self}, function($ctx1) {$ctx1.fill(self,"reselectItem:",{aSelector:aSelector},globals.HLMethodsListWidget)})},
+args: ["aSelector"],
+source: "reselectItem: aSelector\x0a\x09self model forceSelectedMethod: (self methodForSelector: aSelector)",
+messageSends: ["forceSelectedMethod:", "model", "methodForSelector:"],
+referencedClasses: []
+}),
+globals.HLMethodsListWidget);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "selectItem:",
@@ -3560,6 +3592,22 @@ referencedClasses: []
 }),
 globals.HLPackagesListWidget);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "reselectItem:",
+protocol: 'actions',
+fn: function (anItem){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+_st(self._model())._forceSelectedPackage_(anItem);
+return self}, function($ctx1) {$ctx1.fill(self,"reselectItem:",{anItem:anItem},globals.HLPackagesListWidget)})},
+args: ["anItem"],
+source: "reselectItem: anItem\x0a\x09self model forceSelectedPackage: anItem",
+messageSends: ["forceSelectedPackage:", "model"],
+referencedClasses: []
+}),
+globals.HLPackagesListWidget);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "selectItem:",
@@ -3853,6 +3901,22 @@ referencedClasses: []
 }),
 globals.HLProtocolsListWidget);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "reselectItem:",
+protocol: 'actions',
+fn: function (anItem){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+_st(self._model())._forceSelectedProtocol_(anItem);
+return self}, function($ctx1) {$ctx1.fill(self,"reselectItem:",{anItem:anItem},globals.HLProtocolsListWidget)})},
+args: ["anItem"],
+source: "reselectItem: anItem\x0a\x09self model forceSelectedProtocol: anItem",
+messageSends: ["forceSelectedProtocol:", "model"],
+referencedClasses: []
+}),
+globals.HLProtocolsListWidget);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "selectItem:",

+ 17 - 1
src/Helios-Browser.st

@@ -503,6 +503,10 @@ observeSystem
 		to: self
 !
 
+reselectItem: anItem
+	self model forceSelectedClass: anItem
+!
+
 selectItem: aClass
     self model selectedClass: aClass
 !
@@ -688,7 +692,7 @@ renderItem: aClass level: anInteger on: html
             		(html tag: 'i') class: (self cssClassForItem: aClass).
   					self renderItemLabel: aClass level: anInteger on: html ];
 				onClick: [
-                  	self activateListItem: li asJQuery ] ].
+                  	self reactivateListItem: li asJQuery ] ].
                     
     (self getChildrenOf: aClass) do: [ :each |
     	self renderItem: each level: anInteger + 1 on: html ]
@@ -1007,6 +1011,10 @@ observeSystem
 		to: self
 !
 
+reselectItem: aSelector
+	self model forceSelectedMethod: (self methodForSelector: aSelector)
+!
+
 selectItem: aSelector
 	aSelector ifNil: [ ^ self model selectedMethod: nil ].
 
@@ -1192,6 +1200,10 @@ observeSystem
 		to: self
 !
 
+reselectItem: anItem
+	self model forceSelectedPackage: anItem
+!
+
 selectItem: aPackage
 	super selectItem: aPackage.
 	self model selectedPackage: aPackage
@@ -1303,6 +1315,10 @@ observeSystem
 		to: self
 !
 
+reselectItem: anItem
+	self model forceSelectedProtocol: anItem
+!
+
 selectItem: aString
     self model selectedProtocol: aString
 ! !

+ 303 - 60
src/Helios-Core.js

@@ -158,16 +158,20 @@ var self=this;
 function $HLAboutToChange(){return globals.HLAboutToChange||(typeof HLAboutToChange=="undefined"?nil:HLAboutToChange)}
 function $HLChangeForbidden(){return globals.HLChangeForbidden||(typeof HLChangeForbidden=="undefined"?nil:HLChangeForbidden)}
 return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
 _st((function(){
 return smalltalk.withContext(function($ctx2) {
-_st(self._announcer())._announce_(_st(_st($HLAboutToChange())._new())._actionBlock_(aBlock));
+$1=_st($HLAboutToChange())._new();
+_st($1)._actionBlock_(aBlock);
+$2=_st($1)._yourself();
+_st(self._announcer())._announce_($2);
 return _st(aBlock)._value();
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}))._on_do_($HLChangeForbidden(),(function(ex){
 }));
 return self}, function($ctx1) {$ctx1.fill(self,"withChangesDo:",{aBlock:aBlock},globals.HLModel)})},
 args: ["aBlock"],
-source: "withChangesDo: aBlock\x0a\x09[ \x0a\x09\x09self announcer announce: (HLAboutToChange new\x0a\x09\x09\x09actionBlock: aBlock).\x0a\x09\x09aBlock value.\x0a\x09]\x0a\x09\x09on: HLChangeForbidden \x0a\x09\x09do: [ :ex | ]",
-messageSends: ["on:do:", "announce:", "announcer", "actionBlock:", "new", "value"],
+source: "withChangesDo: aBlock\x0a\x09[ \x0a\x09\x09self announcer announce: (HLAboutToChange new\x0a\x09\x09\x09actionBlock: aBlock;\x0a\x09\x09\x09yourself).\x0a\x09\x09aBlock value.\x0a\x09]\x0a\x09\x09on: HLChangeForbidden \x0a\x09\x09do: [ :ex | ]",
+messageSends: ["on:do:", "announce:", "announcer", "actionBlock:", "new", "yourself", "value"],
 referencedClasses: ["HLAboutToChange", "HLChangeForbidden"]
 }),
 globals.HLModel);
@@ -505,6 +509,98 @@ referencedClasses: []
 }),
 globals.HLToolModel);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "forceSelectedClass:",
+protocol: 'accessing',
+fn: function (aClass){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+self._withChangesDo_((function(){
+return smalltalk.withContext(function($ctx2) {
+self._selectedClass_(nil);
+$ctx2.sendIdx["selectedClass:"]=1;
+$1=self._selectedClass_(aClass);
+return $1;
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"forceSelectedClass:",{aClass:aClass},globals.HLToolModel)})},
+args: ["aClass"],
+source: "forceSelectedClass: aClass\x0a\x09self withChangesDo: [\x0a\x09\x09self \x09\x0a\x09\x09\x09selectedClass: nil;\x0a\x09\x09\x09selectedClass: aClass ]",
+messageSends: ["withChangesDo:", "selectedClass:"],
+referencedClasses: []
+}),
+globals.HLToolModel);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "forceSelectedMethod:",
+protocol: 'accessing',
+fn: function (aMethod){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+self._withChangesDo_((function(){
+return smalltalk.withContext(function($ctx2) {
+self._selectedMethod_(nil);
+$ctx2.sendIdx["selectedMethod:"]=1;
+$1=self._selectedMethod_(aMethod);
+return $1;
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"forceSelectedMethod:",{aMethod:aMethod},globals.HLToolModel)})},
+args: ["aMethod"],
+source: "forceSelectedMethod: aMethod\x0a\x09self withChangesDo: [\x0a\x09\x09self \x09\x0a\x09\x09\x09selectedMethod: nil;\x0a\x09\x09\x09selectedMethod: aMethod ]",
+messageSends: ["withChangesDo:", "selectedMethod:"],
+referencedClasses: []
+}),
+globals.HLToolModel);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "forceSelectedPackage:",
+protocol: 'accessing',
+fn: function (aPackage){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+self._withChangesDo_((function(){
+return smalltalk.withContext(function($ctx2) {
+self._selectedPackage_(nil);
+$ctx2.sendIdx["selectedPackage:"]=1;
+$1=self._selectedPackage_(aPackage);
+return $1;
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"forceSelectedPackage:",{aPackage:aPackage},globals.HLToolModel)})},
+args: ["aPackage"],
+source: "forceSelectedPackage: aPackage\x0a\x09self withChangesDo: [\x0a\x09\x09self \x09\x0a\x09\x09\x09selectedPackage: nil;\x0a\x09\x09\x09selectedPackage: aPackage ]",
+messageSends: ["withChangesDo:", "selectedPackage:"],
+referencedClasses: []
+}),
+globals.HLToolModel);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "forceSelectedProtocol:",
+protocol: 'accessing',
+fn: function (aProtocol){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+self._withChangesDo_((function(){
+return smalltalk.withContext(function($ctx2) {
+self._selectedProtocol_(nil);
+$ctx2.sendIdx["selectedProtocol:"]=1;
+$1=self._selectedProtocol_(aProtocol);
+return $1;
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"forceSelectedProtocol:",{aProtocol:aProtocol},globals.HLToolModel)})},
+args: ["aProtocol"],
+source: "forceSelectedProtocol: aProtocol\x0a\x09self withChangesDo: [\x0a\x09\x09self \x09\x0a\x09\x09\x09selectedProtocol: nil;\x0a\x09\x09\x09selectedProtocol: aProtocol ]",
+messageSends: ["withChangesDo:", "selectedProtocol:"],
+referencedClasses: []
+}),
+globals.HLToolModel);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "handleCompileError:",
@@ -1696,6 +1792,37 @@ referencedClasses: []
 }),
 globals.HLWidget);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "confirm:ifTrue:ifFalse:",
+protocol: 'actions',
+fn: function (aString,aBlock,anotherBlock){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+_st(self._manager())._confirm_ifTrue_ifFalse_(aString,aBlock,anotherBlock);
+return self}, function($ctx1) {$ctx1.fill(self,"confirm:ifTrue:ifFalse:",{aString:aString,aBlock:aBlock,anotherBlock:anotherBlock},globals.HLWidget)})},
+args: ["aString", "aBlock", "anotherBlock"],
+source: "confirm: aString ifTrue: aBlock ifFalse: anotherBlock\x0a\x09self manager \x0a\x09\x09confirm: aString \x0a\x09\x09ifTrue: aBlock\x0a\x09\x09ifFalse: anotherBlock",
+messageSends: ["confirm:ifTrue:ifFalse:", "manager"],
+referencedClasses: []
+}),
+globals.HLWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "cssClass",
+protocol: 'accessing',
+fn: function (){
+var self=this;
+return "hl_widget";
+},
+args: [],
+source: "cssClass\x0a\x09^ 'hl_widget'",
+messageSends: [],
+referencedClasses: []
+}),
+globals.HLWidget);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "execute:",
@@ -1836,15 +1963,19 @@ protocol: 'rendering',
 fn: function (html){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-self["@wrapper"]=_st(html)._div();
+var $1,$2;
+$1=_st(html)._div();
+_st($1)._class_(self._cssClass());
+$2=_st($1)._yourself();
+self["@wrapper"]=$2;
 _st((function(renderer){
 return smalltalk.withContext(function($ctx2) {
 return self._renderContentOn_(renderer);
 }, function($ctx2) {$ctx2.fillBlock({renderer:renderer},$ctx1,1)})}))._appendToJQuery_(_st(self["@wrapper"])._asJQuery());
 return self}, function($ctx1) {$ctx1.fill(self,"renderOn:",{html:html},globals.HLWidget)})},
 args: ["html"],
-source: "renderOn: html\x0a\x09wrapper := html div.\x0a    [ :renderer | self renderContentOn: renderer ] appendToJQuery: wrapper asJQuery",
-messageSends: ["div", "appendToJQuery:", "renderContentOn:", "asJQuery"],
+source: "renderOn: html\x0a\x09wrapper := html div\x0a\x09\x09class: self cssClass;\x0a\x09\x09yourself.\x0a    [ :renderer | self renderContentOn: renderer ] appendToJQuery: wrapper asJQuery",
+messageSends: ["class:", "div", "cssClass", "yourself", "appendToJQuery:", "renderContentOn:", "asJQuery"],
 referencedClasses: []
 }),
 globals.HLWidget);
@@ -2146,7 +2277,7 @@ var self=this;
 return smalltalk.withContext(function($ctx1) { 
 var $1,$2,$3,$5,$4,$6,$7;
 $1=_st(html)._div();
-_st($1)._class_("hl_widget");
+_st($1)._class_(self._cssClass());
 $2=_st($1)._yourself();
 self["@wrapper"]=$2;
 _st(self["@wrapper"])._with_((function(){
@@ -2171,8 +2302,8 @@ return _st(_st(self._wrapper())._asJQuery())._addClass_(self._focusClass());
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1,3)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"renderOn:",{html:html},globals.HLFocusableWidget)})},
 args: ["html"],
-source: "renderOn: html\x0a    wrapper := html div \x0a    \x09class: 'hl_widget';\x0a\x09\x09yourself.\x0a\x09\x09\x0a       wrapper with: [ self renderContentOn: html ].\x0a\x09\x0a\x09wrapper\x0a\x09\x09at: 'tabindex' put: '0';\x0a\x09\x09onBlur: [ self wrapper asJQuery removeClass: self focusClass ];\x0a        onFocus: [ self wrapper asJQuery addClass: self focusClass ]",
-messageSends: ["class:", "div", "yourself", "with:", "renderContentOn:", "at:put:", "onBlur:", "removeClass:", "asJQuery", "wrapper", "focusClass", "onFocus:", "addClass:"],
+source: "renderOn: html\x0a    wrapper := html div \x0a    \x09class: self cssClass;\x0a\x09\x09yourself.\x0a\x09\x09\x0a       wrapper with: [ self renderContentOn: html ].\x0a\x09\x0a\x09wrapper\x0a\x09\x09at: 'tabindex' put: '0';\x0a\x09\x09onBlur: [ self wrapper asJQuery removeClass: self focusClass ];\x0a        onFocus: [ self wrapper asJQuery addClass: self focusClass ]",
+messageSends: ["class:", "div", "cssClass", "yourself", "with:", "renderContentOn:", "at:put:", "onBlur:", "removeClass:", "asJQuery", "wrapper", "focusClass", "onFocus:", "addClass:"],
 referencedClasses: []
 }),
 globals.HLFocusableWidget);
@@ -2523,6 +2654,23 @@ referencedClasses: []
 }),
 globals.HLListWidget);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "reactivateListItem:",
+protocol: 'actions',
+fn: function (aListItem){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._activateListItem_(aListItem);
+self._reselectItem_(self._selectedItem());
+return self}, function($ctx1) {$ctx1.fill(self,"reactivateListItem:",{aListItem:aListItem},globals.HLListWidget)})},
+args: ["aListItem"],
+source: "reactivateListItem: aListItem\x0a\x09self activateListItem: aListItem.\x0a\x09self reselectItem: self selectedItem",
+messageSends: ["activateListItem:", "reselectItem:", "selectedItem"],
+referencedClasses: []
+}),
+globals.HLListWidget);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "refresh",
@@ -2618,15 +2766,15 @@ return self._renderItemLabel_on_(anObject,html);
 }, function($ctx3) {$ctx3.fillBlock({},$ctx2,2)})}));
 $5=_st($4)._onClick_((function(){
 return smalltalk.withContext(function($ctx3) {
-return self._activateListItem_(_st(li)._asJQuery());
+return self._reactivateListItem_(_st(li)._asJQuery());
 }, function($ctx3) {$ctx3.fillBlock({},$ctx2,3)})}));
 return $5;
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}));
 $ctx1.sendIdx["with:"]=1;
 return self}, function($ctx1) {$ctx1.fill(self,"renderItem:on:",{anObject:anObject,html:html,li:li},globals.HLListWidget)})},
 args: ["anObject", "html"],
-source: "renderItem: anObject on: html\x0a\x09| li |\x0a    \x0a\x09li := html li.\x0a\x09li asJQuery data: 'item' put: anObject.\x0a    li\x0a\x09\x09class: (self listCssClassForItem: anObject);\x0a        with: [ \x0a        \x09html a\x0a            \x09with: [ \x0a            \x09\x09(html tag: 'i') class: (self cssClassForItem: anObject).\x0a  \x09\x09\x09\x09\x09self renderItemLabel: anObject on: html ];\x0a\x09\x09\x09\x09onClick: [\x0a                  \x09self activateListItem: li asJQuery ] ]",
-messageSends: ["li", "data:put:", "asJQuery", "class:", "listCssClassForItem:", "with:", "a", "tag:", "cssClassForItem:", "renderItemLabel:on:", "onClick:", "activateListItem:"],
+source: "renderItem: anObject on: html\x0a\x09| li |\x0a    \x0a\x09li := html li.\x0a\x09li asJQuery data: 'item' put: anObject.\x0a    li\x0a\x09\x09class: (self listCssClassForItem: anObject);\x0a        with: [ \x0a        \x09html a\x0a            \x09with: [ \x0a            \x09\x09(html tag: 'i') class: (self cssClassForItem: anObject).\x0a  \x09\x09\x09\x09\x09self renderItemLabel: anObject on: html ];\x0a\x09\x09\x09\x09onClick: [\x0a                  \x09self reactivateListItem: li asJQuery ] ]",
+messageSends: ["li", "data:put:", "asJQuery", "class:", "listCssClassForItem:", "with:", "a", "tag:", "cssClassForItem:", "renderItemLabel:on:", "onClick:", "reactivateListItem:"],
 referencedClasses: []
 }),
 globals.HLListWidget);
@@ -2666,6 +2814,20 @@ referencedClasses: []
 }),
 globals.HLListWidget);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "reselectItem:",
+protocol: 'actions',
+fn: function (anObject){
+var self=this;
+return self},
+args: ["anObject"],
+source: "reselectItem: anObject\x0a\x09",
+messageSends: [],
+referencedClasses: []
+}),
+globals.HLListWidget);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "selectItem:",
@@ -2722,7 +2884,7 @@ fn: function (){
 var self=this;
 function $HLRepeatedKeyDownHandler(){return globals.HLRepeatedKeyDownHandler||(typeof HLRepeatedKeyDownHandler=="undefined"?nil:HLRepeatedKeyDownHandler)}
 return smalltalk.withContext(function($ctx1) { 
-var $1,$2;
+var $1,$2,$3;
 $1=_st($HLRepeatedKeyDownHandler())._on_(self);
 _st($1)._whileKeyDown_do_((38),(function(){
 return smalltalk.withContext(function($ctx2) {
@@ -2734,10 +2896,17 @@ return smalltalk.withContext(function($ctx2) {
 return self._activateNextListItem();
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1,2)})}));
 $2=_st($1)._rebindKeys();
+_st(_st(self._wrapper())._asJQuery())._keydown_((function(e){
+return smalltalk.withContext(function($ctx2) {
+$3=_st(_st(e)._which()).__eq((13));
+if(smalltalk.assert($3)){
+return self._reselectItem_(self._selectedItem());
+};
+}, function($ctx2) {$ctx2.fillBlock({e:e},$ctx1,3)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"setupKeyBindings",{},globals.HLListWidget)})},
 args: [],
-source: "setupKeyBindings \x0a\x09(HLRepeatedKeyDownHandler on: self)\x0a\x09\x09whileKeyDown: 38 do: [ self activatePreviousListItem ];\x0a\x09\x09whileKeyDown: 40 do: [ self activateNextListItem ];\x0a\x09\x09rebindKeys",
-messageSends: ["whileKeyDown:do:", "on:", "activatePreviousListItem", "activateNextListItem", "rebindKeys"],
+source: "setupKeyBindings \x0a\x09(HLRepeatedKeyDownHandler on: self)\x0a\x09\x09whileKeyDown: 38 do: [ self activatePreviousListItem ];\x0a\x09\x09whileKeyDown: 40 do: [ self activateNextListItem ];\x0a\x09\x09rebindKeys.\x0a\x09\x09\x0a\x09self wrapper asJQuery keydown: [ :e |\x0a        e which = 13 ifTrue: [ \x0a        \x09self reselectItem: self selectedItem ] ]",
+messageSends: ["whileKeyDown:do:", "on:", "activatePreviousListItem", "activateNextListItem", "rebindKeys", "keydown:", "asJQuery", "wrapper", "ifTrue:", "=", "which", "reselectItem:", "selectedItem"],
 referencedClasses: ["HLRepeatedKeyDownHandler"]
 }),
 globals.HLListWidget);
@@ -3093,6 +3262,25 @@ referencedClasses: []
 }),
 globals.HLToolListWidget);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "reactivateListItem:",
+protocol: 'actions',
+fn: function (anItem){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+_st(self._model())._withChangesDo_((function(){
+return smalltalk.withContext(function($ctx2) {
+return globals.HLToolListWidget.superclass.fn.prototype._reactivateListItem_.apply(_st(self), [anItem]);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"reactivateListItem:",{anItem:anItem},globals.HLToolListWidget)})},
+args: ["anItem"],
+source: "reactivateListItem: anItem\x0a\x09self model withChangesDo: [ super reactivateListItem: anItem ]",
+messageSends: ["withChangesDo:", "model", "reactivateListItem:"],
+referencedClasses: []
+}),
+globals.HLToolListWidget);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "renderContentOn:",
@@ -3446,18 +3634,14 @@ selector: "confirm:ifFalse:",
 protocol: 'actions',
 fn: function (aString,aBlock){
 var self=this;
-function $HLConfirmationWidget(){return globals.HLConfirmationWidget||(typeof HLConfirmationWidget=="undefined"?nil:HLConfirmationWidget)}
 return smalltalk.withContext(function($ctx1) { 
-var $1,$2;
-$1=_st($HLConfirmationWidget())._new();
-_st($1)._confirmationString_(aString);
-_st($1)._cancelBlock_(aBlock);
-$2=_st($1)._show();
+self._confirm_ifTrue_ifFalse_(aString,(function(){
+}),aBlock);
 return self}, function($ctx1) {$ctx1.fill(self,"confirm:ifFalse:",{aString:aString,aBlock:aBlock},globals.HLManager)})},
 args: ["aString", "aBlock"],
-source: "confirm: aString ifFalse: aBlock\x0a\x09HLConfirmationWidget new\x0a\x09\x09confirmationString: aString;\x0a\x09\x09cancelBlock: aBlock;\x0a\x09\x09show",
-messageSends: ["confirmationString:", "new", "cancelBlock:", "show"],
-referencedClasses: ["HLConfirmationWidget"]
+source: "confirm: aString ifFalse: aBlock\x0a\x09self \x0a\x09\x09confirm: aString\x0a\x09\x09ifTrue: []\x0a\x09\x09ifFalse: aBlock",
+messageSends: ["confirm:ifTrue:ifFalse:"],
+referencedClasses: []
 }),
 globals.HLManager);
 
@@ -3467,17 +3651,35 @@ selector: "confirm:ifTrue:",
 protocol: 'actions',
 fn: function (aString,aBlock){
 var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._confirm_ifTrue_ifFalse_(aString,aBlock,(function(){
+}));
+return self}, function($ctx1) {$ctx1.fill(self,"confirm:ifTrue:",{aString:aString,aBlock:aBlock},globals.HLManager)})},
+args: ["aString", "aBlock"],
+source: "confirm: aString ifTrue: aBlock\x0a\x09self \x0a\x09\x09confirm: aString\x0a\x09\x09ifTrue: aBlock\x0a\x09\x09ifFalse: []",
+messageSends: ["confirm:ifTrue:ifFalse:"],
+referencedClasses: []
+}),
+globals.HLManager);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "confirm:ifTrue:ifFalse:",
+protocol: 'actions',
+fn: function (aString,aBlock,anotherBlock){
+var self=this;
 function $HLConfirmationWidget(){return globals.HLConfirmationWidget||(typeof HLConfirmationWidget=="undefined"?nil:HLConfirmationWidget)}
 return smalltalk.withContext(function($ctx1) { 
 var $1,$2;
 $1=_st($HLConfirmationWidget())._new();
 _st($1)._confirmationString_(aString);
 _st($1)._actionBlock_(aBlock);
+_st($1)._cancelBlock_(anotherBlock);
 $2=_st($1)._show();
-return self}, function($ctx1) {$ctx1.fill(self,"confirm:ifTrue:",{aString:aString,aBlock:aBlock},globals.HLManager)})},
-args: ["aString", "aBlock"],
-source: "confirm: aString ifTrue: aBlock\x0a\x09HLConfirmationWidget new\x0a\x09\x09confirmationString: aString;\x0a\x09\x09actionBlock: aBlock;\x0a\x09\x09show",
-messageSends: ["confirmationString:", "new", "actionBlock:", "show"],
+return self}, function($ctx1) {$ctx1.fill(self,"confirm:ifTrue:ifFalse:",{aString:aString,aBlock:aBlock,anotherBlock:anotherBlock},globals.HLManager)})},
+args: ["aString", "aBlock", "anotherBlock"],
+source: "confirm: aString ifTrue: aBlock ifFalse: anotherBlock\x0a\x09HLConfirmationWidget new\x0a\x09\x09confirmationString: aString;\x0a\x09\x09actionBlock: aBlock;\x0a\x09\x09cancelBlock: anotherBlock;\x0a\x09\x09show",
+messageSends: ["confirmationString:", "new", "actionBlock:", "cancelBlock:", "show"],
 referencedClasses: ["HLConfirmationWidget"]
 }),
 globals.HLManager);
@@ -3625,26 +3827,6 @@ referencedClasses: ["HLKeyBinder"]
 }),
 globals.HLManager);
 
-smalltalk.addMethod(
-smalltalk.method({
-selector: "refresh",
-protocol: 'rendering',
-fn: function (){
-var self=this;
-return smalltalk.withContext(function($ctx1) { 
-var $1;
-$1=".navbar"._asJQuery();
-$ctx1.sendIdx["asJQuery"]=1;
-_st($1)._remove();
-self._appendToJQuery_("body"._asJQuery());
-return self}, function($ctx1) {$ctx1.fill(self,"refresh",{},globals.HLManager)})},
-args: [],
-source: "refresh\x0a\x09'.navbar' asJQuery remove.\x0a\x09self appendToJQuery: 'body' asJQuery",
-messageSends: ["remove", "asJQuery", "appendToJQuery:"],
-referencedClasses: []
-}),
-globals.HLManager);
-
 smalltalk.addMethod(
 smalltalk.method({
 selector: "registerErrorHandler",
@@ -4203,7 +4385,7 @@ referencedClasses: []
 globals.HLManager.klass);
 
 
-smalltalk.addClass('HLModalWidget', globals.HLWidget, [], 'Helios-Core');
+smalltalk.addClass('HLModalWidget', globals.HLWidget, ['confirmButtonLabel', 'cancelButtonLabel'], 'Helios-Core');
 globals.HLModalWidget.comment="I implement an abstract modal widget.";
 smalltalk.addMethod(
 smalltalk.method({
@@ -4221,6 +4403,44 @@ referencedClasses: []
 }),
 globals.HLModalWidget);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "cancelButtonLabel",
+protocol: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $2,$1;
+$2=self["@cancelButtonLabel"];
+if(($receiver = $2) == nil || $receiver == null){
+$1="Cancel";
+} else {
+$1=$2;
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"cancelButtonLabel",{},globals.HLModalWidget)})},
+args: [],
+source: "cancelButtonLabel\x0a\x09^ cancelButtonLabel ifNil: [ 'Cancel' ]",
+messageSends: ["ifNil:"],
+referencedClasses: []
+}),
+globals.HLModalWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "cancelButtonLabel:",
+protocol: 'accessing',
+fn: function (anObject){
+var self=this;
+self["@cancelButtonLabel"]=anObject;
+return self},
+args: ["anObject"],
+source: "cancelButtonLabel: anObject\x0a\x09cancelButtonLabel := anObject",
+messageSends: [],
+referencedClasses: []
+}),
+globals.HLModalWidget);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "confirm",
@@ -4239,14 +4459,37 @@ globals.HLModalWidget);
 
 smalltalk.addMethod(
 smalltalk.method({
-selector: "cssClass",
+selector: "confirmButtonLabel",
 protocol: 'accessing',
 fn: function (){
 var self=this;
-return "";
-},
+return smalltalk.withContext(function($ctx1) { 
+var $2,$1;
+$2=self["@confirmButtonLabel"];
+if(($receiver = $2) == nil || $receiver == null){
+$1="Confirm";
+} else {
+$1=$2;
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"confirmButtonLabel",{},globals.HLModalWidget)})},
 args: [],
-source: "cssClass\x0a\x09^ ''",
+source: "confirmButtonLabel\x0a\x09^ confirmButtonLabel ifNil: [ 'Confirm' ]",
+messageSends: ["ifNil:"],
+referencedClasses: []
+}),
+globals.HLModalWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "confirmButtonLabel:",
+protocol: 'accessing',
+fn: function (anObject){
+var self=this;
+self["@confirmButtonLabel"]=anObject;
+return self},
+args: ["anObject"],
+source: "confirmButtonLabel: anObject\x0a\x09confirmButtonLabel := anObject",
 messageSends: [],
 referencedClasses: []
 }),
@@ -4300,11 +4543,11 @@ $2="#overlay"._asJQuery();
 $ctx2.sendIdx["asJQuery"]=2;
 _st($2)._remove();
 $ctx2.sendIdx["remove"]=1;
-return _st(".dialog"._asJQuery())._remove();
+return _st(_st(self["@wrapper"])._asJQuery())._remove();
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}))._valueWithTimeout_((300));
 return self}, function($ctx1) {$ctx1.fill(self,"remove",{},globals.HLModalWidget)})},
 args: [],
-source: "remove\x0a\x09'.dialog' asJQuery removeClass: 'active'.\x0a\x09[ \x0a\x09\x09'#overlay' asJQuery remove.\x0a\x09\x09'.dialog' asJQuery remove\x0a\x09] valueWithTimeout: 300",
+source: "remove\x0a\x09'.dialog' asJQuery removeClass: 'active'.\x0a\x09[ \x0a\x09\x09'#overlay' asJQuery remove.\x0a\x09\x09wrapper asJQuery remove\x0a\x09] valueWithTimeout: 300",
 messageSends: ["removeClass:", "asJQuery", "valueWithTimeout:", "remove"],
 referencedClasses: []
 }),
@@ -4328,7 +4571,7 @@ $3=_st(html)._button();
 $ctx2.sendIdx["button"]=1;
 _st($3)._class_("button");
 $ctx2.sendIdx["class:"]=2;
-_st($3)._with_("Cancel");
+_st($3)._with_(self._cancelButtonLabel());
 $ctx2.sendIdx["with:"]=2;
 $4=_st($3)._onClick_((function(){
 return smalltalk.withContext(function($ctx3) {
@@ -4338,7 +4581,7 @@ $ctx2.sendIdx["onClick:"]=1;
 $4;
 $5=_st(html)._button();
 _st($5)._class_("button default");
-_st($5)._with_("Confirm");
+_st($5)._with_(self._confirmButtonLabel());
 $6=_st($5)._onClick_((function(){
 return smalltalk.withContext(function($ctx3) {
 return self._confirm();
@@ -4350,8 +4593,8 @@ $ctx1.sendIdx["with:"]=1;
 self._giveFocusToButton_(confirmButton);
 return self}, function($ctx1) {$ctx1.fill(self,"renderButtonsOn:",{html:html,confirmButton:confirmButton},globals.HLModalWidget)})},
 args: ["html"],
-source: "renderButtonsOn: html\x0a\x09| confirmButton |\x0a\x09\x0a\x09html div \x0a\x09\x09class: 'buttons';\x0a\x09\x09with: [\x0a\x09\x09\x09html button\x0a\x09\x09\x09\x09class: 'button';\x0a\x09\x09\x09\x09with: 'Cancel';\x0a\x09\x09\x09\x09onClick: [ self cancel ].\x0a\x09\x09\x09confirmButton := html button\x0a\x09\x09\x09\x09class: 'button default';\x0a\x09\x09\x09\x09with: 'Confirm';\x0a\x09\x09\x09\x09onClick: [ self confirm ] ].\x0a\x0a\x09self giveFocusToButton:confirmButton",
-messageSends: ["class:", "div", "with:", "button", "onClick:", "cancel", "confirm", "giveFocusToButton:"],
+source: "renderButtonsOn: html\x0a\x09| confirmButton |\x0a\x09\x0a\x09html div \x0a\x09\x09class: 'buttons';\x0a\x09\x09with: [\x0a\x09\x09\x09html button\x0a\x09\x09\x09\x09class: 'button';\x0a\x09\x09\x09\x09with: self cancelButtonLabel;\x0a\x09\x09\x09\x09onClick: [ self cancel ].\x0a\x09\x09\x09confirmButton := html button\x0a\x09\x09\x09\x09class: 'button default';\x0a\x09\x09\x09\x09with: self confirmButtonLabel;\x0a\x09\x09\x09\x09onClick: [ self confirm ] ].\x0a\x0a\x09self giveFocusToButton:confirmButton",
+messageSends: ["class:", "div", "with:", "button", "cancelButtonLabel", "onClick:", "cancel", "confirmButtonLabel", "confirm", "giveFocusToButton:"],
 referencedClasses: []
 }),
 globals.HLModalWidget);
@@ -4382,7 +4625,7 @@ _st(".dialog"._asJQuery())._addClass_("active");
 self._setupKeyBindings();
 return self}, function($ctx1) {$ctx1.fill(self,"renderContentOn:",{html:html,confirmButton:confirmButton},globals.HLModalWidget)})},
 args: ["html"],
-source: "renderContentOn: html\x0a\x09| confirmButton |\x0a\x09\x0a\x09html div id: 'overlay'.\x0a\x09html div \x0a\x09\x09class: 'dialog ', self cssClass;\x0a\x09\x09with: [\x0a\x09\x09\x09self renderMainOn: html.\x0a\x09\x09\x09self hasButtons ifTrue: [ \x0a\x09\x09\x09\x09self renderButtonsOn: html ] ].\x0a\x0a\x09'.dialog' asJQuery addClass: 'active'.\x0a\x09self setupKeyBindings",
+source: "renderContentOn: html\x0a\x09| confirmButton |\x0a\x09\x0a\x09html div id: 'overlay'.\x0a\x09\x0a\x09html div \x0a\x09\x09class: 'dialog ', self cssClass;\x0a\x09\x09with: [\x0a\x09\x09\x09self renderMainOn: html.\x0a\x09\x09\x09self hasButtons ifTrue: [ \x0a\x09\x09\x09\x09self renderButtonsOn: html ] ].\x0a\x0a\x09'.dialog' asJQuery addClass: 'active'.\x0a\x09self setupKeyBindings",
 messageSends: ["id:", "div", "class:", ",", "cssClass", "with:", "renderMainOn:", "ifTrue:", "hasButtons", "renderButtonsOn:", "addClass:", "asJQuery", "setupKeyBindings"],
 referencedClasses: []
 }),

+ 94 - 20
src/Helios-Core.st

@@ -39,7 +39,8 @@ systemAnnouncer
 withChangesDo: aBlock
 	[ 
 		self announcer announce: (HLAboutToChange new
-			actionBlock: aBlock).
+			actionBlock: aBlock;
+			yourself).
 		aBlock value.
 	]
 		on: HLChangeForbidden 
@@ -122,6 +123,34 @@ availableProtocols
 	^ self environment availableProtocolsFor: self selectedClass
 !
 
+forceSelectedClass: aClass
+	self withChangesDo: [
+		self 	
+			selectedClass: nil;
+			selectedClass: aClass ]
+!
+
+forceSelectedMethod: aMethod
+	self withChangesDo: [
+		self 	
+			selectedMethod: nil;
+			selectedMethod: aMethod ]
+!
+
+forceSelectedPackage: aPackage
+	self withChangesDo: [
+		self 	
+			selectedPackage: nil;
+			selectedPackage: aPackage ]
+!
+
+forceSelectedProtocol: aProtocol
+	self withChangesDo: [
+		self 	
+			selectedProtocol: nil;
+			selectedProtocol: aProtocol ]
+!
+
 packageToCommit
 	"Answer the package to commit depending on the context:
 	- if a Method is selected, answer its package
@@ -604,6 +633,10 @@ I provide common methods, additional behavior to widgets useful for Helios, like
 
 !HLWidget methodsFor: 'accessing'!
 
+cssClass
+	^ 'hl_widget'
+!
+
 manager
 	^ HLManager current
 !
@@ -622,6 +655,13 @@ confirm: aString ifTrue: aBlock
 	self manager confirm: aString ifTrue: aBlock
 !
 
+confirm: aString ifTrue: aBlock ifFalse: anotherBlock
+	self manager 
+		confirm: aString 
+		ifTrue: aBlock
+		ifFalse: anotherBlock
+!
+
 execute: aCommand
 	HLManager current keyBinder
 		activate;
@@ -675,7 +715,9 @@ renderContentOn: html
 !
 
 renderOn: html
-	wrapper := html div.
+	wrapper := html div
+		class: self cssClass;
+		yourself.
     [ :renderer | self renderContentOn: renderer ] appendToJQuery: wrapper asJQuery
 ! !
 
@@ -759,7 +801,7 @@ renderContentOn: html
 
 renderOn: html
     wrapper := html div 
-    	class: 'hl_widget';
+    	class: self cssClass;
 		yourself.
 		
        wrapper with: [ self renderContentOn: html ].
@@ -880,11 +922,19 @@ focus
 		self selectedItem ifNil: [ self activateFirstListItem ] ]
 !
 
+reactivateListItem: aListItem
+	self activateListItem: aListItem.
+	self reselectItem: self selectedItem
+!
+
 refresh
 	super refresh.
 	self selectedItem ifNotNil: [self ensureVisible: (self findListItemFor: self selectedItem)].
 !
 
+reselectItem: anObject
+!
+
 selectItem: anObject
 	self selectedItem: anObject
 ! !
@@ -901,7 +951,11 @@ setupKeyBindings
 	(HLRepeatedKeyDownHandler on: self)
 		whileKeyDown: 38 do: [ self activatePreviousListItem ];
 		whileKeyDown: 40 do: [ self activateNextListItem ];
-		rebindKeys
+		rebindKeys.
+		
+	self wrapper asJQuery keydown: [ :e |
+        e which = 13 ifTrue: [ 
+        	self reselectItem: self selectedItem ] ]
 ! !
 
 !HLListWidget methodsFor: 'rendering'!
@@ -932,7 +986,7 @@ renderItem: anObject on: html
             		(html tag: 'i') class: (self cssClassForItem: anObject).
   					self renderItemLabel: anObject on: html ];
 				onClick: [
-                  	self activateListItem: li asJQuery ] ]
+                  	self reactivateListItem: li asJQuery ] ]
 !
 
 renderItemLabel: anObject on: html
@@ -1054,6 +1108,10 @@ observeModel
 observeSystem
 !
 
+reactivateListItem: anItem
+	self model withChangesDo: [ super reactivateListItem: anItem ]
+!
+
 unregister
 	super unregister.
 	
@@ -1208,16 +1266,24 @@ addToHistory: aTab
 !
 
 confirm: aString ifFalse: aBlock
-	HLConfirmationWidget new
-		confirmationString: aString;
-		cancelBlock: aBlock;
-		show
+	self 
+		confirm: aString
+		ifTrue: []
+		ifFalse: aBlock
 !
 
 confirm: aString ifTrue: aBlock
+	self 
+		confirm: aString
+		ifTrue: aBlock
+		ifFalse: []
+!
+
+confirm: aString ifTrue: aBlock ifFalse: anotherBlock
 	HLConfirmationWidget new
 		confirmationString: aString;
 		actionBlock: aBlock;
+		cancelBlock: anotherBlock;
 		show
 !
 
@@ -1308,11 +1374,6 @@ setupEvents
 
 !HLManager methodsFor: 'rendering'!
 
-refresh
-	'.navbar' asJQuery remove.
-	self appendToJQuery: 'body' asJQuery
-!
-
 renderAddOn: html
     html li 
     	class: 'dropdown';
@@ -1419,15 +1480,27 @@ new
 ! !
 
 HLWidget subclass: #HLModalWidget
-	instanceVariableNames: ''
+	instanceVariableNames: 'confirmButtonLabel cancelButtonLabel'
 	package: 'Helios-Core'!
 !HLModalWidget commentStamp!
 I implement an abstract modal widget.!
 
 !HLModalWidget methodsFor: 'accessing'!
 
-cssClass
-	^ ''
+cancelButtonLabel
+	^ cancelButtonLabel ifNil: [ 'Cancel' ]
+!
+
+cancelButtonLabel: anObject
+	cancelButtonLabel := anObject
+!
+
+confirmButtonLabel
+	^ confirmButtonLabel ifNil: [ 'Confirm' ]
+!
+
+confirmButtonLabel: anObject
+	confirmButtonLabel := anObject
 ! !
 
 !HLModalWidget methodsFor: 'actions'!
@@ -1445,7 +1518,7 @@ remove
 	'.dialog' asJQuery removeClass: 'active'.
 	[ 
 		'#overlay' asJQuery remove.
-		'.dialog' asJQuery remove
+		wrapper asJQuery remove
 	] valueWithTimeout: 300
 !
 
@@ -1473,11 +1546,11 @@ renderButtonsOn: html
 		with: [
 			html button
 				class: 'button';
-				with: 'Cancel';
+				with: self cancelButtonLabel;
 				onClick: [ self cancel ].
 			confirmButton := html button
 				class: 'button default';
-				with: 'Confirm';
+				with: self confirmButtonLabel;
 				onClick: [ self confirm ] ].
 
 	self giveFocusToButton:confirmButton
@@ -1487,6 +1560,7 @@ renderContentOn: html
 	| confirmButton |
 	
 	html div id: 'overlay'.
+	
 	html div 
 		class: 'dialog ', self cssClass;
 		with: [

+ 186 - 37
src/Helios-Debugger.js

@@ -107,7 +107,7 @@ globals.HLDebugger.comment="I am the main widget for the Helios debugger.";
 smalltalk.addMethod(
 smalltalk.method({
 selector: "codeWidget",
-protocol: 'accessing',
+protocol: 'widgets',
 fn: function (){
 var self=this;
 function $HLDebuggerCodeWidget(){return globals.HLDebuggerCodeWidget||(typeof HLDebuggerCodeWidget=="undefined"?nil:HLDebuggerCodeWidget)}
@@ -144,6 +144,24 @@ referencedClasses: ["HLDebuggerCodeWidget", "HLDebuggerCodeModel"]
 }),
 globals.HLDebugger);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "cssClass",
+protocol: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(globals.HLDebugger.superclass.fn.prototype._cssClass.apply(_st(self), [])).__comma(" hl_debugger");
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"cssClass",{},globals.HLDebugger)})},
+args: [],
+source: "cssClass\x0a\x09^ super cssClass, ' hl_debugger'",
+messageSends: [",", "cssClass"],
+referencedClasses: []
+}),
+globals.HLDebugger);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "focus",
@@ -162,17 +180,17 @@ globals.HLDebugger);
 
 smalltalk.addMethod(
 smalltalk.method({
-selector: "initializeFromMethodContext:",
-protocol: 'accessing',
-fn: function (aMethodContext){
+selector: "initializeFromError:",
+protocol: 'initialization',
+fn: function (anError){
 var self=this;
 function $HLDebuggerModel(){return globals.HLDebuggerModel||(typeof HLDebuggerModel=="undefined"?nil:HLDebuggerModel)}
 return smalltalk.withContext(function($ctx1) { 
-self["@model"]=_st($HLDebuggerModel())._on_(aMethodContext);
+self["@model"]=_st($HLDebuggerModel())._on_(anError);
 self._observeModel();
-return self}, function($ctx1) {$ctx1.fill(self,"initializeFromMethodContext:",{aMethodContext:aMethodContext},globals.HLDebugger)})},
-args: ["aMethodContext"],
-source: "initializeFromMethodContext: aMethodContext\x0a\x09model := HLDebuggerModel on: aMethodContext.\x0a\x09self observeModel",
+return self}, function($ctx1) {$ctx1.fill(self,"initializeFromError:",{anError:anError},globals.HLDebugger)})},
+args: ["anError"],
+source: "initializeFromError: anError\x0a\x09model := HLDebuggerModel on: anError.\x0a\x09self observeModel",
 messageSends: ["on:", "observeModel"],
 referencedClasses: ["HLDebuggerModel"]
 }),
@@ -181,7 +199,7 @@ globals.HLDebugger);
 smalltalk.addMethod(
 smalltalk.method({
 selector: "inspectorWidget",
-protocol: 'accessing',
+protocol: 'widgets',
 fn: function (){
 var self=this;
 function $HLInspectorWidget(){return globals.HLInspectorWidget||(typeof HLInspectorWidget=="undefined"?nil:HLInspectorWidget)}
@@ -298,6 +316,7 @@ function $HLHorizontalSplitter(){return globals.HLHorizontalSplitter||(typeof HL
 function $HLVerticalSplitter(){return globals.HLVerticalSplitter||(typeof HLVerticalSplitter=="undefined"?nil:HLVerticalSplitter)}
 return smalltalk.withContext(function($ctx1) { 
 var $2,$1;
+self._renderHeadOn_(html);
 $2=_st($HLHorizontalSplitter())._with_with_(self._stackListWidget(),_st($HLVerticalSplitter())._with_with_(self._codeWidget(),self._inspectorWidget()));
 $ctx1.sendIdx["with:with:"]=1;
 $1=_st($HLContainer())._with_($2);
@@ -305,16 +324,39 @@ _st(html)._with_($1);
 $ctx1.sendIdx["with:"]=1;
 return self}, function($ctx1) {$ctx1.fill(self,"renderContentOn:",{html:html},globals.HLDebugger)})},
 args: ["html"],
-source: "renderContentOn: html\x0a\x09html with: (HLContainer with: (HLHorizontalSplitter\x0a\x09\x09with: self stackListWidget\x0a\x09\x09with: (HLVerticalSplitter\x0a\x09\x09\x09with: self codeWidget\x0a\x09\x09\x09with: self inspectorWidget)))",
-messageSends: ["with:", "with:with:", "stackListWidget", "codeWidget", "inspectorWidget"],
+source: "renderContentOn: html\x0a\x09self renderHeadOn: html.\x0a\x09html with: (HLContainer with: (HLHorizontalSplitter\x0a\x09\x09with: self stackListWidget\x0a\x09\x09with: (HLVerticalSplitter\x0a\x09\x09\x09with: self codeWidget\x0a\x09\x09\x09with: self inspectorWidget)))",
+messageSends: ["renderHeadOn:", "with:", "with:with:", "stackListWidget", "codeWidget", "inspectorWidget"],
 referencedClasses: ["HLContainer", "HLHorizontalSplitter", "HLVerticalSplitter"]
 }),
 globals.HLDebugger);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "renderHeadOn:",
+protocol: 'rendering',
+fn: function (html){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+$1=_st(html)._div();
+_st($1)._class_("head");
+$2=_st($1)._with_((function(){
+return smalltalk.withContext(function($ctx2) {
+return _st(_st(html)._h2())._with_(_st(_st(self._model())._error())._messageText());
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}));
+$ctx1.sendIdx["with:"]=1;
+return self}, function($ctx1) {$ctx1.fill(self,"renderHeadOn:",{html:html},globals.HLDebugger)})},
+args: ["html"],
+source: "renderHeadOn: html\x0a\x09html div \x0a\x09\x09class: 'head'; \x0a\x09\x09with: [ html h2 with: self model error messageText ]",
+messageSends: ["class:", "div", "with:", "h2", "messageText", "error", "model"],
+referencedClasses: []
+}),
+globals.HLDebugger);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "stackListWidget",
-protocol: 'accessing',
+protocol: 'widgets',
 fn: function (){
 var self=this;
 function $HLStackListWidget(){return globals.HLStackListWidget||(typeof HLStackListWidget=="undefined"?nil:HLStackListWidget)}
@@ -362,19 +404,19 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "on:",
 protocol: 'instance creation',
-fn: function (aMethodContext){
+fn: function (anError){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 var $2,$3,$1;
 $2=self._new();
-_st($2)._initializeFromMethodContext_(aMethodContext);
+_st($2)._initializeFromError_(anError);
 $3=_st($2)._yourself();
 $1=$3;
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"on:",{aMethodContext:aMethodContext},globals.HLDebugger.klass)})},
-args: ["aMethodContext"],
-source: "on: aMethodContext\x0a\x09^ self new\x0a\x09\x09initializeFromMethodContext: aMethodContext;\x0a\x09\x09yourself",
-messageSends: ["initializeFromMethodContext:", "new", "yourself"],
+}, function($ctx1) {$ctx1.fill(self,"on:",{anError:anError},globals.HLDebugger.klass)})},
+args: ["anError"],
+source: "on: anError\x0a\x09^ self new\x0a\x09\x09initializeFromError: anError;\x0a\x09\x09yourself",
+messageSends: ["initializeFromError:", "new", "yourself"],
 referencedClasses: []
 }),
 globals.HLDebugger.klass);
@@ -449,15 +491,23 @@ selector: "doIt:",
 protocol: 'actions',
 fn: function (aString){
 var self=this;
+function $ErrorHandler(){return globals.ErrorHandler||(typeof ErrorHandler=="undefined"?nil:ErrorHandler)}
 return smalltalk.withContext(function($ctx1) { 
 var $1;
-$1=_st(self._debuggerModel())._evaluate_(aString);
+$1=self._try_catch_((function(){
+return smalltalk.withContext(function($ctx2) {
+return _st(self._debuggerModel())._evaluate_(aString);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}),(function(e){
+return smalltalk.withContext(function($ctx2) {
+_st($ErrorHandler())._handleError_(e);
+return nil;
+}, function($ctx2) {$ctx2.fillBlock({e:e},$ctx1,2)})}));
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"doIt:",{aString:aString},globals.HLDebuggerCodeModel)})},
 args: ["aString"],
-source: "doIt: aString\x0a\x09^ self debuggerModel evaluate: aString",
-messageSends: ["evaluate:", "debuggerModel"],
-referencedClasses: []
+source: "doIt: aString\x0a\x09^ self \x0a\x09\x09try: [ self debuggerModel evaluate: aString ]\x0a\x09\x09catch: [ :e | \x0a\x09\x09\x09ErrorHandler handleError: e.\x0a\x09\x09\x09nil ]",
+messageSends: ["try:catch:", "evaluate:", "debuggerModel", "handleError:"],
+referencedClasses: ["ErrorHandler"]
 }),
 globals.HLDebuggerCodeModel);
 
@@ -649,7 +699,7 @@ globals.HLDebuggerCodeWidget);
 
 
 
-smalltalk.addClass('HLDebuggerModel', globals.HLToolModel, ['rootContext', 'currentContext', 'contexts'], 'Helios-Debugger');
+smalltalk.addClass('HLDebuggerModel', globals.HLToolModel, ['rootContext', 'currentContext', 'contexts', 'error'], 'Helios-Debugger');
 globals.HLDebuggerModel.comment="I am a model for debugging Amber code in Helios.\x0a\x0aMy instances hold a reference to an `AIContext` instance, built from a `MethodContext`. The context should be the root of the context stack.";
 smalltalk.addMethod(
 smalltalk.method({
@@ -719,6 +769,38 @@ referencedClasses: ["HLDebuggerContextSelected"]
 }),
 globals.HLDebuggerModel);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "error",
+protocol: 'accessing',
+fn: function (){
+var self=this;
+var $1;
+$1=self["@error"];
+return $1;
+},
+args: [],
+source: "error\x0a\x09^ error",
+messageSends: [],
+referencedClasses: []
+}),
+globals.HLDebuggerModel);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "error:",
+protocol: 'accessing',
+fn: function (anError){
+var self=this;
+self["@error"]=anError;
+return self},
+args: ["anError"],
+source: "error: anError\x0a\x09error := anError",
+messageSends: [],
+referencedClasses: []
+}),
+globals.HLDebuggerModel);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "evaluate:",
@@ -804,6 +886,32 @@ referencedClasses: ["AIContext"]
 }),
 globals.HLDebuggerModel);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "initializeFromError:",
+protocol: 'initialization',
+fn: function (anError){
+var self=this;
+function $AIContext(){return globals.AIContext||(typeof AIContext=="undefined"?nil:AIContext)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+self["@error"]=anError;
+_st(console)._log_(self["@error"]);
+$ctx1.sendIdx["log:"]=1;
+$1=console;
+$2=_st(self["@error"])._context();
+$ctx1.sendIdx["context"]=1;
+_st($1)._log_($2);
+self["@rootContext"]=_st($AIContext())._fromMethodContext_(_st(self["@error"])._context());
+self._initializeContexts();
+return self}, function($ctx1) {$ctx1.fill(self,"initializeFromError:",{anError:anError},globals.HLDebuggerModel)})},
+args: ["anError"],
+source: "initializeFromError: anError\x0a\x09error := anError.\x0a\x09console log: error.\x0a\x09console log: error context.\x0a\x09rootContext := (AIContext fromMethodContext: error context).\x0a\x09self initializeContexts",
+messageSends: ["log:", "context", "fromMethodContext:", "initializeContexts"],
+referencedClasses: ["AIContext"]
+}),
+globals.HLDebuggerModel);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "interpreter",
@@ -948,19 +1056,19 @@ smalltalk.addMethod(
 smalltalk.method({
 selector: "on:",
 protocol: 'instance creation',
-fn: function (aMethodContext){
+fn: function (anError){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 var $2,$3,$1;
 $2=self._new();
-_st($2)._initializeFromContext_(aMethodContext);
+_st($2)._initializeFromError_(anError);
 $3=_st($2)._yourself();
 $1=$3;
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"on:",{aMethodContext:aMethodContext},globals.HLDebuggerModel.klass)})},
-args: ["aMethodContext"],
-source: "on: aMethodContext\x0a\x09^ self new\x0a\x09\x09initializeFromContext: aMethodContext;\x0a\x09\x09yourself",
-messageSends: ["initializeFromContext:", "new", "yourself"],
+}, function($ctx1) {$ctx1.fill(self,"on:",{anError:anError},globals.HLDebuggerModel.klass)})},
+args: ["anError"],
+source: "on: anError\x0a\x09^ self new\x0a\x09\x09initializeFromError: anError;\x0a\x09\x09yourself",
+messageSends: ["initializeFromError:", "new", "yourself"],
 referencedClasses: []
 }),
 globals.HLDebuggerModel.klass);
@@ -969,27 +1077,68 @@ globals.HLDebuggerModel.klass);
 smalltalk.addClass('HLErrorHandler', globals.Object, [], 'Helios-Debugger');
 smalltalk.addMethod(
 smalltalk.method({
-selector: "handleError:",
+selector: "confirmDebugError:",
+protocol: 'error handling',
+fn: function (anError){
+var self=this;
+function $HLConfirmationWidget(){return globals.HLConfirmationWidget||(typeof HLConfirmationWidget=="undefined"?nil:HLConfirmationWidget)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2;
+$1=_st($HLConfirmationWidget())._new();
+_st($1)._confirmationString_(_st(anError)._messageText());
+_st($1)._actionBlock_((function(){
+return smalltalk.withContext(function($ctx2) {
+return self._debugError_(anError);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}));
+_st($1)._cancelButtonLabel_("Abandon");
+_st($1)._confirmButtonLabel_("Debug");
+$2=_st($1)._show();
+return self}, function($ctx1) {$ctx1.fill(self,"confirmDebugError:",{anError:anError},globals.HLErrorHandler)})},
+args: ["anError"],
+source: "confirmDebugError: anError\x0a\x09HLConfirmationWidget new\x0a\x09\x09confirmationString: anError messageText;\x0a\x09\x09actionBlock: [ self debugError: anError ];\x0a\x09\x09cancelButtonLabel: 'Abandon';\x0a\x09\x09confirmButtonLabel: 'Debug';\x0a\x09\x09show",
+messageSends: ["confirmationString:", "new", "messageText", "actionBlock:", "debugError:", "cancelButtonLabel:", "confirmButtonLabel:", "show"],
+referencedClasses: ["HLConfirmationWidget"]
+}),
+globals.HLErrorHandler);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "debugError:",
 protocol: 'error handling',
 fn: function (anError){
 var self=this;
 function $HLDebugger(){return globals.HLDebugger||(typeof HLDebugger=="undefined"?nil:HLDebugger)}
 function $Error(){return globals.Error||(typeof Error=="undefined"?nil:Error)}
-function $ErrorHandler(){return globals.ErrorHandler||(typeof ErrorHandler=="undefined"?nil:ErrorHandler)}
+function $ConsoleErrorHandler(){return globals.ConsoleErrorHandler||(typeof ConsoleErrorHandler=="undefined"?nil:ConsoleErrorHandler)}
 return smalltalk.withContext(function($ctx1) { 
-self._onErrorHandled();
 _st((function(){
 return smalltalk.withContext(function($ctx2) {
-return _st(_st($HLDebugger())._on_(_st(anError)._context()))._openAsTab();
+return _st(_st($HLDebugger())._on_(anError))._openAsTab();
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}))._on_do_($Error(),(function(error){
 return smalltalk.withContext(function($ctx2) {
-return _st(_st($ErrorHandler())._new())._handleError_(error);
+return _st(_st($ConsoleErrorHandler())._new())._handleError_(error);
 }, function($ctx2) {$ctx2.fillBlock({error:error},$ctx1,2)})}));
+return self}, function($ctx1) {$ctx1.fill(self,"debugError:",{anError:anError},globals.HLErrorHandler)})},
+args: ["anError"],
+source: "debugError: anError\x0a\x0a\x09[ \x0a\x09\x09(HLDebugger on: anError) openAsTab \x0a\x09] \x0a\x09\x09on: Error \x0a\x09\x09do: [ :error | ConsoleErrorHandler new handleError: error ]",
+messageSends: ["on:do:", "openAsTab", "on:", "handleError:", "new"],
+referencedClasses: ["HLDebugger", "Error", "ConsoleErrorHandler"]
+}),
+globals.HLErrorHandler);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "handleError:",
+protocol: 'error handling',
+fn: function (anError){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._confirmDebugError_(anError);
 return self}, function($ctx1) {$ctx1.fill(self,"handleError:",{anError:anError},globals.HLErrorHandler)})},
 args: ["anError"],
-source: "handleError: anError\x0a\x09self onErrorHandled.\x0a\x0a\x09[ \x0a\x09\x09(HLDebugger on: anError context) openAsTab \x0a\x09] \x0a\x09\x09on: Error \x0a\x09\x09do: [ :error | ErrorHandler new handleError: error ]",
-messageSends: ["onErrorHandled", "on:do:", "openAsTab", "on:", "context", "handleError:", "new"],
-referencedClasses: ["HLDebugger", "Error", "ErrorHandler"]
+source: "handleError: anError\x0a\x09self confirmDebugError: anError",
+messageSends: ["confirmDebugError:"],
+referencedClasses: []
 }),
 globals.HLErrorHandler);
 

+ 81 - 34
src/Helios-Debugger.st

@@ -51,34 +51,12 @@ I am the main widget for the Helios debugger.!
 
 !HLDebugger methodsFor: 'accessing'!
 
-codeWidget
-	^ codeWidget ifNil: [ codeWidget := HLDebuggerCodeWidget new
-		model: (HLDebuggerCodeModel new
-			debuggerModel: self model;
-			yourself);
-		browserModel: self model;
-		yourself ]
-!
-
-initializeFromMethodContext: aMethodContext
-	model := HLDebuggerModel on: aMethodContext.
-	self observeModel
-!
-
-inspectorWidget
-	^ inspectorWidget ifNil: [ 
-		inspectorWidget := HLInspectorWidget new ]
+cssClass
+	^ super cssClass, ' hl_debugger'
 !
 
 model
 	^ model ifNil: [ model := HLDebuggerModel new ]
-!
-
-stackListWidget
-	^ stackListWidget ifNil: [ 
-		stackListWidget := (HLStackListWidget on: self model)
-			next: self codeWidget;
-			yourself ]
 ! !
 
 !HLDebugger methodsFor: 'actions'!
@@ -104,6 +82,13 @@ unregister
 	self inspectorWidget unregister
 ! !
 
+!HLDebugger methodsFor: 'initialization'!
+
+initializeFromError: anError
+	model := HLDebuggerModel on: anError.
+	self observeModel
+! !
+
 !HLDebugger methodsFor: 'keybindings'!
 
 registerBindingsOn: aBindingGroup
@@ -121,11 +106,41 @@ onContextSelected: anAnnouncement
 !HLDebugger methodsFor: 'rendering'!
 
 renderContentOn: html
+	self renderHeadOn: html.
 	html with: (HLContainer with: (HLHorizontalSplitter
 		with: self stackListWidget
 		with: (HLVerticalSplitter
 			with: self codeWidget
 			with: self inspectorWidget)))
+!
+
+renderHeadOn: html
+	html div 
+		class: 'head'; 
+		with: [ html h2 with: self model error messageText ]
+! !
+
+!HLDebugger methodsFor: 'widgets'!
+
+codeWidget
+	^ codeWidget ifNil: [ codeWidget := HLDebuggerCodeWidget new
+		model: (HLDebuggerCodeModel new
+			debuggerModel: self model;
+			yourself);
+		browserModel: self model;
+		yourself ]
+!
+
+inspectorWidget
+	^ inspectorWidget ifNil: [ 
+		inspectorWidget := HLInspectorWidget new ]
+!
+
+stackListWidget
+	^ stackListWidget ifNil: [ 
+		stackListWidget := (HLStackListWidget on: self model)
+			next: self codeWidget;
+			yourself ]
 ! !
 
 !HLDebugger class methodsFor: 'accessing'!
@@ -140,9 +155,9 @@ tabLabel
 
 !HLDebugger class methodsFor: 'instance creation'!
 
-on: aMethodContext
+on: anError
 	^ self new
-		initializeFromMethodContext: aMethodContext;
+		initializeFromError: anError;
 		yourself
 ! !
 
@@ -163,7 +178,11 @@ debuggerModel: anObject
 !HLDebuggerCodeModel methodsFor: 'actions'!
 
 doIt: aString
-	^ self debuggerModel evaluate: aString
+	^ self 
+		try: [ self debuggerModel evaluate: aString ]
+		catch: [ :e | 
+			ErrorHandler handleError: e.
+			nil ]
 ! !
 
 HLBrowserCodeWidget subclass: #HLDebuggerCodeWidget
@@ -239,7 +258,7 @@ onContextSelected
 ! !
 
 HLToolModel subclass: #HLDebuggerModel
-	instanceVariableNames: 'rootContext currentContext contexts'
+	instanceVariableNames: 'rootContext currentContext contexts error'
 	package: 'Helios-Debugger'!
 !HLDebuggerModel commentStamp!
 I am a model for debugging Amber code in Helios.
@@ -266,6 +285,14 @@ currentContext: aContext
 			yourself) ]
 !
 
+error
+	^ error
+!
+
+error: anError
+	error := anError
+!
+
 interpreter
 	^ self currentContext interpreter
 !
@@ -337,6 +364,14 @@ initializeContexts
 initializeFromContext: aMethodContext
 	rootContext := (AIContext fromMethodContext: aMethodContext).
 	self initializeContexts
+!
+
+initializeFromError: anError
+	error := anError.
+	console log: error.
+	console log: error context.
+	rootContext := (AIContext fromMethodContext: error context).
+	self initializeContexts
 ! !
 
 !HLDebuggerModel methodsFor: 'private'!
@@ -352,9 +387,9 @@ flushInnerContexts
 
 !HLDebuggerModel class methodsFor: 'instance creation'!
 
-on: aMethodContext
+on: anError
 	^ self new
-		initializeFromContext: aMethodContext;
+		initializeFromError: anError;
 		yourself
 ! !
 
@@ -364,14 +399,26 @@ Object subclass: #HLErrorHandler
 
 !HLErrorHandler methodsFor: 'error handling'!
 
-handleError: anError
-	self onErrorHandled.
+confirmDebugError: anError
+	HLConfirmationWidget new
+		confirmationString: anError messageText;
+		actionBlock: [ self debugError: anError ];
+		cancelButtonLabel: 'Abandon';
+		confirmButtonLabel: 'Debug';
+		show
+!
+
+debugError: anError
 
 	[ 
-		(HLDebugger on: anError context) openAsTab 
+		(HLDebugger on: anError) openAsTab 
 	] 
 		on: Error 
-		do: [ :error | ErrorHandler new handleError: error ]
+		do: [ :error | ConsoleErrorHandler new handleError: error ]
+!
+
+handleError: anError
+	self confirmDebugError: anError
 !
 
 onErrorHandled

+ 125 - 63
src/Helios-Inspector.js

@@ -2,19 +2,19 @@ define("amber_core/Helios-Inspector", ["amber_vm/smalltalk", "amber_vm/nil", "am
 smalltalk.addPackage('Helios-Inspector');
 smalltalk.packages["Helios-Inspector"].transport = {"type":"amd","amdNamespace":"amber_core"};
 
-smalltalk.addClass('HLInspectorDisplayWidget', globals.HLNavigationListWidget, ['model'], 'Helios-Inspector');
+smalltalk.addClass('HLInspectorDisplayWidget', globals.HLNavigationListWidget, ['inspector'], 'Helios-Inspector');
 smalltalk.addMethod(
 smalltalk.method({
-selector: "model",
+selector: "inspector",
 protocol: 'accessing',
 fn: function (){
 var self=this;
 var $1;
-$1=self["@model"];
+$1=self["@inspector"];
 return $1;
 },
 args: [],
-source: "model\x0a\x0a\x09^ model",
+source: "inspector\x0a\x09^ inspector",
 messageSends: [],
 referencedClasses: []
 }),
@@ -22,19 +22,37 @@ globals.HLInspectorDisplayWidget);
 
 smalltalk.addMethod(
 smalltalk.method({
-selector: "model:",
+selector: "inspector:",
 protocol: 'accessing',
-fn: function (aModel){
+fn: function (anInspector){
 var self=this;
-self["@model"]=aModel;
+self["@inspector"]=anInspector;
 return self},
-args: ["aModel"],
-source: "model: aModel\x0a\x0a\x09model := aModel",
+args: ["anInspector"],
+source: "inspector: anInspector\x0a\x09inspector := anInspector",
 messageSends: [],
 referencedClasses: []
 }),
 globals.HLInspectorDisplayWidget);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "model",
+protocol: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(self._inspector())._model();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"model",{},globals.HLInspectorDisplayWidget)})},
+args: [],
+source: "model\x0a\x0a\x09^ self inspector model",
+messageSends: ["model", "inspector"],
+referencedClasses: []
+}),
+globals.HLInspectorDisplayWidget);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "renderContentOn:",
@@ -59,19 +77,24 @@ fn: function (){
 var self=this;
 var selection;
 return smalltalk.withContext(function($ctx1) { 
-var $2,$1;
-selection=_st(self["@model"])._selection();
-$2=_st(_st(self["@model"])._variables())._includesKey_(selection);
-if(smalltalk.assert($2)){
-$1=_st(_st(self["@model"])._instVarObjectAt_(selection))._printString();
+var $1,$5,$4,$3,$2;
+$1=self._model();
+$ctx1.sendIdx["model"]=1;
+selection=_st($1)._selection();
+$5=self._model();
+$ctx1.sendIdx["model"]=2;
+$4=_st($5)._variables();
+$3=_st($4)._includesKey_(selection);
+if(smalltalk.assert($3)){
+$2=_st(_st(self._model())._instVarObjectAt_(selection))._printString();
 } else {
-$1="";
+$2="";
 };
-return $1;
+return $2;
 }, function($ctx1) {$ctx1.fill(self,"selectionDisplayString",{selection:selection},globals.HLInspectorDisplayWidget)})},
 args: [],
-source: "selectionDisplayString\x0a\x09|selection|\x0a\x09selection := model selection.\x0a    ^ (model variables includesKey: selection)\x0a    \x09ifTrue:[ (model instVarObjectAt: selection) printString ]\x0a      \x09ifFalse:[ '' ]",
-messageSends: ["selection", "ifTrue:ifFalse:", "includesKey:", "variables", "printString", "instVarObjectAt:"],
+source: "selectionDisplayString\x0a\x09|selection|\x0a\x09selection := self model selection.\x0a    ^ (self model variables includesKey: selection)\x0a    \x09ifTrue:[ (self model instVarObjectAt: selection) printString ]\x0a      \x09ifFalse:[ '' ]",
+messageSends: ["selection", "model", "ifTrue:ifFalse:", "includesKey:", "variables", "printString", "instVarObjectAt:"],
 referencedClasses: []
 }),
 globals.HLInspectorDisplayWidget);
@@ -364,7 +387,7 @@ referencedClasses: []
 globals.HLInspectorModel.klass);
 
 
-smalltalk.addClass('HLInspectorVariablesWidget', globals.HLNavigationListWidget, ['announcer', 'model', 'list', 'diveButton'], 'Helios-Inspector');
+smalltalk.addClass('HLInspectorVariablesWidget', globals.HLNavigationListWidget, ['announcer', 'inspector', 'list', 'diveButton'], 'Helios-Inspector');
 smalltalk.addMethod(
 smalltalk.method({
 selector: "announcer",
@@ -410,34 +433,33 @@ globals.HLInspectorVariablesWidget);
 
 smalltalk.addMethod(
 smalltalk.method({
-selector: "label",
-protocol: 'accessing',
+selector: "dive",
+protocol: 'actions',
 fn: function (){
 var self=this;
+function $HLDiveRequested(){return globals.HLDiveRequested||(typeof HLDiveRequested=="undefined"?nil:HLDiveRequested)}
 return smalltalk.withContext(function($ctx1) { 
-var $1;
-$1=_st(self._model())._label();
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"label",{},globals.HLInspectorVariablesWidget)})},
+_st(self._announcer())._announce_(_st($HLDiveRequested())._new());
+return self}, function($ctx1) {$ctx1.fill(self,"dive",{},globals.HLInspectorVariablesWidget)})},
 args: [],
-source: "label\x0a\x09^ self model label",
-messageSends: ["label", "model"],
-referencedClasses: []
+source: "dive\x0a\x09self announcer announce: HLDiveRequested new",
+messageSends: ["announce:", "announcer", "new"],
+referencedClasses: ["HLDiveRequested"]
 }),
 globals.HLInspectorVariablesWidget);
 
 smalltalk.addMethod(
 smalltalk.method({
-selector: "model",
+selector: "inspector",
 protocol: 'accessing',
 fn: function (){
 var self=this;
 var $1;
-$1=self["@model"];
+$1=self["@inspector"];
 return $1;
 },
 args: [],
-source: "model\x0a    ^ model",
+source: "inspector\x0a\x09^ inspector",
 messageSends: [],
 referencedClasses: []
 }),
@@ -445,19 +467,55 @@ globals.HLInspectorVariablesWidget);
 
 smalltalk.addMethod(
 smalltalk.method({
-selector: "model:",
+selector: "inspector:",
 protocol: 'accessing',
-fn: function (aModel){
+fn: function (anInspector){
 var self=this;
-self["@model"]=aModel;
+self["@inspector"]=anInspector;
 return self},
-args: ["aModel"],
-source: "model: aModel\x0a    model := aModel",
+args: ["anInspector"],
+source: "inspector: anInspector\x0a\x09inspector := anInspector",
 messageSends: [],
 referencedClasses: []
 }),
 globals.HLInspectorVariablesWidget);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "label",
+protocol: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(self._model())._label();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"label",{},globals.HLInspectorVariablesWidget)})},
+args: [],
+source: "label\x0a\x09^ self model label",
+messageSends: ["label", "model"],
+referencedClasses: []
+}),
+globals.HLInspectorVariablesWidget);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "model",
+protocol: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(self._inspector())._model();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"model",{},globals.HLInspectorVariablesWidget)})},
+args: [],
+source: "model\x0a    ^ self inspector model",
+messageSends: ["model", "inspector"],
+referencedClasses: []
+}),
+globals.HLInspectorVariablesWidget);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "refresh",
@@ -485,7 +543,6 @@ selector: "renderButtonsOn:",
 protocol: 'rendering',
 fn: function (html){
 var self=this;
-function $HLDiveRequested(){return globals.HLDiveRequested||(typeof HLDiveRequested=="undefined"?nil:HLDiveRequested)}
 return smalltalk.withContext(function($ctx1) { 
 var $1,$2;
 $1=_st(html)._button();
@@ -493,14 +550,14 @@ _st($1)._class_("btn");
 _st($1)._with_("Dive");
 $2=_st($1)._onClick_((function(){
 return smalltalk.withContext(function($ctx2) {
-return _st(self._announcer())._announce_(_st($HLDiveRequested())._new());
+return self._dive();
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}));
 self["@diveButton"]=$2;
 return self}, function($ctx1) {$ctx1.fill(self,"renderButtonsOn:",{html:html},globals.HLInspectorVariablesWidget)})},
 args: ["html"],
-source: "renderButtonsOn: html\x0a\x09diveButton := html button \x0a\x09\x09class: 'btn';\x0a\x09\x09with: 'Dive'; \x0a\x09\x09onClick: [ self announcer announce: HLDiveRequested new ]",
-messageSends: ["class:", "button", "with:", "onClick:", "announce:", "announcer", "new"],
-referencedClasses: ["HLDiveRequested"]
+source: "renderButtonsOn: html\x0a\x09diveButton := html button \x0a\x09\x09class: 'btn';\x0a\x09\x09with: 'Dive'; \x0a\x09\x09onClick: [ self dive ]",
+messageSends: ["class:", "button", "with:", "onClick:", "dive"],
+referencedClasses: []
 }),
 globals.HLInspectorVariablesWidget);
 
@@ -513,10 +570,14 @@ var self=this;
 return smalltalk.withContext(function($ctx1) { 
 self._renderHeadOn_(html);
 globals.HLInspectorVariablesWidget.superclass.fn.prototype._renderContentOn_.apply(_st(self), [html]);
+_st(self._wrapper())._onDblClick_((function(){
+return smalltalk.withContext(function($ctx2) {
+return self._dive();
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}));
 return self}, function($ctx1) {$ctx1.fill(self,"renderContentOn:",{html:html},globals.HLInspectorVariablesWidget)})},
 args: ["html"],
-source: "renderContentOn: html\x0a\x09self renderHeadOn: html.\x0a\x09super renderContentOn: html",
-messageSends: ["renderHeadOn:", "renderContentOn:"],
+source: "renderContentOn: html\x0a\x09self renderHeadOn: html.\x0a\x09super renderContentOn: html.\x0a\x09self wrapper onDblClick: [ self dive ]",
+messageSends: ["renderHeadOn:", "renderContentOn:", "onDblClick:", "wrapper", "dive"],
 referencedClasses: []
 }),
 globals.HLInspectorVariablesWidget);
@@ -619,14 +680,18 @@ fn: function (){
 var self=this;
 function $HLCodeWidget(){return globals.HLCodeWidget||(typeof HLCodeWidget=="undefined"?nil:HLCodeWidget)}
 return smalltalk.withContext(function($ctx1) { 
-var $2,$3,$4,$1;
+var $2,$3,$4,$6,$5,$7,$1;
 $2=self["@codeWidget"];
 if(($receiver = $2) == nil || $receiver == null){
 $3=_st($HLCodeWidget())._new();
-_st($3)._model_(_st(self["@model"])._code());
-_st($3)._receiver_(_st(self["@model"])._inspectee());
-$4=_st($3)._yourself();
-self["@codeWidget"]=$4;
+$4=$3;
+$6=self._model();
+$ctx1.sendIdx["model"]=1;
+$5=_st($6)._code();
+_st($4)._model_($5);
+_st($3)._receiver_(_st(self._model())._inspectee());
+$7=_st($3)._yourself();
+self["@codeWidget"]=$7;
 $1=self["@codeWidget"];
 } else {
 $1=$2;
@@ -634,8 +699,8 @@ $1=$2;
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"codeWidget",{},globals.HLInspectorWidget)})},
 args: [],
-source: "codeWidget\x0a\x09^ codeWidget ifNil: [\x0a\x09\x09codeWidget := HLCodeWidget new\x0a    \x09\x09model: model code;\x0a        \x09receiver: model inspectee;\x0a        \x09yourself ]",
-messageSends: ["ifNil:", "model:", "new", "code", "receiver:", "inspectee", "yourself"],
+source: "codeWidget\x0a\x09^ codeWidget ifNil: [\x0a\x09\x09codeWidget := HLCodeWidget new\x0a    \x09\x09model: self model code;\x0a        \x09receiver: self model inspectee;\x0a        \x09yourself ]",
+messageSends: ["ifNil:", "model:", "new", "code", "model", "receiver:", "inspectee", "yourself"],
 referencedClasses: ["HLCodeWidget"]
 }),
 globals.HLInspectorWidget);
@@ -652,7 +717,7 @@ var $2,$3,$4,$1;
 $2=self["@displayWidget"];
 if(($receiver = $2) == nil || $receiver == null){
 $3=_st($HLInspectorDisplayWidget())._new();
-_st($3)._model_(self._model());
+_st($3)._inspector_(self);
 $4=_st($3)._yourself();
 self["@displayWidget"]=$4;
 $1=self["@displayWidget"];
@@ -662,8 +727,8 @@ $1=$2;
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"displayWidget",{},globals.HLInspectorWidget)})},
 args: [],
-source: "displayWidget\x0a\x09^ displayWidget ifNil: [\x0a\x09\x09displayWidget := HLInspectorDisplayWidget new\x0a    \x09\x09model: self model;\x0a        \x09yourself ]",
-messageSends: ["ifNil:", "model:", "new", "model", "yourself"],
+source: "displayWidget\x0a\x09^ displayWidget ifNil: [\x0a\x09\x09displayWidget := HLInspectorDisplayWidget new\x0a    \x09\x09inspector: self;\x0a        \x09yourself ]",
+messageSends: ["ifNil:", "inspector:", "new", "yourself"],
 referencedClasses: ["HLInspectorDisplayWidget"]
 }),
 globals.HLInspectorWidget);
@@ -848,14 +913,11 @@ fn: function (){
 var self=this;
 function $HLDiveRequested(){return globals.HLDiveRequested||(typeof HLDiveRequested=="undefined"?nil:HLDiveRequested)}
 return smalltalk.withContext(function($ctx1) { 
-_st(_st(self._variablesWidget())._announcer())._on_do_($HLDiveRequested(),(function(){
-return smalltalk.withContext(function($ctx2) {
-return self._onDive();
-}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}));
+_st(_st(self._variablesWidget())._announcer())._on_send_to_($HLDiveRequested(),"onDive",self);
 return self}, function($ctx1) {$ctx1.fill(self,"observeVariablesWidget",{},globals.HLInspectorWidget)})},
 args: [],
-source: "observeVariablesWidget\x0a\x09self variablesWidget announcer \x0a        on: HLDiveRequested do:[ self onDive ]",
-messageSends: ["on:do:", "announcer", "variablesWidget", "onDive"],
+source: "observeVariablesWidget\x0a\x09self variablesWidget announcer \x0a        on: HLDiveRequested \x0a\x09\x09send: #onDive\x0a\x09\x09to: self",
+messageSends: ["on:send:to:", "announcer", "variablesWidget"],
 referencedClasses: ["HLDiveRequested"]
 }),
 globals.HLInspectorWidget);
@@ -874,7 +936,7 @@ _st($1)._inspect_(_st(self._model())._selectedInstVarObject());
 $2=_st($1)._openAsTab();
 return self}, function($ctx1) {$ctx1.fill(self,"onDive",{},globals.HLInspectorWidget)})},
 args: [],
-source: "onDive\x0a\x0a\x09HLInspector new \x0a\x09\x09inspect: self model selectedInstVarObject;\x0a\x09\x09openAsTab",
+source: "onDive\x0a\x09HLInspector new \x0a\x09\x09inspect: self model selectedInstVarObject;\x0a\x09\x09openAsTab",
 messageSends: ["inspect:", "new", "selectedInstVarObject", "model", "openAsTab"],
 referencedClasses: ["HLInspector"]
 }),
@@ -1103,7 +1165,7 @@ var $2,$3,$4,$1;
 $2=self["@variablesWidget"];
 if(($receiver = $2) == nil || $receiver == null){
 $3=_st($HLInspectorVariablesWidget())._new();
-_st($3)._model_(self._model());
+_st($3)._inspector_(self);
 $4=_st($3)._yourself();
 self["@variablesWidget"]=$4;
 $1=self["@variablesWidget"];
@@ -1113,8 +1175,8 @@ $1=$2;
 return $1;
 }, function($ctx1) {$ctx1.fill(self,"variablesWidget",{},globals.HLInspectorWidget)})},
 args: [],
-source: "variablesWidget\x0a\x09^ variablesWidget ifNil: [\x0a\x09\x09variablesWidget := HLInspectorVariablesWidget new\x0a    \x09\x09model: self model;\x0a        \x09yourself ]",
-messageSends: ["ifNil:", "model:", "new", "model", "yourself"],
+source: "variablesWidget\x0a\x09^ variablesWidget ifNil: [\x0a\x09\x09variablesWidget := HLInspectorVariablesWidget new\x0a    \x09\x09inspector: self;\x0a        \x09yourself ]",
+messageSends: ["ifNil:", "inspector:", "new", "yourself"],
 referencedClasses: ["HLInspectorVariablesWidget"]
 }),
 globals.HLInspectorWidget);

+ 35 - 22
src/Helios-Inspector.st

@@ -1,18 +1,21 @@
 Smalltalk createPackage: 'Helios-Inspector'!
 HLNavigationListWidget subclass: #HLInspectorDisplayWidget
-	instanceVariableNames: 'model'
+	instanceVariableNames: 'inspector'
 	package: 'Helios-Inspector'!
 
 !HLInspectorDisplayWidget methodsFor: 'accessing'!
 
-model
+inspector
+	^ inspector
+!
 
-	^ model
+inspector: anInspector
+	inspector := anInspector
 !
 
-model: aModel
+model
 
-	model := aModel
+	^ self inspector model
 ! !
 
 !HLInspectorDisplayWidget methodsFor: 'rendering'!
@@ -24,9 +27,9 @@ renderContentOn: html
 
 selectionDisplayString
 	|selection|
-	selection := model selection.
-    ^ (model variables includesKey: selection)
-    	ifTrue:[ (model instVarObjectAt: selection) printString ]
+	selection := self model selection.
+    ^ (self model variables includesKey: selection)
+    	ifTrue:[ (self model instVarObjectAt: selection) printString ]
       	ifFalse:[ '' ]
 ! !
 
@@ -115,7 +118,7 @@ on: anEnvironment
 ! !
 
 HLNavigationListWidget subclass: #HLInspectorVariablesWidget
-	instanceVariableNames: 'announcer model list diveButton'
+	instanceVariableNames: 'announcer inspector list diveButton'
 	package: 'Helios-Inspector'!
 
 !HLInspectorVariablesWidget methodsFor: 'accessing'!
@@ -124,16 +127,20 @@ announcer
 	^ announcer ifNil:[ announcer := Announcer new ]
 !
 
+inspector
+	^ inspector
+!
+
+inspector: anInspector
+	inspector := anInspector
+!
+
 label
 	^ self model label
 !
 
 model
-    ^ model
-!
-
-model: aModel
-    model := aModel
+    ^ self inspector model
 !
 
 selection
@@ -146,6 +153,10 @@ variables
 
 !HLInspectorVariablesWidget methodsFor: 'actions'!
 
+dive
+	self announcer announce: HLDiveRequested new
+!
+
 refresh
 	self variables = self items ifFalse: [
 		self resetItems.
@@ -175,12 +186,13 @@ renderButtonsOn: html
 	diveButton := html button 
 		class: 'btn';
 		with: 'Dive'; 
-		onClick: [ self announcer announce: HLDiveRequested new ]
+		onClick: [ self dive ]
 !
 
 renderContentOn: html
 	self renderHeadOn: html.
-	super renderContentOn: html
+	super renderContentOn: html.
+	self wrapper onDblClick: [ self dive ]
 !
 
 renderHeadOn: html
@@ -198,15 +210,15 @@ HLWidget subclass: #HLInspectorWidget
 codeWidget
 	^ codeWidget ifNil: [
 		codeWidget := HLCodeWidget new
-    		model: model code;
-        	receiver: model inspectee;
+    		model: self model code;
+        	receiver: self model inspectee;
         	yourself ]
 !
 
 displayWidget
 	^ displayWidget ifNil: [
 		displayWidget := HLInspectorDisplayWidget new
-    		model: self model;
+    		inspector: self;
         	yourself ]
 !
 
@@ -250,7 +262,7 @@ tabLabel
 variablesWidget
 	^ variablesWidget ifNil: [
 		variablesWidget := HLInspectorVariablesWidget new
-    		model: self model;
+    		inspector: self;
         	yourself ]
 ! !
 
@@ -280,7 +292,9 @@ observeModel
 
 observeVariablesWidget
 	self variablesWidget announcer 
-        on: HLDiveRequested do:[ self onDive ]
+        on: HLDiveRequested 
+		send: #onDive
+		to: self
 !
 
 refresh
@@ -306,7 +320,6 @@ setVariables: aDictionary
 !HLInspectorWidget methodsFor: 'reactions'!
 
 onDive
-
 	HLInspector new 
 		inspect: self model selectedInstVarObject;
 		openAsTab

+ 20 - 11
src/Helios-KeyBindings.js

@@ -1240,29 +1240,38 @@ protocol: 'events',
 fn: function (event){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-var $3,$2,$1,$4;
+var $3,$2,$6,$5,$4,$1,$7;
 $3=_st(event)._which();
 $ctx1.sendIdx["which"]=1;
 $2=_st($3).__eq(self._escapeKey());
 $ctx1.sendIdx["="]=1;
 $1=_st($2)._or_((function(){
 return smalltalk.withContext(function($ctx2) {
-return _st(_st(_st(event)._which()).__eq((71)))._and_((function(){
+$6=_st(event)._which();
+$ctx2.sendIdx["which"]=2;
+$5=_st($6).__eq((71));
+$ctx2.sendIdx["="]=2;
+$4=_st($5)._or_((function(){
 return smalltalk.withContext(function($ctx3) {
-return _st(event)._ctrlKey();
+return _st(_st(event)._which()).__eq(self._activationKey());
 }, function($ctx3) {$ctx3.fillBlock({},$ctx2,2)})}));
+return _st($4)._and_((function(){
+return smalltalk.withContext(function($ctx3) {
+return _st(event)._ctrlKey();
+}, function($ctx3) {$ctx3.fillBlock({},$ctx2,3)})}));
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}));
+$ctx1.sendIdx["or:"]=1;
 if(smalltalk.assert($1)){
 self._deactivate();
 _st(event)._preventDefault();
 return false;
 };
-$4=self._handleBindingFor_(event);
-return $4;
+$7=self._handleBindingFor_(event);
+return $7;
 }, function($ctx1) {$ctx1.fill(self,"handleActiveKeyDown:",{event:event},globals.HLKeyBinder)})},
 args: ["event"],
-source: "handleActiveKeyDown: event\x0a\x0a\x09\x22ESC or ctrl+g deactivate the keyBinder\x22\x0a\x09(event which = self escapeKey or: [\x0a\x09\x09event which = 71 and: [ event ctrlKey ] ])\x0a        \x09ifTrue: [ \x0a            \x09self deactivate.\x0a\x09\x09\x09\x09event preventDefault.\x0a\x09\x09\x09\x09^ false ].\x0a            \x0a    \x22Handle the keybinding\x22\x0a    ^ self handleBindingFor: event",
-messageSends: ["ifTrue:", "or:", "=", "which", "escapeKey", "and:", "ctrlKey", "deactivate", "preventDefault", "handleBindingFor:"],
+source: "handleActiveKeyDown: event\x0a\x0a\x09\x22ESC, ctrl+g ctrl+space deactivate the keyBinder\x22\x0a\x09(event which = self escapeKey or: [\x0a\x09\x09(event which = 71 or: [ event which = self activationKey ]) \x0a\x09\x09\x09and: [ event ctrlKey ] ])\x0a        \x09\x09ifTrue: [ \x0a           \x09\x09\x09self deactivate.\x0a\x09\x09\x09\x09\x09event preventDefault.\x0a\x09\x09\x09\x09\x09^ false ].\x0a            \x0a    \x22Handle the keybinding\x22\x0a    ^ self handleBindingFor: event",
+messageSends: ["ifTrue:", "or:", "=", "which", "escapeKey", "and:", "activationKey", "ctrlKey", "deactivate", "preventDefault", "handleBindingFor:"],
 referencedClasses: []
 }),
 globals.HLKeyBinder);
@@ -1583,11 +1592,11 @@ $1=_st(".".__comma(self._cssClass()))._asJQuery();
 $ctx1.sendIdx["asJQuery"]=1;
 _st($1)._remove();
 $ctx1.sendIdx["remove"]=1;
-_st("#overlay"._asJQuery())._remove();
+_st(".helper_overlay"._asJQuery())._remove();
 self._showCog();
 return self}, function($ctx1) {$ctx1.fill(self,"hide",{},globals.HLKeyBinderHelperWidget)})},
 args: [],
-source: "hide\x0a\x09('.', self cssClass) asJQuery remove.\x0a\x09'#overlay' asJQuery remove.\x0a\x09self showCog",
+source: "hide\x0a\x09('.', self cssClass) asJQuery remove.\x0a\x09'.helper_overlay' asJQuery remove.\x0a\x09self showCog",
 messageSends: ["remove", "asJQuery", ",", "cssClass", "showCog"],
 referencedClasses: []
 }),
@@ -1793,7 +1802,7 @@ $1=_st(html)._div();
 $ctx1.sendIdx["div"]=1;
 _st($1)._id_("overlay");
 $ctx1.sendIdx["id:"]=1;
-_st($1)._class_("light");
+_st($1)._class_("helper_overlay");
 $ctx1.sendIdx["class:"]=1;
 $2=_st($1)._onClick_((function(){
 return smalltalk.withContext(function($ctx2) {
@@ -1818,7 +1827,7 @@ $ctx1.sendIdx["with:"]=1;
 _st(":focus"._asJQuery())._blur();
 return self}, function($ctx1) {$ctx1.fill(self,"renderContentOn:",{html:html},globals.HLKeyBinderHelperWidget)})},
 args: ["html"],
-source: "renderContentOn: html\x0a\x09html div \x0a\x09\x09id: 'overlay';\x0a\x09\x09class: 'light';\x0a\x09\x09onClick: [ self deactivate ].\x0a\x09\x0a\x09html div class: self cssClass; with: [\x0a      \x09self renderLabelOn: html.\x0a\x09\x09html div\x0a\x09\x09\x09id: self mainId;\x0a\x09\x09\x09with: [ self renderSelectedBindingOn: html ].\x0a\x09\x09self renderCloseOn: html ].\x0a\x09\x09\x0a\x09':focus' asJQuery blur",
+source: "renderContentOn: html\x0a\x09html div \x0a\x09\x09id: 'overlay';\x0a\x09\x09class: 'helper_overlay';\x0a\x09\x09onClick: [ self deactivate ].\x0a\x09\x0a\x09html div class: self cssClass; with: [\x0a      \x09self renderLabelOn: html.\x0a\x09\x09html div\x0a\x09\x09\x09id: self mainId;\x0a\x09\x09\x09with: [ self renderSelectedBindingOn: html ].\x0a\x09\x09self renderCloseOn: html ].\x0a\x09\x09\x0a\x09':focus' asJQuery blur",
 messageSends: ["id:", "div", "class:", "onClick:", "deactivate", "cssClass", "with:", "renderLabelOn:", "mainId", "renderSelectedBindingOn:", "renderCloseOn:", "blur", "asJQuery"],
 referencedClasses: []
 }),

+ 9 - 8
src/Helios-KeyBindings.st

@@ -429,13 +429,14 @@ defaultBindings
 
 handleActiveKeyDown: event
 
-	"ESC or ctrl+g deactivate the keyBinder"
+	"ESC, ctrl+g ctrl+space deactivate the keyBinder"
 	(event which = self escapeKey or: [
-		event which = 71 and: [ event ctrlKey ] ])
-        	ifTrue: [ 
-            	self deactivate.
-				event preventDefault.
-				^ false ].
+		(event which = 71 or: [ event which = self activationKey ]) 
+			and: [ event ctrlKey ] ])
+        		ifTrue: [ 
+           			self deactivate.
+					event preventDefault.
+					^ false ].
             
     "Handle the keybinding"
     ^ self handleBindingFor: event
@@ -542,7 +543,7 @@ deactivate
 
 hide
 	('.', self cssClass) asJQuery remove.
-	'#overlay' asJQuery remove.
+	'.helper_overlay' asJQuery remove.
 	self showCog
 !
 
@@ -607,7 +608,7 @@ renderCog
 renderContentOn: html
 	html div 
 		id: 'overlay';
-		class: 'light';
+		class: 'helper_overlay';
 		onClick: [ self deactivate ].
 	
 	html div class: self cssClass; with: [

+ 142 - 10
src/Kernel-Announcements.js

@@ -175,6 +175,110 @@ globals.AnnouncementSubscription);
 
 
 
+smalltalk.addClass('AnnouncementValuable', globals.Object, ['valuable', 'receiver'], 'Kernel-Announcements');
+globals.AnnouncementValuable.comment="I wrap `valuable` objects (typically instances of `BlockClosure`) with a `receiver` to be able to unregister subscriptions based on a `receiver`.";
+smalltalk.addMethod(
+smalltalk.method({
+selector: "receiver",
+protocol: 'accessing',
+fn: function (){
+var self=this;
+var $1;
+$1=self["@receiver"];
+return $1;
+},
+args: [],
+source: "receiver\x0a\x09^ receiver",
+messageSends: [],
+referencedClasses: []
+}),
+globals.AnnouncementValuable);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "receiver:",
+protocol: 'accessing',
+fn: function (anObject){
+var self=this;
+self["@receiver"]=anObject;
+return self},
+args: ["anObject"],
+source: "receiver: anObject\x0a\x09receiver := anObject",
+messageSends: [],
+referencedClasses: []
+}),
+globals.AnnouncementValuable);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "valuable",
+protocol: 'accessing',
+fn: function (){
+var self=this;
+var $1;
+$1=self["@valuable"];
+return $1;
+},
+args: [],
+source: "valuable\x0a\x09^ valuable",
+messageSends: [],
+referencedClasses: []
+}),
+globals.AnnouncementValuable);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "valuable:",
+protocol: 'accessing',
+fn: function (anObject){
+var self=this;
+self["@valuable"]=anObject;
+return self},
+args: ["anObject"],
+source: "valuable: anObject\x0a\x09valuable := anObject",
+messageSends: [],
+referencedClasses: []
+}),
+globals.AnnouncementValuable);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "value",
+protocol: 'evaluating',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(self._valuable())._value();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"value",{},globals.AnnouncementValuable)})},
+args: [],
+source: "value\x0a\x09^ self valuable value",
+messageSends: ["value", "valuable"],
+referencedClasses: []
+}),
+globals.AnnouncementValuable);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "value:",
+protocol: 'evaluating',
+fn: function (anObject){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(self._valuable())._value_(anObject);
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"value:",{anObject:anObject},globals.AnnouncementValuable)})},
+args: ["anObject"],
+source: "value: anObject\x0a\x09^ self valuable value: anObject",
+messageSends: ["value:", "valuable"],
+referencedClasses: []
+}),
+globals.AnnouncementValuable);
+
+
+
 smalltalk.addClass('Announcer', globals.Object, ['registry', 'subscriptions'], 'Kernel-Announcements');
 globals.Announcer.comment="I hold annoncement subscriptions (instances of `AnnouncementSubscription`) in a private registry.\x0aI announce (trigger) announces, which are then dispatched to all subscriptions.\x0a\x0aThe code is based on the announcements as [described by Vassili Bykov](http://www.cincomsmalltalk.com/userblogs/vbykov/blogView?searchCategory=Announcements%20Framework).\x0a\x0a## API\x0a\x0aUse `#announce:` to trigger an announcement.\x0a\x0aUse `#on:do:` or `#on:send:to:` to register subscriptions.\x0a\x0aWhen using `#on:send:to:`, unregistration can be done with `#unregister:`.\x0a\x0a## Usage example:\x0a\x0a    SystemAnnouncer current\x0a        on: ClassAdded\x0a        do: [ :ann | window alert: ann theClass name, ' added' ].";
 smalltalk.addMethod(
@@ -220,19 +324,47 @@ selector: "on:do:",
 protocol: 'subscribing',
 fn: function (aClass,aBlock){
 var self=this;
-function $AnnouncementSubscription(){return globals.AnnouncementSubscription||(typeof AnnouncementSubscription=="undefined"?nil:AnnouncementSubscription)}
 return smalltalk.withContext(function($ctx1) { 
-var $1,$2;
-$1=_st($AnnouncementSubscription())._new();
-_st($1)._valuable_(aBlock);
-_st($1)._announcementClass_(aClass);
-$2=_st($1)._yourself();
-_st(self["@subscriptions"])._add_($2);
+self._on_do_for_(aClass,aBlock,nil);
 return self}, function($ctx1) {$ctx1.fill(self,"on:do:",{aClass:aClass,aBlock:aBlock},globals.Announcer)})},
 args: ["aClass", "aBlock"],
-source: "on: aClass do: aBlock\x0a\x09subscriptions add: (AnnouncementSubscription new\x0a\x09\x09valuable: aBlock;\x0a\x09\x09announcementClass: aClass;\x0a\x09\x09yourself)",
-messageSends: ["add:", "valuable:", "new", "announcementClass:", "yourself"],
-referencedClasses: ["AnnouncementSubscription"]
+source: "on: aClass do: aBlock\x0a\x09self on: aClass do: aBlock for: nil",
+messageSends: ["on:do:for:"],
+referencedClasses: []
+}),
+globals.Announcer);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "on:do:for:",
+protocol: 'subscribing',
+fn: function (aClass,aBlock,aReceiver){
+var self=this;
+function $AnnouncementSubscription(){return globals.AnnouncementSubscription||(typeof AnnouncementSubscription=="undefined"?nil:AnnouncementSubscription)}
+function $AnnouncementValuable(){return globals.AnnouncementValuable||(typeof AnnouncementValuable=="undefined"?nil:AnnouncementValuable)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$3,$4,$6,$7,$5,$8,$2;
+$1=self["@subscriptions"];
+$3=_st($AnnouncementSubscription())._new();
+$ctx1.sendIdx["new"]=1;
+$4=$3;
+$6=_st($AnnouncementValuable())._new();
+_st($6)._valuable_(aBlock);
+_st($6)._receiver_(aReceiver);
+$7=_st($6)._yourself();
+$ctx1.sendIdx["yourself"]=1;
+$5=$7;
+_st($4)._valuable_($5);
+$ctx1.sendIdx["valuable:"]=1;
+_st($3)._announcementClass_(aClass);
+$8=_st($3)._yourself();
+$2=$8;
+_st($1)._add_($2);
+return self}, function($ctx1) {$ctx1.fill(self,"on:do:for:",{aClass:aClass,aBlock:aBlock,aReceiver:aReceiver},globals.Announcer)})},
+args: ["aClass", "aBlock", "aReceiver"],
+source: "on: aClass do: aBlock for: aReceiver\x0a\x09subscriptions add: (AnnouncementSubscription new\x0a\x09\x09valuable: (AnnouncementValuable new\x0a\x09\x09\x09valuable: aBlock;\x0a\x09\x09\x09receiver: aReceiver;\x0a\x09\x09\x09yourself);\x0a\x09\x09announcementClass: aClass;\x0a\x09\x09yourself)",
+messageSends: ["add:", "valuable:", "new", "receiver:", "yourself", "announcementClass:"],
+referencedClasses: ["AnnouncementSubscription", "AnnouncementValuable"]
 }),
 globals.Announcer);
 

+ 42 - 1
src/Kernel-Announcements.st

@@ -58,6 +58,40 @@ handlesAnnouncement: anAnnouncement
 		(Smalltalk globals at: anAnnouncement class theNonMetaClass name) includesBehavior: class ]
 ! !
 
+Object subclass: #AnnouncementValuable
+	instanceVariableNames: 'valuable receiver'
+	package: 'Kernel-Announcements'!
+!AnnouncementValuable commentStamp!
+I wrap `valuable` objects (typically instances of `BlockClosure`) with a `receiver` to be able to unregister subscriptions based on a `receiver`.!
+
+!AnnouncementValuable methodsFor: 'accessing'!
+
+receiver
+	^ receiver
+!
+
+receiver: anObject
+	receiver := anObject
+!
+
+valuable
+	^ valuable
+!
+
+valuable: anObject
+	valuable := anObject
+! !
+
+!AnnouncementValuable methodsFor: 'evaluating'!
+
+value
+	^ self valuable value
+!
+
+value: anObject
+	^ self valuable value: anObject
+! !
+
 Object subclass: #Announcer
 	instanceVariableNames: 'registry subscriptions'
 	package: 'Kernel-Announcements'!
@@ -98,8 +132,15 @@ initialize
 !Announcer methodsFor: 'subscribing'!
 
 on: aClass do: aBlock
+	self on: aClass do: aBlock for: nil
+!
+
+on: aClass do: aBlock for: aReceiver
 	subscriptions add: (AnnouncementSubscription new
-		valuable: aBlock;
+		valuable: (AnnouncementValuable new
+			valuable: aBlock;
+			receiver: aReceiver;
+			yourself);
 		announcementClass: aClass;
 		yourself)
 !

+ 46 - 2
src/Kernel-Tests.js

@@ -44,7 +44,7 @@ smalltalk.addClass('AnnouncerTest', globals.TestCase, [], 'Kernel-Tests');
 smalltalk.addMethod(
 smalltalk.method({
 selector: "testOnDo",
-protocol: 'not yet classified',
+protocol: 'tests',
 fn: function (){
 var self=this;
 var counter,announcer;
@@ -77,10 +77,54 @@ referencedClasses: ["Announcer", "SystemAnnouncement"]
 }),
 globals.AnnouncerTest);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "testOnDoFor",
+protocol: 'tests',
+fn: function (){
+var self=this;
+var counter,announcer;
+function $Announcer(){return globals.Announcer||(typeof Announcer=="undefined"?nil:Announcer)}
+function $SystemAnnouncement(){return globals.SystemAnnouncement||(typeof SystemAnnouncement=="undefined"?nil:SystemAnnouncement)}
+return smalltalk.withContext(function($ctx1) { 
+var $1,$2,$3,$4;
+counter=(0);
+announcer=_st($Announcer())._new();
+$ctx1.sendIdx["new"]=1;
+_st(announcer)._on_do_for_($SystemAnnouncement(),(function(){
+return smalltalk.withContext(function($ctx2) {
+counter=_st(counter).__plus((1));
+return counter;
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}),self);
+$1=announcer;
+$2=_st($SystemAnnouncement())._new();
+$ctx1.sendIdx["new"]=2;
+_st($1)._announce_($2);
+$ctx1.sendIdx["announce:"]=1;
+self._assert_equals_(counter,(1));
+$ctx1.sendIdx["assert:equals:"]=1;
+$3=announcer;
+$4=_st($SystemAnnouncement())._new();
+$ctx1.sendIdx["new"]=3;
+_st($3)._announce_($4);
+$ctx1.sendIdx["announce:"]=2;
+self._assert_equals_(counter,(2));
+$ctx1.sendIdx["assert:equals:"]=2;
+_st(announcer)._unsubscribe_(self);
+_st(announcer)._announce_(_st($SystemAnnouncement())._new());
+self._assert_equals_(counter,(2));
+return self}, function($ctx1) {$ctx1.fill(self,"testOnDoFor",{counter:counter,announcer:announcer},globals.AnnouncerTest)})},
+args: [],
+source: "testOnDoFor\x0a\x09| counter announcer |\x0a\x09\x0a\x09counter := 0.\x0a\x09announcer := Announcer new.\x0a\x09announcer on: SystemAnnouncement do: [ counter := counter + 1 ] for: self.\x0a\x0a\x09announcer announce: (SystemAnnouncement new).\x0a\x09self assert: counter equals: 1.\x0a\x0a\x09announcer announce: (SystemAnnouncement new).\x0a\x09self assert: counter equals: 2.\x0a\x09\x0a\x09announcer unsubscribe: self.\x0a\x09\x0a\x09announcer announce: (SystemAnnouncement new).\x0a\x09self assert: counter equals: 2.",
+messageSends: ["new", "on:do:for:", "+", "announce:", "assert:equals:", "unsubscribe:"],
+referencedClasses: ["Announcer", "SystemAnnouncement"]
+}),
+globals.AnnouncerTest);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "testOnDoOnce",
-protocol: 'not yet classified',
+protocol: 'tests',
 fn: function (){
 var self=this;
 var counter,announcer;

+ 20 - 1
src/Kernel-Tests.st

@@ -26,7 +26,7 @@ TestCase subclass: #AnnouncerTest
 	instanceVariableNames: ''
 	package: 'Kernel-Tests'!
 
-!AnnouncerTest methodsFor: 'not yet classified'!
+!AnnouncerTest methodsFor: 'tests'!
 
 testOnDo
 	| counter announcer |
@@ -42,6 +42,25 @@ testOnDo
 	self assert: counter equals: 2.
 !
 
+testOnDoFor
+	| counter announcer |
+	
+	counter := 0.
+	announcer := Announcer new.
+	announcer on: SystemAnnouncement do: [ counter := counter + 1 ] for: self.
+
+	announcer announce: (SystemAnnouncement new).
+	self assert: counter equals: 1.
+
+	announcer announce: (SystemAnnouncement new).
+	self assert: counter equals: 2.
+	
+	announcer unsubscribe: self.
+	
+	announcer announce: (SystemAnnouncement new).
+	self assert: counter equals: 2.
+!
+
 testOnDoOnce
 	| counter announcer |