コード例 #1
0
ファイル: info_report.c プロジェクト: danielmarg/blender-main
static int report_copy_exec(bContext *C, wmOperator *UNUSED(op))
{
	SpaceInfo *sinfo = CTX_wm_space_info(C);
	ReportList *reports = CTX_wm_reports(C);
	int report_mask = info_report_mask(sinfo);

	Report *report;

	DynStr *buf_dyn = BLI_dynstr_new();
	char *buf_str;

	for (report = reports->list.first; report; report = report->next) {
		if ((report->type & report_mask) && (report->flag & SELECT)) {
			BLI_dynstr_append(buf_dyn, report->message);
			BLI_dynstr_append(buf_dyn, "\n");
		}
	}

	buf_str = BLI_dynstr_get_cstring(buf_dyn);
	BLI_dynstr_free(buf_dyn);

	WM_clipboard_text_set(buf_str, 0);

	MEM_freeN(buf_str);
	return OPERATOR_FINISHED;
}
コード例 #2
0
static int console_copy_exec(bContext *C, wmOperator *UNUSED(op))
{
	SpaceConsole *sc = CTX_wm_space_console(C);

	DynStr *buf_dyn;
	char *buf_str;
	
	ConsoleLine *cl;
	int sel[2];
	int offset = 0;

	ConsoleLine cl_dummy = {NULL};

	if (sc->sel_start == sc->sel_end)
		return OPERATOR_CANCELLED;

	console_scrollback_prompt_begin(sc, &cl_dummy);

	for (cl = sc->scrollback.first; cl; cl = cl->next) {
		offset += cl->len + 1;
	}

	if (offset == 0) {
		console_scrollback_prompt_end(sc, &cl_dummy);
		return OPERATOR_CANCELLED;
	}

	buf_dyn = BLI_dynstr_new();
	offset -= 1;
	sel[0] = offset - sc->sel_end;
	sel[1] = offset - sc->sel_start;

	for (cl = sc->scrollback.first; cl; cl = cl->next) {
		if (sel[0] <= cl->len && sel[1] >= 0) {
			int sta = max_ii(sel[0], 0);
			int end = min_ii(sel[1], cl->len);

			if (BLI_dynstr_get_len(buf_dyn))
				BLI_dynstr_append(buf_dyn, "\n");

			BLI_dynstr_nappend(buf_dyn, cl->line + sta, end - sta);
		}

		sel[0] -= cl->len + 1;
		sel[1] -= cl->len + 1;
	}

	buf_str = BLI_dynstr_get_cstring(buf_dyn);

	BLI_dynstr_free(buf_dyn);
	WM_clipboard_text_set(buf_str, 0);

	MEM_freeN(buf_str);

	console_scrollback_prompt_end(sc, &cl_dummy);

	return OPERATOR_FINISHED;
}
コード例 #3
0
static void console_cursor_set_exit(bContext *UNUSED(C), wmOperator *op)
{
//	SpaceConsole *sc = CTX_wm_space_console(C);
	SetConsoleCursor *scu = op->customdata;

#if 0
	if (txt_has_sel(text)) {
		buffer = txt_sel_to_buf(text);
		WM_clipboard_text_set(buffer, 1);
		MEM_freeN(buffer);
	}
#endif

	MEM_freeN(scu);
}
コード例 #4
0
ファイル: interface_ops.c プロジェクト: dfelinto/blender
static int copy_python_command_button_exec(bContext *C, wmOperator *UNUSED(op))
{
  uiBut *but = UI_context_active_but_get(C);

  if (but && (but->optype != NULL)) {
    PointerRNA *opptr;
    char *str;
    opptr = UI_but_operator_ptr_get(but); /* allocated when needed, the button owns it */

    str = WM_operator_pystring_ex(C, NULL, false, true, but->optype, opptr);

    WM_clipboard_text_set(str, 0);

    MEM_freeN(str);

    return OPERATOR_FINISHED;
  }

  return OPERATOR_CANCELLED;
}
コード例 #5
0
static int copy_data_path_button_exec(bContext *C, wmOperator *UNUSED(op))
{
	PointerRNA ptr;
	PropertyRNA *prop;
	char *path;
	int index;

	/* try to create driver using property retrieved from UI */
	uiContextActiveProperty(C, &ptr, &prop, &index);

	if (ptr.id.data && ptr.data && prop) {
		path = RNA_path_from_ID_to_property(&ptr, prop);
		
		if (path) {
			WM_clipboard_text_set(path, false);
			MEM_freeN(path);
			return OPERATOR_FINISHED;
		}
	}

	return OPERATOR_CANCELLED;
}
コード例 #6
0
ファイル: interface_ops.c プロジェクト: mik0001/Blender
static int copy_data_path_button_exec(bContext *C, wmOperator *UNUSED(op))
{
	PointerRNA ptr;
	PropertyRNA *prop;
	char *path;
	int success= 0;
	int index;

	/* try to create driver using property retrieved from UI */
	uiContextActiveProperty(C, &ptr, &prop, &index);

	if (ptr.id.data && ptr.data && prop) {
		path= RNA_path_from_ID_to_property(&ptr, prop);
		
		if (path) {
			WM_clipboard_text_set(path, FALSE);
			MEM_freeN(path);
		}
	}

	/* since we're just copying, we don't really need to do anything else...*/
	return (success)? OPERATOR_FINISHED: OPERATOR_CANCELLED;
}
コード例 #7
0
ファイル: interface_ops.c プロジェクト: dfelinto/blender
static int copy_data_path_button_exec(bContext *C, wmOperator *op)
{
  PointerRNA ptr;
  PropertyRNA *prop;
  char *path;
  int index;

  const bool full_path = RNA_boolean_get(op->ptr, "full_path");

  /* try to create driver using property retrieved from UI */
  UI_context_active_but_prop_get(C, &ptr, &prop, &index);

  if (ptr.id.data != NULL) {

    if (full_path) {

      if (prop) {
        path = RNA_path_full_property_py_ex(&ptr, prop, index, true);
      }
      else {
        path = RNA_path_full_struct_py(&ptr);
      }
    }
    else {
      path = RNA_path_from_ID_to_property(&ptr, prop);
    }

    if (path) {
      WM_clipboard_text_set(path, false);
      MEM_freeN(path);
      return OPERATOR_FINISHED;
    }
  }

  return OPERATOR_CANCELLED;
}
コード例 #8
0
static int view3d_ruler_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
	bool do_draw = false;
	int exit_code = OPERATOR_RUNNING_MODAL;
	RulerInfo *ruler_info = op->customdata;
	ScrArea *sa = ruler_info->sa;
	ARegion *ar = ruler_info->ar;
	RegionView3D *rv3d = ar->regiondata;

	/* its possible to change  spaces while running the operator [#34894] */
	if (UNLIKELY(ar != CTX_wm_region(C))) {
		exit_code = OPERATOR_FINISHED;
		goto exit;
	}

	switch (event->type) {
		case LEFTMOUSE:
			if (event->val == KM_RELEASE) {
				if (ruler_info->state == RULER_STATE_DRAG) {
					/* rubber-band angle removal */
					RulerItem *ruler_item = ruler_item_active_get(ruler_info);
					if (ruler_item && (ruler_item->co_index == 1) && (ruler_item->flag & RULERITEM_USE_ANGLE)) {
						if (!BLI_rcti_isect_pt_v(&ar->winrct, &event->x)) {
							ruler_item->flag &= ~RULERITEM_USE_ANGLE;
							do_draw = true;
						}
					}
					if (ruler_info->snap_flag & RULER_SNAP_OK) {
						ruler_info->snap_flag &= ~RULER_SNAP_OK;
						do_draw = true;
					}
					ruler_info->state = RULER_STATE_NORMAL;
				}
			}
			else {
				if (ruler_info->state == RULER_STATE_NORMAL) {

					if (event->ctrl ||
					    /* weak - but user friendly */
					    BLI_listbase_is_empty(&ruler_info->items))
					{
						View3D *v3d = CTX_wm_view3d(C);
						const bool use_depth = (v3d->drawtype >= OB_SOLID);

						/* Create new line */
						RulerItem *ruler_item_prev = ruler_item_active_get(ruler_info);
						RulerItem *ruler_item;
						/* check if we want to drag an existing point or add a new one */
						ruler_info->state = RULER_STATE_DRAG;

						ruler_item = ruler_item_add(ruler_info);
						ruler_item_active_set(ruler_info, ruler_item);

						if (use_depth) {
							/* snap the first point added, not essential but handy */
							ruler_item->co_index = 0;
							view3d_ruler_item_mousemove(C, ruler_info, event->mval, false, true);
							copy_v3_v3(ruler_info->drag_start_co, ruler_item->co[ruler_item->co_index]);
						}
						else {
							/* initial depth either previous ruler, view offset */
							if (ruler_item_prev) {
								copy_v3_v3(ruler_info->drag_start_co, ruler_item_prev->co[ruler_item_prev->co_index]);
							}
							else {
								negate_v3_v3(ruler_info->drag_start_co, rv3d->ofs);
							}

							copy_v3_v3(ruler_item->co[0], ruler_info->drag_start_co);
							view3d_ruler_item_project(ruler_info, ruler_item->co[0], event->mval);
						}

						copy_v3_v3(ruler_item->co[2], ruler_item->co[0]);
						ruler_item->co_index = 2;

						do_draw = true;
					}
					else {
						float mval_fl[2] = {UNPACK2(event->mval)};
						RulerItem *ruler_item_pick;
						int co_index;

						/* select and drag */
						if (view3d_ruler_pick(ruler_info, mval_fl, &ruler_item_pick, &co_index)) {
							if (co_index == -1) {
								if ((ruler_item_pick->flag & RULERITEM_USE_ANGLE) == 0) {
									/* Add Center Point */
									ruler_item_active_set(ruler_info, ruler_item_pick);
									ruler_item_pick->flag |= RULERITEM_USE_ANGLE;
									ruler_item_pick->co_index = 1;
									ruler_info->state = RULER_STATE_DRAG;

									/* find the factor */
									{
										float co_ss[2][2];
										float fac;

										ED_view3d_project_float_global(ar, ruler_item_pick->co[0], co_ss[0], V3D_PROJ_TEST_NOP);
										ED_view3d_project_float_global(ar, ruler_item_pick->co[2], co_ss[1], V3D_PROJ_TEST_NOP);

										fac = line_point_factor_v2(mval_fl, co_ss[0], co_ss[1]);
										CLAMP(fac, 0.0f, 1.0f);

										interp_v3_v3v3(ruler_item_pick->co[1],
										               ruler_item_pick->co[0],
										               ruler_item_pick->co[2], fac);
									}

									/* update the new location */
									view3d_ruler_item_mousemove(C, ruler_info, event->mval,
									                            event->shift != 0, event->ctrl != 0);
									do_draw = true;
								}
							}
							else {
								ruler_item_active_set(ruler_info, ruler_item_pick);
								ruler_item_pick->co_index = co_index;
								ruler_info->state = RULER_STATE_DRAG;

								/* store the initial depth */
								copy_v3_v3(ruler_info->drag_start_co, ruler_item_pick->co[ruler_item_pick->co_index]);

								do_draw = true;
							}
						}
						else {
							exit_code = OPERATOR_PASS_THROUGH;
						}

					}
				}
			}
			break;
		case CKEY:
		{
			if (event->ctrl) {
				RulerItem *ruler_item = ruler_item_active_get(ruler_info);
				if (ruler_item) {
					const int prec = 8;
					char numstr[256];
					Scene *scene = CTX_data_scene(C);
					UnitSettings *unit = &scene->unit;

					ruler_item_as_string(ruler_item, unit, numstr, sizeof(numstr), prec);
					WM_clipboard_text_set((void *) numstr, false);
				}
			}
			break;
		}
		case RIGHTCTRLKEY:
		case LEFTCTRLKEY:
		{
			WM_event_add_mousemove(C);
			break;
		}
		case MOUSEMOVE:
		{
			if (ruler_info->state == RULER_STATE_DRAG) {
				if (view3d_ruler_item_mousemove(C, ruler_info, event->mval,
				                                event->shift != 0, event->ctrl != 0))
				{
					do_draw = true;
				}
			}
			break;
		}
		case ESCKEY:
		{
			do_draw = true;
			exit_code = OPERATOR_CANCELLED;
			break;
		}
		case RETKEY:
		{
			view3d_ruler_to_gpencil(C, ruler_info);
			do_draw = true;
			exit_code = OPERATOR_FINISHED;
			break;
		}
		case DELKEY:
		{
			if (event->val == KM_PRESS) {
				if (ruler_info->state == RULER_STATE_NORMAL) {
					RulerItem *ruler_item = ruler_item_active_get(ruler_info);
					if (ruler_item) {
						RulerItem *ruler_item_other = ruler_item->prev ? ruler_item->prev : ruler_item->next;
						ruler_item_remove(ruler_info, ruler_item);
						ruler_item_active_set(ruler_info, ruler_item_other);
						do_draw = true;
					}
				}
			}
			break;
		}
		default:
			exit_code = OPERATOR_PASS_THROUGH;
			break;

	}

	if (do_draw) {
		view3d_ruler_header_update(sa);

		/* all 3d views draw rulers */
		WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
	}

exit:
	if (ELEM(exit_code, OPERATOR_FINISHED, OPERATOR_CANCELLED)) {
		WM_cursor_modal_restore(ruler_info->win);

		view3d_ruler_end(C, ruler_info);
		view3d_ruler_free(ruler_info);
		op->customdata = NULL;

		ED_area_headerprint(sa, NULL);
	}

	return exit_code;
}
コード例 #9
0
bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
{
	const char *utf8_buf = NULL;
	char ascii[2] = {'\0', '\0'};
	bool updated = false;
	short idx = n->idx, idx_max = n->idx_max;
	short dir = STRCUR_DIR_NEXT, mode = STRCUR_JUMP_NONE;
	int cur;
	double val;

	switch (event->type) {
		case EVT_MODAL_MAP:
			if (ELEM(event->val, NUM_MODAL_INCREMENT_UP, NUM_MODAL_INCREMENT_DOWN)) {
				n->val[idx] += (event->val == NUM_MODAL_INCREMENT_UP) ? n->val_inc[idx] : -n->val_inc[idx];
				value_to_editstr(n, idx);
				n->val_flag[idx] |= NUM_EDITED;
				updated = true;
			}
			else {
				/* might be a char too... */
				utf8_buf = event->utf8_buf;
				ascii[0] = event->ascii;
			}
			break;
		case BACKSPACEKEY:
			/* Part specific to backspace... */
			if (!(n->val_flag[idx] & NUM_EDITED)) {
				copy_v3_v3(n->val, n->val_org);
				n->val_flag[0] &= ~NUM_EDITED;
				n->val_flag[1] &= ~NUM_EDITED;
				n->val_flag[2] &= ~NUM_EDITED;
				updated = true;
				break;
			}
			else if (event->shift || !n->str[0]) {
				n->val[idx] = n->val_org[idx];
				n->val_flag[idx] &= ~NUM_EDITED;
				n->str[0] = '\0';
				n->str_cur = 0;
				updated = true;
				break;
			}
			/* Else, common behavior with DELKEY, only difference is remove char(s) before/after the cursor. */
			dir = STRCUR_DIR_PREV;
			/* fall-through */
		case DELKEY:
			if ((n->val_flag[idx] & NUM_EDITED) && n->str[0]) {
				int t_cur = cur = n->str_cur;
				if (event->ctrl) {
					mode = STRCUR_JUMP_DELIM;
				}
				BLI_str_cursor_step_utf8(n->str, strlen(n->str), &t_cur, dir, mode, true);
				if (t_cur != cur) {
					if (t_cur < cur) {
						SWAP(int, t_cur, cur);
						n->str_cur = cur;
					}
					memmove(&n->str[cur], &n->str[t_cur], strlen(&n->str[t_cur]) + 1);  /* +1 for trailing '\0'. */
					updated = true;
				}
			}
			else {
				return false;
			}
			break;
		case LEFTARROWKEY:
			dir = STRCUR_DIR_PREV;
			/* fall-through */
		case RIGHTARROWKEY:
			cur = n->str_cur;
			if (event->ctrl) {
				mode = STRCUR_JUMP_DELIM;
			}
			BLI_str_cursor_step_utf8(n->str, strlen(n->str), &cur, dir, mode, true);
			if (cur != n->str_cur) {
				n->str_cur = cur;
				return true;
			}
			return false;
		case HOMEKEY:
			if (n->str[0]) {
				n->str_cur = 0;
				return true;
			}
			return false;
		case ENDKEY:
			if (n->str[0]) {
				n->str_cur = strlen(n->str);
				return true;
			}
			return false;
		case TABKEY:
			n->val_org[idx] = n->val[idx];
			n->val_flag[idx] &= ~(NUM_NEGATE | NUM_INVERSE);

			idx += event->ctrl ? -1 : 1;
			idx %= idx_max + 1;
			n->idx = idx;
			n->val[idx] = n->val_org[idx];
			if (n->val_flag[idx] & NUM_EDITED) {
				value_to_editstr(n, idx);
			}
			else {
				n->str[0] = '\0';
				n->str_cur = 0;
			}
			return true;
		case PADPERIOD:
			/* Force numdot, some OSs/countries generate a comma char in this case, sic...  (T37992) */
			ascii[0] = '.';
			utf8_buf = ascii;
			break;
		case EQUALKEY:
		case PADASTERKEY:
			if (!(n->flag & NUM_EDIT_FULL)) {
				n->flag |= NUM_EDIT_FULL;
				n->val_flag[idx] |= NUM_EDITED;
				return true;
			}
			else if (event->ctrl) {
				n->flag &= ~NUM_EDIT_FULL;
				return true;
			}
			/* fall-through */
		case PADMINUS:
		case MINUSKEY:
			if (event->ctrl || !(n->flag & NUM_EDIT_FULL)) {
				n->val_flag[idx] ^= NUM_NEGATE;
				updated = true;
				break;
			}
			/* fall-through */
		case PADSLASHKEY:
		case SLASHKEY:
			if (event->ctrl || !(n->flag & NUM_EDIT_FULL)) {
				n->val_flag[idx] ^= NUM_INVERSE;
				updated = true;
				break;
			}
			/* fall-through */
		case CKEY:
			if (event->ctrl) {
				/* Copy current str to the copypaste buffer. */
				WM_clipboard_text_set(n->str, 0);
				updated = true;
				break;
			}
			/* fall-through */
		case VKEY:
			if (event->ctrl) {
				/* extract the first line from the clipboard */
				int pbuf_len;
				char *pbuf = WM_clipboard_text_get_firstline(false, &pbuf_len);

				if (pbuf) {
					bool success;

					success = editstr_insert_at_cursor(n, pbuf, pbuf_len);

					MEM_freeN(pbuf);
					if (!success) {
						return false;
					}

					n->val_flag[idx] |= NUM_EDITED;
				}
				updated = true;
				break;
			}
			/* fall-through */
		default:
			utf8_buf = event->utf8_buf;
			ascii[0] = event->ascii;
			break;
	}

	if (utf8_buf && !utf8_buf[0] && ascii[0]) {
		/* Fallback to ascii. */
		utf8_buf = ascii;
	}

	if (utf8_buf && utf8_buf[0]) {
		if (!(n->flag & NUM_EDIT_FULL)) {
			/* In simple edit mode, we only keep a few chars as valid! */
			/* no need to decode unicode, ascii is first char only */
			if (!editstr_is_simple_numinput(utf8_buf[0])) {
				return false;
			}
		}

		if (!editstr_insert_at_cursor(n, utf8_buf, BLI_str_utf8_size(utf8_buf))) {
			return false;
		}

		n->val_flag[idx] |= NUM_EDITED;
	}
	else if (!updated) {
		return false;
	}

	/* At this point, our value has changed, try to interpret it with python (if str is not empty!). */
	if (n->str[0]) {
#ifdef WITH_PYTHON
		char str_unit_convert[NUM_STR_REP_LEN * 6];  /* Should be more than enough! */
		const char *default_unit = NULL;

		/* Make radian default unit when needed. */
		if (n->unit_use_radians && n->unit_type[idx] == B_UNIT_ROTATION)
			default_unit = "r";

		BLI_strncpy(str_unit_convert, n->str, sizeof(str_unit_convert));

		bUnit_ReplaceString(str_unit_convert, sizeof(str_unit_convert), default_unit, 1.0,
		                    n->unit_sys, n->unit_type[idx]);

		/* Note: with angles, we always get values as radians here... */
		if (BPY_button_exec(C, str_unit_convert, &val, false) != -1) {
			n->val[idx] = (float)val;
			n->val_flag[idx] &= ~NUM_INVALID;
		}
		else {
			n->val_flag[idx] |= NUM_INVALID;
		}
#else  /* Very unlikely, but does not harm... */
		n->val[idx] = (float)atof(n->str);
#endif  /* WITH_PYTHON */

		if (n->val_flag[idx] & NUM_NEGATE) {
			n->val[idx] = -n->val[idx];
		}
		if (n->val_flag[idx] & NUM_INVERSE) {
			n->val[idx] = 1.0f / n->val[idx];
		}
	}

	/* REDRAW SINCE NUMBERS HAVE CHANGED */
	return true;
}
コード例 #10
0
ファイル: numinput.c プロジェクト: wisaac407/blender
bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event)
{
	const char *utf8_buf = NULL;
	char ascii[2] = {'\0', '\0'};
	bool updated = false;
	short idx = n->idx, idx_max = n->idx_max;
	short dir = STRCUR_DIR_NEXT, mode = STRCUR_JUMP_NONE;
	int cur;

	switch (event->type) {
		case EVT_MODAL_MAP:
			if (ELEM(event->val, NUM_MODAL_INCREMENT_UP, NUM_MODAL_INCREMENT_DOWN)) {
				n->val[idx] += (event->val == NUM_MODAL_INCREMENT_UP) ? n->val_inc[idx] : -n->val_inc[idx];
				value_to_editstr(n, idx);
				n->val_flag[idx] |= NUM_EDITED;
				updated = true;
			}
			else {
				/* might be a char too... */
				utf8_buf = event->utf8_buf;
				ascii[0] = event->ascii;
			}
			break;
		case BACKSPACEKEY:
			/* Part specific to backspace... */
			if (!(n->val_flag[idx] & NUM_EDITED)) {
				copy_v3_v3(n->val, n->val_org);
				n->val_flag[0] &= ~NUM_EDITED;
				n->val_flag[1] &= ~NUM_EDITED;
				n->val_flag[2] &= ~NUM_EDITED;
				n->flag |= NUM_FAKE_EDITED;
				updated = true;
				break;
			}
			else if (event->shift || !n->str[0]) {
				n->val[idx] = n->val_org[idx];
				n->val_flag[idx] &= ~NUM_EDITED;
				n->str[0] = '\0';
				n->str_cur = 0;
				updated = true;
				break;
			}
			/* Else, common behavior with DELKEY, only difference is remove char(s) before/after the cursor. */
			dir = STRCUR_DIR_PREV;
			ATTR_FALLTHROUGH;
		case DELKEY:
			if ((n->val_flag[idx] & NUM_EDITED) && n->str[0]) {
				int t_cur = cur = n->str_cur;
				if (event->ctrl) {
					mode = STRCUR_JUMP_DELIM;
				}
				BLI_str_cursor_step_utf8(n->str, strlen(n->str), &t_cur, dir, mode, true);
				if (t_cur != cur) {
					if (t_cur < cur) {
						SWAP(int, t_cur, cur);
						n->str_cur = cur;
					}
					memmove(&n->str[cur], &n->str[t_cur], strlen(&n->str[t_cur]) + 1);  /* +1 for trailing '\0'. */
					updated = true;
				}
				if (!n->str[0]) {
					n->val[idx] = n->val_org[idx];
				}
			}
			else {
				return false;
			}
			break;
		case LEFTARROWKEY:
			dir = STRCUR_DIR_PREV;
			ATTR_FALLTHROUGH;
		case RIGHTARROWKEY:
			cur = n->str_cur;
			if (event->ctrl) {
				mode = STRCUR_JUMP_DELIM;
			}
			BLI_str_cursor_step_utf8(n->str, strlen(n->str), &cur, dir, mode, true);
			if (cur != n->str_cur) {
				n->str_cur = cur;
				return true;
			}
			return false;
		case HOMEKEY:
			if (n->str[0]) {
				n->str_cur = 0;
				return true;
			}
			return false;
		case ENDKEY:
			if (n->str[0]) {
				n->str_cur = strlen(n->str);
				return true;
			}
			return false;
		case TABKEY:
			n->val_flag[idx] &= ~(NUM_NEGATE | NUM_INVERSE);

			idx = (idx + idx_max + (event->ctrl ? 0 : 2)) % (idx_max + 1);
			n->idx = idx;
			if (n->val_flag[idx] & NUM_EDITED) {
				value_to_editstr(n, idx);
			}
			else {
				n->str[0] = '\0';
				n->str_cur = 0;
			}
			return true;
		case PADPERIOD:
		case PERIODKEY:
			/* Force numdot, some OSs/countries generate a comma char in this case, sic...  (T37992) */
			ascii[0] = '.';
			utf8_buf = ascii;
			break;
#if 0
		/* Those keys are not directly accessible in all layouts, preventing to generate matching events.
		 * So we use a hack (ascii value) instead, see below.
		 */
		case EQUALKEY:
		case PADASTERKEY:
			if (!(n->flag & NUM_EDIT_FULL)) {
				n->flag |= NUM_EDIT_FULL;
				n->val_flag[idx] |= NUM_EDITED;
				return true;
			}
			else if (event->ctrl) {
				n->flag &= ~NUM_EDIT_FULL;
				return true;
			}
			break;
#endif
		case PADMINUS:
		case MINUSKEY:
			if (event->ctrl || !(n->flag & NUM_EDIT_FULL)) {
				n->val_flag[idx] ^= NUM_NEGATE;
				updated = true;
			}
			break;
		case PADSLASHKEY:
		case SLASHKEY:
			if (event->ctrl || !(n->flag & NUM_EDIT_FULL)) {
				n->val_flag[idx] ^= NUM_INVERSE;
				updated = true;
			}
			break;
		case CKEY:
			if (event->ctrl) {
				/* Copy current str to the copypaste buffer. */
				WM_clipboard_text_set(n->str, 0);
				updated = true;
			}
			break;
		case VKEY:
			if (event->ctrl) {
				/* extract the first line from the clipboard */
				int pbuf_len;
				char *pbuf = WM_clipboard_text_get_firstline(false, &pbuf_len);

				if (pbuf) {
					const bool success = editstr_insert_at_cursor(n, pbuf, pbuf_len);

					MEM_freeN(pbuf);
					if (!success) {
						return false;
					}

					n->val_flag[idx] |= NUM_EDITED;
				}
				updated = true;
			}
			break;
		default:
			break;
	}

	if (!updated && !utf8_buf && (event->utf8_buf[0] || event->ascii)) {
		utf8_buf = event->utf8_buf;
		ascii[0] = event->ascii;
	}

	/* XXX Hack around keyboards without direct access to '=' nor '*'... */
	if (ELEM(ascii[0], '=', '*')) {
		if (!(n->flag & NUM_EDIT_FULL)) {
			n->flag |= NUM_EDIT_FULL;
			n->val_flag[idx] |= NUM_EDITED;
			return true;
		}
		else if (event->ctrl) {
			n->flag &= ~NUM_EDIT_FULL;
			return true;
		}
	}

	/* Up to this point, if we have a ctrl modifier, skip.
	 * This allows to still access most of modals' shortcuts even in numinput mode.
	 */
	if (!updated && event->ctrl) {
		return false;
	}

	if ((!utf8_buf || !utf8_buf[0]) && ascii[0]) {
		/* Fallback to ascii. */
		utf8_buf = ascii;
	}

	if (utf8_buf && utf8_buf[0]) {
		if (!(n->flag & NUM_EDIT_FULL)) {
			/* In simple edit mode, we only keep a few chars as valid! */
			/* no need to decode unicode, ascii is first char only */
			if (!editstr_is_simple_numinput(utf8_buf[0])) {
				return false;
			}
		}

		if (!editstr_insert_at_cursor(n, utf8_buf, BLI_str_utf8_size(utf8_buf))) {
			return false;
		}

		n->val_flag[idx] |= NUM_EDITED;
	}
	else if (!updated) {
		return false;
	}

	/* At this point, our value has changed, try to interpret it with python (if str is not empty!). */
	if (n->str[0]) {
		const float val_prev = n->val[idx];
#ifdef WITH_PYTHON
		Scene *sce = CTX_data_scene(C);
		double val;
		char str_unit_convert[NUM_STR_REP_LEN * 6];  /* Should be more than enough! */
		const char *default_unit = NULL;

		/* Use scale_length if needed! */
		const float fac = (float)BKE_scene_unit_scale(&sce->unit, n->unit_type[idx], 1.0);

		/* Make radian default unit when needed. */
		if (n->unit_use_radians && n->unit_type[idx] == B_UNIT_ROTATION)
			default_unit = "r";

		BLI_strncpy(str_unit_convert, n->str, sizeof(str_unit_convert));

		bUnit_ReplaceString(str_unit_convert, sizeof(str_unit_convert), default_unit, fac,
		                    n->unit_sys, n->unit_type[idx]);

		/* Note: with angles, we always get values as radians here... */
		if (BPY_execute_string_as_number(C, str_unit_convert, false, &val)) {
			n->val[idx] = (float)val;
			n->val_flag[idx] &= ~NUM_INVALID;
		}
		else {
			n->val_flag[idx] |= NUM_INVALID;
		}
#else  /* Very unlikely, but does not harm... */
		n->val[idx] = (float)atof(n->str);
		(void)C;
#endif  /* WITH_PYTHON */

		if (n->val_flag[idx] & NUM_NEGATE) {
			n->val[idx] = -n->val[idx];
		}
		if (n->val_flag[idx] & NUM_INVERSE) {
			n->val[idx] = 1.0f / n->val[idx];
		}

		if (UNLIKELY(!isfinite(n->val[idx]))) {
			n->val[idx] = val_prev;
			n->val_flag[idx] |= NUM_INVALID;
		}
	}

	/* REDRAW SINCE NUMBERS HAVE CHANGED */
	return true;
}