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 * ini file setting 19 * 20 * Author: dokutoku, https://twitter.com/dokutoku3 21 * License: GPL-2.0 or later 22 */ 23 module npp_api.pluginfunc.ini_setting; 24 25 26 version (Windows): 27 version (Not_betterC): 28 29 private static import core.sys.windows.winbase; 30 private static import core.sys.windows.windef; 31 private static import core.sys.windows.winnt; 32 private static import core.sys.windows.winuser; 33 private static import npp_api.PowerEditor.MISC.PluginsManager.Notepad_plus_msgs; 34 private static import npp_api.PowerEditor.MISC.PluginsManager.PluginInterface; 35 private static import npp_api.pluginfunc.config_file; 36 private static import npp_api.pluginfunc.menu; 37 private static import npp_api.pluginfunc.npp_msgs; 38 private static import npp_api.pluginfunc.path; 39 private static import npp_api.pluginfunc.string; 40 private static import std.conv; 41 private static import std.file; 42 private static import std.path; 43 private static import std.stdio; 44 private static import std.string; 45 private static import std.traits; 46 private static import std.utf; 47 48 struct ini_setting_item 49 { 50 mixin npp_api.pluginfunc.config_file.setting_item_internal!(wchar); 51 } 52 53 ini_setting_item[output_length] convert_setting(size_t output_length)(npp_api.pluginfunc.config_file.setting_item[] input_setting) 54 55 do 56 { 57 ini_setting_item[output_length] output; 58 59 for (size_t i = 0; i < input_setting.length; i++) { 60 output[i].name = std.utf.toUTF16(input_setting[i].name[0 .. $]); 61 output[i].name = npp_api.pluginfunc..string.copy_string(output[i].name); 62 output[i].type = input_setting[i].type; 63 64 switch (input_setting[i].type) { 65 case npp_api.pluginfunc.config_file.value_type.string_type: 66 case npp_api.pluginfunc.config_file.value_type.path_type: 67 if (input_setting[i].value.string_value != null) { 68 output[i].value.wstring_value = std.utf.toUTF16(input_setting[i].value.string_value[0 .. $]); 69 } 70 71 break; 72 73 default: 74 output[i].value = input_setting[i].value; 75 76 break; 77 } 78 79 output[i].extra_filter = input_setting[i].extra_filter; 80 } 81 82 return output; 83 } 84 85 pure nothrow @safe 86 wstring[] create_setting_idetifiers(size_t setting_length)(ini_setting_item[setting_length] setting_items) 87 88 do 89 { 90 wstring[] output = new wstring[setting_length]; 91 92 for (size_t i = 0; i < setting_items.length; i++) { 93 output[i] = npp_api.pluginfunc..string.from_stringz(setting_items[i].name); 94 } 95 96 return output; 97 } 98 99 struct npp_ini_session 100 { 101 private: 102 wstring npp_config_path = null; 103 wstring plugin_config_path = null; 104 wstring config_file = null; 105 core.sys.windows.winnt.WCHAR[] section_name = null; 106 bool is_valid = false; 107 wchar[npp_api.pluginfunc.config_file.setting_name_length] setting_name_buf; 108 //path_buf; 109 110 public: 111 this(core.sys.windows.windef.HWND _nppHandle, const wchar[] plugin_directry, const wchar[] config_filename, const wchar[] section_name) 112 113 in 114 { 115 assert(section_name.length != 0); 116 117 if (plugin_directry.length != 0) { 118 assert(std.path.isValidPath(plugin_directry)); 119 } 120 } 121 122 do 123 { 124 this.section_name = section_name.dup; 125 this.section_name ~= "\0"w; 126 core.sys.windows.winnt.WCHAR[npp_api.pluginfunc.path.OS_MAX_PATH] input_buf = '\0'; 127 npp_api.pluginfunc.npp_msgs.send_NPPM_GETPLUGINSCONFIGDIR(_nppHandle, input_buf.length, &(input_buf[0])); 128 this.npp_config_path = npp_api.pluginfunc..string.from_stringz(input_buf); 129 130 if (std.file.exists(this.npp_config_path) && std.file.isDir(this.npp_config_path)) { 131 if (plugin_directry.length != 0) { 132 this.plugin_config_path = std.path.buildPath(this.npp_config_path, plugin_directry).idup; 133 134 if (!std.file.exists(this.plugin_config_path) || !std.file.isDir(this.plugin_config_path)) { 135 std.file.mkdir(this.plugin_config_path); 136 } 137 } else { 138 this.plugin_config_path = this.npp_config_path.idup; 139 } 140 141 this.config_file = std.path.buildPath(this.plugin_config_path, std.path.setExtension(config_filename, `.ini`w)).idup; 142 143 if (!std.file.exists(this.config_file) || !std.file.isFile(this.config_file)) { 144 try { 145 std.stdio.File new_file = std.stdio.File(this.config_file, `w`w); 146 new_file.close(); 147 this.is_valid = true; 148 } catch (Exception e) { 149 //ToDo: 150 } 151 } else { 152 this.is_valid = true; 153 } 154 155 this.config_file ~= "\0"w; 156 } else { 157 throw new Exception(`Unknown Notepad++ config path`); 158 } 159 } 160 161 pragma(inline, true) 162 pure nothrow @safe @nogc 163 bool is_valid_setting() const 164 165 do 166 { 167 return this.is_valid; 168 } 169 170 /** 171 * メニューにチェックをつけるかどうか 172 * 173 * Params: 174 * key_name = 設定名 175 * default_value = 設定の読み込みに失敗した場合のデフォルト値 176 * 177 * Returns: メニューにチェックをつけるかどうか 178 */ 179 nothrow @nogc 180 bool is_mfs_checked(const wchar[] key_name, bool default_value = false) 181 182 in 183 { 184 assert(key_name.length != 0); 185 } 186 187 do 188 { 189 npp_api.pluginfunc..string.copy_string(this.setting_name_buf, key_name); 190 core.sys.windows.windef.UINT result = core.sys.windows.winbase.GetPrivateProfileIntW(&(this.section_name[0]), &(this.setting_name_buf[0]), int.max, &(this.config_file[0])); 191 192 if (result == int.max) { 193 return default_value; 194 } 195 196 return (result == 1) ? (true) : (false); 197 } 198 199 nothrow @nogc 200 void load_menu_checked(size_t menu_index_length)(ref npp_api.pluginfunc.menu.sub_menu_index[menu_index_length] menu_index) 201 202 do 203 { 204 for (size_t i = 0; i < menu_index.length; i++) { 205 if (menu_index[i].menu_checked_id.length != 0) { 206 menu_index[i].func_item._init2Check = this.is_mfs_checked(menu_index[i].menu_checked_id, menu_index[i].func_item._init2Check); 207 } 208 } 209 } 210 211 nothrow 212 void auto_load(size_t setting_length)(ref .ini_setting_item[setting_length] settings) const 213 214 do 215 { 216 npp_api.pluginfunc.config_file.value_buf value_buf; 217 218 bool result_temp; 219 220 foreach (ref setting; settings) { 221 result_temp = false; 222 223 switch (setting.type) { 224 case npp_api.pluginfunc.config_file.value_type.bool_type: 225 result_temp = this.load_config(setting.name, value_buf.bool_value); 226 227 if ((result_temp) && (setting.extra_filter != null)) { 228 result_temp = setting.extra_filter(value_buf); 229 } 230 231 if (result_temp) { 232 setting.value.bool_value = value_buf.bool_value; 233 } 234 235 break; 236 237 case npp_api.pluginfunc.config_file.value_type.int_type: 238 result_temp = this.load_config(setting.name, int.max, value_buf.int_value); 239 240 if ((result_temp) && (setting.extra_filter != null)) { 241 result_temp = setting.extra_filter(value_buf); 242 } 243 244 if (result_temp) { 245 setting.value.int_value = value_buf.int_value; 246 } 247 248 break; 249 250 case npp_api.pluginfunc.config_file.value_type.uint_type: 251 result_temp = this.load_config(setting.name, uint.max, value_buf.uint_value); 252 253 if ((result_temp) && (setting.extra_filter != null)) { 254 result_temp = setting.extra_filter(value_buf); 255 } 256 257 if (result_temp) { 258 setting.value.uint_value = value_buf.uint_value; 259 } 260 261 break; 262 263 case npp_api.pluginfunc.config_file.value_type.string_type: 264 value_buf.wstring_value[] = '\0'; 265 result_temp = this.load_config(setting.name, value_buf.wstring_value); 266 267 if ((result_temp) && (setting.extra_filter != null)) { 268 result_temp = setting.extra_filter(value_buf); 269 } 270 271 if (result_temp) { 272 setting.value.wstring_value = npp_api.pluginfunc..string.from_stringz(value_buf.wstring_value); 273 } 274 275 break; 276 277 case npp_api.pluginfunc.config_file.value_type.path_type: 278 value_buf.wstring_value[] = '\0'; 279 result_temp = this.load_config(setting.name, value_buf.wstring_value); 280 281 if ((result_temp) && (setting.extra_filter != null)) { 282 result_temp = setting.extra_filter(value_buf); 283 } 284 285 if (result_temp) { 286 setting.value.wstring_value = npp_api.pluginfunc..string.from_stringz(value_buf.path_value); 287 } 288 289 break; 290 291 default: 292 break; 293 } 294 } 295 } 296 297 pragma(inline, true) 298 nothrow 299 void auto_load(size_t setting_length, size_t menu_index_length)(ref .ini_setting_item[setting_length] settings, ref npp_api.pluginfunc.menu.sub_menu_index[menu_index_length] menu_index) 300 301 do 302 { 303 this.auto_load(settings); 304 this.load_menu_checked(menu_index); 305 } 306 307 pragma(inline, true) 308 nothrow @nogc 309 void write_menu_checked(const wchar[] key_name, bool value) 310 311 in 312 { 313 assert(key_name.length != 0); 314 } 315 316 do 317 { 318 npp_api.pluginfunc..string.copy_string(this.setting_name_buf, key_name); 319 core.sys.windows.winbase.WritePrivateProfileStringW(&(this.section_name[0]), &(this.setting_name_buf[0]), &(((value) ? (npp_api.pluginfunc..string.c_wstring!(`1`w)) : (npp_api.pluginfunc..string.c_wstring!(`0`w)))[0]), &(this.config_file[0])); 320 } 321 322 pragma(inline, true) 323 nothrow @nogc 324 void write_menu_checked(size_t menu_index_length)(ref npp_api.pluginfunc.menu.sub_menu_index[menu_index_length] menu_index) 325 326 do 327 { 328 for (size_t i = 0; i < menu_index.length; i++) { 329 if (menu_index[i].menu_checked_id.length != 0) { 330 this.write_menu_checked(menu_index[i].menu_checked_id, menu_index[i].func_item._init2Check); 331 } 332 } 333 } 334 335 pragma(inline, true) 336 nothrow 337 bool auto_write(size_t setting_length)(const ref .ini_setting_item[setting_length] settings) 338 339 do 340 { 341 foreach (setting; settings) { 342 switch (setting.type) { 343 case npp_api.pluginfunc.config_file.value_type.bool_type: 344 this.write_config(setting.name, setting.value.bool_value); 345 346 break; 347 348 case npp_api.pluginfunc.config_file.value_type.int_type: 349 this.write_config(setting.name, setting.value.int_value); 350 351 break; 352 353 case npp_api.pluginfunc.config_file.value_type.uint_type: 354 this.write_config(setting.name, setting.value.uint_value); 355 356 break; 357 358 case npp_api.pluginfunc.config_file.value_type.string_type: 359 this.write_config(setting.name, setting.value.wstring_value); 360 361 break; 362 363 case npp_api.pluginfunc.config_file.value_type.path_type: 364 this.write_config(setting.name, setting.value.wstring_value); 365 366 break; 367 368 default: 369 break; 370 } 371 } 372 } 373 374 pragma(inline, true) 375 nothrow 376 void auto_write(size_t setting_length, size_t menu_index_length)(ref .ini_setting_item[setting_length] settings, ref npp_api.pluginfunc.menu.sub_menu_index[menu_index_length] menu_index) 377 378 do 379 { 380 this.auto_write(settings); 381 this.load_menu_checked(menu_index); 382 } 383 384 /** 385 * ??? 386 * 387 * Params: 388 * key_name = ??? 389 * result_value = オプションの格納場所 390 * 391 * Returns: 正常にloadできたかどうか 392 */ 393 nothrow @nogc 394 bool load_config(const ref wchar[npp_api.pluginfunc.config_file.setting_name_length] key_name, ref bool result_value) const 395 396 in 397 { 398 assert(key_name[$ - 1] == '\0'); 399 } 400 401 do 402 { 403 core.sys.windows.windef.UINT temp_value = core.sys.windows.winbase.GetPrivateProfileIntW(&(this.section_name[0]), &(key_name[0]), int.max, &(this.config_file[0])); 404 405 switch (temp_value) { 406 case 0: 407 result_value = false; 408 409 return true; 410 411 case 1: 412 result_value = true; 413 414 return true; 415 416 default: 417 return false; 418 } 419 } 420 421 /** 422 * ??? 423 * 424 * Params: 425 * key_name = ??? 426 * invaild_value = ??? 427 * result_value = オプションの格納場所 428 * 429 * Returns: 正常にloadできたかどうか 430 */ 431 nothrow @nogc 432 bool load_config(const ref wchar[npp_api.pluginfunc.config_file.setting_name_length] key_name, core.sys.windows.windef.INT invaild_value, ref core.sys.windows.windef.UINT result_value) const 433 434 in 435 { 436 assert(key_name[$ - 1] == '\0'); 437 } 438 439 do 440 { 441 core.sys.windows.windef.UINT temp_value = core.sys.windows.winbase.GetPrivateProfileIntW(&(this.section_name[0]), &(key_name[0]), invaild_value, &(this.config_file[0])); 442 443 if (temp_value == invaild_value) { 444 return false; 445 } 446 447 result_value = temp_value; 448 449 return true; 450 } 451 452 /** 453 * ??? 454 * 455 * Params: 456 * key_name = ??? 457 * invaild_value = ??? 458 * result_value = オプションの格納場所 459 * 460 * Returns: 正常にloadできたかどうか 461 */ 462 nothrow @nogc 463 bool load_config(const ref wchar[npp_api.pluginfunc.config_file.setting_name_length] key_name, core.sys.windows.windef.INT invaild_value, ref int result_value) const 464 465 in 466 { 467 assert(key_name[$ - 1] == '\0'); 468 } 469 470 do 471 { 472 core.sys.windows.windef.UINT temp_value = core.sys.windows.winbase.GetPrivateProfileIntW(&(this.section_name[0]), &(key_name[0]), invaild_value, &(this.config_file[0])); 473 474 if (temp_value == invaild_value) { 475 return false; 476 } 477 478 result_value = cast(int)(temp_value); 479 480 return true; 481 } 482 483 /** 484 * ??? 485 * 486 * Params: 487 * key_name = ??? 488 * invaild_value = ??? 489 * result_value = オプションの格納場所 490 * 491 * Returns: 正常にloadできたかどうか 492 */ 493 pragma(inline, true) 494 nothrow @nogc 495 bool load_config(size_t value_length)(const ref wchar[npp_api.pluginfunc.config_file.setting_name_length] key_name, ref wchar[value_length] result_value) const 496 497 in 498 { 499 assert(key_name[$ - 1] == '\0'); 500 } 501 502 do 503 { 504 core.sys.windows.windef.DWORD result_temp = core.sys.windows.winbase.GetPrivateProfileStringW(&(this.section_name[0]), &(key_name[0]), &("\0"w[0]), &(result_value[0]), result_value.length, &(this.config_file[0])); 505 506 if ((result_temp == 0) || (result_value[0] == '\0')) { 507 return false; 508 } 509 510 return true; 511 } 512 513 /** 514 * ??? 515 * 516 * Params: 517 * key_name = ??? 518 * result_value = オプションの格納場所 519 * 520 * Returns: 正常にloadできたかどうか 521 */ 522 pragma(inline, true) 523 bool load_config(C)(const (C)[] key_name, ref bool result_value) const 524 if (std.traits.isSomeChar!(C)) 525 526 in 527 { 528 } 529 530 do 531 { 532 core.sys.windows.windef.UINT temp_value = core.sys.windows.winbase.GetPrivateProfileIntW(&(this.section_name[0]), std.utf.toUTF16z(key_name), int.max, &(this.config_file[0])); 533 534 switch (temp_value) { 535 case 0: 536 result_value = false; 537 538 return true; 539 540 case 1: 541 result_value = true; 542 543 return true; 544 545 default: 546 return false; 547 } 548 } 549 550 /** 551 * ??? 552 * 553 * Params: 554 * key_name = ??? 555 * invaild_value = ??? 556 * result_value = オプションの格納場所 557 * 558 * Returns: 正常にloadできたかどうか 559 */ 560 pragma(inline, true) 561 bool load_config(C)(const (C)[] key_name, core.sys.windows.windef.INT invaild_value, ref core.sys.windows.windef.UINT result_value) const 562 if (std.traits.isSomeChar!(C)) 563 564 in 565 { 566 } 567 568 do 569 { 570 core.sys.windows.windef.UINT temp_value = core.sys.windows.winbase.GetPrivateProfileIntW(&(this.section_name[0]), std.utf.toUTF16z(key_name), invaild_value, &(this.config_file[0])); 571 572 if (temp_value == invaild_value) { 573 return false; 574 } 575 576 result_value = temp_value; 577 578 return true; 579 } 580 581 /** 582 * ??? 583 * 584 * Params: 585 * key_name = ??? 586 * value = ??? 587 * 588 * Returns: WritePrivateProfileStringWの結果 589 */ 590 pragma(inline, true) 591 nothrow @nogc 592 bool write_config(size_t value_length)(const ref wchar[npp_api.pluginfunc.config_file.setting_name_length] key_name, bool value) const 593 594 in 595 { 596 assert(key_name[$ - 1] == '\0'); 597 } 598 599 do 600 { 601 return (core.sys.windows.winbase.WritePrivateProfileStringW(&(this.section_name[0]), &(key_name[0]), &(((value) ? (npp_api.pluginfunc..string.c_wstring!(`1`w)) : (npp_api.pluginfunc..string.c_wstring!(`0`w)))[0]), &(this.config_file[0])) != 0) ? (true) : (false); 602 } 603 604 /** 605 * ??? 606 * 607 * Params: 608 * key_name = ??? 609 * value = ??? 610 * 611 * Returns: 正常にwriteできたかどうか 612 */ 613 pragma(inline, true) 614 nothrow 615 bool write_config(size_t value_length, D)(const ref wchar[npp_api.pluginfunc.config_file.setting_name_length] key_name, const D value) const 616 if (std.traits.isNumeric!(D)) 617 618 in 619 { 620 assert(key_name[$ - 1] == '\0'); 621 } 622 623 do 624 { 625 return (core.sys.windows.winbase.WritePrivateProfileStringW(&(this.section_name[0]), &(key_name[0]), std.utf.toUTF16z(std.conv.to!(wstring)(value)), &(this.config_file[0])) != 0) ? (true) : (false); 626 } 627 628 /** 629 * ??? 630 * 631 * Params: 632 * key_name = ??? 633 * value = ??? 634 * 635 * Returns: 正常にwriteできたかどうか 636 */ 637 pragma(inline, true) 638 nothrow 639 bool write_config(size_t value_length)(const ref wchar[npp_api.pluginfunc.config_file.setting_name_length] key_name, const wchar[] value) const 640 641 in 642 { 643 assert(key_name[$ - 1] == '\0'); 644 } 645 646 do 647 { 648 649 return (core.sys.windows.winbase.WritePrivateProfileStringW(&(this.section_name[0]), &(key_name[0]), (value.length != 0) ? (std.utf.toUTF16z(value)) : (core.sys.windows.windef.NULL), &(this.config_file[0])) != 0) ? (true) : (false); 650 } 651 652 /** 653 * ??? 654 * 655 * Params: 656 * key_name = ??? 657 * value = ??? 658 * 659 * Returns: WritePrivateProfileStringWの結果 660 */ 661 pragma(inline, true) 662 bool write_config(C)(const (C)[] key_name, bool value) const 663 if (std.traits.isSomeChar!(C)) 664 665 in 666 { 667 assert(key_name.length != 0); 668 } 669 670 do 671 { 672 return (core.sys.windows.winbase.WritePrivateProfileStringW(&(this.section_name[0]), std.utf.toUTF16z(key_name), &(((value) ? (npp_api.pluginfunc..string.c_wstring!(`1`w)) : (npp_api.pluginfunc..string.c_wstring!(`0`w)))[0]), &(this.config_file[0])) != 0) ? (true) : (false); 673 } 674 675 /** 676 * ??? 677 * 678 * Params: 679 * key_name = ??? 680 * value = ??? 681 * 682 * Returns: 正常にwriteできたかどうか 683 */ 684 pragma(inline, true) 685 bool write_config(C, D)(const (C)[] key_name, const D value) const 686 if (std.traits.isSomeChar!(C) && std.traits.isNumeric!(D)) 687 688 in 689 { 690 } 691 692 do 693 { 694 return (core.sys.windows.winbase.WritePrivateProfileStringW(&(this.section_name[0]), std.utf.toUTF16z(key_name), std.utf.toUTF16z(std.conv.to!(wstring)(value)), &(this.config_file[0])) != 0) ? (true) : (false); 695 } 696 697 /** 698 * ??? 699 * 700 * Params: 701 * key_name = ??? 702 * value = ??? 703 * 704 * Returns: 正常にwriteできたかどうか 705 */ 706 bool write_config(C)(const (C)[] key_name, const (C)[] value) const 707 if (std.traits.isSomeChar!(C)) 708 709 in 710 { 711 } 712 713 do 714 { 715 if (value.length == 0) { 716 return (core.sys.windows.winbase.WritePrivateProfileStringW(&(this.section_name[0]), std.utf.toUTF16z(key_name), core.sys.windows.windef.NULL, &(this.config_file[0])) != 0) ? (true) : (false); 717 } else { 718 return (core.sys.windows.winbase.WritePrivateProfileStringW(&(this.section_name[0]), std.utf.toUTF16z(key_name), std.utf.toUTF16z(value), &(this.config_file[0])) != 0) ? (true) : (false); 719 } 720 } 721 722 /** 723 * ??? 724 * 725 * Params: 726 * ??? = ??? 727 */ 728 void write_config(C)(const (C)[] key_name, const npp_api.PowerEditor.MISC.PluginsManager.PluginInterface.FuncItem func_item) const 729 if (std.traits.isSomeChar!(C)) 730 731 do 732 { 733 this.write_config(key_name, func_item._init2Check); 734 } 735 736 pragma(inline, true) 737 pure nothrow @safe 738 wstring get_config_file_path() const 739 740 do 741 { 742 return this.config_file.idup; 743 } 744 745 pragma(inline, true) 746 pure nothrow @safe 747 wstring get_config_folder_path() const 748 749 do 750 { 751 return this.plugin_config_path.idup; 752 } 753 754 /** 755 * 756 */ 757 void mkdir(const wchar[] sub_directry = ``w) 758 759 in 760 { 761 if (sub_directry.length != 0) { 762 assert(std.path.isValidPath(sub_directry)); 763 } 764 } 765 766 do 767 { 768 wstring path = std.path.buildPath(this.plugin_config_path, sub_directry); 769 770 if (!std.file.exists(path) && !std.file.isDir(path)) { 771 std.file.mkdir(path); 772 } 773 } 774 775 invariant 776 { 777 assert((this.section_name.length != 0) && (this.section_name[$ - 1] == '\0')); 778 assert((this.config_file.length != 0)); 779 } 780 }