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