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 }