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