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