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  * プラグインの定義から自動的にインターフェイスをミックスインするためのもの
19  *
20  * Author: dokutoku, https://twitter.com/dokutoku3
21  * License: GPL-2.0 or later
22  */
23 module npp_api.pluginfunc.extra_interfece;
24 
25 
26 version (Windows):
27 version (Not_betterC):
28 
29 private static import core.sys.windows.windef;
30 private static import std.traits;
31 private static import npp_api.powereditor.misc.pluginsmanager.plugininterface;
32 private static import npp_api.pluginfunc.config_file;
33 private static import npp_api.pluginfunc.menu;
34 
35 enum current_mixin_version = 0.001;
36 
37 /**
38  * ToDo:
39  */
40 struct npp_plugin_definition
41 {
42 	static import npp_api.powereditor.misc.pluginsmanager.plugininterface;
43 	static import npp_api.pluginfunc.config_file;
44 	static import npp_api.pluginfunc.menu;
45 	static import npp_api.pluginfunc.config_file;
46 
47 	//immutable string released = null;
48 
49 	float mixin_version = current_mixin_version;
50 
51 	npp_api.pluginfunc.config_file.config_type_t config_type = npp_api.pluginfunc.config_file.config_type_t.none;
52 
53 	/**
54 	 * Plugin name
55 	 */
56 	string name = null;
57 
58 	/**
59 	 * Plugin version
60 	 */
61 	float version_number = 0.001;
62 
63 	/**
64 	 * version version string
65 	 */
66 	string version_string = null;
67 
68 	/**
69 	 * Author name
70 	 */
71 	string author = null;
72 
73 	/**
74 	 * plugin config file info
75 	 */
76 	npp_api.pluginfunc.config_file.plugin_config_info config_info;
77 
78 	/**
79 	 * menu list
80 	 */
81 	npp_api.pluginfunc.menu.menu_item_t[] menu_items = null;
82 
83 	//ToDo: export?
84 
85 	invariant
86 	{
87 	}
88 }
89 
90 mixin template npp_main_menu(npp_api.pluginfunc.menu.menu_item_t[] main_menu_items)
91 {
92 	private static import npp_api.pluginfunc.menu;
93 
94 	static if (!__traits(compiles, .main_menu_def)) {
95 		/*
96 		 * main menu
97 		 */
98 		enum npp_api.powereditor.misc.pluginsmanager.plugininterface.FuncItem[main_menu_items.length] main_menu_def = npp_api.pluginfunc.menu.create_main_menu!(main_menu_items.length)(main_menu_items);
99 		npp_api.powereditor.misc.pluginsmanager.plugininterface.FuncItem[main_menu_items.length] main_menu = main_menu_def;
100 	}
101 }
102 
103 mixin template npp_menu_actions(npp_api.pluginfunc.menu.menu_item_t[] menu_container)
104 {
105 	private static import npp_api.pluginfunc.menu;
106 
107 	static if (!__traits(compiles, .sub_menu_actions_def)) {
108 		/*
109 		 * sub menu actions
110 		 */
111 		enum sub_menu_actions_def = npp_api.pluginfunc.menu.create_sub_menu_actions(menu_container);
112 		static assert(sub_menu_actions_def.length == npp_api.pluginfunc.menu.count_sub_menu_identifiers(menu_container));
113 		npp_api.pluginfunc.menu.menu_action[sub_menu_actions_def.length] sub_menu_actions = .sub_menu_actions_def;
114 	}
115 }
116 
117 mixin template npp_menu_index(npp_api.pluginfunc.menu.menu_item_t[] menu_container)
118 {
119 	private static import npp_api.pluginfunc.menu;
120 
121 	static if (!__traits(compiles, .menu_index_def)) {
122 		/*
123 		 *
124 		 */
125 		enum menu_index_length = npp_api.pluginfunc.menu.count_all_menu_items(menu_container);
126 		enum npp_api.pluginfunc.menu.sub_menu_index[menu_index_length] menu_index_def = npp_api.pluginfunc.menu.create_menu_index!(menu_index_length)(menu_container); 
127 		npp_api.pluginfunc.menu.sub_menu_index[menu_index_length] menu_index = .menu_index_def;
128 
129 		/*
130 		 * メニュー部分のインデックスを返す
131 		 */
132 		template search_menu_index(string identifier)
133 		{
134 			enum size_t search_menu_index = npp_api.pluginfunc.menu.search_menu_index(.menu_index_def, identifier);
135 		}
136 
137 		template search_index(string identifier)
138 		{
139 			enum size_t search_index = npp_api.pluginfunc.menu.search_index(.menu_index_def, identifier);
140 			static assert(menu_index_length >= search_index);
141 		}
142 	}
143 
144 	static assert(.menu_index_def.length == npp_api.pluginfunc.menu.count_all_menu_items(menu_container));
145 }
146 
147 mixin template npp_autoload(npp_api.pluginfunc.config_file.config_type_t config_type, npp_api.pluginfunc.config_file.setting_item[] settings)
148 {
149 	private static import std.algorithm;
150 	private static import npp_api.pluginfunc.config_file;
151 	private static import npp_api.pluginfunc.ini_setting;
152 
153 	static if ((!__traits(compiles, .auto_settings)) && (settings.length != 0)) {
154 		static if (config_type == npp_api.pluginfunc.config_file.config_type_t.ini) {
155 			enum auto_settings_def = npp_api.pluginfunc.ini_setting.convert_setting!(settings.length)(settings);
156 			npp_api.pluginfunc.ini_setting.ini_setting_item[settings.length] auto_settings = auto_settings_def;
157 			enum wstring[] setting_identifiers = npp_api.pluginfunc.ini_setting.create_setting_idetifiers(auto_settings_def);
158 
159 			size_t search_setting_identifier_index(wstring[] identifier_list, wstring identifier)
160 
161 				do
162 				{
163 					for (size_t i = 0; i < identifier_list.length; i++) {
164 						if (std.algorithm.cmp(identifier_list[i], identifier) == 0) {
165 							return i;
166 						}
167 					}
168 
169 					assert(0);
170 				}
171 
172 			template search_setting_identifier(wstring identifier)
173 			{
174 				enum search_setting_identifier = search_setting_identifier_index(.setting_identifiers, identifier);
175 				static assert(auto_settings_def.length > search_setting_identifier);
176 			}
177 		} else static if (config_type == npp_api.pluginfunc.config_file.config_type_t.none) {
178 		} else {
179 			static assert(false);
180 		}
181 	}
182 }
183 
184 mixin template menu_checked_list(size_t main_menu_length, npp_api.pluginfunc.menu.sub_menu_index[] menu_index)
185 {
186 	private static import npp_api.pluginfunc.menu;
187 
188 	static if (!__traits(compiles, .menu_index_checked_def)) {
189 		enum wstring[] menu_index_checked_identifiers = npp_api.pluginfunc.menu.create_menu_index_checked_identifier(menu_index);
190 		enum wstring[] main_menu_checked_identifiers = npp_api.pluginfunc.menu.create_main_menu_checked_identifier(menu_index);
191 	}
192 }
193 
194 /**
195  * menu_index_checkedからmain_menu_checkedへコピーする
196  */
197 pure nothrow @safe @nogc
198 void update_main_menu_checked(size_t main_menu_length, size_t menu_index_length)(ref npp_api.powereditor.misc.pluginsmanager.plugininterface.FuncItem[main_menu_length] main_menu, const ref npp_api.pluginfunc.menu.sub_menu_index[menu_index_length] menu_index)
199 
200 	in
201 	{
202 	}
203 
204 	do
205 	{
206 		for (size_t i = 0, j = 0; i < menu_index.length; i++) {
207 			if (menu_index[i].depth == 1) {
208 				main_menu[j]._init2Check = menu_index[i].func_item._init2Check;
209 				j++;
210 			}
211 		}
212 	}
213 
214 mixin template npp_plugin_config(npp_api.pluginfunc.config_file.config_type_t type)
215 {
216 	static if (!__traits(compiles, .plugin_config_file)) {
217 		static if (type == npp_api.pluginfunc.config_file.config_type_t.ini) {
218 			npp_api.pluginfunc.ini_setting.npp_ini_session plugin_config_file;
219 		} else static if (type == npp_api.pluginfunc.config_file.config_type_t.none) {
220 		} else {
221 			static assert(false);
222 		}
223 	}
224 }
225 
226 mixin template npp_DLLMain(npp_api.pluginfunc.config_file.config_type_t type, wstring[] menu_index_checked_identifiers, alias menu_index)
227 {
228 	private static import core.sys.windows.basetsd;
229 	private static import core.sys.windows.dll;
230 	private static import core.sys.windows.windef;
231 	private static import core.sys.windows.winnt;
232 	private static import npp_api.pluginfunc.config_file;
233 
234 	static if (!__traits(compiles, .DllMain)) {
235 		pragma(mangle, "DllMain")
236 		extern (Windows)
237 		export core.sys.windows.windef.BOOL DllMain(core.sys.windows.basetsd.HANDLE hModule, core.sys.windows.windef.DWORD reasonForCall, core.sys.windows.winnt.LPVOID lpReserved)
238 
239 			do
240 			{
241 				static import core.sys.windows.dll;
242 				static import core.sys.windows.windef;
243 				static import core.sys.windows.winnt;
244 
245 				switch (reasonForCall) {
246 					case core.sys.windows.winnt.DLL_PROCESS_ATTACH:
247 						core.sys.windows.dll.dll_process_attach(hModule);
248 
249 						static if (__traits(compiles, .pluginInit)) {
250 							.pluginInit(hModule);
251 						}
252 
253 						break;
254 
255 					case core.sys.windows.winnt.DLL_PROCESS_DETACH:
256 						static if ((type != npp_api.pluginfunc.config_file.config_type_t.none) && (menu_index_checked_identifiers.length != 0)) {
257 							plugin_config_file.write_menu_checked(menu_index);
258 						}
259 
260 						static if (__traits(compiles, .pluginCleanUp)) {
261 							.pluginCleanUp();
262 						}
263 
264 						core.sys.windows.dll.dll_process_detach(hModule);
265 
266 						break;
267 
268 					case core.sys.windows.winnt.DLL_THREAD_ATTACH:
269 						core.sys.windows.dll.dll_thread_attach(true, true);
270 
271 						break;
272 
273 					case core.sys.windows.winnt.DLL_THREAD_DETACH:
274 						core.sys.windows.dll.dll_thread_detach(true, true);
275 
276 						break;
277 
278 					default:
279 						break;
280 				}
281 
282 				return core.sys.windows.windef.TRUE;
283 			}
284 	}
285 }
286 
287 mixin template npp_setInfo(alias nppData, npp_api.pluginfunc.config_file.plugin_config_info config_info, alias plugin_config_file, alias auto_settings, alias main_menu, alias menu_index)
288 	if (is(typeof(nppData) == npp_api.powereditor.misc.pluginsmanager.plugininterface.NppData) && std.traits.isStaticArray!(typeof(main_menu)) && std.traits.isStaticArray!(typeof(menu_index)))
289 {
290 	private static import npp_api.powereditor.misc.pluginsmanager.plugininterface;
291 	private static import npp_api.pluginfunc.extra_interfece;
292 	private static import npp_api.pluginfunc.config_file;
293 
294 	static if (!__traits(compiles, .setInfo)) {
295 		pragma(mangle, "setInfo")
296 		extern (C)
297 		nothrow
298 		export void setInfo(npp_api.powereditor.misc.pluginsmanager.plugininterface.NppData notpadPlusData)
299 
300 			do
301 			{
302 				static import std.utf;
303 				static import npp_api.pluginfunc.extra_interfece;
304 
305 				nppData = notpadPlusData;
306 
307 				static if (__traits(compiles, .gshared_nppData)) {
308 					.gshared_nppData = notpadPlusData;
309 				}
310 
311 				try {
312 					static if (config_info.type != npp_api.pluginfunc.config_file.config_type_t.none) {
313 						enum directory_name = std.utf.toUTF16(config_info.directory_name);
314 						enum file_name = std.utf.toUTF16(config_info.file_name);
315 						enum section_name = std.utf.toUTF16(config_info.ini_section_name);
316 						plugin_config_file = npp_api.pluginfunc.ini_setting.npp_ini_session(nppData._nppHandle, directory_name, file_name, section_name);
317 
318 						static if (main_menu.length != 0) {
319 							plugin_config_file.auto_load(auto_settings, menu_index);
320 						} else {
321 							plugin_config_file.auto_load(auto_settings);
322 						}
323 					}
324 
325 					static if (main_menu.length != 0) {
326 						npp_api.pluginfunc.extra_interfece.update_main_menu_checked(main_menu, menu_index);
327 					}
328 				} catch (Exception e) {
329 					//ToDo:
330 				}
331 
332 				static if (__traits(compiles, .pluginSetInfo)) {
333 					.pluginSetInfo(notpadPlusData);
334 				}
335 			}
336 	}
337 }
338 
339 mixin template npp_beNotified(alias nppData, alias main_menu, alias menu_index, alias menu_index_def, alias sub_menu_actions)
340 {
341 	private static import std.traits;
342 	private static import npp_api.scintilla.scintilla;
343 	private static import npp_api.powereditor.misc.pluginsmanager.notepad_plus_msgs;
344 	private static import npp_api.pluginfunc.menu;
345 
346 	static if (!__traits(compiles, .beNotified) && (npp_api.pluginfunc.menu.allocate_sub_menu_length(menu_index_def) != 0)) {
347 		static if (__traits(compiles, .pluginBeNotified) && !std.traits.hasFunctionAttributes!(.pluginBeNotified, "@nogc")) {
348 			pragma(mangle, "beNotified")
349 			extern (C)
350 			nothrow
351 			export void beNotified(npp_api.scintilla.scintilla.SCNotification* notifyCode)
352 
353 				do
354 				{
355 					static import npp_api.powereditor.misc.pluginsmanager.notepad_plus_msgs;
356 					static import npp_api.pluginfunc.menu;
357 
358 					/+
359 					if (notifyCode == null) {
360 						return;
361 					}
362 					+/
363 
364 					switch ((*notifyCode).nmhdr.code) {
365 						case npp_api.powereditor.misc.pluginsmanager.notepad_plus_msgs.NPPN_TBMODIFICATION:
366 							//init submenu
367 							enum required = npp_api.pluginfunc.menu.allocate_sub_menu_length(menu_index_def);
368 							npp_api.pluginfunc.menu.init_submenu(nppData._nppHandle, main_menu, menu_index, sub_menu_actions, required);
369 
370 							break;
371 
372 						default:
373 							break;
374 					}
375 
376 					.pluginBeNotified(*notifyCode);
377 				}
378 		} else {
379 			pragma(mangle, "beNotified")
380 			extern (C)
381 			nothrow @nogc
382 			export void beNotified(npp_api.scintilla.scintilla.SCNotification* notifyCode)
383 
384 				do
385 				{
386 					static import npp_api.powereditor.misc.pluginsmanager.notepad_plus_msgs;
387 					static import npp_api.pluginfunc.menu;
388 
389 					/+
390 					if (notifyCode == null) {
391 						return;
392 					}
393 					+/
394 
395 					switch ((*notifyCode).nmhdr.code) {
396 						case npp_api.powereditor.misc.pluginsmanager.notepad_plus_msgs.NPPN_TBMODIFICATION:
397 							//init submenu
398 							enum required = npp_api.pluginfunc.menu.allocate_sub_menu_length(menu_index_def);
399 							npp_api.pluginfunc.menu.init_submenu(nppData._nppHandle, main_menu, menu_index, sub_menu_actions, required);
400 
401 							break;
402 
403 						default:
404 							break;
405 					}
406 
407 					static if (__traits(compiles, .pluginBeNotified)) {
408 						.pluginBeNotified(*notifyCode);
409 					}
410 				}
411 		}
412 	}
413 }
414 
415 mixin template npp_messageProc(alias sub_menu_actions_def)
416 	if (std.traits.isArray!(typeof(sub_menu_actions_def)))
417 {
418 	private static import core.sys.windows.windef;
419 	private static import core.sys.windows.winuser;
420 	private static import npp_api.pluginfunc.menu;
421 	private static import npp_api.pluginfunc.basic_interface;
422 
423 	static if ((!__traits(compiles, .messageProc)) && (sub_menu_actions_def.length != 0)) {
424 		pragma(mangle, "messageProc")
425 		extern (C)
426 		nothrow
427 		export core.sys.windows.windef.LRESULT messageProc(core.sys.windows.windef.UINT Message, core.sys.windows.windef.WPARAM wParam, core.sys.windows.windef.LPARAM lParam)
428 
429 			do
430 			{
431 				static import core.sys.windows.windef;
432 				static import core.sys.windows.winuser;
433 				static import npp_api.pluginfunc.menu;
434 				static import npp_api.pluginfunc.basic_interface;
435 
436 				if (Message == core.sys.windows.winuser.WM_COMMAND) {
437 					int id = core.sys.windows.windef.LOWORD(wParam);
438 
439 					if (npp_api.pluginfunc.menu.sub_menu_action(id, .sub_menu_actions)) {
440 						return core.sys.windows.windef.TRUE;
441 					}
442 				}
443 
444 				static if (__traits(compiles, .pluginMessageProc)) {
445 					if (pluginMessageProc(Message, wParam, lParam)) {
446 						return core.sys.windows.windef.TRUE;
447 					} else {
448 						return core.sys.windows.windef.FALSE;
449 					}
450 				} else {
451 					return core.sys.windows.windef.TRUE;
452 				}
453 			}
454 	}
455 }
456 
457 mixin template npp_plugin_interface(.npp_plugin_definition plugin_def)
458 {
459 	private static import std.utf;
460 	private static import npp_api.pluginfunc.basic;
461 	private static import npp_api.pluginfunc.string;
462 	private static import npp_api.pluginfunc.basic_interface;
463 	private static import npp_api.pluginfunc.extra_interfece;
464 
465 	static assert(npp_api.pluginfunc.extra_interfece.current_mixin_version >= plugin_def.mixin_version);
466 	enum plugin_name = npp_api.pluginfunc..string.c_wstring!(std.utf.toUTF16(plugin_def.name)).idup;
467 
468 	mixin npp_api.pluginfunc.basic_interface.npp_nppData!();
469 	mixin npp_api.pluginfunc.basic.plugin_messageBox!(plugin_name);
470 	mixin npp_api.pluginfunc.extra_interfece.npp_menu_actions!(plugin_def.menu_items);
471 	mixin npp_api.pluginfunc.extra_interfece.npp_main_menu!(plugin_def.menu_items);
472 	mixin npp_api.pluginfunc.extra_interfece.npp_messageProc!(sub_menu_actions_def);
473 	mixin npp_api.pluginfunc.extra_interfece.npp_menu_index!(plugin_def.menu_items);
474 	mixin npp_api.pluginfunc.extra_interfece.npp_autoload!(plugin_def.config_info.type, plugin_def.config_info.settings);
475 	mixin npp_api.pluginfunc.extra_interfece.menu_checked_list!(.main_menu.length, .menu_index_def);
476 	mixin npp_api.pluginfunc.extra_interfece.npp_plugin_config!(plugin_def.config_info.type);
477 	mixin npp_api.pluginfunc.extra_interfece.npp_DLLMain!(plugin_def.config_info.type, .menu_index_checked_identifiers, .menu_index);
478 
479 	//ToDo:
480 	static if (__traits(compiles, .plugin_config_file) && __traits(compiles, .auto_settings)) {
481 		mixin npp_api.pluginfunc.extra_interfece.npp_setInfo!(.nppData, plugin_def.config_info, .plugin_config_file, .auto_settings, .main_menu, .menu_index);
482 	}
483 
484 	mixin npp_api.pluginfunc.extra_interfece.npp_beNotified!(.nppData, .main_menu, .menu_index, .menu_index_def, .sub_menu_actions);
485 	mixin npp_api.pluginfunc.basic_interface.npp_plugin_interface!(plugin_name, main_menu_def);
486 	mixin npp_api.pluginfunc.basic_interface.npp_plugin_interface!();
487 }