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