| 
									
										
										
										
											2017-11-03 20:26:35 +11:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or | 
					
						
							|  |  |  |  * modify it under the terms of the GNU General Public License | 
					
						
							|  |  |  |  * as published by the Free Software Foundation; either version 2 | 
					
						
							|  |  |  |  * of the License, or (at your option) any later version. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  |  * GNU General Public License for more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * You should have received a copy of the GNU General Public License | 
					
						
							|  |  |  |  * along with this program; if not, write to the Free Software Foundation, | 
					
						
							|  |  |  |  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The Original Code is Copyright (C) 2008 Blender Foundation. | 
					
						
							|  |  |  |  * All rights reserved. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-18 08:08:12 +11:00
										 |  |  | /** \file
 | 
					
						
							|  |  |  |  * \ingroup edinterface | 
					
						
							| 
									
										
										
										
											2017-11-03 20:26:35 +11:00
										 |  |  |  * | 
					
						
							|  |  |  |  * PopUp Menu Region | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <stdarg.h>
 | 
					
						
							|  |  |  | #include <stdlib.h>
 | 
					
						
							|  |  |  | #include <string.h>
 | 
					
						
							|  |  |  | #include <assert.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "MEM_guardedalloc.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "DNA_userdef_types.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "BLI_math.h"
 | 
					
						
							|  |  |  | #include "BLI_listbase.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "BLI_string.h"
 | 
					
						
							|  |  |  | #include "BLI_rect.h"
 | 
					
						
							|  |  |  | #include "BLI_utildefines.h"
 | 
					
						
							|  |  |  | #include "BLI_ghash.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "BKE_context.h"
 | 
					
						
							|  |  |  | #include "BKE_screen.h"
 | 
					
						
							|  |  |  | #include "BKE_report.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "WM_api.h"
 | 
					
						
							|  |  |  | #include "WM_types.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "RNA_access.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "UI_interface.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "BLT_translation.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "ED_screen.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "interface_intern.h"
 | 
					
						
							|  |  |  | #include "interface_regions_intern.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* -------------------------------------------------------------------- */ | 
					
						
							|  |  |  | /** \name Utility Functions
 | 
					
						
							|  |  |  |  * \{ */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool ui_but_menu_step_poll(const uiBut *but) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	BLI_assert(but->type == UI_BTYPE_MENU); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* currently only RNA buttons */ | 
					
						
							|  |  |  | 	return ((but->menu_step_func != NULL) || | 
					
						
							|  |  |  | 	        (but->rnaprop && RNA_property_type(but->rnaprop) == PROP_ENUM)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int ui_but_menu_step(uiBut *but, int direction) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (ui_but_menu_step_poll(but)) { | 
					
						
							|  |  |  | 		if (but->menu_step_func) { | 
					
						
							|  |  |  | 			return but->menu_step_func(but->block->evil_C, direction, but->poin); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		else { | 
					
						
							|  |  |  | 			const int curval = RNA_property_enum_get(&but->rnapoin, but->rnaprop); | 
					
						
							|  |  |  | 			return RNA_property_enum_step(but->block->evil_C, &but->rnapoin, but->rnaprop, curval, direction); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	printf("%s: cannot cycle button '%s'\n", __func__, but->str); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static uint ui_popup_string_hash(const char *str) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* sometimes button contains hotkey, sometimes not, strip for proper compare */ | 
					
						
							|  |  |  | 	int hash; | 
					
						
							|  |  |  | 	const char *delimit = strrchr(str, UI_SEP_CHAR); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (delimit) { | 
					
						
							|  |  |  | 		hash = BLI_ghashutil_strhash_n(str, delimit - str); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	else { | 
					
						
							|  |  |  | 		hash = BLI_ghashutil_strhash(str); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return hash; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | uint ui_popup_menu_hash(const char *str) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return BLI_ghashutil_strhash(str); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* but == NULL read, otherwise set */ | 
					
						
							|  |  |  | static uiBut *ui_popup_menu_memory__internal(uiBlock *block, uiBut *but) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	static uint mem[256]; | 
					
						
							|  |  |  | 	static bool first = true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	const uint hash = block->puphash; | 
					
						
							|  |  |  | 	const uint hash_mod = hash & 255; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (first) { | 
					
						
							|  |  |  | 		/* init */ | 
					
						
							|  |  |  | 		memset(mem, -1, sizeof(mem)); | 
					
						
							|  |  |  | 		first = 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (but) { | 
					
						
							|  |  |  | 		/* set */ | 
					
						
							|  |  |  | 		mem[hash_mod] = ui_popup_string_hash(but->str); | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	else { | 
					
						
							|  |  |  | 		/* get */ | 
					
						
							| 
									
										
										
										
											2018-04-23 10:00:42 +02:00
										 |  |  | 		for (but = block->buttons.first; but; but = but->next) { | 
					
						
							|  |  |  | 			if (ui_popup_string_hash(but->str) == mem[hash_mod]) { | 
					
						
							| 
									
										
										
										
											2017-11-03 20:26:35 +11:00
										 |  |  | 				return but; | 
					
						
							| 
									
										
										
										
											2018-04-23 10:00:42 +02:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-11-03 20:26:35 +11:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | uiBut *ui_popup_menu_memory_get(uiBlock *block) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return ui_popup_menu_memory__internal(block, NULL); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void ui_popup_menu_memory_set(uiBlock *block, uiBut *but) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	ui_popup_menu_memory__internal(block, but); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** \} */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* -------------------------------------------------------------------- */ | 
					
						
							|  |  |  | /** \name Popup Menu with Callback or String
 | 
					
						
							|  |  |  |  * \{ */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct uiPopupMenu { | 
					
						
							|  |  |  | 	uiBlock *block; | 
					
						
							|  |  |  | 	uiLayout *layout; | 
					
						
							|  |  |  | 	uiBut *but; | 
					
						
							|  |  |  | 	ARegion *butregion; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	int mx, my; | 
					
						
							|  |  |  | 	bool popup, slideout; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	uiMenuCreateFunc menu_func; | 
					
						
							|  |  |  | 	void *menu_arg; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, void *arg_pup) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	uiBlock *block; | 
					
						
							|  |  |  | 	uiPopupMenu *pup = arg_pup; | 
					
						
							| 
									
										
										
										
											2018-04-23 10:00:42 +02:00
										 |  |  | 	int minwidth, width, height; | 
					
						
							| 
									
										
										
										
											2017-11-03 20:26:35 +11:00
										 |  |  | 	char direction; | 
					
						
							|  |  |  | 	bool flip; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (pup->menu_func) { | 
					
						
							|  |  |  | 		pup->block->handle = handle; | 
					
						
							|  |  |  | 		pup->menu_func(C, pup->layout, pup->menu_arg); | 
					
						
							|  |  |  | 		pup->block->handle = NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (pup->but) { | 
					
						
							|  |  |  | 		/* minimum width to enforece */ | 
					
						
							| 
									
										
										
										
											2018-04-25 21:54:29 +02:00
										 |  |  | 		if (pup->but->drawstr[0]) { | 
					
						
							|  |  |  | 			minwidth = BLI_rctf_size_x(&pup->but->rect); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		else { | 
					
						
							|  |  |  | 			/* For buttons with no text, use the minimum (typically icon only). */ | 
					
						
							|  |  |  | 			minwidth = UI_MENU_WIDTH_MIN; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-11-03 20:26:35 +11:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if (pup->block->direction != 0) { | 
					
						
							|  |  |  | 			/* allow overriding the direction from menu_func */ | 
					
						
							|  |  |  | 			direction = pup->block->direction; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		else { | 
					
						
							| 
									
										
										
										
											2019-02-21 18:16:25 +01:00
										 |  |  | 			direction = UI_DIR_DOWN; | 
					
						
							| 
									
										
										
										
											2017-11-03 20:26:35 +11:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	else { | 
					
						
							| 
									
										
										
										
											2018-04-25 21:54:29 +02:00
										 |  |  | 		minwidth = UI_MENU_WIDTH_MIN; | 
					
						
							| 
									
										
										
										
											2017-11-03 20:26:35 +11:00
										 |  |  | 		direction = UI_DIR_DOWN; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	flip = (direction == UI_DIR_DOWN); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	block = pup->block; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* in some cases we create the block before the region,
 | 
					
						
							|  |  |  | 	 * so we set it delayed here if necessary */ | 
					
						
							| 
									
										
										
										
											2018-04-23 10:00:42 +02:00
										 |  |  | 	if (BLI_findindex(&handle->region->uiblocks, block) == -1) { | 
					
						
							| 
									
										
										
										
											2017-11-03 20:26:35 +11:00
										 |  |  | 		UI_block_region_set(block, handle->region); | 
					
						
							| 
									
										
										
										
											2018-04-23 10:00:42 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-11-03 20:26:35 +11:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	block->direction = direction; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	UI_block_layout_resolve(block, &width, &height); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	UI_block_flag_enable(block, UI_BLOCK_MOVEMOUSE_QUIT); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (pup->popup) { | 
					
						
							| 
									
										
										
										
											2018-04-23 10:00:42 +02:00
										 |  |  | 		uiBut *bt; | 
					
						
							|  |  |  | 		int offset[2]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-03 20:26:35 +11:00
										 |  |  | 		uiBut *but_activate = NULL; | 
					
						
							|  |  |  | 		UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_NUMSELECT); | 
					
						
							| 
									
										
										
										
											2018-09-11 10:56:08 +10:00
										 |  |  | 		UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP); | 
					
						
							| 
									
										
										
										
											2017-11-03 20:26:35 +11:00
										 |  |  | 		UI_block_direction_set(block, direction); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* offset the mouse position, possibly based on earlier selection */ | 
					
						
							|  |  |  | 		if ((block->flag & UI_BLOCK_POPUP_MEMORY) && | 
					
						
							|  |  |  | 		    (bt = ui_popup_menu_memory_get(block))) | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			/* position mouse on last clicked item, at 0.8*width of the
 | 
					
						
							|  |  |  | 			 * button, so it doesn't overlap the text too much, also note | 
					
						
							|  |  |  | 			 * the offset is negative because we are inverse moving the | 
					
						
							|  |  |  | 			 * block to be under the mouse */ | 
					
						
							|  |  |  | 			offset[0] = -(bt->rect.xmin + 0.8f * BLI_rctf_size_x(&bt->rect)); | 
					
						
							|  |  |  | 			offset[1] = -(bt->rect.ymin + 0.5f * UI_UNIT_Y); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (ui_but_is_editable(bt)) { | 
					
						
							|  |  |  | 				but_activate = bt; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		else { | 
					
						
							|  |  |  | 			/* position mouse at 0.8*width of the button and below the tile
 | 
					
						
							|  |  |  | 			 * on the first item */ | 
					
						
							|  |  |  | 			offset[0] = 0; | 
					
						
							| 
									
										
										
										
											2018-04-23 10:00:42 +02:00
										 |  |  | 			for (bt = block->buttons.first; bt; bt = bt->next) { | 
					
						
							| 
									
										
										
										
											2017-11-03 20:26:35 +11:00
										 |  |  | 				offset[0] = min_ii(offset[0], -(bt->rect.xmin + 0.8f * BLI_rctf_size_x(&bt->rect))); | 
					
						
							| 
									
										
										
										
											2018-04-23 10:00:42 +02:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2017-11-03 20:26:35 +11:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			offset[1] = 2.1 * UI_UNIT_Y; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			for (bt = block->buttons.first; bt; bt = bt->next) { | 
					
						
							|  |  |  | 				if (ui_but_is_editable(bt)) { | 
					
						
							|  |  |  | 					but_activate = bt; | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* in rare cases this is needed since moving the popup
 | 
					
						
							|  |  |  | 		 * to be within the window bounds may move it away from the mouse, | 
					
						
							|  |  |  | 		 * This ensures we set an item to be active. */ | 
					
						
							|  |  |  | 		if (but_activate) { | 
					
						
							|  |  |  | 			ui_but_activate_over(C, handle->region, but_activate); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		block->minbounds = minwidth; | 
					
						
							| 
									
										
										
										
											2019-03-13 16:35:24 +11:00
										 |  |  | 		UI_block_bounds_set_menu(block, 1, offset); | 
					
						
							| 
									
										
										
										
											2017-11-03 20:26:35 +11:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	else { | 
					
						
							|  |  |  | 		/* for a header menu we set the direction automatic */ | 
					
						
							|  |  |  | 		if (!pup->slideout && flip) { | 
					
						
							|  |  |  | 			ScrArea *sa = CTX_wm_area(C); | 
					
						
							| 
									
										
										
										
											2018-05-06 18:36:54 +02:00
										 |  |  | 			if (sa && ED_area_header_alignment(sa) == RGN_ALIGN_BOTTOM) { | 
					
						
							| 
									
										
										
										
											2017-11-03 20:26:35 +11:00
										 |  |  | 				ARegion *ar = CTX_wm_region(C); | 
					
						
							|  |  |  | 				if (ar && ar->regiontype == RGN_TYPE_HEADER) { | 
					
						
							|  |  |  | 					UI_block_direction_set(block, UI_DIR_UP); | 
					
						
							|  |  |  | 					UI_block_order_flip(block); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		block->minbounds = minwidth; | 
					
						
							|  |  |  | 		UI_block_bounds_set_text(block, 3.0f * UI_UNIT_X); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* if menu slides out of other menu, override direction */ | 
					
						
							| 
									
										
										
										
											2018-04-23 10:00:42 +02:00
										 |  |  | 	if (pup->slideout) { | 
					
						
							| 
									
										
										
										
											2017-11-03 20:26:35 +11:00
										 |  |  | 		UI_block_direction_set(block, UI_DIR_RIGHT); | 
					
						
							| 
									
										
										
										
											2018-04-23 10:00:42 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-11-03 20:26:35 +11:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return pup->block; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | uiPopupBlockHandle *ui_popup_menu_create( | 
					
						
							|  |  |  |         bContext *C, ARegion *butregion, uiBut *but, | 
					
						
							|  |  |  |         uiMenuCreateFunc menu_func, void *arg) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	wmWindow *window = CTX_wm_window(C); | 
					
						
							|  |  |  | 	uiStyle *style = UI_style_get_dpi(); | 
					
						
							|  |  |  | 	uiPopupBlockHandle *handle; | 
					
						
							|  |  |  | 	uiPopupMenu *pup; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pup = MEM_callocN(sizeof(uiPopupMenu), __func__); | 
					
						
							|  |  |  | 	pup->block = UI_block_begin(C, NULL, __func__, UI_EMBOSS_PULLDOWN); | 
					
						
							|  |  |  | 	pup->block->flag |= UI_BLOCK_NUMSELECT;  /* default menus to numselect */ | 
					
						
							|  |  |  | 	pup->layout = UI_block_layout(pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, MENU_PADDING, style); | 
					
						
							|  |  |  | 	pup->slideout = but ? ui_block_is_menu(but->block) : false; | 
					
						
							|  |  |  | 	pup->but = but; | 
					
						
							|  |  |  | 	uiLayoutSetOperatorContext(pup->layout, WM_OP_INVOKE_REGION_WIN); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!but) { | 
					
						
							|  |  |  | 		/* no button to start from, means we are a popup */ | 
					
						
							|  |  |  | 		pup->mx = window->eventstate->x; | 
					
						
							|  |  |  | 		pup->my = window->eventstate->y; | 
					
						
							|  |  |  | 		pup->popup = true; | 
					
						
							|  |  |  | 		pup->block->flag |= UI_BLOCK_NO_FLIP; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	/* some enums reversing is strange, currently we have no good way to
 | 
					
						
							|  |  |  | 	 * reverse some enum's but not others, so reverse all so the first menu | 
					
						
							|  |  |  | 	 * items are always close to the mouse cursor */ | 
					
						
							|  |  |  | 	else { | 
					
						
							|  |  |  | #if 0
 | 
					
						
							|  |  |  | 		/* if this is an rna button then we can assume its an enum
 | 
					
						
							|  |  |  | 		 * flipping enums is generally not good since the order can be | 
					
						
							|  |  |  | 		 * important [#28786] */ | 
					
						
							|  |  |  | 		if (but->rnaprop && RNA_property_type(but->rnaprop) == PROP_ENUM) { | 
					
						
							|  |  |  | 			pup->block->flag |= UI_BLOCK_NO_FLIP; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2018-04-23 10:00:42 +02:00
										 |  |  | 		if (but->context) { | 
					
						
							| 
									
										
										
										
											2017-11-03 20:26:35 +11:00
										 |  |  | 			uiLayoutContextCopy(pup->layout, but->context); | 
					
						
							| 
									
										
										
										
											2018-04-23 10:00:42 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-11-03 20:26:35 +11:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* menu is created from a callback */ | 
					
						
							|  |  |  | 	pup->menu_func = menu_func; | 
					
						
							|  |  |  | 	pup->menu_arg = arg; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	handle = ui_popup_block_create(C, butregion, but, NULL, ui_block_func_POPUP, pup); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!but) { | 
					
						
							|  |  |  | 		handle->popup = true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		UI_popup_handlers_add(C, &window->modalhandlers, handle, 0); | 
					
						
							|  |  |  | 		WM_event_add_mousemove(C); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	MEM_freeN(pup); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return handle; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** \} */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* -------------------------------------------------------------------- */ | 
					
						
							|  |  |  | /** \name Popup Menu API with begin & end
 | 
					
						
							|  |  |  |  * \{ */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * Only return handler, and set optional title. | 
					
						
							|  |  |  |  * \param block_name: Assigned to uiBlock.name (useful info for debugging). | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | uiPopupMenu *UI_popup_menu_begin_ex(bContext *C, const char *title, const char *block_name, int icon) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	uiStyle *style = UI_style_get_dpi(); | 
					
						
							|  |  |  | 	uiPopupMenu *pup = MEM_callocN(sizeof(uiPopupMenu), "popup menu"); | 
					
						
							|  |  |  | 	uiBut *but; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pup->block = UI_block_begin(C, NULL, block_name, UI_EMBOSS_PULLDOWN); | 
					
						
							|  |  |  | 	pup->block->flag |= UI_BLOCK_POPUP_MEMORY | UI_BLOCK_IS_FLIP; | 
					
						
							|  |  |  | 	pup->block->puphash = ui_popup_menu_hash(title); | 
					
						
							|  |  |  | 	pup->layout = UI_block_layout(pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, MENU_PADDING, style); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* note, this intentionally differs from the menu & submenu default because many operators
 | 
					
						
							| 
									
										
										
										
											2019-01-15 23:24:20 +11:00
										 |  |  | 	 * use popups like this to select one of their options - | 
					
						
							|  |  |  | 	 * where having invoke doesn't make sense */ | 
					
						
							| 
									
										
										
										
											2017-11-03 20:26:35 +11:00
										 |  |  | 	uiLayoutSetOperatorContext(pup->layout, WM_OP_EXEC_REGION_WIN); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* create in advance so we can let buttons point to retval already */ | 
					
						
							|  |  |  | 	pup->block->handle = MEM_callocN(sizeof(uiPopupBlockHandle), "uiPopupBlockHandle"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* create title button */ | 
					
						
							|  |  |  | 	if (title[0]) { | 
					
						
							|  |  |  | 		char titlestr[256]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (icon) { | 
					
						
							|  |  |  | 			BLI_snprintf(titlestr, sizeof(titlestr), " %s", title); | 
					
						
							|  |  |  | 			uiDefIconTextBut( | 
					
						
							|  |  |  | 			        pup->block, UI_BTYPE_LABEL, 0, icon, titlestr, 0, 0, 200, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, ""); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		else { | 
					
						
							|  |  |  | 			but = uiDefBut(pup->block, UI_BTYPE_LABEL, 0, title, 0, 0, 200, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, ""); | 
					
						
							|  |  |  | 			but->drawflag = UI_BUT_TEXT_LEFT; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		uiItemS(pup->layout); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return pup; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | uiPopupMenu *UI_popup_menu_begin(bContext *C, const char *title, int icon) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return UI_popup_menu_begin_ex(C, title, __func__, icon); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * Setting the button makes the popup open from the button instead of the cursor. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void UI_popup_menu_but_set(uiPopupMenu *pup, struct ARegion *butregion, uiBut *but) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	pup->but = but; | 
					
						
							|  |  |  | 	pup->butregion = butregion; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* set the whole structure to work */ | 
					
						
							|  |  |  | void UI_popup_menu_end(bContext *C, uiPopupMenu *pup) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	wmWindow *window = CTX_wm_window(C); | 
					
						
							|  |  |  | 	uiPopupBlockHandle *menu; | 
					
						
							|  |  |  | 	uiBut *but = NULL; | 
					
						
							|  |  |  | 	ARegion *butregion = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pup->popup = true; | 
					
						
							|  |  |  | 	pup->mx = window->eventstate->x; | 
					
						
							|  |  |  | 	pup->my = window->eventstate->y; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (pup->but) { | 
					
						
							|  |  |  | 		but = pup->but; | 
					
						
							|  |  |  | 		butregion = pup->butregion; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	menu = ui_popup_block_create(C, butregion, but, NULL, ui_block_func_POPUP, pup); | 
					
						
							|  |  |  | 	menu->popup = true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	UI_popup_handlers_add(C, &window->modalhandlers, menu, 0); | 
					
						
							|  |  |  | 	WM_event_add_mousemove(C); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	MEM_freeN(pup); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-23 10:31:10 +02:00
										 |  |  | bool UI_popup_menu_end_or_cancel(bContext *C, uiPopupMenu *pup) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (!UI_block_is_empty(pup->block)) { | 
					
						
							|  |  |  | 		UI_popup_menu_end(C, pup); | 
					
						
							|  |  |  | 		return true; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	else { | 
					
						
							|  |  |  | 		UI_block_layout_resolve(pup->block, NULL, NULL); | 
					
						
							|  |  |  | 		MEM_freeN(pup->block->handle); | 
					
						
							|  |  |  | 		UI_block_free(C, pup->block); | 
					
						
							|  |  |  | 		MEM_freeN(pup); | 
					
						
							|  |  |  | 		return false; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-03 20:26:35 +11:00
										 |  |  | uiLayout *UI_popup_menu_layout(uiPopupMenu *pup) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return pup->layout; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** \} */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* -------------------------------------------------------------------- */ | 
					
						
							|  |  |  | /** \name Standard Popup Menus
 | 
					
						
							|  |  |  |  * \{ */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void UI_popup_menu_reports(bContext *C, ReportList *reports) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	Report *report; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	uiPopupMenu *pup = NULL; | 
					
						
							|  |  |  | 	uiLayout *layout; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!CTX_wm_window(C)) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (report = reports->list.first; report; report = report->next) { | 
					
						
							|  |  |  | 		int icon; | 
					
						
							|  |  |  | 		const char *msg, *msg_next; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (report->type < reports->printlevel) { | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (pup == NULL) { | 
					
						
							|  |  |  | 			char title[UI_MAX_DRAW_STR]; | 
					
						
							|  |  |  | 			BLI_snprintf(title, sizeof(title), "%s: %s", IFACE_("Report"), report->typestr); | 
					
						
							|  |  |  | 			/* popup_menu stuff does just what we need (but pass meaningful block name) */ | 
					
						
							|  |  |  | 			pup = UI_popup_menu_begin_ex(C, title, __func__, ICON_NONE); | 
					
						
							|  |  |  | 			layout = UI_popup_menu_layout(pup); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		else { | 
					
						
							|  |  |  | 			uiItemS(layout); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* split each newline into a label */ | 
					
						
							|  |  |  | 		msg = report->message; | 
					
						
							|  |  |  | 		icon = UI_icon_from_report_type(report->type); | 
					
						
							|  |  |  | 		do { | 
					
						
							|  |  |  | 			char buf[UI_MAX_DRAW_STR]; | 
					
						
							|  |  |  | 			msg_next = strchr(msg, '\n'); | 
					
						
							|  |  |  | 			if (msg_next) { | 
					
						
							|  |  |  | 				msg_next++; | 
					
						
							|  |  |  | 				BLI_strncpy(buf, msg, MIN2(sizeof(buf), msg_next - msg)); | 
					
						
							|  |  |  | 				msg = buf; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			uiItemL(layout, msg, icon); | 
					
						
							|  |  |  | 			icon = ICON_NONE; | 
					
						
							|  |  |  | 		} while ((msg = msg_next) && *msg); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (pup) { | 
					
						
							|  |  |  | 		UI_popup_menu_end(C, pup); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int UI_popup_menu_invoke(bContext *C, const char *idname, ReportList *reports) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	uiPopupMenu *pup; | 
					
						
							|  |  |  | 	uiLayout *layout; | 
					
						
							|  |  |  | 	MenuType *mt = WM_menutype_find(idname, true); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (mt == NULL) { | 
					
						
							|  |  |  | 		BKE_reportf(reports, RPT_ERROR, "Menu \"%s\" not found", idname); | 
					
						
							|  |  |  | 		return OPERATOR_CANCELLED; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-23 17:01:22 +11:00
										 |  |  | 	if (WM_menutype_poll(C, mt) == false) { | 
					
						
							| 
									
										
										
										
											2017-11-03 20:26:35 +11:00
										 |  |  | 		/* cancel but allow event to pass through, just like operators do */ | 
					
						
							|  |  |  | 		return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH); | 
					
						
							| 
									
										
										
										
											2018-02-23 17:01:22 +11:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-11-03 20:26:35 +11:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	pup = UI_popup_menu_begin(C, IFACE_(mt->label), ICON_NONE); | 
					
						
							|  |  |  | 	layout = UI_popup_menu_layout(pup); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	UI_menutype_draw(C, mt, layout); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	UI_popup_menu_end(C, pup); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return OPERATOR_INTERFACE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** \} */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* -------------------------------------------------------------------- */ | 
					
						
							|  |  |  | /** \name Popup Block API
 | 
					
						
							|  |  |  |  * \{ */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void UI_popup_block_invoke_ex(bContext *C, uiBlockCreateFunc func, void *arg, const char *opname, int opcontext) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	wmWindow *window = CTX_wm_window(C); | 
					
						
							|  |  |  | 	uiPopupBlockHandle *handle; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	handle = ui_popup_block_create(C, NULL, NULL, func, NULL, arg); | 
					
						
							|  |  |  | 	handle->popup = true; | 
					
						
							| 
									
										
										
										
											2018-04-30 10:27:28 +02:00
										 |  |  | 	handle->can_refresh = true; | 
					
						
							| 
									
										
										
										
											2017-11-03 20:26:35 +11:00
										 |  |  | 	handle->optype = (opname) ? WM_operatortype_find(opname, 0) : NULL; | 
					
						
							|  |  |  | 	handle->opcontext = opcontext; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	UI_popup_handlers_add(C, &window->modalhandlers, handle, 0); | 
					
						
							|  |  |  | 	WM_event_add_mousemove(C); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void UI_popup_block_invoke(bContext *C, uiBlockCreateFunc func, void *arg) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	UI_popup_block_invoke_ex(C, func, arg, NULL, WM_OP_INVOKE_DEFAULT); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void UI_popup_block_ex( | 
					
						
							|  |  |  |         bContext *C, uiBlockCreateFunc func, uiBlockHandleFunc popup_func, uiBlockCancelFunc cancel_func, | 
					
						
							|  |  |  |         void *arg, wmOperator *op) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	wmWindow *window = CTX_wm_window(C); | 
					
						
							|  |  |  | 	uiPopupBlockHandle *handle; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	handle = ui_popup_block_create(C, NULL, NULL, func, NULL, arg); | 
					
						
							|  |  |  | 	handle->popup = true; | 
					
						
							|  |  |  | 	handle->retvalue = 1; | 
					
						
							| 
									
										
										
										
											2018-04-30 10:27:28 +02:00
										 |  |  | 	handle->can_refresh = true; | 
					
						
							| 
									
										
										
										
											2017-11-03 20:26:35 +11:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	handle->popup_op = op; | 
					
						
							|  |  |  | 	handle->popup_arg = arg; | 
					
						
							|  |  |  | 	handle->popup_func = popup_func; | 
					
						
							|  |  |  | 	handle->cancel_func = cancel_func; | 
					
						
							|  |  |  | 	// handle->opcontext = opcontext;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	UI_popup_handlers_add(C, &window->modalhandlers, handle, 0); | 
					
						
							|  |  |  | 	WM_event_add_mousemove(C); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #if 0 /* UNUSED */
 | 
					
						
							|  |  |  | void uiPupBlockOperator(bContext *C, uiBlockCreateFunc func, wmOperator *op, int opcontext) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	wmWindow *window = CTX_wm_window(C); | 
					
						
							|  |  |  | 	uiPopupBlockHandle *handle; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	handle = ui_popup_block_create(C, NULL, NULL, func, NULL, op); | 
					
						
							|  |  |  | 	handle->popup = 1; | 
					
						
							|  |  |  | 	handle->retvalue = 1; | 
					
						
							| 
									
										
										
										
											2018-04-30 10:27:28 +02:00
										 |  |  | 	handle->can_refresh = true; | 
					
						
							| 
									
										
										
										
											2017-11-03 20:26:35 +11:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	handle->popup_arg = op; | 
					
						
							|  |  |  | 	handle->popup_func = operator_cb; | 
					
						
							|  |  |  | 	handle->cancel_func = confirm_cancel_operator; | 
					
						
							|  |  |  | 	handle->opcontext = opcontext; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	UI_popup_handlers_add(C, &window->modalhandlers, handle, 0); | 
					
						
							|  |  |  | 	WM_event_add_mousemove(C); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void UI_popup_block_close(bContext *C, wmWindow *win, uiBlock *block) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	/* if loading new .blend while popup is open, window will be NULL */ | 
					
						
							|  |  |  | 	if (block->handle) { | 
					
						
							|  |  |  | 		if (win) { | 
					
						
							| 
									
										
										
										
											2017-11-03 21:22:27 +11:00
										 |  |  | 			const bScreen *screen = WM_window_get_active_screen(win); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-03 20:26:35 +11:00
										 |  |  | 			UI_popup_handlers_remove(&win->modalhandlers, block->handle); | 
					
						
							|  |  |  | 			ui_popup_block_free(C, block->handle); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-15 23:24:20 +11:00
										 |  |  | 			/* In the case we have nested popups,
 | 
					
						
							|  |  |  | 			 * closing one may need to redraw another, see: T48874 */ | 
					
						
							| 
									
										
										
										
											2017-11-03 21:22:27 +11:00
										 |  |  | 			for (ARegion *ar = screen->regionbase.first; ar; ar = ar->next) { | 
					
						
							| 
									
										
										
										
											2017-11-03 20:26:35 +11:00
										 |  |  | 				ED_region_tag_refresh_ui(ar); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-04 14:23:49 +01:00
										 |  |  | bool UI_popup_block_name_exists(bContext *C, const char *name) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	bScreen *sc = CTX_wm_screen(C); | 
					
						
							|  |  |  | 	uiBlock *block; | 
					
						
							|  |  |  | 	ARegion *ar; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (ar = sc->regionbase.first; ar; ar = ar->next) { | 
					
						
							|  |  |  | 		for (block = ar->uiblocks.first; block; block = block->next) { | 
					
						
							|  |  |  | 			if (STREQ(block->name, name)) { | 
					
						
							|  |  |  | 				return true; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-03 20:26:35 +11:00
										 |  |  | /** \} */ |