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 }