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 /**
18  * npp menu
19  *
20  * Author: dokutoku, https://twitter.com/dokutoku3
21  * License: GPL-2.0 or later
22  */
23 module npp_api.pluginfunc.menu;
24 
25 
26 version (Windows):
27 version (Not_betterC):
28 
29 private static import core.sys.windows.windef;
30 private static import core.sys.windows.winuser;
31 private static import std.traits;
32 private static import std.utf;
33 private static import std.algorithm;
34 private static import npp_api.powereditor.misc.pluginsmanager.notepad_plus_msgs;
35 private static import npp_api.pluginfunc.npp_msgs;
36 private static import npp_api.pluginfunc.string;
37 private static import npp_api.powereditor.misc.pluginsmanager.plugininterface;
38 private static import npp_api.powereditor.resource;
39 
40 enum max_submenu_length = npp_api.powereditor.resource.ID_PLUGINS_CMD_DYNAMIC_LIMIT - npp_api.powereditor.resource.ID_PLUGINS_CMD_DYNAMIC;
41 
42 /**
43  * メニューのアクションを照会・実行するための情報を保存する構造体
44  */
45 struct menu_action
46 {
47 	int _cmdID = 0;
48 	npp_api.powereditor.misc.pluginsmanager.plugininterface.PFUNCPLUGINCMD _pFunc = null;
49 }
50 
51 /**
52  * create_sub_menu_actionsの再起関数
53  */
54 pure nothrow @safe @nogc
55 private void internal_create_sub_menu_actions(ref .menu_action[] output, ref size_t i, .menu_item_t[] sub_menu)
56 
57 	in
58 	{
59 		assert(sub_menu != null);
60 	}
61 
62 	do
63 	{
64 		.menu_action output_temp;
65 
66 		foreach (menu; sub_menu) {
67 			output_temp._pFunc = menu.func_item._pFunc;
68 			output[i] = output_temp;
69 			i++;
70 
71 			if (menu.sub_menu != null) {
72 				internal_create_sub_menu_actions(output, i, menu.sub_menu);
73 			}
74 		}
75 	}
76 
77 /**
78  * サブメニューのアクションのリストを作成する
79  */
80 pure nothrow @safe
81 .menu_action[] create_sub_menu_actions(.menu_item_t[] menu_container)
82 
83 	do
84 	{
85 		.menu_action[] output = new .menu_action[.count_sub_menu_ids(menu_container)];
86 
87 		size_t i = 0;
88 
89 		foreach (menu; menu_container) {
90 			if (menu.sub_menu != null) {
91 				internal_create_sub_menu_actions(output, i, menu.sub_menu);
92 			}
93 		}
94 
95 		return output;
96 	}
97 
98 /**
99  * 登録されているアクションのリストからIDを探し出して、関数を実行する
100  */
101 nothrow
102 bool sub_menu_action(size_t actions_length)(const int cmdID, const ref .menu_action[actions_length] menu_actions)
103 
104 	do
105 	{
106 		foreach (menu_func; menu_actions) {
107 			if (cmdID == menu_func._cmdID) {
108 				if (menu_func._pFunc != null) {
109 					menu_func._pFunc();
110 				}
111 
112 				return true;
113 			}
114 		}
115 
116 		return false;
117 	}
118 
119 /**
120  * メインメニューやサブメニューを格納するための構造体
121  */
122 struct menu_item_t
123 {
124 	string id = null;
125 	string menu_checked_id = null;
126 	core.sys.windows.windef.HMENU menu_handle = core.sys.windows.windef.NULL;
127 	npp_api.powereditor.misc.pluginsmanager.plugininterface.FuncItem func_item = void;
128 	.menu_item_t[] sub_menu = null;
129 
130 	deprecated
131 	alias identifier = id;
132 
133 	deprecated
134 	alias menu_checked_identifier = menu_checked_id;
135 
136 	invariant
137 	{
138 		if (this.menu_checked_id != null) {
139 			assert(this.id != null);
140 		}
141 
142 		if (this.id != null) {
143 			assert((npp_api.powereditor.misc.pluginsmanager.plugininterface.nbChar - 1) >= npp_api.pluginfunc..string.count_string(this.func_item._itemName));
144 		} else {
145 			assert(this.menu_checked_id == null);
146 			assert(this.menu_handle == core.sys.windows.windef.NULL);
147 			assert(this.sub_menu == null);
148 		}
149 	}
150 
151 	invariant
152 	{
153 		assert(this.menu_checked_id.length <= 63);
154 	}
155 }
156 
157 deprecated
158 alias max_identifier_length = .max_id_length;
159 
160 pure nothrow @safe @nogc
161 size_t max_id_length(bool is_c_string)(const .menu_item_t[] menu_container)
162 
163 	in
164 	{
165 		assert(menu_container.length != 0);
166 	}
167 
168 	do
169 	{
170 		void max_id_length_internal(const .menu_item_t[] menu_items, ref size_t i)
171 
172 			do
173 			{
174 				foreach (menu_item; menu_items) {
175 					i = (menu_item.id.length > i) ? (menu_item.id.length) : (i);
176 
177 					if (menu_item.sub_menu != null) {
178 						max_id_length_internal(menu_item.sub_menu, i);
179 					}
180 				}
181 			}
182 
183 		size_t max_length = 0;
184 		max_id_length_internal(menu_container, max_length);
185 
186 		static if (is_c_string) {
187 			max_length++;
188 		}
189 
190 		return max_length;
191 	}
192 
193 /**
194  * メインメニューを含むメニューのインデックス
195  */
196 struct sub_menu_index
197 {
198 	size_t parent_index = 0;
199 	size_t parent_cmdID = 0;
200 	size_t depth = 0;
201 	size_t menu_index = 0;
202 	string id = null;
203 	wstring menu_checked_id = null;
204 	npp_api.powereditor.misc.pluginsmanager.plugininterface.FuncItem func_item;
205 
206 	deprecated
207 	alias identifier = id;
208 
209 	deprecated
210 	alias menu_checked_identifier = menu_checked_id;
211 
212 	/**
213 	 * サブメニュー用のハンドル
214 	 */
215 	core.sys.windows.windef.HMENU menu_handle = core.sys.windows.windef.NULL;
216 
217 	invariant
218 	{
219 		if (this.menu_checked_id != null) {
220 			assert(this.id != null);
221 		}
222 	}
223 }
224 
225 /**
226  * メモリ確保するための、サブメニューの数をカウントする
227  * idがnullでもカウントする。
228  */
229 pure nothrow @safe @nogc
230 int allocate_sub_menu_length(const .sub_menu_index[] menu_index)
231 
232 	in
233 	{
234 		assert(menu_index.length != 0);
235 	}
236 
237 	out(result)
238 	{
239 		static import npp_api.powereditor.resource;
240 
241 		assert(result >= 0);
242 		assert(max_submenu_length >= result);
243 	}
244 
245 	do
246 	{
247 		size_t alloc_count = 0;
248 
249 		foreach (menu; menu_index) {
250 			if (menu.depth <= 1) {
251 				continue;
252 			}
253 
254 			alloc_count++;
255 		}
256 
257 		return cast(int)(alloc_count);
258 	}
259 
260 /**
261  * create_menu_indexの再帰関数
262  */
263 private void create_menu_index_internal(size_t OUTPUT_LENGTH)(.menu_item_t[] menu_items, ref .sub_menu_index[OUTPUT_LENGTH] output, ref size_t i, size_t depth, size_t parent_index)
264 
265 	do
266 	{
267 		static import std.utf;
268 		static import npp_api.pluginfunc.auto_pFunc;
269 
270 		depth++;
271 
272 		.sub_menu_index index_temp = {parent_index: parent_index, depth: depth};
273 
274 		size_t index = 0;
275 
276 		foreach (menu_item; menu_items) {
277 			index_temp.menu_index = index;
278 			index_temp.id = menu_item.id;
279 			index_temp.menu_checked_id = std.utf.toUTF16(menu_item.menu_checked_id);
280 			index_temp.func_item = menu_item.func_item;
281 
282 			/+
283 			//ToDo:
284 			if ((index_temp.menu_checked_id != null) && ((index_temp.func_item._pFunc == null) || (index_temp.func_item._pFunc == &npp_api.pluginfunc.auto_pFunc.auto_dummy_func))) {
285 				index_temp.func_item._pFunc = ;
286 			}
287 			+/
288 
289 			output[i] = index_temp;
290 			i++;
291 
292 			if (menu_item.sub_menu != null) {
293 				create_menu_index_internal(menu_item.sub_menu, output, i, depth, index);
294 			}
295 
296 			index++;
297 		}
298 	}
299 
300 /*
301  * メニューの設定から検索用のインデックスを作成する
302  */
303 .sub_menu_index[OUTPUT_LENGTH] create_menu_index(size_t OUTPUT_LENGTH)(.menu_item_t[] menu_container)
304 
305 	in
306 	{
307 		assert(OUTPUT_LENGTH == .count_all_menu_items(menu_container));
308 	}
309 
310 	do
311 	{
312 		static import std.utf;
313 		static import std.string;
314 
315 		.sub_menu_index[OUTPUT_LENGTH] output;
316 		size_t count = 0;
317 		size_t depth = 0;
318 		size_t i = 0;
319 		create_menu_index_internal(menu_container, output, i, depth, 0);
320 
321 		return output;
322 	}
323 
324 pure nothrow @safe @nogc
325 size_t search_menu_index(menu_t)(const menu_t menu_index, string id)
326 	if (std.traits.isArray!(menu_t))
327 
328 	do
329 	{
330 		size_t i = 0;
331 
332 		for (; i < menu_index.length; i++) {
333 			if (menu_index[i].id.length == 0) {
334 				continue;
335 			}
336 
337 			if (std.algorithm.cmp(menu_index[i].id, id) == 0) {
338 				return menu_index[i].menu_index;
339 			}
340 		}
341 
342 		assert(false);
343 	}
344 
345 pure nothrow @safe @nogc
346 size_t search_index(menu_t)(const menu_t menu_index, string id)
347 	if (std.traits.isArray!(menu_t))
348 
349 	in
350 	{
351 		assert(id.length != 0);
352 	}
353 
354 	do
355 	{
356 		size_t i = 0;
357 
358 		for (; i < menu_index.length; i++) {
359 			if (menu_index[i].id.length == 0) {
360 				continue;
361 			}
362 
363 			if (std.algorithm.cmp(menu_index[i].id, id) == 0) {
364 				return i;
365 			}
366 		}
367 
368 		assert(false);
369 	}
370 
371 /**
372  * メモリ確保するための、サブメニューの数をカウントする
373  */
374 pure nothrow @safe @nogc
375 int allocate_sub_menu_length(const .menu_item_t[] menu_container)
376 
377 	in
378 	{
379 		assert(menu_container != null);
380 	}
381 
382 	out(result)
383 	{
384 		static import npp_api.powereditor.resource;
385 
386 		assert(result >= 0);
387 		assert(max_submenu_length >= result);
388 	}
389 
390 	do
391 	{
392 		void internal_count(const .menu_item_t[] sub_menu, ref size_t i)
393 
394 			do
395 			{
396 				foreach (menu; sub_menu) {
397 					i++;
398 
399 					if (menu.sub_menu != null) {
400 						internal_count(menu.sub_menu, i);
401 					}
402 				}
403 			}
404 
405 		size_t count = 0;
406 
407 		foreach (menu; menu_container) {
408 			if (menu.sub_menu != null) {
409 				internal_count(menu.sub_menu, count);
410 			}
411 		}
412 
413 		return cast(int)(count);
414 	}
415 
416 /**
417  * idがnullでもカウントする。
418  */
419 pure nothrow @safe @nogc
420 int count_all_menu_items(const .menu_item_t[] menu_container)
421 
422 	in
423 	{
424 		assert(menu_container != null);
425 	}
426 
427 	out(result)
428 	{
429 		static import npp_api.powereditor.resource;
430 
431 		enum int max_count = npp_api.powereditor.resource.ID_PLUGINS_CMD_DYNAMIC_LIMIT - npp_api.powereditor.resource.ID_PLUGINS_CMD_DYNAMIC;
432 
433 		assert(result >= 0);
434 		assert(max_count >= result);
435 	}
436 
437 	do
438 	{
439 		void internal_count(const .menu_item_t[] sub_menu, ref size_t i)
440 
441 			do
442 			{
443 				foreach (menu; sub_menu) {
444 					i++;
445 
446 					if (menu.sub_menu != null) {
447 						internal_count(menu.sub_menu, i);
448 					}
449 				}
450 			}
451 
452 		size_t count = 0;
453 
454 		foreach (menu; menu_container) {
455 			count++;
456 
457 			if (menu.sub_menu != null) {
458 				internal_count(menu.sub_menu, count);
459 			}
460 		}
461 
462 		return cast(int)(count);
463 	}
464 
465 deprecated
466 alias count_menu_identifiers = .count_menu_ids;
467 
468 /**
469  * idがnullでないすべてのメニューの数をカウントする
470  */
471 pure nothrow @safe @nogc
472 size_t count_menu_ids(const .menu_item_t[] menu_container)
473 
474 	in
475 	{
476 		assert(menu_container != null);
477 	}
478 
479 	do
480 	{
481 		void internal_count(const .menu_item_t[] menu_list, ref size_t i)
482 
483 			do
484 			{
485 				foreach (menu; menu_list) {
486 					if (menu.id != null) {
487 						i++;
488 
489 						if (menu.sub_menu != null) {
490 							internal_count(menu.sub_menu, i);
491 						}
492 					}
493 				}
494 			}
495 
496 		size_t count = 0;
497 
498 		internal_count(menu_container, count);
499 
500 		return count;
501 	}
502 
503 deprecated
504 alias count_sub_menu_identifiers = .count_sub_menu_ids;
505 
506 /**
507  * idがnullでないサブメニューの数をカウントする
508  */
509 pure nothrow @safe @nogc
510 size_t count_sub_menu_ids(const .menu_item_t[] menu_container)
511 
512 	in
513 	{
514 		assert(menu_container != null);
515 	}
516 
517 	do
518 	{
519 		void internal_count(const .menu_item_t[] menu_list, ref size_t i)
520 
521 			do
522 			{
523 				foreach (menu; menu_list) {
524 					if (menu.id != null) {
525 						i++;
526 
527 						if (menu.sub_menu != null) {
528 							internal_count(menu.sub_menu, i);
529 						}
530 					}
531 				}
532 			}
533 
534 		size_t count = 0;
535 
536 		foreach (menu; menu_container) {
537 			if (menu.sub_menu != null) {
538 				internal_count(menu.sub_menu, count);
539 			}
540 		}
541 
542 		return count;
543 	}
544 
545 pure nothrow @safe @nogc
546 bool is_sub_menu_exists(.menu_item_t[] menu_container)
547 
548 	do
549 	{
550 		foreach (menu; menu_container) {
551 			if (menu.sub_menu != null) {
552 				return true;
553 			}
554 		}
555 
556 		return false;
557 	}
558 
559 /**
560  * Notepad++に渡す用のメインメニューの静的配列を作成する
561  */
562 pure nothrow @safe @nogc
563 npp_api.powereditor.misc.pluginsmanager.plugininterface.FuncItem[menu_length] create_main_menu(size_t menu_length)(.menu_item_t[] main_menu_items)
564 
565 	do
566 	{
567 		static import npp_api.powereditor.misc.pluginsmanager.plugininterface;
568 		static import npp_api.pluginfunc.string;
569 
570 		npp_api.powereditor.misc.pluginsmanager.plugininterface.FuncItem[menu_length] output;
571 
572 		for (size_t i = 0; i < main_menu_items.length; i++) {
573 			output[i]._itemName = npp_api.pluginfunc..string.copy_string(main_menu_items[i].func_item._itemName);
574 			output[i]._pFunc = main_menu_items[i].func_item._pFunc;
575 			output[i]._cmdID = main_menu_items[i].func_item._cmdID;
576 			output[i]._init2Check = main_menu_items[i].func_item._init2Check;
577 			output[i]._pShKey = main_menu_items[i].func_item._pShKey;
578 		}
579 
580 		return output;
581 	}
582 
583 pure nothrow @safe @nogc
584 bool[output_length] create_main_menu_checked(size_t output_length)(const .sub_menu_index[] menu_index)
585 
586 	do
587 	{
588 		bool[output_length] output;
589 
590 		for (size_t i = 0, j = 0; i < menu_index.length; i++) {
591 			if (menu_index[i].depth == 1) {
592 				output[j] = menu_index[i].func_item._init2Check;
593 				j++;
594 			}
595 		}
596 
597 		return output;
598 	}
599 
600 pure nothrow @safe @nogc
601 bool[output_length] create_menu_index_checked(size_t output_length)(const .sub_menu_index[] menu_index)
602 
603 	do
604 	{
605 		bool[output_length] output;
606 
607 		for (size_t i = 0, j = 0; i < menu_index.length; i++) {
608 			output[j] = menu_index[i].func_item._init2Check;
609 			j++;
610 		}
611 
612 		return output;
613 	}
614 
615 
616 deprecated
617 alias create_main_menu_checked_identifier = .create_main_menu_checked_id;
618 
619 pure nothrow @safe
620 wstring[] create_main_menu_checked_id(const .sub_menu_index[] menu_index)
621 
622 	do
623 	{
624 		wstring[] output;
625 
626 		for (size_t i = 0; i < menu_index.length; i++) {
627 			if ((menu_index[i].depth == 1) && (menu_index[i].menu_checked_id.length != 0)) {
628 				output ~= menu_index[i].menu_checked_id;
629 			}
630 		}
631 
632 		return output;
633 	}
634 
635 deprecated
636 alias create_menu_index_checked_identifier = .create_menu_index_checked_id;
637 
638 pure nothrow @safe
639 wstring[] create_menu_index_checked_id(const .sub_menu_index[] menu_index)
640 
641 	do
642 	{
643 		wstring[] output;
644 
645 		for (size_t i = 0; i < menu_index.length; i++) {
646 			if (menu_index[i].menu_checked_id.length != 0) {
647 				output ~= menu_index[i].menu_checked_id;
648 			}
649 		}
650 
651 		return output;
652 	}
653 
654 pure nothrow @safe @nogc
655 size_t first_sub_menu_pos(size_t LENGTH)(const .sub_menu_index[LENGTH] menu_index, string id)
656 
657 	in
658 	{
659 		assert(LENGTH >= (search_index(menu_index, id)));
660 	}
661 
662 	do
663 	{
664 		size_t i = search_index(menu_index, id);
665 		size_t depth = menu_index[i].depth;
666 
667 		if (i == 0) {
668 			return i;
669 		}
670 
671 		if (menu_index[i - 1].depth != depth) {
672 			return i;
673 		}
674 
675 		for (; ((i - 1) != 0) && (menu_index[i - 1].depth == depth); i--) {
676 		}
677 
678 		return i;
679 	}
680 
681 pure nothrow @safe @nogc
682 size_t end_sub_menu_pos(size_t LENGTH)(const .sub_menu_index[LENGTH] menu_index, string id)
683 
684 	in
685 	{
686 		assert(LENGTH >= (search_index(menu_index, id)));
687 	}
688 
689 	do
690 	{
691 		size_t i = search_index(menu_index, id);
692 		size_t depth = menu_index[i].depth;
693 
694 		if ((i + 1) >= LENGTH) {
695 			return i;
696 		}
697 
698 		if (menu_index[i + 1].depth != depth) {
699 			return i;
700 		}
701 
702 		for (; ((i + 1) < LENGTH) && (menu_index[i + 1].depth == depth); i++) {
703 		}
704 
705 		return i;
706 	}
707 
708 pure nothrow @safe @nogc
709 size_t sub_menu_length(size_t LENGTH)(const .sub_menu_index[LENGTH] menu_index, string id)
710 
711 	in
712 	{
713 		assert(LENGTH >= (search_index(menu_index, id)));
714 	}
715 
716 	do
717 	{
718 		size_t i = search_index(menu_index, id);
719 		size_t depth = menu_index[i].depth;
720 		size_t count = 0;
721 
722 		for (; (i < LENGTH) && (menu_index[i].depth == depth); i++, count++) {
723 		}
724 
725 		return count;
726 	}
727 
728 pragma(inline, true)
729 nothrow @nogc
730 void enable_check(core.sys.windows.windef.HWND _nppHandle, ref npp_api.powereditor.misc.pluginsmanager.plugininterface.FuncItem menu_item)
731 
732 	do
733 	{
734 		static import core.sys.windows.windef;
735 		static import npp_api.pluginfunc.npp_msgs;
736 
737 		menu_item._init2Check = true;
738 		npp_api.pluginfunc.npp_msgs.send_NPPM_SETMENUITEMCHECK(_nppHandle, menu_item._cmdID, core.sys.windows.windef.TRUE);
739 	}
740 
741 pragma(inline, true)
742 nothrow @nogc
743 void disable_check(core.sys.windows.windef.HWND _nppHandle, ref npp_api.powereditor.misc.pluginsmanager.plugininterface.FuncItem menu_item)
744 
745 	do
746 	{
747 		static import core.sys.windows.windef;
748 		static import npp_api.pluginfunc.npp_msgs;
749 
750 		menu_item._init2Check = false;
751 		npp_api.pluginfunc.npp_msgs.send_NPPM_SETMENUITEMCHECK(_nppHandle, menu_item._cmdID, core.sys.windows.windef.FALSE);
752 	}
753 
754 pragma(inline, true)
755 nothrow @nogc
756 void change_check(core.sys.windows.windef.HWND _nppHandle, ref npp_api.powereditor.misc.pluginsmanager.plugininterface.FuncItem menu_item)
757 
758 	do
759 	{
760 		static import core.sys.windows.windef;
761 		static import npp_api.pluginfunc.npp_msgs;
762 
763 		menu_item._init2Check = !menu_item._init2Check;
764 
765 		if (menu_item._init2Check) {
766 			npp_api.pluginfunc.npp_msgs.send_NPPM_SETMENUITEMCHECK(_nppHandle, menu_item._cmdID, core.sys.windows.windef.TRUE);
767 		} else {
768 			if (menu_item._pFunc != null) {
769 				npp_api.pluginfunc.npp_msgs.send_NPPM_SETMENUITEMCHECK(_nppHandle, menu_item._cmdID, core.sys.windows.windef.FALSE);
770 			}
771 		}
772 	}
773 
774 pragma(inline, true)
775 nothrow @nogc
776 void enable_sub_menu_check(core.sys.windows.windef.HWND _nppHandle, .menu_item_t[] sub_menu_items, size_t menu_index)
777 
778 	in
779 	{
780 		assert(sub_menu_items.length >= menu_index);
781 	}
782 
783 	do
784 	{
785 		for (size_t i = 0; i < sub_menu_items.length; i++) {
786 			if (i == menu_index) {
787 				enable_check(_nppHandle, sub_menu_items[i].func_item);
788 			} else {
789 				if (sub_menu_items[i].func_item._pFunc != null) {
790 					disable_check(_nppHandle, sub_menu_items[i].func_item);
791 				}
792 			}
793 		}
794 	}
795 
796 pragma(inline, true)
797 nothrow @nogc
798 void disable_sub_menu_check(core.sys.windows.windef.HWND _nppHandle, .menu_item_t[] sub_menu_items, size_t menu_index)
799 
800 	in
801 	{
802 		assert(sub_menu_items.length >= menu_index);
803 	}
804 
805 	do
806 	{
807 		for (size_t i = 0; i < sub_menu_items.length; i++) {
808 			if (sub_menu_items[i].func_item._pFunc != null) {
809 				disable_check(_nppHandle, sub_menu_items[i].func_item);
810 			}
811 		}
812 	}
813 
814 pragma(inline, true)
815 nothrow @nogc
816 void change_sub_menu_check(core.sys.windows.windef.HWND _nppHandle, .menu_item_t[] sub_menu_items, size_t menu_index)
817 
818 	in
819 	{
820 		assert(sub_menu_items.length >= menu_index);
821 	}
822 
823 	do
824 	{
825 		for (size_t i = 0; i < sub_menu_items.length; i++) {
826 			if (i == menu_index) {
827 				change_check(_nppHandle, sub_menu_items[i].func_item);
828 			} else {
829 				if (sub_menu_items[i].func_item._pFunc != null) {
830 					disable_check(_nppHandle, sub_menu_items[i].func_item);
831 				}
832 			}
833 		}
834 	}
835 
836 pragma(inline, true)
837 nothrow @nogc
838 void change_sub_menu_check(core.sys.windows.windef.HWND _nppHandle, .sub_menu_index[] index_list, string enable_id)
839 
840 	in
841 	{
842 		assert(index_list.length != 0);
843 		assert(size_t.max > index_list.length);
844 		assert(index_list.length >= search_index(index_list, enable_id));
845 	}
846 
847 	do
848 	{
849 		size_t index = search_index(index_list, enable_id);
850 		size_t depth = index_list[index].depth;
851 
852 		change_check(_nppHandle, index_list[index].func_item);
853 
854 		for (size_t i = index + 1; (i < index_list.length) && (index_list[i].depth == depth); i++) {
855 			if (index_list[i].func_item._pFunc != null) {
856 				disable_check(_nppHandle, index_list[i].func_item);
857 			}
858 		}
859 
860 		for (size_t i = index - 1; (i != 0) && (index_list[i].depth == depth); i--) {
861 			if (index_list[i].func_item._pFunc != null) {
862 				disable_check(_nppHandle, index_list[i].func_item);
863 			}
864 		}
865 	}
866 
867 pragma(inline, true)
868 nothrow @nogc
869 void change_sub_menu_check(core.sys.windows.windef.HWND _nppHandle, .sub_menu_index[] index_list, size_t start, size_t end, size_t pos)
870 
871 	in
872 	{
873 		assert(index_list.length > end);
874 		assert(end >= start);
875 		assert(pos >= start);
876 		assert(end >= pos);
877 	}
878 
879 	do
880 	{
881 		for (size_t i = start; i <= end; i++) {
882 			if (i == pos) {
883 				change_check(_nppHandle, index_list[i].func_item);
884 			} else {
885 				disable_check(_nppHandle, index_list[i].func_item);
886 			}
887 		}
888 	}
889 
890 pure nothrow @safe @nogc
891 bool is_chid_menu_checked(const .sub_menu_index[] menu_index_list, size_t parent_menu_pos)
892 
893 	do
894 	{
895 		size_t i = parent_menu_pos + 1;
896 
897 		if (i >= menu_index_list.length) {
898 			return false;
899 		}
900 
901 		size_t depth = menu_index_list[parent_menu_pos].depth + 1;
902 
903 		for (; (i < menu_index_list.length) && (depth == menu_index_list[i].depth); i++) {
904 			if (menu_index_list[i].func_item._init2Check) {
905 				return true;
906 			}
907 		}
908 
909 		return false;
910 	}
911 
912 /**
913  * 同じ階層のメニューの最初のindexの位置を返す
914  */
915 nothrow @nogc
916 size_t same_menu_start_pos(const .sub_menu_index[] menu_index, size_t pos)
917 
918 	in
919 	{
920 		assert(menu_index.length > pos);
921 	}
922 
923 	out(result)
924 	{
925 		assert(pos >= result);
926 	}
927 
928 	do
929 	{
930 		size_t depth = menu_index[pos].depth;
931 		size_t i = pos;
932 
933 		for (; (i != 0); i--) {
934 			if (depth != menu_index[i - 1].depth) {
935 				break;
936 			}
937 		}
938 
939 		return i;
940 	}
941 
942 /**
943  * 同じ階層のメニューの最後のindexの位置を返す
944  */
945 nothrow @nogc
946 size_t same_menu_end_pos(const .sub_menu_index[] menu_index, size_t pos)
947 
948 	in
949 	{
950 		assert(menu_index.length > pos);
951 	}
952 
953 	out(result)
954 	{
955 		assert(result >= pos);
956 	}
957 
958 	do
959 	{
960 		size_t depth = menu_index[pos].depth;
961 		size_t i = pos;
962 
963 		for (; (i < menu_index.length); i++) {
964 			if (depth != menu_index[i + 1].depth) {
965 				break;
966 			}
967 		}
968 
969 		return i;
970 	}
971 
972 enum core.sys.windows.winuser.MENUITEMINFOW default_submenu =
973 {
974 	cbSize: core.sys.windows.winuser.MENUITEMINFOW.sizeof,
975 	fMask: core.sys.windows.winuser.MIIM_STATE|core.sys.windows.winuser.MIIM_ID|core.sys.windows.winuser.MIIM_TYPE,
976 	fType: core.sys.windows.winuser.MFT_STRING,
977 	fState: core.sys.windows.winuser.MFS_ENABLED,
978 	wID: 0,
979 	hSubMenu: core.sys.windows.windef.NULL,
980 	hbmpChecked: core.sys.windows.windef.NULL,
981 	hbmpUnchecked: core.sys.windows.windef.NULL,
982 	dwItemData: 0,
983 	dwTypeData: core.sys.windows.windef.NULL,
984 	cch: 0,
985 	hbmpItem: core.sys.windows.windef.NULL,
986 };
987 
988 enum core.sys.windows.winuser.MENUITEMINFOW default_add_menu =
989 {
990 	cbSize: core.sys.windows.winuser.MENUITEMINFOW.sizeof,
991 	fMask: core.sys.windows.winuser.MIIM_SUBMENU,
992 	fType: 0,
993 	fState: 0,
994 	wID: 0,
995 	hSubMenu: core.sys.windows.windef.NULL,
996 	hbmpChecked: core.sys.windows.windef.NULL,
997 	hbmpUnchecked: core.sys.windows.windef.NULL,
998 	dwItemData: 0,
999 	dwTypeData: core.sys.windows.windef.NULL,
1000 	cch: 0,
1001 	hbmpItem: core.sys.windows.windef.NULL,
1002 };
1003 
1004 nothrow @nogc
1005 private void init_sub_menu_internal(size_t menu_index_length, size_t actions_length)(core.sys.windows.windef.HMENU plugin_menu, ref wchar[npp_api.pluginfunc.config_file.setting_name_length] setting_name_buffer, ref .sub_menu_index[menu_index_length] menu_index_list, ref size_t j, size_t depth, ref .menu_action[actions_length] actions, ref size_t actions_pos, ref int nextNumber, ref core.sys.windows.winuser.MENUITEMINFOW sub_menu_buf)
1006 
1007 	in
1008 	{
1009 	}
1010 
1011 	do
1012 	{
1013 		static import npp_api.pluginfunc.string;
1014 		static import core.sys.windows.winuser;
1015 
1016 		++depth;
1017 		size_t parent_id = j;
1018 
1019 		//insert sub menu parent
1020 		menu_index_list[parent_id].menu_handle = core.sys.windows.winuser.CreatePopupMenu();
1021 		core.sys.windows.winuser.MENUITEMINFOW parent_menu = .default_add_menu;
1022 		parent_menu.hSubMenu = menu_index_list[parent_id].menu_handle;
1023 		core.sys.windows.winuser.SetMenuItemInfoW(plugin_menu, menu_index_list[parent_id].func_item._cmdID, core.sys.windows.windef.FALSE, &parent_menu);
1024 
1025 		size_t menu_count = 0;
1026 		j++;
1027 
1028 		for (; (j < menu_index_list.length) && (menu_index_list[j].depth >= depth); j++) {
1029 			menu_index_list[j].parent_cmdID = menu_index_list[parent_id].func_item._cmdID;
1030 			menu_index_list[j].func_item._cmdID = nextNumber;
1031 
1032 			//update action
1033 			assert(actions.length > actions_pos);
1034 			actions[actions_pos]._cmdID = nextNumber;
1035 			++actions_pos;
1036 
1037 			//Insert Sub menu
1038 			sub_menu_buf.wID = nextNumber;
1039 
1040 			if (menu_index_list[j].func_item._init2Check) {
1041 				sub_menu_buf.fState = core.sys.windows.winuser.MFS_CHECKED;
1042 			} else {
1043 				sub_menu_buf.fState = core.sys.windows.winuser.MFS_UNCHECKED;
1044 			}
1045 
1046 			sub_menu_buf.dwTypeData = &(menu_index_list[j].func_item._itemName[0]);
1047 			sub_menu_buf.cch = cast(core.sys.windows.windef.UINT)(npp_api.pluginfunc..string.count_string(menu_index_list[j].func_item._itemName));
1048 			core.sys.windows.winuser.InsertMenuItemW(menu_index_list[parent_id].menu_handle, cast(int)(menu_count), core.sys.windows.windef.TRUE, &sub_menu_buf);
1049 
1050 			++nextNumber;
1051 
1052 			if ((menu_index_list.length > (j + 1)) && (menu_index_list[j + 1].depth == (depth + 1))) {
1053 				init_sub_menu_internal(plugin_menu, setting_name_buffer, menu_index_list, j, depth, actions, actions_pos, nextNumber, sub_menu_buf);
1054 			}
1055 
1056 			menu_count++;
1057 
1058 			if (((menu_index_list.length > (j + 1)) && ((depth - 1) >= menu_index_list[j + 1].depth))) {
1059 				break;
1060 			}
1061 		}
1062 	}
1063 
1064 /**
1065  * サブメニューを初期化する
1066  * _cmdIDとかも同期する
1067  */
1068 nothrow @nogc
1069 void init_submenu(size_t main_menu_length, size_t menu_index_length, size_t actions_length)(core.sys.windows.windef.HWND _nppHandle, const ref npp_api.powereditor.misc.pluginsmanager.plugininterface.FuncItem[main_menu_length] main_menu, ref .sub_menu_index[menu_index_length] menu_index_list, ref .menu_action[actions_length] actions, int required_length)
1070 
1071 	in
1072 	{
1073 		static import core.sys.windows.windef;
1074 
1075 		static assert(int.max >= actions_length);
1076 		assert(_nppHandle != core.sys.windows.windef.NULL);
1077 		assert(required_length != 0);
1078 		assert(required_length == allocate_sub_menu_length(menu_index_list));
1079 	}
1080 
1081 	do
1082 	{
1083 		static import core.sys.windows.windef;
1084 		static import core.sys.windows.winuser;
1085 		static import npp_api.powereditor.misc.pluginsmanager.notepad_plus_msgs;
1086 		static import npp_api.pluginfunc.npp_msgs;
1087 		static import npp_api.pluginfunc.config_file;
1088 
1089 		wchar[npp_api.pluginfunc.config_file.setting_name_length] setting_name_buffer;
1090 
1091 		core.sys.windows.winuser.MENUITEMINFOW sub_menu_buf = .default_submenu;
1092 
1093 		if (npp_api.pluginfunc.npp_msgs.send_NPPM_ALLOCATESUPPORTED(_nppHandle) == core.sys.windows.windef.TRUE) {
1094 			core.sys.windows.windef.HMENU plugin_menu = npp_api.pluginfunc.npp_msgs.send_NPPM_GETMENUHANDLE(_nppHandle, npp_api.powereditor.misc.pluginsmanager.notepad_plus_msgs.NPPPLUGINMENU);
1095 			int startNumber;
1096 
1097 			if (!npp_api.pluginfunc.npp_msgs.send_NPPM_ALLOCATECMDID(_nppHandle, cast(int)(required_length), startNumber)) {
1098 				return;
1099 			}
1100 
1101 			int nextNumber = startNumber;
1102 			size_t actions_pos = 0;
1103 
1104 			for (size_t i = 0, j = 0; (i < main_menu.length); i++, j++) {
1105 				assert(menu_index_list.length > j);
1106 				menu_index_list[j].parent_cmdID = 0;
1107 				menu_index_list[j].func_item._cmdID = main_menu[i]._cmdID;
1108 
1109 				if ((menu_index_list.length > (j + 1)) && (menu_index_list[j + 1].depth == 2)) {
1110 					.init_sub_menu_internal(plugin_menu, setting_name_buffer, menu_index_list, j, 1, actions, actions_pos, nextNumber, sub_menu_buf);
1111 				}
1112 			}
1113 		}
1114 	}