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