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);