1 //this file is part of notepad++ 2 //Copyright (C)2003 Don HO <donho@altern.org> 3 // 4 //This program is free software; you can redistribute it and/or 5 //modify it under the terms of the GNU General Public License 6 //as published by the Free Software Foundation; either 7 //version 2 of the License, or (at your option) any later version. 8 // 9 //This program is distributed in the hope that it will be useful, 10 //but WITHOUT ANY WARRANTY; without even the implied warranty of 11 //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 //GNU General Public License for more details. 13 // 14 //You should have received a copy of the GNU General Public License 15 //along with this program; if not, write to the Free Software 16 //Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17 module npp_converter.plugindefinition; 18 19 20 private static import core.stdc.stdio; 21 private static import core.stdc.wchar_; 22 private static import core.sys.windows.basetsd; 23 private static import core.sys.windows.shlwapi; 24 private static import core.sys.windows.winbase; 25 private static import core.sys.windows.windef; 26 private static import core.sys.windows.winnt; 27 private static import core.sys.windows.winuser; 28 private static import std.file; 29 private static import std.path; 30 private static import std.stdio; 31 private static import npp_api.scintilla.scintilla; 32 private static import npp_api.pluginfunc.basic_interface; 33 private static import npp_api.pluginfunc.npp_msgs; 34 private static import npp_api.pluginfunc.scintilla_msg; 35 private static import npp_api.pluginfunc.string; 36 private static import npp_api.powereditor.misc.pluginsmanager.plugininterface; 37 private static import npp_api.powereditor.wincontrols.dockingwnd.docking; 38 private static import npp_converter.conversionpanel; 39 private static import npp_converter.resource; 40 41 enum plugin_name = "Converter sample"w; 42 enum plugin_cname = npp_api.pluginfunc..string.c_wstring!(plugin_name); 43 44 extern (C) 45 __gshared npp_api.powereditor.misc.pluginsmanager.plugininterface.NppData gshared_nppData; 46 47 enum npp_api.powereditor.misc.pluginsmanager.plugininterface.FuncItem[] main_menu_items_def = 48 [ 49 { 50 _itemName: "ASCII -> HEX"w, 51 _pFunc: &.ascii2Hex, 52 _init2Check: false, 53 _pShKey: null, 54 }, 55 { 56 _itemName: "HEX -> ASCII"w, 57 _pFunc: &.hex2Ascii, 58 _init2Check: false, 59 _pShKey: null, 60 }, 61 { 62 _pFunc: null, 63 }, 64 { 65 _itemName: "Conversion Panel"w, 66 _pFunc: &.conversionPanel, 67 _init2Check: false, 68 _pShKey: null, 69 }, 70 { 71 _itemName: "Edit Configuration File"w, 72 _pFunc: &.editConf, 73 _init2Check: false, 74 _pShKey: null, 75 }, 76 { 77 _itemName: "About"w, 78 _pFunc: &.about, 79 _init2Check: false, 80 _pShKey: null, 81 }, 82 ]; 83 84 enum CONVERSIONPANEL_INDEX = 3; 85 86 core.sys.windows.windef.HINSTANCE handle_test; 87 88 pragma(inline, true) 89 nothrow 90 void pluginInit(core.sys.windows.basetsd.HANDLE hModule) 91 92 do 93 { 94 static import core.sys.windows.windef; 95 static import npp_converter.conversionpanel; 96 97 try { 98 npp_converter.conversionpanel._conversionPanel = new npp_converter.conversionpanel.ConversionPanel(); 99 npp_converter.conversionpanel._conversionPanel.initialize(cast(core.sys.windows.windef.HINSTANCE)(hModule), core.sys.windows.windef.NULL); 100 .handle_test = cast(core.sys.windows.windef.HINSTANCE)(hModule); 101 } catch (Exception e) { 102 //ToDo: 103 core.sys.windows.winuser.MessageBoxW(.nppData._nppHandle, &("Exception\0"w[0]), &(plugin_cname[0]), core.sys.windows.winuser.MB_OK); 104 } 105 } 106 107 nothrow @nogc 108 core.sys.windows.windef.HWND getCurrentScintillaHandle() 109 110 do 111 { 112 static import npp_api.pluginfunc.npp_msgs; 113 114 int currentEdit; 115 npp_api.pluginfunc.npp_msgs.send_NPPM_GETCURRENTSCINTILLA(.nppData._nppHandle, currentEdit); 116 117 return (currentEdit == 0) ? (.nppData._scintillaMainHandle) : (.nppData._scintillaSecondHandle); 118 } 119 120 class SelectedString 121 { 122 public: 123 nothrow 124 this() 125 126 do 127 { 128 this._str = null; 129 this._selStartPos = 0; 130 this._selEndPos = 0; 131 132 core.sys.windows.windef.HWND hCurrScintilla = .getCurrentScintillaHandle(); 133 size_t start = npp_api.pluginfunc.scintilla_msg.send_SCI_GETSELECTIONSTART(hCurrScintilla); 134 size_t end = npp_api.pluginfunc.scintilla_msg.send_SCI_GETSELECTIONEND(hCurrScintilla); 135 136 if (end < start) { 137 size_t tmp = start; 138 start = end; 139 end = tmp; 140 } 141 142 this._selStartPos = start; 143 this._selEndPos = end; 144 145 size_t textLen = end - start; 146 147 if (textLen == 0) { 148 return; 149 } 150 151 char[] temp = new char[textLen + 1]; 152 153 npp_api.pluginfunc.scintilla_msg.send_SCI_GETSELTEXT(hCurrScintilla, &(temp[0])); 154 this._str = npp_api.pluginfunc..string.from_stringz(temp).dup; 155 } 156 157 pure nothrow @safe 158 string getStr() 159 160 do 161 { 162 return this._str.idup; 163 } 164 165 pure nothrow @safe @nogc 166 size_t length() 167 168 do 169 { 170 if (this._str != null) { 171 return (this._selEndPos - this._selStartPos); 172 } 173 174 return 0; 175 } 176 177 pure nothrow @safe @nogc 178 size_t getSelStartPos() 179 180 do 181 { 182 return this._selStartPos; 183 } 184 185 pure nothrow @safe @nogc 186 size_t getSelEndPos() 187 188 do 189 { 190 return this._selEndPos; 191 } 192 193 pure nothrow @safe @nogc 194 int getChar(size_t i) 195 196 do 197 { 198 if (i >= (this._selEndPos - this._selStartPos)) { 199 return -1; 200 } 201 202 return this._str[i]; 203 } 204 205 protected: 206 char[] _str; 207 size_t _selStartPos; 208 size_t _selEndPos; 209 } 210 211 class HexString : SelectedString 212 { 213 public: 214 /** 215 * rule : 216 * 0. only 0-9 a-f A-F withe space (32) and return (10, 13) are allowed. 217 * 1. 2 char to form 1 byte, so there are alway 2 char w/o space. 218 * 2. if there is a space between the 1st char and the 2nd char, then it should be it in all string, and vice and versa 219 * 3. All the white space (32) \n(10) and \r(13) will be ignored. 220 */ 221 nothrow @nogc 222 bool toAscii() 223 224 do 225 { 226 size_t l = this.length(); 227 bool hasWs = false; 228 229 if ((!l) || (l < 2)) { 230 return false; 231 } 232 233 if (l < 5) //3 : "00X" or "X00" where X == \n or " " 234 //4 : "0000" 235 { 236 237 } 238 // Check 5 first characters 239 else { // 5: "00 00" or "00000" 240 hasWs = this._str[2] == ' '; 241 } 242 243 // Begin conversion 244 .hexStat stat = .hexStat.st_init; 245 size_t i = 0; 246 size_t j = 0; 247 248 for ( ; (i < this._str.length) && (this._str[i] != '\0') ; i++) { 249 if (this._str[i] == ' ') { 250 if (!hasWs) { 251 return false; 252 } 253 254 if (stat != .hexStat.st_cc) { 255 return false; 256 } 257 258 stat = .hexStat.st_init; 259 } 260 /* 261 else if (this._str[i] == '\t') 262 { 263 264 } 265 */ 266 else if ((this._str[i] == '\n') || (this._str[i] == '\r')) { 267 if ((stat != .hexStat.st_cc) && (stat != .hexStat.st_init)) { 268 return false; 269 } 270 271 stat = .hexStat.st_init; 272 } else if (((this._str[i] >= 'a') && (this._str[i] <= 'f')) || ((this._str[i] >= 'A') && (this._str[i] <= 'F')) || ((this._str[i] >= '0') && (this._str[i] <= '9'))) { 273 if (stat == .hexStat.st_cc) { 274 if (hasWs) { 275 return false; 276 } 277 278 stat = .hexStat.st_c; 279 } else if (stat == .hexStat.st_c) { 280 // Process 281 282 int hi = .getTrueHexValue(this._str[i - 1]); 283 284 if (hi == -1) { 285 return false; 286 } 287 288 int lo = .getTrueHexValue(this._str[i]); 289 290 if (lo == -1) { 291 return false; 292 } 293 294 this._str[j++] = cast(char)((hi * 16) + lo); 295 stat = .hexStat.st_cc; 296 } else if (stat == .hexStat.st_init) { 297 stat = .hexStat.st_c; 298 } 299 } 300 /* 301 else if (this._str[i] == ' ') 302 { 303 304 } 305 */ 306 else { 307 return false; 308 } 309 310 } 311 312 // finalize 313 if (stat == .hexStat.st_c) { 314 return false; 315 } 316 317 this._selEndPos = this._selStartPos + j; 318 319 return true; 320 } 321 } 322 323 struct Param 324 { 325 bool _isMaj = true; 326 bool _insertSpace = false; 327 size_t _nbCharPerLine = 0; 328 } 329 330 Param param; 331 wstring confPath; 332 wstring c_confPath; 333 334 enum pluginConfName = "converter.ini"w; 335 enum ascii2HexSectionName = npp_api.pluginfunc..string.c_wstring!("ascii2Hex"w); 336 enum ascii2HexSpace = npp_api.pluginfunc..string.c_wstring!("insertSpace"w); 337 enum ascii2HexMaj = npp_api.pluginfunc..string.c_wstring!("uppercase"w); 338 enum ascii2HexNbCharPerLine = npp_api.pluginfunc..string.c_wstring!("nbCharPerLine"w); 339 340 nothrow @nogc 341 void getCmdsFromConf(const ref core.sys.windows.winnt.WCHAR[] c_confPath, ref Param param) 342 343 in 344 { 345 assert(c_confPath.length != 0); 346 assert(c_confPath[$ - 1] == '\0'); 347 } 348 349 do 350 { 351 static import core.sys.windows.winbase; 352 static import core.sys.windows.windef; 353 static import core.sys.windows.winnt; 354 static import core.stdc.wchar_; 355 356 core.sys.windows.winnt.WCHAR[core.sys.windows.windef.MAX_PATH] cmdNames; 357 core.sys.windows.winbase.GetPrivateProfileSectionNamesW(&(cmdNames[0]), core.sys.windows.windef.MAX_PATH, &(c_confPath[0])); 358 core.sys.windows.winnt.WCHAR* pFn = &(cmdNames[0]); 359 360 if ((*pFn != '\0') && (core.stdc.wchar_.wcscmp(pFn, &(ascii2HexSectionName[0])) == 0)) { 361 int val = core.sys.windows.winbase.GetPrivateProfileIntW(pFn, &(ascii2HexSpace[0]), 0, &(c_confPath[0])); 362 param._insertSpace = val != 0; 363 val = core.sys.windows.winbase.GetPrivateProfileIntW(pFn, &(ascii2HexMaj[0]), 0, &(c_confPath[0])); 364 param._isMaj = val != 0; 365 val = core.sys.windows.winbase.GetPrivateProfileIntW(pFn, &(ascii2HexNbCharPerLine[0]), 0, &(c_confPath[0])); 366 param._nbCharPerLine = val; 367 } 368 } 369 370 // 371 // if conf file does not exist, then create it and load it. 372 nothrow 373 void loadConfFile() 374 375 do 376 { 377 static import core.stdc.stdio; 378 static import core.sys.windows.shlwapi; 379 static import core.sys.windows.windef; 380 static import core.sys.windows.winnt; 381 static import core.sys.windows.winuser; 382 static import core.stdc.wchar_; 383 static import std.file; 384 static import std.path; 385 static import std.stdio; 386 static import npp_api.pluginfunc.npp_msgs; 387 static import npp_api.pluginfunc.string; 388 389 core.sys.windows.winnt.WCHAR[core.sys.windows.windef.MAX_PATH] confDir; 390 npp_api.pluginfunc.npp_msgs.send_NPPM_GETPLUGINSCONFIGDIR(.nppData._nppHandle, confDir.length, &(confDir[0])); 391 .confPath = std.path.buildPath(npp_api.pluginfunc..string.from_stringz(confDir), pluginConfName); 392 .c_confPath = ""w; 393 .c_confPath ~= .confPath; 394 .c_confPath ~= '\0'; 395 396 const char[] confContent = import("confContent.ini"); 397 398 if (!std.file.exists(.confPath)) { 399 try { 400 std.stdio.File f = std.stdio.File(.confPath, "w"); 401 f.write(confContent); 402 f.flush(); 403 f.close(); 404 } catch (Exception e) { 405 //ToDo: 406 core.sys.windows.winuser.MessageBoxW(.nppData._nppHandle, &("Exception\0"w[0]), &(plugin_cname[0]), core.sys.windows.winuser.MB_OK); 407 } 408 409 /* 410 else { 411 wstring msg = .confPath ~ " is absent, and this file cannot be create.\0"w; 412 core.sys.windows.winuser.MessageBoxW(.nppData._nppHandle, &(msg[0]), &(npp_api.pluginfunc.string.c_wstring!("Not present"w)[0]), core.sys.windows.winuser.MB_OK); 413 } 414 */ 415 } 416 417 getCmdsFromConf(.c_confPath, .param); 418 } 419 420 pragma(inline, true) 421 nothrow 422 void pluginSetInfo(ref npp_api.powereditor.misc.pluginsmanager.plugininterface.NppData notpadPlusData) 423 424 do 425 { 426 .loadConfFile(); 427 } 428 429 nothrow 430 void ascii2hex(bool insertSpace, bool isMaj, size_t nbCharPerLine) 431 432 do 433 { 434 static import core.stdc.stdio; 435 static import core.sys.windows.windef; 436 static import npp_api.scintilla.scintilla; 437 static import npp_api.pluginfunc.scintilla_msg; 438 439 .SelectedString selText = new .SelectedString(); 440 size_t textLen = selText.length(); 441 442 if (textLen == 0) { 443 return; 444 } 445 446 core.sys.windows.windef.HWND hCurrScintilla = .getCurrentScintillaHandle(); 447 int eolMode = cast(int)(npp_api.pluginfunc.scintilla_msg.send_SCI_GETEOLMODE(hCurrScintilla)); 448 size_t eolNbCharUnit = (eolMode == npp_api.scintilla.scintilla.SC_EOL_CRLF) ? (2) : (1); 449 size_t eolNbChar = 0; 450 451 if (nbCharPerLine) { 452 eolNbChar = (textLen / nbCharPerLine) * eolNbCharUnit; 453 } 454 455 size_t inc = (insertSpace) ? (3) : (2); 456 char[] pDestText = new char[textLen * (inc + eolNbChar) + 1]; 457 458 size_t j = 0; 459 460 for (size_t i = 0, k = 1 ; i < textLen ; i++) { 461 bool isEOL = false; 462 463 if (nbCharPerLine) { 464 if (k >= nbCharPerLine) { 465 isEOL = true; 466 k = 1; 467 } else { 468 k++; 469 } 470 } 471 472 int val = selText.getChar(i); 473 474 if (val == -1) { 475 return; 476 } 477 478 if (!insertSpace || isEOL) { 479 string format = (isMaj) ? ("%02X") : ("%02x"); 480 core.stdc.stdio.sprintf(&(pDestText[j]), &(format[0]), cast(ubyte)(val)); 481 j += 2; 482 } else { 483 string format = (isMaj) ? ("%02X ") : ("%02x "); 484 core.stdc.stdio.sprintf(&(pDestText[j]), &(format[0]), cast(ubyte)(val)); 485 j += 3; 486 } 487 488 if (isEOL) { 489 if (eolMode == npp_api.scintilla.scintilla.SC_EOL_CRLF) { 490 pDestText[j++] = 0x0D; 491 pDestText[j++] = 0x0A; 492 } else if (eolMode == npp_api.scintilla.scintilla.SC_EOL_CR) { 493 pDestText[j++] = 0x0D; 494 } else if (eolMode == npp_api.scintilla.scintilla.SC_EOL_LF) { 495 pDestText[j++] = 0x0A; 496 } 497 } 498 } 499 500 pDestText[j] = 0x00; 501 size_t start = selText.getSelStartPos(); 502 npp_api.pluginfunc.scintilla_msg.send_SCI_REPLACESEL(hCurrScintilla, &("\0"[0])); 503 npp_api.pluginfunc.scintilla_msg.send_SCI_ADDTEXT(hCurrScintilla, j, &(pDestText[0])); 504 npp_api.pluginfunc.scintilla_msg.send_SCI_SETSEL(hCurrScintilla, start, start + j); 505 } 506 507 extern (C) 508 nothrow 509 void ascii2Hex() 510 511 do 512 { 513 .ascii2hex(.param._insertSpace, .param._isMaj, .param._nbCharPerLine); 514 } 515 516 enum hexStat 517 { 518 st_init, 519 st_c, 520 st_cc, 521 } 522 523 pure nothrow @safe @nogc 524 int getTrueHexValue(char c) 525 526 do 527 { 528 switch (c) { 529 case '0': .. case '9': 530 return (c - '0'); 531 532 case 'a': 533 case 'A': 534 return 10; 535 536 case 'b': 537 case 'B': 538 return 11; 539 540 case 'c': 541 case 'C': 542 return 12; 543 544 case 'd': 545 case 'D': 546 return 13; 547 548 case 'e': 549 case 'E': 550 return 14; 551 552 case 'f': 553 case 'F': 554 return 15; 555 556 default: 557 return -1; 558 } 559 } 560 561 extern (C) 562 nothrow 563 void hex2Ascii() 564 565 do 566 { 567 static import core.sys.windows.windef; 568 static import core.sys.windows.winuser; 569 static import npp_api.pluginfunc.scintilla_msg; 570 571 .HexString transformer = new .HexString(); 572 573 if (transformer.length() == 0) { 574 return; 575 } 576 577 if (transformer.toAscii()) { 578 string hexStr = transformer.getStr(); 579 core.sys.windows.windef.HWND hCurrScintilla = .getCurrentScintillaHandle(); 580 size_t start = transformer.getSelStartPos(); 581 size_t len = transformer.length(); 582 npp_api.pluginfunc.scintilla_msg.send_SCI_REPLACESEL(hCurrScintilla, &("\0"[0])); 583 npp_api.pluginfunc.scintilla_msg.send_SCI_ADDTEXT(hCurrScintilla, len, &(hexStr[0])); 584 npp_api.pluginfunc.scintilla_msg.send_SCI_SETSEL(hCurrScintilla, start, start + len); 585 } else { 586 core.sys.windows.winuser.MessageBoxW(core.sys.windows.windef.NULL, "Hex format is not conformed", &(plugin_cname[0]), core.sys.windows.winuser.MB_OK); 587 } 588 } 589 590 enum aboutMsg = npp_api.pluginfunc..string.c_wstring!("Npp API Converter plugin sample\r\rVersion: "w ~ npp_converter.resource.VERSION_VALUE ~ "\r\rLicense: GPL\r\rAuthor: Don Ho <don.h@free.fr>\r"w); 591 592 extern (C) 593 nothrow @nogc 594 void about() 595 596 do 597 { 598 static import core.sys.windows.winuser; 599 static import npp_api.pluginfunc.string; 600 601 core.sys.windows.winuser.MessageBoxW(.nppData._nppHandle, &(aboutMsg[0]), &(plugin_cname[0]), core.sys.windows.winuser.MB_OK); 602 } 603 604 extern (C) 605 nothrow 606 void editConf() 607 608 do 609 { 610 static import core.sys.windows.winnt; 611 static import core.sys.windows.winuser; 612 static import std.file; 613 static import npp_api.pluginfunc.npp_msgs; 614 static import npp_api.pluginfunc.string; 615 616 if (!std.file.exists(.confPath)) { 617 const core.sys.windows.winnt.WCHAR[] msg = (.confPath ~ " is not present.\rPlease create this file manually.\0"w).idup; 618 core.sys.windows.winuser.MessageBoxW(.nppData._nppHandle, &(msg[0]), &(npp_api.pluginfunc..string.c_wstring!("Configuration file is absent"w)[0]), core.sys.windows.winuser.MB_OK); 619 620 return; 621 } 622 623 npp_api.pluginfunc.npp_msgs.send_NPPM_DOOPEN(.nppData._nppHandle, &(.c_confPath[0])); 624 } 625 626 extern (C) 627 nothrow 628 void conversionPanel() 629 630 do 631 { 632 static import npp_api.pluginfunc.npp_msgs; 633 static import npp_converter.conversionpanel; 634 static import npp_api.powereditor.wincontrols.dockingwnd.docking; 635 636 npp_converter.conversionpanel._conversionPanel.setParent(.gshared_nppData._nppHandle); 637 npp_api.powereditor.wincontrols.dockingwnd.docking.tTbData data; 638 639 try { 640 if (!npp_converter.conversionpanel._conversionPanel.isCreated()) { 641 npp_converter.conversionpanel._conversionPanel.create(&npp_converter.conversionpanel._conversionPanel, data); 642 643 // define the default docking behaviour 644 data.uMask = npp_api.powereditor.wincontrols.dockingwnd.docking.DWS_DF_FLOATING; 645 646 data.pszModuleName = npp_converter.conversionpanel._conversionPanel.getPluginFileName(); 647 648 // the dlgDlg should be the index of funcItem where the current function pointer is 649 // in this case is DOCKABLE_DEMO_INDEX 650 data.dlgID = .CONVERSIONPANEL_INDEX; 651 652 npp_api.pluginfunc.npp_msgs.send_NPPM_DMMREGASDCKDLG(.gshared_nppData._nppHandle, &data); 653 } 654 655 npp_converter.conversionpanel._conversionPanel.display(); 656 } catch (Exception e) { 657 core.sys.windows.winuser.MessageBoxW(core.sys.windows.windef.NULL, &("Exception\0"w[0]), &("In StaticDialog.create()\0"w[0]), core.sys.windows.winuser.MB_OK); 658 } 659 } 660 661 mixin npp_api.pluginfunc.basic_interface.npp_plugin_interface!(plugin_name, main_menu_items_def);