source: EcnlProtoTool/trunk/webapp/webmrbc/Blocks/Logic.cs@ 321

Last change on this file since 321 was 321, checked in by coas-nagasima, 7 years ago

文字コードを設定

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-csharp;charset=UTF-8
File size: 16.3 KB
Line 
1/**
2 * @license
3 * Visual Blocks Editor
4 *
5 * Copyright 2012 Google Inc.
6 * https://developers.google.com/blockly/
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21/**
22 * @fileoverview Logic blocks for Blockly.
23 * @author q.neutron@gmail.com (Quynh Neutron)
24 */
25using System;
26using System.Linq;
27using System.Text;
28using Bridge;
29using Bridge.Html5;
30using Bridge.jQuery2;
31
32namespace WebMrbc
33{
34 public class Logic
35 {
36 /**
37 * Common HSV hue for all blocks in this category.
38 */
39 public static int HUE = 210;
40 }
41
42 public class ControlsIfBlock : Block
43 {
44 public const string type_name = "controls_if";
45 internal int elseifCount_;
46 internal int elseCount_;
47
48 public ControlsIfBlock()
49 : base(type_name)
50 {
51 }
52
53 /**
54 * Block for if/elseif/else condition.
55 * @this Blockly.Block
56 */
57 public void init()
58 {
59 this.setHelpUrl(Msg.CONTROLS_IF_HELPURL);
60 this.setColour(Logic.HUE);
61 this.appendValueInput("IF0")
62 .setCheck("Boolean")
63 .appendField(Msg.CONTROLS_IF_MSG_IF);
64 this.appendStatementInput("DO0")
65 .appendField(Msg.CONTROLS_IF_MSG_THEN);
66 this.setPreviousStatement(true);
67 this.setNextStatement(true);
68 this.setMutator(new Mutator(new[] { ControlsIfElseIfBlock.type_name, ControlsIfElseBlock.type_name }));
69 // Assign "this" to a variable for use in the tooltip closure below.
70 var thisBlock = this;
71 this.setTooltip(new Func<string>(() => {
72 if (thisBlock.elseifCount_ == 0 && thisBlock.elseCount_ == 0) {
73 return Msg.CONTROLS_IF_TOOLTIP_1;
74 }
75 else if (thisBlock.elseifCount_ == 0 && thisBlock.elseCount_ != 0) {
76 return Msg.CONTROLS_IF_TOOLTIP_2;
77 }
78 else if (thisBlock.elseifCount_ != 0 && thisBlock.elseCount_ == 0) {
79 return Msg.CONTROLS_IF_TOOLTIP_3;
80 }
81 else if (thisBlock.elseifCount_ != 0 && thisBlock.elseCount_ != 0) {
82 return Msg.CONTROLS_IF_TOOLTIP_4;
83 }
84 return "";
85 }));
86 this.elseifCount_ = 0;
87 this.elseCount_ = 0;
88 }
89
90 /**
91 * Create XML to represent the number of else-if and else inputs.
92 * @return {Element} XML storage element.
93 * @this Blockly.Block
94 */
95 public Element mutationToDom()
96 {
97 if (this.elseifCount_ == 0 && this.elseCount_ == 0) {
98 return null;
99 }
100 var container = Document.CreateElement("mutation");
101 if (this.elseifCount_ != 0) {
102 container.SetAttribute("elseif", this.elseifCount_.ToString());
103 }
104 if (this.elseCount_ != 0) {
105 container.SetAttribute("else", "1");
106 }
107 return container;
108 }
109
110 /**
111 * Parse XML to restore the else-if and else inputs.
112 * @param {!Element} xmlElement XML storage element.
113 * @this Blockly.Block
114 */
115 public void domToMutation(Element xmlElement)
116 {
117 var count = xmlElement.GetAttribute("elseif");
118 this.elseifCount_ = count == null ? 0 : Script.ParseInt(count, 10);
119 count = xmlElement.GetAttribute("else");
120 this.elseCount_ = count == null ? 0 : Script.ParseInt(count, 10);
121 this.updateShape_();
122 }
123
124 /**
125 * Populate the mutator's dialog with this block's components.
126 * @param {!Workspace} workspace Mutator's workspace.
127 * @return {!Blockly.Block} Root block in mutator.
128 * @this Blockly.Block
129 */
130 public Block decompose(Workspace workspace)
131 {
132 var containerBlock = workspace.newBlock(ControlsIfIfBlock.type_name);
133 containerBlock.initSvg();
134 var connection = containerBlock.nextConnection;
135 for (var i = 1; i <= this.elseifCount_; i++) {
136 var elseifBlock = workspace.newBlock(ControlsIfElseIfBlock.type_name);
137 elseifBlock.initSvg();
138 connection.connect(elseifBlock.previousConnection);
139 connection = elseifBlock.nextConnection;
140 }
141 if (this.elseCount_ != 0) {
142 var elseBlock = workspace.newBlock(ControlsIfElseBlock.type_name);
143 elseBlock.initSvg();
144 connection.connect(elseBlock.previousConnection);
145 }
146 return containerBlock;
147 }
148
149 /**
150 * Reconfigure this block based on the mutator dialog's components.
151 * @param {!Blockly.Block} containerBlock Root block in mutator.
152 * @this Blockly.Block
153 */
154 public void compose(Block containerBlock)
155 {
156 var clauseBlock = containerBlock.nextConnection.targetBlock();
157 // Count number of inputs.
158 this.elseifCount_ = 0;
159 this.elseCount_ = 0;
160 var valueConnections = new Connection[] { null };
161 var statementConnections = new Connection[] { null };
162 var elseStatementConnection = (Connection)null;
163 while (clauseBlock != null) {
164 switch (clauseBlock.type) {
165 case ControlsIfElseIfBlock.type_name:
166 this.elseifCount_++;
167 valueConnections.Push(((ControlsIfElseIfBlock)clauseBlock).valueConnection_);
168 statementConnections.Push(((ControlsIfElseIfBlock)clauseBlock).statementConnection_);
169 break;
170 case ControlsIfElseBlock.type_name:
171 this.elseCount_++;
172 elseStatementConnection = ((ControlsIfElseBlock)clauseBlock).statementConnection_;
173 break;
174 default:
175 throw new Exception("Unknown block type.");
176 }
177 clauseBlock = (clauseBlock.nextConnection != null) ?
178 clauseBlock.nextConnection.targetBlock() : null;
179 }
180 this.updateShape_();
181 // Reconnect any child blocks.
182 for (var i = 1; i <= this.elseifCount_; i++) {
183 Mutator.reconnect(valueConnections[i], this, "IF" + i);
184 Mutator.reconnect(statementConnections[i], this, "DO" + i);
185 }
186 Mutator.reconnect(elseStatementConnection, this, "ELSE");
187 }
188
189 /**
190 * Store pointers to any connected child blocks.
191 * @param {!Blockly.Block} containerBlock Root block in mutator.
192 * @this Blockly.Block
193 */
194 public void saveConnections(Block containerBlock)
195 {
196 var clauseBlock = containerBlock.nextConnection.targetBlock();
197 var i = 1;
198 while (clauseBlock != null) {
199 switch (clauseBlock.type) {
200 case ControlsIfElseIfBlock.type_name: {
201 var inputIf = this.getInput("IF" + i);
202 var inputDo = this.getInput("DO" + i);
203 ((ControlsIfElseIfBlock)clauseBlock).valueConnection_ =
204 (inputIf != null) ? inputIf.connection.targetConnection : null;
205 ((ControlsIfElseIfBlock)clauseBlock).statementConnection_ =
206 (inputDo != null) ? inputDo.connection.targetConnection : null;
207 i++;
208 }
209 break;
210 case ControlsIfElseBlock.type_name: {
211 var inputDo = this.getInput("ELSE");
212 ((ControlsIfElseBlock)clauseBlock).statementConnection_ =
213 (inputDo != null) ? inputDo.connection.targetConnection : null;
214 }
215 break;
216 default:
217 throw new Exception("Unknown block type.");
218 }
219 clauseBlock = (clauseBlock.nextConnection != null) ?
220 clauseBlock.nextConnection.targetBlock() : null;
221 }
222 }
223
224 /**
225 * Modify this block to have the correct number of inputs.
226 * @private
227 * @this Blockly.Block
228 */
229 private void updateShape_()
230 {
231 // Delete everything.
232 if (this.getInput("ELSE") != null) {
233 this.removeInput("ELSE");
234 }
235 var i = 1;
236 while (this.getInput("IF" + i) != null) {
237 this.removeInput("IF" + i);
238 this.removeInput("DO" + i);
239 i++;
240 }
241 // Rebuild block.
242 for (i = 1; i <= this.elseifCount_; i++) {
243 this.appendValueInput("IF" + i)
244 .setCheck("Boolean")
245 .appendField(Msg.CONTROLS_IF_MSG_ELSEIF);
246 this.appendStatementInput("DO" + i)
247 .appendField(Msg.CONTROLS_IF_MSG_THEN);
248 }
249 if (this.elseCount_ != 0) {
250 this.appendStatementInput("ELSE")
251 .appendField(Msg.CONTROLS_IF_MSG_ELSE);
252 }
253 }
254 }
255
256 public class ControlsIfIfBlock : Block
257 {
258 public const string type_name = "controls_if_if";
259
260 public ControlsIfIfBlock()
261 : base(type_name)
262 {
263 }
264
265 /**
266 * Mutator block for if container.
267 * @this Blockly.Block
268 */
269 public void init()
270 {
271 this.setColour(Logic.HUE);
272 this.appendDummyInput()
273 .appendField(Msg.CONTROLS_IF_IF_TITLE_IF);
274 this.setNextStatement(true);
275 this.setTooltip(Msg.CONTROLS_IF_IF_TOOLTIP);
276 this.contextMenu = false;
277 }
278 }
279
280 [IgnoreCast]
281 public class ControlsIfElseIfBlock : Block
282 {
283 public const string type_name = "controls_if_elseif";
284 public Connection valueConnection_;
285 public Connection statementConnection_;
286
287 public ControlsIfElseIfBlock()
288 : base(type_name)
289 {
290 }
291
292 /**
293 * Mutator bolck for else-if condition.
294 * @this Blockly.Block
295 */
296 public void init()
297 {
298 this.setColour(Logic.HUE);
299 this.appendDummyInput()
300 .appendField(Msg.CONTROLS_IF_ELSEIF_TITLE_ELSEIF);
301 this.setPreviousStatement(true);
302 this.setNextStatement(true);
303 this.setTooltip(Msg.CONTROLS_IF_ELSEIF_TOOLTIP);
304 this.contextMenu = false;
305 }
306 }
307
308 [IgnoreCast]
309 public class ControlsIfElseBlock : Block
310 {
311 public const string type_name = "controls_if_else";
312 public Connection statementConnection_;
313
314 public ControlsIfElseBlock()
315 : base(type_name)
316 {
317 }
318
319 /**
320 * Mutator block for else condition.
321 * @this Blockly.Block
322 */
323 public void init()
324 {
325 this.setColour(Logic.HUE);
326 this.appendDummyInput()
327 .appendField(Msg.CONTROLS_IF_ELSE_TITLE_ELSE);
328 this.setPreviousStatement(true);
329 this.setTooltip(Msg.CONTROLS_IF_ELSE_TOOLTIP);
330 this.contextMenu = false;
331 }
332 }
333
334 public class LogicCompareBlock : Block
335 {
336 public const string type_name = "logic_compare";
337
338 public LogicCompareBlock()
339 : base(type_name)
340 {
341 }
342
343 /**
344 * Block for comparison operator.
345 * @this Blockly.Block
346 */
347 public void init()
348 {
349 var rtlOperators = new[] {
350 new [] {"=", "EQ"},
351 new [] {"\u2260", "NEQ"},
352 new [] {"\u200F<\u200F", "LT"},
353 new [] {"\u200F\u2264\u200F", "LTE"},
354 new [] {"\u200F>\u200F", "GT"},
355 new [] {"\u200F\u2265\u200F", "GTE"}
356 };
357 var ltrOperators = new[] {
358 new [] {"=", "EQ"},
359 new [] {"\u2260", "NEQ"},
360 new [] {"<", "LT"},
361 new [] {"\u2264", "LTE"},
362 new [] {">", "GT"},
363 new [] {"\u2265", "GTE"}
364 };
365 var OPERATORS = this.RTL ? rtlOperators : ltrOperators;
366 this.setHelpUrl(Msg.LOGIC_COMPARE_HELPURL);
367 this.setColour(Logic.HUE);
368 this.setOutput(true, "Boolean");
369 this.appendValueInput("A");
370 this.appendValueInput("B")
371 .appendField(new FieldDropdown(OPERATORS), "OP");
372 this.setInputsInline(true);
373 // Assign "this" to a variable for use in the tooltip closure below.
374 var thisBlock = this;
375 this.setTooltip(new Func<string>(() => {
376 switch (thisBlock.getFieldValue("OP")) {
377 case "EQ": return Msg.LOGIC_COMPARE_TOOLTIP_EQ;
378 case "NEQ": return Msg.LOGIC_COMPARE_TOOLTIP_NEQ;
379 case "LT": return Msg.LOGIC_COMPARE_TOOLTIP_LT;
380 case "LTE": return Msg.LOGIC_COMPARE_TOOLTIP_LTE;
381 case "GT": return Msg.LOGIC_COMPARE_TOOLTIP_GT;
382 case "GTE": return Msg.LOGIC_COMPARE_TOOLTIP_GTE;
383 }
384 return "";
385 }));
386 this.prevBlocks_ = new Block[] { null, null };
387 }
388
389 /**
390 * Called whenever anything on the workspace changes.
391 * Prevent mismatched types from being compared.
392 * @param {!Abstract} e Change event.
393 * @this Blockly.Block
394 */
395 public void onchange(Abstract e)
396 {
397 var blockA = this.getInputTargetBlock("A");
398 var blockB = this.getInputTargetBlock("B");
399 // Disconnect blocks that existed prior to this change if they don"t match.
400 if (blockA != null && blockB != null &&
401 !blockA.outputConnection.checkType_(blockB.outputConnection)) {
402 // Mismatch between two inputs. Disconnect previous and bump it away.
403 // Ensure that any disconnections are grouped with the causing event.
404 Blockly.Events.setGroup(e.group);
405 for (var i = 0; i < this.prevBlocks_.Length; i++) {
406 var block = this.prevBlocks_[i];
407 if (block == blockA || block == blockB) {
408 block.unplug();
409 block.bumpNeighbours_();
410 }
411 }
412 Blockly.Events.setGroup(false.ToString());
413 }
414 this.prevBlocks_[0] = blockA;
415 this.prevBlocks_[1] = blockB;
416 }
417 }
418
419 public class LogicOperationBlock : Block
420 {
421 public const string type_name = "logic_operation";
422
423 public LogicOperationBlock()
424 : base(type_name)
425 {
426 }
427
428 /**
429 * Block for logical operations: "and", "or".
430 * @this Blockly.Block
431 */
432 public void init()
433 {
434 var OPERATORS = new[] {
435 new [] {Msg.LOGIC_OPERATION_AND, "AND"},
436 new [] {Msg.LOGIC_OPERATION_OR, "OR"}
437 };
438 this.setHelpUrl(Msg.LOGIC_OPERATION_HELPURL);
439 this.setColour(Logic.HUE);
440 this.setOutput(true, "Boolean");
441 this.appendValueInput("A")
442 .setCheck("Boolean");
443 this.appendValueInput("B")
444 .setCheck("Boolean")
445 .appendField(new FieldDropdown(OPERATORS), "OP");
446 this.setInputsInline(true);
447 // Assign "this" to a variable for use in the tooltip closure below.
448 var thisBlock = this;
449 this.setTooltip(new Func<string>(() => {
450 switch (thisBlock.getFieldValue("OP")) {
451 case "AND": return Msg.LOGIC_OPERATION_TOOLTIP_AND;
452 case "OR": return Msg.LOGIC_OPERATION_TOOLTIP_OR;
453 }
454 return "";
455 }));
456 }
457 }
458
459 public class LogicNegateBlock : Block
460 {
461 public const string type_name = "logic_negate";
462
463 public LogicNegateBlock()
464 : base(type_name)
465 {
466 }
467
468 /**
469 * Block for negation.
470 * @this Blockly.Block
471 */
472 public void init()
473 {
474 this.jsonInit(new {
475 message0 = Msg.LOGIC_NEGATE_TITLE,
476 args0 = new object[] {
477 new {
478 type = "input_value",
479 name = "BOOL",
480 check = "Boolean"
481 }
482 },
483 output = "Boolean",
484 colour = Logic.HUE,
485 tooltip = Msg.LOGIC_NEGATE_TOOLTIP,
486 helpUrl = Msg.LOGIC_NEGATE_HELPURL
487 });
488 }
489 }
490
491 public class LogicBooleanBlock : Block
492 {
493 public const string type_name = "logic_boolean";
494
495 public LogicBooleanBlock()
496 : base(type_name)
497 {
498 }
499
500 /**
501 * Block for boolean data type: true and false.
502 * @this Blockly.Block
503 */
504 public void init()
505 {
506 this.jsonInit(new {
507 message0 = "%1",
508 args0 = new object[] {
509 new {
510 type = "field_dropdown",
511 name = "BOOL",
512 options = new [] {
513 new [] {Msg.LOGIC_BOOLEAN_TRUE, "TRUE"},
514 new [] {Msg.LOGIC_BOOLEAN_FALSE, "FALSE"}
515 }
516 }
517 },
518 output = "Boolean",
519 colour = Logic.HUE,
520 tooltip = Msg.LOGIC_BOOLEAN_TOOLTIP,
521 helpUrl = Msg.LOGIC_BOOLEAN_HELPURL
522 });
523 }
524 }
525
526 public class LogicNullBlock : Block
527 {
528 public const string type_name = "logic_null";
529
530 public LogicNullBlock()
531 : base(type_name)
532 {
533 }
534
535 /**
536 * Block for null data type.
537 * @this Blockly.Block
538 */
539 public void init()
540 {
541 this.jsonInit(new {
542 message0 = Msg.LOGIC_NULL,
543 output = (string[])null,
544 colour = Logic.HUE,
545 tooltip = Msg.LOGIC_NULL_TOOLTIP,
546 helpUrl = Msg.LOGIC_NULL_HELPURL
547 });
548 }
549 }
550
551 public class LogicTernaryBlock : Block
552 {
553 public const string type_name = "logic_ternary";
554 Connection prevParentConnection_;
555
556 public LogicTernaryBlock()
557 : base(type_name)
558 {
559 }
560
561 /**
562 * Block for ternary operator.
563 * @this Blockly.Block
564 */
565 public void init()
566 {
567 this.setHelpUrl(Msg.LOGIC_TERNARY_HELPURL);
568 this.setColour(Logic.HUE);
569 this.appendValueInput("IF")
570 .setCheck("Boolean")
571 .appendField(Msg.LOGIC_TERNARY_CONDITION);
572 this.appendValueInput("THEN")
573 .appendField(Msg.LOGIC_TERNARY_IF_TRUE);
574 this.appendValueInput("ELSE")
575 .appendField(Msg.LOGIC_TERNARY_IF_FALSE);
576 this.setOutput(true);
577 this.setTooltip(Msg.LOGIC_TERNARY_TOOLTIP);
578 this.prevParentConnection_ = null;
579 }
580
581 /**
582 * Called whenever anything on the workspace changes.
583 * Prevent mismatched types.
584 * @param {!Abstract} e Change event.
585 * @this Blockly.Block
586 */
587 public void onchange(Abstract e)
588 {
589 var blockA = this.getInputTargetBlock("THEN");
590 var blockB = this.getInputTargetBlock("ELSE");
591 var parentConnection = this.outputConnection.targetConnection;
592 // Disconnect blocks that existed prior to this change if they don"t match.
593 if ((blockA != null || blockB != null) && parentConnection != null) {
594 for (var i = 0; i < 2; i++) {
595 var block = (i == 1) ? blockA : blockB;
596 if (block != null && !block.outputConnection.checkType_(parentConnection)) {
597 // Ensure that any disconnections are grouped with the causing event.
598 Blockly.Events.setGroup(e.group);
599 if (parentConnection == this.prevParentConnection_) {
600 this.unplug();
601 parentConnection.getSourceBlock().bumpNeighbours_();
602 }
603 else {
604 block.unplug();
605 block.bumpNeighbours_();
606 }
607 Blockly.Events.setGroup(false.ToString());
608 }
609 }
610 }
611 this.prevParentConnection_ = parentConnection;
612 }
613 }
614}
Note: See TracBrowser for help on using the repository browser.