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