1/* 2* Copyright (c) 2022 Shenzhen Kaihong Digital Industry Development Co., Ltd. 3* Licensed under the Apache License, Version 2.0 (the "License"); 4* you may not use this file except in compliance with the License. 5* You may obtain a copy of the License at 6* 7* http://www.apache.org/licenses/LICENSE-2.0 8* 9* Unless required by applicable law or agreed to in writing, software 10* distributed under the License is distributed on an "AS IS" BASIS, 11* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12* See the License for the specific language governing permissions and 13* limitations under the License. 14*/ 15 16const { XMessage } = require("./message/XMessage"); 17const { Lexer } = require("./hcs/lexer"); 18const { Generator } = require("./hcs/Generator"); 19const { Scr } = require("./engine/XDefine"); 20const { XButton } = require("./engine/control/XButton"); 21const { AttrEditor } = require("./AttrEditor"); 22const { NapiLog } = require("./hcs/NapiLog"); 23const { XSelect } = require("./engine/control/XSelect"); 24const { NodeTools, DataType } = require("./hcs/NodeTools"); 25const { ModifyNode } = require("./hcs/ModifyNode"); 26 27function rgba(r, g, b, a) { 28 if (a == undefined) a = 255 29 return a << 24 | r << 16 | g << 8 | b 30} 31class MainEditor { 32 constructor() { 33 this.files_ = {} 34 this.nodeCount_ = {}; 35 this.filePoint_ = null; 36 this.rootPoint_ = null; 37 this.nodePoint_ = null; 38 this.offX_ = 100; 39 this.offY_ = 100; 40 this.touchQueue_ = []; 41 this.keyQueue_ = []; 42 this.dropAll_ = { 43 locked: false, 44 oldx: -1, 45 oldy: -1 46 } 47 this.nodeBtns = []; 48 this.nodeMoreBtns = []; 49 this.nodeBtnPoint_ = 0; 50 this.nodeMoreBtnPoint_ = 0; 51 XMessage.gi().registRecvCallback(this.onReceive); 52 XMessage.gi().send("inited", ""); 53 AttrEditor.gi().freshEditor(); 54 55 this.sltInclude = new XSelect(["a", "b", "c"], "b"); 56 this.sltInclude.registCallback(this.onSelectInclude) 57 NapiLog.registError(this.onError); 58 this.errorMsg_ = [] 59 this.delay_ = 0; 60 61 this.selectNode_ = { 62 type: null, 63 pnode: null 64 }; 65 this.btnCancelSelect_ = new XButton(); 66 67 AttrEditor.gi().registCallback(this.onAttributeChange); 68 69 this.mousePos_ = { 70 x: 0, 71 y: 0 72 } 73 } 74 75 calcPostionY(data, y) { 76 data.posY = y; 77 let ty = y; 78 switch (data.type_) { 79 case 1://uint8 80 case 2://uint16 81 case 3://uint32 82 case 4://uint64 83 y += MainEditor.LINE_HEIGHT; 84 break; 85 case 5://string 86 y += MainEditor.LINE_HEIGHT; 87 break; 88 case 6://ConfigNode 89 for (let i in data.value_) { 90 y = this.calcPostionY(data.value_[i], y) 91 } 92 break; 93 case 7://ConfigTerm 94 y = this.calcPostionY(data.value_, y) 95 break; 96 case 8://Array class attribute value 97 y += MainEditor.LINE_HEIGHT; 98 break; 99 case 9://Attribute equal to leaf 100 y += MainEditor.LINE_HEIGHT 101 break; 102 case 10://Delete attribute 103 y += MainEditor.LINE_HEIGHT 104 break; 105 case 11://bool 106 y += MainEditor.LINE_HEIGHT 107 break; 108 default: 109 NapiLog.logError("unknow" + data.type_); 110 break; 111 } 112 if (y > ty) { 113 data.posY = (ty + y - MainEditor.LINE_HEIGHT) / 2 114 } 115 return y > (ty + MainEditor.LINE_HEIGHT) ? y : (ty + MainEditor.LINE_HEIGHT); 116 } 117 getNodeText(data) { 118 switch (data.nodeType_) { 119 case 0://Data class nodes, not inheri 120 return data.name_; 121 case 3://Deletion class nodes 122 return data.name_ + " : delete"; 123 case 4://Templete Class nodes 124 return "templete " + data.name_; 125 case 5://Data class nodes, inherit 126 return data.name_ + ' :: ' + data.ref_; 127 case 1://Copy class nodes 128 return data.name_ + " : " + data.ref_; 129 case 2://Reference modification class nodes 130 return data.name_ + " : &" + data.ref_; 131 default: 132 return "unknow node type"; 133 } 134 } 135 136 drawNode(pm2f, s, size, x, y, color, borderColor, bkcolor) { 137 let w = pm2f.getTextWidth(s, size); 138 pm2f.drawRect(x - 3, y - 3, w + 20, 20 + 6, borderColor, 2); 139 pm2f.fillRect(x - 3, y - 3, w + 20, 20 + 6, bkcolor); 140 pm2f.drawText(s, size, x, y, 1, 1, 0, -1, -1, color); 141 return w; 142 } 143 144 arrayNodeProc(w, pm2f, data, offx, offy) { 145 let ss = "[" + data.value_.length + "]" + NodeTools.arrayToString(data) 146 if (ss.length > 32) { 147 ss = ss.substring(0, 29) + "..." 148 } 149 w = pm2f.drawText(ss, 18, offx, offy + data.posY, 1, 1, 0, -1, -1, 0xffffffff); 150 } 151 152 configNodeProc(w, pm2f, data, offx, offy, path) { 153 this.setNodeButton(pm2f, offx, offy + data.posY, w, 20, path, data); 154 155 if (data.value_.length > 0) { 156 this.setNodeMoreButton(pm2f, offx, offy + data.posY, w, 20, data); 157 } 158 if (data.type_ == 6) { 159 for (let i in data.value_) { 160 if (data.value_[i].parent_.type_ == 6 && data.value_[i].parent_.isOpen_) { 161 this.drawObj(pm2f, data.value_[i], offx + w + 50, offy, path + "."); 162 pm2f.drawLine(data.posX + w, offy + data.posY + 10, 163 data.value_[i].posX, offy + data.value_[i].posY + 10, 0xffffffff, 2) 164 } else if (data.value_[i].parent_.type_ == 7) { 165 this.drawObj(pm2f, data.value_[i], offx + w + 50, offy, path + "."); 166 pm2f.drawLine(data.posX + w, offy + data.posY + 10, 167 data.value_[i].posX, offy + data.value_[i].posY + 10, 0xffffffff, 2) 168 } 169 else { 170 NapiLog.logInfo("Node collapse does not need to draw child node"); 171 } 172 } 173 } else { 174 for (let i in data.value_) { 175 this.drawObj(pm2f, data.value_[i], offx + w + 50, offy, path + "."); 176 pm2f.drawLine(data.posX + w, offy + data.posY + 10, 177 data.value_[i].posX, offy + data.value_[i].posY + 10, 0xffffffff, 2) 178 } 179 } 180 } 181 drawObj(pm2f, data, offx, offy, path) { 182 let w; 183 path += data.name_; 184 data.posX = offx; 185 186 switch (data.type_) { 187 case 1://uint8 188 case 2://uint16 189 case 3://uint32 190 case 4://uint64 191 w = pm2f.drawText(NodeTools.jinZhi10ToX(data.value_, data.jinzhi_), 192 18, offx, offy + data.posY, 1, 1, 0, -1, -1, 0xE6000000); 193 break; 194 case 5://string 195 w = pm2f.drawText('"' + data.value_ + '"', 18, offx, offy + data.posY, 1, 1, 0, -1, -1, 0xE6000000); 196 break; 197 case 6://ConfigNode 198 var color = data.errMsg_ != null ? 0xE6FF0000 : 0xE6000000; 199 w = this.drawNode(pm2f, this.getNodeText(data), 18, offx, offy + data.posY, 200 color, rgba(196, 196, 196), 0xffffffff); 201 this.configNodeProc(w, pm2f, data, offx, offy, path) 202 break; 203 case 7://ConfigTerm 204 w = this.drawNode(pm2f, data.name_ + "=", 18, offx, offy + data.posY, 0xE6000000, rgba(244,145,38), 0xffffffff); 205 this.setNodeButton(pm2f, offx, offy + data.posY, w, 20, path, data); 206 this.drawObj(pm2f, data.value_, offx + w, offy, path); 207 break; 208 case 8://Array class attribute value 209 this.arrayNodeProc(w, pm2f, data, offx, offy); 210 break; 211 case 9://Attribute equal to leaf 212 w = pm2f.drawText("&" + data.value_, 18, offx, offy + data.posY, 1, 1, 0, -1, -1, 0xffff0000); 213 break; 214 case 10://Delete attribute 215 w = pm2f.drawText("delete", 18, offx, offy + data.posY, 1, 1, 0, -1, -1, 0xffff0000); 216 break; 217 case 11://bool 218 if (data.value_) w = pm2f.drawText("true", 18, offx, offy + data.posY, 1, 1, 0, -1, -1, 0xffff0000); 219 else w = pm2f.drawText("false", 18, offx, offy + data.posY, 1, 1, 0, -1, -1, 0xffff0000); 220 break; 221 default: 222 NapiLog.logError("unknow" + data.type_); 223 break; 224 } 225 if (data.errMsg_ != null) { 226 if (parseInt(this.delay_ / 10) % 2 == 0) { 227 pm2f.drawRect(offx - 5, offy + data.posY - 5, w + 10, 20 + 10, 0xffff0000, 3); 228 } 229 pm2f.drawText(data.errMsg_, 18, offx + w - 5, offy + data.posY + 5, 1, 1, 0, -1, -3, 0xffff0000); 230 } 231 } 232 233 setNodeButton(pm2f, x, y, w, h, path, node) { 234 if (this.nodePoint_ == node) { 235 pm2f.drawRect(x - 3, y - 3, w + 20, h + 6, 0xff487EB8, 3); 236 } 237 if (this.nodeBtnPoint_ >= this.nodeBtns.length) { 238 this.nodeBtns.push(new XButton()); 239 } 240 let pbtn = this.nodeBtns[this.nodeBtnPoint_]; 241 pbtn.move(x - 3, y - 3, w + 6, h + 6); 242 pbtn.name_ = path; 243 pbtn.node_ = node; 244 this.nodeBtnPoint_ += 1; 245 } 246 247 setNodeMoreButton(pm2f, x, y, w, h, node) { 248 if (this.nodeMoreBtnPoint_ >= this.nodeMoreBtns.length) { 249 this.nodeMoreBtns.push(new XButton()); 250 } 251 let pbtn = this.nodeMoreBtns[this.nodeMoreBtnPoint_]; 252 pbtn.move(x + w + 18, y - 3, 15, h + 6); 253 pbtn.draw(); 254 pm2f.drawLine(x + w + 20, y + 10, x + w + 30, y + 10, 0xffffffff, 2) 255 if(!node.isOpen_){ 256 pm2f.drawLine(x + w + 25, y + 5, x + w + 25, y + 15, 0xffffffff, 2) 257 } 258 pbtn.node_ = node; 259 this.nodeMoreBtnPoint_ += 1; 260 } 261 262 draw(pm2f) { 263 pm2f.fillRect(0, 0, Scr.logicw, Scr.logich, 0xff303030); 264 265 if (this.filePoint_ != null && this.filePoint_ in this.files_) { 266 let data = this.files_[this.filePoint_]; 267 this.calcPostionY(data, 0); 268 269 this.nodeBtnPoint_ = 0; 270 this.nodeMoreBtnPoint_ = 0; 271 this.drawObj(pm2f, data, this.offX_, this.offY_, ""); 272 } 273 this.sltInclude.move(10, 10, 200, 20).draw(); 274 275 if (this.selectNode_.type != null) { 276 if (this.selectNode_.type == "change_target") { 277 pm2f.drawText("点击选择目标", 18, this.mousePos_.x, this.mousePos_.y, 1, 1, 0, -3, -3, 0xff487EB8); 278 this.btnCancelSelect_.name_ = "取消选择"; 279 } 280 else if (this.selectNode_.type == "copy_node") { 281 pm2f.drawText("已复制" + this.selectNode_.pnode.name_, 18, this.mousePos_.x, this.mousePos_.y, 1, 282 1, 0, -3, -3, 0xff487EB8); 283 this.btnCancelSelect_.name_ = "取消复制"; 284 } 285 else if (this.selectNode_.type == "cut_node") { 286 pm2f.drawText("已剪切" + this.selectNode_.pnode.name_, 18, this.mousePos_.x, this.mousePos_.y, 1, 287 1, 0, -3, -3, 0xff487EB8); 288 this.btnCancelSelect_.name_ = "取消剪切"; 289 } 290 291 this.btnCancelSelect_.move(Scr.logicw - 250, Scr.logich - 30, 100, 20).draw(); 292 } 293 294 if (this.errorMsg_.length > 0) { 295 let ts = (new Date()).getTime() 296 while (this.errorMsg_.length > 0 && this.errorMsg_[0][0] < ts) { 297 this.errorMsg_.shift(); 298 } 299 for (let i in this.errorMsg_) { 300 let y = Scr.logich / 2 - this.errorMsg_.length * 20 + i * 20 301 let a = parseInt((this.errorMsg_[i][0] - ts) / 2); 302 if (a > 255) a = 255; 303 NapiLog.logError(a); 304 a = a << 24; 305 pm2f.fillRect(0, y, Scr.logicw, 20, 0xff0000 | a); 306 pm2f.drawText(this.errorMsg_[i][1], 18, Scr.logicw / 2, y, 1, 1, 0, -2, -1, 0xffffff | a); 307 } 308 309 } 310 this.delay_ += 1; 311 this.procAll(); 312 } 313 314 buttonClickedProc(nodeBtns) { 315 if (this.selectNode_.type == null || 316 (this.selectNode_.type == "copy_node" || this.selectNode_.type == "cut_node")) { 317 this.nodePoint_ = nodeBtns.node_; 318 AttrEditor.gi().freshEditor(this.filePoint_, this.nodePoint_); 319 return true; 320 } 321 //What can be copied: data does not inherit, copy class nodes, template class nodes and data class inheritance 322 if (this.selectNode_.type == "change_target") { 323 let pn = nodeBtns.node_; 324 if (pn.type_ == DataType.NODE) { 325 if (this.selectNode_.pnode.type_ == DataType.NODE) { 326 if (NodeTools.getPathByNode(this.selectNode_.pnode.parent_) == 327 NodeTools.getPathByNode(pn.parent_)) { 328 this.selectNode_.pnode.ref_ = pn.name_; 329 } 330 else { 331 this.selectNode_.pnode.ref_ = NodeTools.getPathByNode(pn); 332 } 333 } 334 else if (this.selectNode_.pnode.type_ == DataType.REFERENCE) { 335 if (NodeTools.getPathByNode(this.selectNode_.pnode.parent_.parent_) == 336 NodeTools.getPathByNode(pn.parent_)) { 337 this.selectNode_.pnode.value_ = pn.name_; 338 } 339 else { 340 this.selectNode_.pnode.value_ = NodeTools.getPathByNode(pn); 341 } 342 } 343 344 this.selectNode_.type = null; 345 AttrEditor.gi().freshEditor(this.filePoint_, this.nodePoint_); 346 this.onAttributeChange("writefile"); 347 } 348 } 349 return true 350 } 351 352 dropAllLocked(msg, x, y) { 353 if (msg == 2) { 354 this.offX_ += (x - this.dropAll_.oldx); 355 this.offY_ += (y - this.dropAll_.oldy); 356 this.dropAll_.oldx = x; 357 this.dropAll_.oldy = y; 358 } 359 if (msg == 3) { 360 this.dropAll_.locked = false; 361 } 362 } 363 364 procTouch(msg, x, y) { 365 this.mousePos_.x = x; 366 this.mousePos_.y = y; 367 if (this.dropAll_.locked) { 368 this.dropAllLocked(msg, x, y); 369 return true; 370 } 371 372 if (this.sltInclude.procTouch(msg, x, y)) { 373 return true; 374 } 375 376 if (this.selectNode_.type != null) { 377 if (this.btnCancelSelect_.procTouch(msg, x, y)) { 378 if (this.btnCancelSelect_.isClicked()) { 379 this.selectNode_.type = null; 380 } 381 return true; 382 } 383 } 384 385 for (let i = 0; i < this.nodeBtnPoint_; i++) { 386 if (this.nodeBtns[i].procTouch(msg, x, y)) { 387 let nodeBtns = this.nodeBtns[i]; 388 if (nodeBtns.isClicked()) { 389 this.buttonClickedProc(nodeBtns); 390 } 391 return true; 392 } 393 } 394 395 for (let i = 0; i < this.nodeMoreBtnPoint_; i++) { 396 if (this.nodeMoreBtns[i].procTouch(msg, x, y)) { 397 let nodeMoreBtn = this.nodeMoreBtns[i]; 398 if (nodeMoreBtn.isClicked()) { 399 this.buttonClickedProc(nodeMoreBtn); 400 this.nodeMoreBtns[i].node_.isOpen_ = !this.nodeMoreBtns[i].node_.isOpen_; 401 } 402 return true; 403 } 404 } 405 406 //Drag screen 407 if (msg == 1 && !this.dropAll_.locked) { 408 this.dropAll_.locked = true; 409 this.dropAll_.oldx = x; 410 this.dropAll_.oldy = y; 411 return true; 412 } 413 } 414 procKey(k) { 415 if (k == "ctrl+c") { 416 if (this.nodePoint_ != null) { 417 this.selectNode_ = { 418 type: "copy_node", 419 pnode: this.nodePoint_ 420 }; 421 } 422 } 423 else if (k == "ctrl+x") { 424 if (this.nodePoint_ != null) { 425 this.selectNode_ = { 426 type: "cut_node", 427 pnode: this.nodePoint_ 428 }; 429 } 430 } 431 else if (k == "ctrl+v") { 432 if (this.selectNode_.type != null && this.nodePoint_ != null) { 433 let parent = this.nodePoint_ 434 if (this.nodePoint_.type_ != DataType.NODE) 435 parent = this.nodePoint_.parent_; 436 parent.value_.push(NodeTools.copyNode(this.selectNode_.pnode, parent)) 437 if (this.selectNode_.type == "cut_node") { 438 ModifyNode.deleteNode(this.selectNode_.pnode) 439 this.selectNode_.type = null; 440 } 441 this.checkAllError(); 442 } 443 } 444 else if (k == "Delete") { 445 if (this.nodePoint_ != null) { 446 ModifyNode.deleteNode(this.nodePoint_) 447 AttrEditor.gi().freshEditor(); 448 } 449 } 450 } 451 procAll() { 452 while (this.touchQueue_.length > 0) { 453 let touch = this.touchQueue_.shift(); 454 this.procTouch(touch[0], touch[1], touch[2]); 455 } 456 457 while (this.keyQueue_.length > 0) { 458 let k = this.keyQueue_.shift(); 459 this.procKey(k); 460 } 461 } 462 onSelectInclude(sel) { 463 MainEditor.gi().filePoint_ = sel; 464 AttrEditor.gi().freshEditor(); 465 } 466 467 nodeCount(data) { 468 let ret = 1; 469 switch (data.type_) { 470 case 1://uint8 471 case 2://uint16 472 case 3://uint32 473 case 4://uint64 474 case 5://string 475 break; 476 case 6://ConfigNode 477 for (let i in data.value_) { 478 ret += this.nodeCount(data.value_[i]); 479 } 480 break; 481 case 7://ConfigTerm 482 ret += this.nodeCount(data.value_); 483 break; 484 case 8://Array class attribute value 485 case 9://Attribute equal to leaf 486 case 10://Delete attribute 487 case 11://bool 488 break; 489 default: 490 NapiLog.logError("unknow" + data.type_); 491 break; 492 } 493 return ret; 494 } 495 isNodeCountChanged(fn) { 496 if (!(fn in this.nodeCount_)) { 497 this.nodeCount_[fn] = -1; 498 } 499 let newcount = this.nodeCount(this.files_[fn]); 500 if (this.nodeCount_[fn] != newcount) { 501 this.nodeCount_[fn] = newcount; 502 return true; 503 } 504 return false; 505 } 506 onAttributeChange(type, value) { 507 let pme = MainEditor.gi(); 508 if (type == "writefile") { 509 let data1 = Generator.gi().makeHcs(pme.filePoint_, pme.files_[pme.filePoint_]) 510 if (pme.isNodeCountChanged(pme.filePoint_)) { 511 let data2 = [] 512 for (let j in data1) { 513 data2.push(data1.charCodeAt(j)) 514 } 515 Lexer.FILE_AND_DATA[pme.filePoint_] = data2; 516 pme.parse(pme.filePoint_) 517 let t=NodeTools.getPathByNode(pme.nodePoint_); 518 if(t){ 519 pme.nodePoint_=NodeTools.getNodeByPath(pme.files_[pme.filePoint_],t) 520 } 521 else pme.nodePoint_= null; 522 523 // pme.nodePoint_ = null; 524 AttrEditor.gi().freshEditor(pme.filePoint_, pme.nodePoint_); 525 } 526 pme.checkAllError(); 527 XMessage.gi().send("writefile", { 528 fn: pme.filePoint_, 529 data: data1 530 }) 531 } 532 else if (type.substring(0, 13) == "change_target") { 533 pme.selectNode_.type = type; 534 pme.selectNode_.pnode = value; 535 } 536 } 537 onError(msg) { 538 // MainEditor.gi().errorMsg_.push([(new Date()).getTime() + 5000, msg]) 539 } 540 onTouch(msg, x, y) { 541 this.touchQueue_.push([msg, x, y]); 542 } 543 onKey(k) { 544 this.keyQueue_.push(k) 545 } 546 onReceive(type, data) { 547 NapiLog.logError(type); 548 let me = MainEditor.gi(); 549 if (type == "parse") { 550 me.parse(data) 551 } 552 else if (type == "filedata") { 553 Lexer.FILE_AND_DATA[data.fn] = data.data; 554 me.parse(data.fn) 555 } 556 else if (type == "freshfiledata") { 557 Lexer.FILE_AND_DATA[data.fn] = data.data; 558 } 559 } 560 parse(fn) { 561 if (this.rootPoint_ == null) { 562 this.rootPoint_ = fn 563 } 564 let t = Generator.gi().hcsToAst(fn); 565 if (!t) return; 566 567 let fs = [] 568 for (let i in t) { 569 this.files_[i] = Generator.gi().astToObj(t[i].ast.astRoot_); 570 fs.push(i) 571 } 572 this.filePoint_ = this.rootPoint_; 573 this.sltInclude.resetList(fs, this.filePoint_) 574 AttrEditor.gi().setFiles(this.files_) 575 576 this.checkAllError(); 577 } 578 checkAllError() { 579 NapiLog.clearError() 580 let n1 = Generator.gi().mergeObj(this.files_) 581 if (n1) { 582 n1 = Generator.gi().expandObj(n1) 583 if (NapiLog.getResult()[0]) 584 return true; 585 } 586 return false; 587 } 588 589} 590MainEditor.LINE_HEIGHT = 30 591 592MainEditor.pInstance_ = null; 593MainEditor.gi = function () { 594 if (MainEditor.pInstance_ == null) MainEditor.pInstance_ = new MainEditor(); 595 return MainEditor.pInstance_; 596} 597 598module.exports = { 599 MainEditor 600}