Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/curve')
-rw-r--r--source/blender/editors/curve/Makefile56
-rw-r--r--source/blender/editors/curve/SConscript11
-rw-r--r--source/blender/editors/curve/curve_intern.h109
-rw-r--r--source/blender/editors/curve/curve_ops.c245
-rw-r--r--source/blender/editors/curve/editcurve.c5235
-rw-r--r--source/blender/editors/curve/editfont.c1625
-rw-r--r--source/blender/editors/curve/lorem.c513
7 files changed, 7794 insertions, 0 deletions
diff --git a/source/blender/editors/curve/Makefile b/source/blender/editors/curve/Makefile
new file mode 100644
index 00000000000..6b1f628f231
--- /dev/null
+++ b/source/blender/editors/curve/Makefile
@@ -0,0 +1,56 @@
+#
+# $Id: Makefile 14 2002-10-13 15:57:19Z hans $
+#
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# The Original Code is Copyright (C) 2007 Blender Foundation
+# All rights reserved.
+#
+# The Original Code is: all of this file.
+#
+# Contributor(s): none yet.
+#
+# ***** END GPL LICENSE BLOCK *****
+#
+# Makes module object directory and bounces make to subdirectories.
+
+LIBNAME = ed_curve
+DIR = $(OCGDIR)/blender/$(LIBNAME)
+
+include nan_compile.mk
+
+CFLAGS += $(LEVEL_1_C_WARNINGS)
+
+CPPFLAGS += -I$(NAN_GLEW)/include
+CPPFLAGS += -I$(OPENGL_HEADERS)
+
+CPPFLAGS += -I$(NAN_GUARDEDALLOC)/include
+CPPFLAGS += -I$(NAN_ELBEEM)/include
+
+CPPFLAGS += -I../../windowmanager
+CPPFLAGS += -I../../blenkernel
+CPPFLAGS += -I../../blenloader
+CPPFLAGS += -I../../blenlib
+CPPFLAGS += -I../../makesdna
+CPPFLAGS += -I../../makesrna
+CPPFLAGS += -I../../imbuf
+CPPFLAGS += -I../../gpu
+CPPFLAGS += -I../../render/extern/include
+
+# own include
+
+CPPFLAGS += -I../include
diff --git a/source/blender/editors/curve/SConscript b/source/blender/editors/curve/SConscript
new file mode 100644
index 00000000000..3a1930899d3
--- /dev/null
+++ b/source/blender/editors/curve/SConscript
@@ -0,0 +1,11 @@
+#!/usr/bin/python
+Import ('env')
+
+sources = env.Glob('*.c')
+
+incs = '../include ../../blenlib ../../blenkernel ../../makesdna ../../imbuf'
+incs += ' ../../windowmanager #/intern/guardedalloc #/extern/glew/include'
+incs += ' #/intern/guardedalloc ../../gpu'
+incs += ' ../../makesrna ../../render/extern/include #/intern/elbeem/extern'
+
+env.BlenderLib ( 'bf_editors_curve', sources, Split(incs), [], libtype=['core'], priority=[45] )
diff --git a/source/blender/editors/curve/curve_intern.h b/source/blender/editors/curve/curve_intern.h
new file mode 100644
index 00000000000..a73a54323ee
--- /dev/null
+++ b/source/blender/editors/curve/curve_intern.h
@@ -0,0 +1,109 @@
+/**
+ * $Id:
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2008 Blender Foundation.
+ * All rights reserved.
+ *
+ *
+ * Contributor(s): Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef ED_CURVE_INTERN_H
+#define ED_CURVE_INTERN_H
+
+/* internal exports only */
+struct wmOperatorType;
+
+/* lorem.c */
+char *ED_lorem;
+
+/* editfont.c */
+enum { DEL_ALL, DEL_NEXT_CHAR, DEL_PREV_CHAR, DEL_SELECTION, DEL_NEXT_SEL, DEL_PREV_SEL };
+enum { CASE_LOWER, CASE_UPPER };
+enum { LINE_BEGIN, LINE_END, PREV_CHAR, NEXT_CHAR, PREV_WORD, NEXT_WORD,
+ PREV_LINE, NEXT_LINE, PREV_PAGE, NEXT_PAGE };
+
+void FONT_OT_text_insert(struct wmOperatorType *ot);
+void FONT_OT_line_break(struct wmOperatorType *ot);
+void FONT_OT_insert_lorem(struct wmOperatorType *ot);
+
+void FONT_OT_case_toggle(struct wmOperatorType *ot);
+void FONT_OT_case_set(struct wmOperatorType *ot);
+void FONT_OT_style_toggle(struct wmOperatorType *ot);
+void FONT_OT_style_set(struct wmOperatorType *ot);
+void FONT_OT_material_set(struct wmOperatorType *ot);
+
+void FONT_OT_text_copy(struct wmOperatorType *ot);
+void FONT_OT_text_cut(struct wmOperatorType *ot);
+void FONT_OT_text_paste(struct wmOperatorType *ot);
+void FONT_OT_file_paste(struct wmOperatorType *ot);
+void FONT_OT_buffer_paste(struct wmOperatorType *ot);
+
+void FONT_OT_move(struct wmOperatorType *ot);
+void FONT_OT_move_select(struct wmOperatorType *ot);
+void FONT_OT_delete(struct wmOperatorType *ot);
+
+void FONT_OT_change_character(struct wmOperatorType *ot);
+void FONT_OT_change_spacing(struct wmOperatorType *ot);
+
+/* editcurve.c */
+void CURVE_OT_hide(struct wmOperatorType *ot);
+void CURVE_OT_reveal(struct wmOperatorType *ot);
+
+void CURVE_OT_separate(struct wmOperatorType *ot);
+void CURVE_OT_duplicate(struct wmOperatorType *ot);
+void CURVE_OT_delete(struct wmOperatorType *ot);
+
+void CURVE_OT_spline_type_set(struct wmOperatorType *ot);
+void CURVE_OT_radius_set(struct wmOperatorType *ot);
+void CURVE_OT_spline_weight_set(struct wmOperatorType *ot);
+void CURVE_OT_handle_type_set(struct wmOperatorType *ot);
+void CURVE_OT_smooth_set(struct wmOperatorType *ot);
+void CURVE_OT_tilt_clear(struct wmOperatorType *ot);
+
+void CURVE_OT_smooth(struct wmOperatorType *ot);
+void CURVE_OT_smooth_radius(struct wmOperatorType *ot);
+
+void CURVE_OT_de_select_first(struct wmOperatorType *ot);
+void CURVE_OT_de_select_last(struct wmOperatorType *ot);
+void CURVE_OT_select_all_toggle(struct wmOperatorType *ot);
+void CURVE_OT_select_invert(struct wmOperatorType *ot);
+void CURVE_OT_select_linked(struct wmOperatorType *ot);
+void CURVE_OT_select_row(struct wmOperatorType *ot);
+void CURVE_OT_select_next(struct wmOperatorType *ot);
+void CURVE_OT_select_previous(struct wmOperatorType *ot);
+void CURVE_OT_select_more(struct wmOperatorType *ot);
+void CURVE_OT_select_less(struct wmOperatorType *ot);
+void CURVE_OT_select_random(struct wmOperatorType *ot);
+void CURVE_OT_select_every_nth(struct wmOperatorType *ot);
+
+void CURVE_OT_switch_direction(struct wmOperatorType *ot);
+void CURVE_OT_subdivide(struct wmOperatorType *ot);
+void CURVE_OT_make_segment(struct wmOperatorType *ot);
+void CURVE_OT_spin(struct wmOperatorType *ot);
+void CURVE_OT_vertex_add(struct wmOperatorType *ot);
+void CURVE_OT_extrude(struct wmOperatorType *ot);
+void CURVE_OT_cyclic_toggle(struct wmOperatorType *ot);
+
+void CURVE_OT_specials_menu(struct wmOperatorType *ot);
+
+#endif /* ED_UTIL_INTERN_H */
+
diff --git a/source/blender/editors/curve/curve_ops.c b/source/blender/editors/curve/curve_ops.c
new file mode 100644
index 00000000000..5292d86d3c9
--- /dev/null
+++ b/source/blender/editors/curve/curve_ops.c
@@ -0,0 +1,245 @@
+/**
+ * $Id:
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2009 Blender Foundation.
+ * All rights reserved.
+ *
+ *
+ * Contributor(s): Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <stdlib.h>
+#include <math.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_curve_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+#include "DNA_userdef_types.h"
+#include "DNA_view3d_types.h"
+#include "DNA_windowmanager_types.h"
+
+#include "BLI_arithb.h"
+#include "BLI_blenlib.h"
+
+#include "BKE_context.h"
+#include "BKE_global.h"
+#include "BKE_utildefines.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_screen.h"
+#include "ED_object.h"
+
+#include "BIF_transform.h"
+
+#include "UI_interface.h"
+
+#include "curve_intern.h"
+
+
+/**************************** menus *****************************/
+
+static int specials_menu_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ uiPopupMenu *pup;
+ uiLayout *layout;
+
+ pup= uiPupMenuBegin(C, "Specials", 0);
+ layout= uiPupMenuLayout(pup);
+ uiItemO(layout, NULL, 0, "CURVE_OT_subdivide");
+ uiItemO(layout, NULL, 0, "CURVE_OT_switch_direction");
+ uiItemO(layout, NULL, 0, "CURVE_OT_spline_weight_set");
+ uiItemO(layout, NULL, 0, "CURVE_OT_radius_set");
+ uiItemO(layout, NULL, 0, "CURVE_OT_smooth");
+ uiItemO(layout, NULL, 0, "CURVE_OT_smooth_radius");
+ uiPupMenuEnd(C, pup);
+
+ return OPERATOR_CANCELLED;
+}
+
+void CURVE_OT_specials_menu(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Specials Menu";
+ ot->idname= "CURVE_OT_specials_menu";
+
+ /* api clastbacks */
+ ot->invoke= specials_menu_invoke;
+ ot->poll= ED_operator_editsurfcurve;
+}
+
+/************************* registration ****************************/
+
+void ED_operatortypes_curve(void)
+{
+ WM_operatortype_append(FONT_OT_text_insert);
+ WM_operatortype_append(FONT_OT_line_break);
+ WM_operatortype_append(FONT_OT_insert_lorem);
+
+ WM_operatortype_append(FONT_OT_case_toggle);
+ WM_operatortype_append(FONT_OT_case_set);
+ WM_operatortype_append(FONT_OT_style_toggle);
+ WM_operatortype_append(FONT_OT_style_set);
+ WM_operatortype_append(FONT_OT_material_set);
+
+ WM_operatortype_append(FONT_OT_text_copy);
+ WM_operatortype_append(FONT_OT_text_cut);
+ WM_operatortype_append(FONT_OT_text_paste);
+ WM_operatortype_append(FONT_OT_file_paste);
+ WM_operatortype_append(FONT_OT_buffer_paste);
+
+ WM_operatortype_append(FONT_OT_move);
+ WM_operatortype_append(FONT_OT_move_select);
+ WM_operatortype_append(FONT_OT_delete);
+
+ WM_operatortype_append(FONT_OT_change_character);
+ WM_operatortype_append(FONT_OT_change_spacing);
+
+ WM_operatortype_append(CURVE_OT_hide);
+ WM_operatortype_append(CURVE_OT_reveal);
+
+ WM_operatortype_append(CURVE_OT_separate);
+ WM_operatortype_append(CURVE_OT_duplicate);
+ WM_operatortype_append(CURVE_OT_delete);
+
+ WM_operatortype_append(CURVE_OT_spline_type_set);
+ WM_operatortype_append(CURVE_OT_radius_set);
+ WM_operatortype_append(CURVE_OT_spline_weight_set);
+ WM_operatortype_append(CURVE_OT_handle_type_set);
+ WM_operatortype_append(CURVE_OT_smooth_set);
+ WM_operatortype_append(CURVE_OT_tilt_clear);
+
+ WM_operatortype_append(CURVE_OT_smooth);
+ WM_operatortype_append(CURVE_OT_smooth_radius);
+
+ WM_operatortype_append(CURVE_OT_de_select_first);
+ WM_operatortype_append(CURVE_OT_de_select_last);
+ WM_operatortype_append(CURVE_OT_select_all_toggle);
+ WM_operatortype_append(CURVE_OT_select_invert);
+ WM_operatortype_append(CURVE_OT_select_linked);
+ WM_operatortype_append(CURVE_OT_select_row);
+ WM_operatortype_append(CURVE_OT_select_next);
+ WM_operatortype_append(CURVE_OT_select_previous);
+ WM_operatortype_append(CURVE_OT_select_more);
+ WM_operatortype_append(CURVE_OT_select_less);
+ WM_operatortype_append(CURVE_OT_select_random);
+ WM_operatortype_append(CURVE_OT_select_every_nth);
+
+ WM_operatortype_append(CURVE_OT_switch_direction);
+ WM_operatortype_append(CURVE_OT_subdivide);
+ WM_operatortype_append(CURVE_OT_make_segment);
+ WM_operatortype_append(CURVE_OT_spin);
+ WM_operatortype_append(CURVE_OT_vertex_add);
+ WM_operatortype_append(CURVE_OT_extrude);
+ WM_operatortype_append(CURVE_OT_cyclic_toggle);
+
+ WM_operatortype_append(CURVE_OT_specials_menu);
+}
+
+void ED_keymap_curve(wmWindowManager *wm)
+{
+ ListBase *keymap= WM_keymap_listbase(wm, "Font", 0, 0);
+
+ /* only set in editmode font, by space_view3d listener */
+ RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_style_toggle", BKEY, KM_PRESS, KM_CTRL, 0)->ptr, "style", CU_BOLD);
+ RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_style_toggle", IKEY, KM_PRESS, KM_CTRL, 0)->ptr, "style", CU_ITALIC);
+ RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_style_toggle", UKEY, KM_PRESS, KM_CTRL, 0)->ptr, "style", CU_UNDERLINE);
+
+ RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_delete", DELKEY, KM_PRESS, 0, 0)->ptr, "type", DEL_NEXT_SEL);
+ RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_delete", BACKSPACEKEY, KM_PRESS, 0, 0)->ptr, "type", DEL_PREV_SEL);
+ RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_delete", BACKSPACEKEY, KM_PRESS, KM_CTRL, 0)->ptr, "type", DEL_ALL);
+
+ RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move", HOMEKEY, KM_PRESS, 0, 0)->ptr, "type", LINE_BEGIN);
+ RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move", ENDKEY, KM_PRESS, 0, 0)->ptr, "type", LINE_END);
+ RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move", LEFTARROWKEY, KM_PRESS, 0, 0)->ptr, "type", PREV_CHAR);
+ RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move", RIGHTARROWKEY, KM_PRESS, 0, 0)->ptr, "type", NEXT_CHAR);
+ RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move", LEFTARROWKEY, KM_PRESS, KM_CTRL, 0)->ptr, "type", PREV_WORD);
+ RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move", RIGHTARROWKEY, KM_PRESS, KM_CTRL, 0)->ptr, "type", NEXT_WORD);
+ RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move", UPARROWKEY, KM_PRESS, 0, 0)->ptr, "type", PREV_LINE);
+ RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move", DOWNARROWKEY, KM_PRESS, 0, 0)->ptr, "type", NEXT_LINE);
+ RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move", PAGEUPKEY, KM_PRESS, 0, 0)->ptr, "type", PREV_PAGE);
+ RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move", PAGEDOWNKEY, KM_PRESS, 0, 0)->ptr, "type", NEXT_PAGE);
+
+ RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move_select", HOMEKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "type", LINE_BEGIN);
+ RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move_select", ENDKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "type", LINE_END);
+ RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move_select", LEFTARROWKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "type", PREV_CHAR);
+ RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move_select", RIGHTARROWKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "type", NEXT_CHAR);
+ RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move_select", LEFTARROWKEY, KM_PRESS, KM_SHIFT|KM_CTRL, 0)->ptr, "type", PREV_WORD);
+ RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move_select", RIGHTARROWKEY, KM_PRESS, KM_SHIFT|KM_CTRL, 0)->ptr, "type", NEXT_WORD);
+ RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move_select", UPARROWKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "type", PREV_LINE);
+ RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move_select", DOWNARROWKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "type", NEXT_LINE);
+ RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move_select", PAGEUPKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "type", PREV_PAGE);
+ RNA_enum_set(WM_keymap_add_item(keymap, "FONT_OT_move_select", PAGEDOWNKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "type", NEXT_PAGE);
+
+ RNA_int_set(WM_keymap_add_item(keymap, "FONT_OT_change_spacing", LEFTARROWKEY, KM_PRESS, KM_ALT, 0)->ptr, "delta", -1);
+ RNA_int_set(WM_keymap_add_item(keymap, "FONT_OT_change_spacing", RIGHTARROWKEY, KM_PRESS, KM_ALT, 0)->ptr, "delta", 1);
+ RNA_int_set(WM_keymap_add_item(keymap, "FONT_OT_change_character", UPARROWKEY, KM_PRESS, KM_ALT, 0)->ptr, "delta", 1);
+ RNA_int_set(WM_keymap_add_item(keymap, "FONT_OT_change_character", DOWNARROWKEY, KM_PRESS, KM_ALT, 0)->ptr, "delta", -1);
+
+ WM_keymap_add_item(keymap, "FONT_OT_text_copy", CKEY, KM_PRESS, KM_CTRL, 0);
+ WM_keymap_add_item(keymap, "FONT_OT_text_cut", XKEY, KM_PRESS, KM_CTRL, 0);
+ WM_keymap_add_item(keymap, "FONT_OT_text_paste", PKEY, KM_PRESS, KM_CTRL, 0);
+
+ WM_keymap_add_item(keymap, "FONT_OT_line_break", RETKEY, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "FONT_OT_text_insert", KM_TEXTINPUT, KM_ANY, KM_ANY, 0); // last!
+
+ /* only set in editmode curve, by space_view3d listener */
+ keymap= WM_keymap_listbase(wm, "Curve", 0, 0);
+
+ WM_keymap_add_item(keymap, "OBJECT_OT_curve_add", AKEY, KM_PRESS, KM_SHIFT, 0);
+ WM_keymap_add_item(keymap, "CURVE_OT_vertex_add", ACTIONMOUSE, KM_PRESS, KM_CTRL, 0);
+
+ WM_keymap_add_item(keymap, "CURVE_OT_select_all_toggle", AKEY, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "CURVE_OT_select_row", RKEY, KM_PRESS, KM_SHIFT, 0);
+ WM_keymap_add_item(keymap, "CURVE_OT_select_more", PADPLUSKEY, KM_PRESS, KM_CTRL, 0);
+ WM_keymap_add_item(keymap, "CURVE_OT_select_less", PADMINUS, KM_PRESS, KM_CTRL, 0);
+ WM_keymap_add_item(keymap, "CURVE_OT_select_linked", LKEY, KM_PRESS, 0, 0);
+ RNA_boolean_set(WM_keymap_add_item(keymap, "CURVE_OT_select_linked", LKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "deselect", 1);
+
+ WM_keymap_add_item(keymap, "CURVE_OT_separate", PKEY, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "CURVE_OT_extrude", EKEY, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "CURVE_OT_duplicate", DKEY, KM_PRESS, KM_SHIFT, 0);
+ WM_keymap_add_item(keymap, "CURVE_OT_make_segment", FKEY, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "CURVE_OT_cyclic_toggle", CKEY, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "CURVE_OT_delete", XKEY, KM_PRESS, 0, 0);
+ WM_keymap_add_item(keymap, "CURVE_OT_delete", DELKEY, KM_PRESS, 0, 0);
+
+ WM_keymap_add_item(keymap, "CURVE_OT_tilt_clear", TKEY, KM_PRESS, KM_ALT, 0);
+ RNA_enum_set(WM_keymap_add_item(keymap, "TFM_OT_transform", TKEY, KM_PRESS, 0, 0)->ptr, "mode", TFM_TILT);
+ RNA_enum_set(WM_keymap_add_item(keymap, "CURVE_OT_handle_type_set", HKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "type", 1);
+ RNA_enum_set(WM_keymap_add_item(keymap, "CURVE_OT_handle_type_set", HKEY, KM_PRESS, 0, 0)->ptr, "type", 3);
+ RNA_enum_set(WM_keymap_add_item(keymap, "CURVE_OT_handle_type_set", VKEY, KM_PRESS, 0, 0)->ptr, "type", 2);
+
+ WM_keymap_add_item(keymap, "CURVE_OT_reveal", HKEY, KM_PRESS, KM_ALT, 0);
+ WM_keymap_add_item(keymap, "CURVE_OT_hide", HKEY, KM_PRESS, KM_ALT|KM_CTRL, 0);
+ RNA_enum_set(WM_keymap_add_item(keymap, "CURVE_OT_hide", HKEY, KM_PRESS, KM_ALT|KM_SHIFT, 0)->ptr, "unselected", 1);
+
+ WM_keymap_add_item(keymap, "CURVE_OT_specials_menu", WKEY, KM_PRESS, 0, 0);
+}
+
diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c
new file mode 100644
index 00000000000..45b9a589bd5
--- /dev/null
+++ b/source/blender/editors/curve/editcurve.c
@@ -0,0 +1,5235 @@
+/**
+ * $Id$
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <math.h>
+#include <string.h>
+
+#ifndef WIN32
+#include <unistd.h>
+#else
+#include <io.h>
+#endif
+#include <stdlib.h>
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_arithb.h"
+#include "BLI_dynstr.h"
+#include "BLI_rand.h"
+
+#include "DNA_curve_types.h"
+#include "DNA_key_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+#include "DNA_view3d_types.h"
+#include "DNA_userdef_types.h"
+
+#include "BKE_context.h"
+#include "BKE_curve.h"
+#include "BKE_depsgraph.h"
+#include "BKE_fcurve.h"
+#include "BKE_global.h"
+#include "BKE_key.h"
+#include "BKE_library.h"
+#include "BKE_main.h"
+#include "BKE_object.h"
+#include "BKE_report.h"
+#include "BKE_utildefines.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_anim_api.h"
+#include "ED_curve.h"
+#include "ED_keyframes_edit.h"
+#include "ED_object.h"
+#include "ED_screen.h"
+#include "ED_types.h"
+#include "ED_util.h"
+#include "ED_view3d.h"
+
+#include "UI_interface.h"
+
+#include "BIF_transform.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+/* still need to eradicate a few :( */
+#define callocstructN(x,y,name) (x*)MEM_callocN((y)* sizeof(x),name)
+
+/* for curve objects in editmode that can have hidden handles */
+#define BEZSELECTED_HIDDENHANDLES(bezt) ((G.f & G_HIDDENHANDLES) ? (bezt)->f2 & SELECT : BEZSELECTED(bezt))
+
+float nurbcircle[8][2]= {
+ {0.0, -1.0}, {-1.0, -1.0}, {-1.0, 0.0}, {-1.0, 1.0},
+ {0.0, 1.0}, { 1.0, 1.0}, { 1.0, 0.0}, { 1.0, -1.0}
+};
+
+ListBase *curve_get_editcurve(Object *ob)
+{
+ if(ob && ELEM(ob->type, OB_CURVE, OB_SURF)) {
+ Curve *cu= ob->data;
+ return cu->editnurb;
+ }
+ return NULL;
+}
+
+/* this replaces the active flag used in uv/face mode */
+void set_actNurb(Object *obedit, Nurb *nu)
+{
+ Curve *cu= obedit->data;
+
+ if(nu==NULL)
+ cu->actnu = -1;
+ else
+ cu->actnu = BLI_findindex(cu->editnurb, nu);
+}
+
+Nurb *get_actNurb(Object *obedit)
+{
+ Curve *cu= obedit->data;
+
+ return BLI_findlink(cu->editnurb, cu->actnu);
+}
+
+/* ******************* SELECTION FUNCTIONS ********************* */
+
+#define HIDDEN 1
+#define VISIBLE 0
+
+#define FIRST 1
+#define LAST 0
+
+
+/* returns 1 in case (de)selection was successful */
+static short select_beztriple(BezTriple *bezt, short selstatus, short flag, short hidden)
+{
+ if(bezt) {
+ if((bezt->hide==0) || (hidden==1)) {
+ if(selstatus==1) { /* selects */
+ bezt->f1 |= flag;
+ bezt->f2 |= flag;
+ bezt->f3 |= flag;
+ return 1;
+ }
+ else { /* deselects */
+ bezt->f1 &= ~flag;
+ bezt->f2 &= ~flag;
+ bezt->f3 &= ~flag;
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* returns 1 in case (de)selection was successful */
+static short select_bpoint(BPoint *bp, short selstatus, short flag, short hidden)
+{
+ if(bp) {
+ if((bp->hide==0) || (hidden==1)) {
+ if(selstatus==1) {
+ bp->f1 |= flag;
+ return 1;
+ }
+ else {
+ bp->f1 &= ~flag;
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static short swap_selection_beztriple(BezTriple *bezt)
+{
+ if(bezt->f2 & SELECT)
+ return select_beztriple(bezt, DESELECT, 1, VISIBLE);
+ else
+ return select_beztriple(bezt, SELECT, 1, VISIBLE);
+}
+
+static short swap_selection_bpoint(BPoint *bp)
+{
+ if(bp->f1 & SELECT)
+ return select_bpoint(bp, DESELECT, 1, VISIBLE);
+ else
+ return select_bpoint(bp, SELECT, 1, VISIBLE);
+}
+
+short isNurbsel(Nurb *nu)
+{
+ BezTriple *bezt;
+ BPoint *bp;
+ int a;
+
+ if((nu->type & 7)==CU_BEZIER) {
+ bezt= nu->bezt;
+ a= nu->pntsu;
+ while(a--) {
+ if( (bezt->f1 & SELECT) || (bezt->f2 & SELECT) || (bezt->f3 & SELECT) ) return 1;
+ bezt++;
+ }
+ }
+ else {
+ bp= nu->bp;
+ a= nu->pntsu*nu->pntsv;
+ while(a--) {
+ if( (bp->f1 & SELECT) ) return 1;
+ bp++;
+ }
+ }
+ return 0;
+}
+
+int isNurbsel_count(Nurb *nu)
+{
+ BezTriple *bezt;
+ BPoint *bp;
+ int a, sel=0;
+
+ if((nu->type & 7)==CU_BEZIER) {
+ bezt= nu->bezt;
+ a= nu->pntsu;
+ while(a--) {
+ if (BEZSELECTED_HIDDENHANDLES(bezt)) sel++;
+ bezt++;
+ }
+ }
+ else {
+ bp= nu->bp;
+ a= nu->pntsu*nu->pntsv;
+ while(a--) {
+ if( (bp->f1 & SELECT) ) sel++;
+ bp++;
+ }
+ }
+ return sel;
+}
+
+/* ******************* PRINTS ********************* */
+
+void printknots(Object *obedit)
+{
+ ListBase *editnurb= curve_get_editcurve(obedit);
+ Nurb *nu;
+ int a, num;
+
+ for(nu= editnurb->first; nu; nu= nu->next) {
+ if(isNurbsel(nu) && (nu->type & 7)==CU_NURBS) {
+ if(nu->knotsu) {
+ num= KNOTSU(nu);
+ for(a=0;a<num;a++) printf("knotu %d: %f\n", a, nu->knotsu[a]);
+ }
+ if(nu->knotsv) {
+ num= KNOTSV(nu);
+ for(a=0;a<num;a++) printf("knotv %d: %f\n", a, nu->knotsv[a]);
+ }
+ }
+ }
+}
+
+/* ********************* LOAD and MAKE *************** */
+
+/* load editNurb in object */
+void load_editNurb(Object *obedit)
+{
+ ListBase *editnurb= curve_get_editcurve(obedit);
+
+ if(obedit==NULL) return;
+
+ if (ELEM(obedit->type, OB_CURVE, OB_SURF)) {
+ Curve *cu= obedit->data;
+ Nurb *nu, *newnu;
+ KeyBlock *actkey;
+ int totvert= count_curveverts(editnurb);
+
+ /* are there keys? */
+ actkey = ob_get_keyblock(obedit);
+ if(actkey) {
+ /* active key: the vertices */
+
+ if(totvert) {
+ if(actkey->data) MEM_freeN(actkey->data);
+
+ actkey->data= MEM_callocN(cu->key->elemsize*totvert, "actkey->data");
+ actkey->totelem= totvert;
+
+ curve_to_key(cu, actkey, editnurb);
+ }
+ }
+
+ if(cu->key && actkey!=cu->key->refkey) {
+ ;
+ }
+ else {
+ freeNurblist(&(cu->nurb));
+
+ for(nu= editnurb->first; nu; nu= nu->next) {
+ newnu= duplicateNurb(nu);
+ BLI_addtail(&(cu->nurb), newnu);
+
+ if((nu->type & 7)==CU_NURBS) {
+ clamp_nurb_order_u(nu);
+ }
+ }
+ }
+ }
+
+ set_actNurb(obedit, NULL);
+}
+
+/* make copy in cu->editnurb */
+void make_editNurb(Object *obedit)
+{
+ ListBase *editnurb= curve_get_editcurve(obedit);
+ Nurb *nu, *newnu;
+ KeyBlock *actkey;
+
+ if(obedit==NULL) return;
+
+ if (ELEM(obedit->type, OB_CURVE, OB_SURF)) {
+ Curve *cu= obedit->data;
+
+ if(editnurb)
+ freeNurblist(editnurb);
+ else
+ editnurb= cu->editnurb= MEM_callocN(sizeof(ListBase), "editnurb");
+
+ nu= cu->nurb.first;
+ cu->lastselbp= NULL; /* for select row */
+
+ while(nu) {
+ newnu= duplicateNurb(nu);
+ test2DNurb(newnu); // after join, or any other creation of curve
+ BLI_addtail(editnurb, newnu);
+ nu= nu->next;
+ }
+
+ actkey = ob_get_keyblock(obedit);
+ if(actkey) {
+ // XXX strcpy(G.editModeTitleExtra, "(Key) ");
+ key_to_curve(actkey, cu, editnurb);
+ }
+ }
+
+ set_actNurb(obedit, NULL);
+}
+
+void free_editNurb(Object *obedit)
+{
+ Curve *cu= obedit->data;
+
+ if(cu->editnurb) {
+ freeNurblist(cu->editnurb);
+ MEM_freeN(cu->editnurb);
+ cu->editnurb= NULL;
+ }
+}
+
+/******************** separate operator ***********************/
+
+static int separate_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene= CTX_data_scene(C);
+ Nurb *nu, *nu1;
+ Object *oldob, *newob;
+ Base *oldbase, *newbase;
+ Curve *oldcu, *newcu;
+ ListBase *oldedit, *newedit;
+
+ oldbase= CTX_data_active_base(C);
+ oldob= oldbase->object;
+ oldcu= oldob->data;
+ oldedit= oldcu->editnurb;
+
+ if(oldcu->key) {
+ BKE_report(op->reports, RPT_ERROR, "Can't separate a curve with vertex keys.");
+ return OPERATOR_CANCELLED;
+ }
+
+ WM_cursor_wait(1);
+
+ /* 1. duplicate the object and data */
+ newbase= ED_object_add_duplicate(scene, oldbase, 0); /* 0 = fully linked */
+ ED_base_object_select(newbase, BA_DESELECT);
+ newob= newbase->object;
+
+ newcu= newob->data= copy_curve(oldcu);
+ newcu->editnurb= NULL;
+ oldcu->id.us--; /* because new curve is a copy: reduce user count */
+
+ /* 2. put new object in editmode and clear it */
+ make_editNurb(newob);
+ newedit= newcu->editnurb;
+ freeNurblist(newedit);
+
+ /* 3. move over parts from old object */
+ for(nu= oldedit->first; nu; nu=nu1) {
+ nu1= nu->next;
+
+ if(isNurbsel(nu)) {
+ BLI_remlink(oldedit, nu);
+ BLI_addtail(newedit, nu);
+ }
+ }
+
+ /* 4. put old object out of editmode */
+ load_editNurb(newob);
+ free_editNurb(newob);
+
+ DAG_object_flush_update(scene, oldob, OB_RECALC_DATA); /* this is the original one */
+ DAG_object_flush_update(scene, newob, OB_RECALC_DATA); /* this is the separated one */
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, oldob);
+
+ WM_cursor_wait(0);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_separate(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Separate";
+ ot->idname= "CURVE_OT_separate";
+
+ /* api callbacks */
+ ot->exec= separate_exec;
+ ot->poll= ED_operator_editsurfcurve;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/* ******************* FLAGS ********************* */
+
+static short isNurbselUV(Nurb *nu, int *u, int *v, int flag)
+{
+ /* return u!=-1: 1 row in u-direction selected. U has value between 0-pntsv
+ * return v!=-1: 1 collumn in v-direction selected. V has value between 0-pntsu
+ */
+ BPoint *bp;
+ int a, b, sel;
+
+ *u= *v= -1;
+
+ bp= nu->bp;
+ for(b=0; b<nu->pntsv; b++) {
+ sel= 0;
+ for(a=0; a<nu->pntsu; a++, bp++) {
+ if(bp->f1 & flag) sel++;
+ }
+ if(sel==nu->pntsu) {
+ if(*u== -1) *u= b;
+ else return 0;
+ }
+ else if(sel>1) return 0; /* because sel==1 is still ok */
+ }
+
+ for(a=0; a<nu->pntsu; a++) {
+ sel= 0;
+ bp= nu->bp+a;
+ for(b=0; b<nu->pntsv; b++, bp+=nu->pntsu) {
+ if(bp->f1 & flag) sel++;
+ }
+ if(sel==nu->pntsv) {
+ if(*v== -1) *v= a;
+ else return 0;
+ }
+ else if(sel>1) return 0;
+ }
+
+ if(*u==-1 && *v>-1) return 1;
+ if(*v==-1 && *u>-1) return 1;
+ return 0;
+}
+
+static void setflagsNurb(ListBase *editnurb, short flag)
+{
+ Nurb *nu;
+ BezTriple *bezt;
+ BPoint *bp;
+ int a;
+
+ for(nu= editnurb->first; nu; nu= nu->next) {
+ if( (nu->type & 7)==CU_BEZIER) {
+ a= nu->pntsu;
+ bezt= nu->bezt;
+ while(a--) {
+ bezt->f1= bezt->f2= bezt->f3= flag;
+ bezt++;
+ }
+ }
+ else {
+ a= nu->pntsu*nu->pntsv;
+ bp= nu->bp;
+ while(a--) {
+ bp->f1= flag;
+ bp++;
+ }
+ }
+ }
+}
+
+static void rotateflagNurb(ListBase *editnurb, short flag, float *cent, float rotmat[][3])
+{
+ /* all verts with (flag & 'flag') rotate */
+ Nurb *nu;
+ BPoint *bp;
+ int a;
+
+ for(nu= editnurb->first; nu; nu= nu->next) {
+ if((nu->type & 7)==CU_NURBS) {
+ bp= nu->bp;
+ a= nu->pntsu*nu->pntsv;
+
+ while(a--) {
+ if(bp->f1 & flag) {
+ bp->vec[0]-=cent[0];
+ bp->vec[1]-=cent[1];
+ bp->vec[2]-=cent[2];
+ Mat3MulVecfl(rotmat, bp->vec);
+ bp->vec[0]+=cent[0];
+ bp->vec[1]+=cent[1];
+ bp->vec[2]+=cent[2];
+ }
+ bp++;
+ }
+ }
+ }
+}
+
+static void translateflagNurb(ListBase *editnurb, short flag, float *vec)
+{
+ /* all verts with ('flag' & flag) translate */
+ Nurb *nu;
+ BezTriple *bezt;
+ BPoint *bp;
+ int a;
+
+ for(nu= editnurb->first; nu; nu= nu->next) {
+ if( (nu->type & 7)==CU_BEZIER) {
+ a= nu->pntsu;
+ bezt= nu->bezt;
+ while(a--) {
+ if(bezt->f1 & flag) VecAddf(bezt->vec[0], bezt->vec[0], vec);
+ if(bezt->f2 & flag) VecAddf(bezt->vec[1], bezt->vec[1], vec);
+ if(bezt->f3 & flag) VecAddf(bezt->vec[2], bezt->vec[2], vec);
+ bezt++;
+ }
+ }
+ else {
+ a= nu->pntsu*nu->pntsv;
+ bp= nu->bp;
+ while(a--) {
+ if(bp->f1 & flag) VecAddf(bp->vec, bp->vec, vec);
+ bp++;
+ }
+ }
+
+ test2DNurb(nu);
+ }
+}
+
+static void weightflagNurb(ListBase *editnurb, short flag, float w, int mode) /* mode==0: replace, mode==1: multiply */
+{
+ Nurb *nu;
+ BPoint *bp;
+ int a;
+
+ for(nu= editnurb->first; nu; nu= nu->next) {
+ if((nu->type & 7)==CU_NURBS) {
+ a= nu->pntsu*nu->pntsv;
+ bp= nu->bp;
+ while(a--) {
+ if(bp->f1 & flag) {
+ if(mode==1) bp->vec[3]*= w;
+ else bp->vec[3]= w;
+ }
+ bp++;
+ }
+ }
+ }
+}
+
+static int deleteflagNurb(bContext *C, wmOperator *op, int flag)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ Curve *cu= obedit->data;
+ ListBase *editnurb= curve_get_editcurve(obedit);
+ Nurb *nu, *next;
+ BPoint *bp, *bpn, *newbp;
+ int a, b, newu, newv, sel;
+
+ if(obedit && obedit->type==OB_SURF);
+ else return OPERATOR_CANCELLED;
+
+ cu->lastselbp= NULL;
+
+ nu= editnurb->first;
+ while(nu) {
+ next= nu->next;
+
+ /* is entire nurb selected */
+ bp= nu->bp;
+ a= nu->pntsu*nu->pntsv;
+ while(a) {
+ a--;
+ if(bp->f1 & flag);
+ else break;
+ bp++;
+ }
+ if(a==0) {
+ BLI_remlink(editnurb, nu);
+ freeNurb(nu); nu=NULL;
+ }
+ else {
+ /* is nurb in U direction selected */
+ newv= nu->pntsv;
+ bp= nu->bp;
+ for(b=0; b<nu->pntsv; b++) {
+ sel= 0;
+ for(a=0; a<nu->pntsu; a++, bp++) {
+ if(bp->f1 & flag) sel++;
+ }
+ if(sel==nu->pntsu) {
+ newv--;
+ }
+ else if(sel>=1) {
+ /* don't delete */
+ break;
+ }
+ }
+ if(newv!=nu->pntsv && b==nu->pntsv) {
+ /* delete */
+ bp= nu->bp;
+ bpn = newbp =
+ (BPoint*) MEM_mallocN(newv * nu->pntsu * sizeof(BPoint), "deleteNurb");
+ for(b=0; b<nu->pntsv; b++) {
+ if((bp->f1 & flag)==0) {
+ memcpy(bpn, bp, nu->pntsu*sizeof(BPoint));
+ bpn+= nu->pntsu;
+ }
+ bp+= nu->pntsu;
+ }
+ nu->pntsv= newv;
+ MEM_freeN(nu->bp);
+ nu->bp= newbp;
+ clamp_nurb_order_v(nu);
+
+ makeknots(nu, 2);
+ }
+ else {
+ /* is the nurb in V direction selected */
+ newu= nu->pntsu;
+ for(a=0; a<nu->pntsu; a++) {
+ bp= nu->bp+a;
+ sel= 0;
+ for(b=0; b<nu->pntsv; b++, bp+=nu->pntsu) {
+ if(bp->f1 & flag) sel++;
+ }
+ if(sel==nu->pntsv) {
+ newu--;
+ }
+ else if(sel>=1) {
+ /* don't delete */
+ break;
+ }
+ }
+ if(newu!=nu->pntsu && a==nu->pntsu) {
+ /* delete */
+ bp= nu->bp;
+ bpn = newbp =
+ (BPoint*) MEM_mallocN(newu * nu->pntsv * sizeof(BPoint), "deleteNurb");
+ for(b=0; b<nu->pntsv; b++) {
+ for(a=0; a<nu->pntsu; a++, bp++) {
+ if((bp->f1 & flag)==0) {
+ *bpn= *bp;
+ bpn++;
+ }
+ }
+ }
+ MEM_freeN(nu->bp);
+ nu->bp= newbp;
+ if(newu==1 && nu->pntsv>1) { /* make a U spline */
+ nu->pntsu= nu->pntsv;
+ nu->pntsv= 1;
+ SWAP(short, nu->orderu, nu->orderv);
+ clamp_nurb_order_u(nu);
+ if(nu->knotsv) MEM_freeN(nu->knotsv);
+ nu->knotsv= NULL;
+ }
+ else {
+ nu->pntsu= newu;
+ clamp_nurb_order_u(nu);
+ }
+ makeknots(nu, 1);
+ }
+ }
+ }
+ nu= next;
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+/* only for OB_SURF */
+static short extrudeflagNurb(ListBase *editnurb, int flag)
+{
+ Nurb *nu;
+ BPoint *bp, *bpn, *newbp;
+ int ok= 0, a, u, v, len;
+
+ nu= editnurb->first;
+ while(nu) {
+
+ if(nu->pntsv==1) {
+ bp= nu->bp;
+ a= nu->pntsu;
+ while(a) {
+ if(bp->f1 & flag);
+ else break;
+ bp++;
+ a--;
+ }
+ if(a==0) {
+ ok= 1;
+ newbp =
+ (BPoint*)MEM_mallocN(2 * nu->pntsu * sizeof(BPoint), "extrudeNurb1");
+ memcpy(newbp, nu->bp, nu->pntsu*sizeof(BPoint) );
+ bp= newbp+ nu->pntsu;
+ memcpy(bp, nu->bp, nu->pntsu*sizeof(BPoint) );
+ MEM_freeN(nu->bp);
+ nu->bp= newbp;
+ a= nu->pntsu;
+ while(a--) {
+ select_bpoint(bp, SELECT, flag, HIDDEN);
+ select_bpoint(newbp, DESELECT, flag, HIDDEN);
+ bp++;
+ newbp++;
+ }
+
+ nu->pntsv= 2;
+ nu->orderv= 2;
+ makeknots(nu, 2);
+ }
+ }
+ else {
+ /* which row or collumn is selected */
+
+ if( isNurbselUV(nu, &u, &v, flag) ) {
+
+ /* deselect all */
+ bp= nu->bp;
+ a= nu->pntsu*nu->pntsv;
+ while(a--) {
+ select_bpoint(bp, DESELECT, flag, HIDDEN);
+ bp++;
+ }
+
+ if(u==0 || u== nu->pntsv-1) { /* row in u-direction selected */
+ ok= 1;
+ newbp =
+ (BPoint*) MEM_mallocN(nu->pntsu*(nu->pntsv + 1)
+ * sizeof(BPoint), "extrudeNurb1");
+ if(u==0) {
+ len= nu->pntsv*nu->pntsu;
+ memcpy(newbp+nu->pntsu, nu->bp, len*sizeof(BPoint) );
+ memcpy(newbp, nu->bp, nu->pntsu*sizeof(BPoint) );
+ bp= newbp;
+ }
+ else {
+ len= nu->pntsv*nu->pntsu;
+ memcpy(newbp, nu->bp, len*sizeof(BPoint) );
+ memcpy(newbp+len, nu->bp+len-nu->pntsu, nu->pntsu*sizeof(BPoint) );
+ bp= newbp+len;
+ }
+
+ a= nu->pntsu;
+ while(a--) {
+ select_bpoint(bp, SELECT, flag, HIDDEN);
+ bp++;
+ }
+
+ MEM_freeN(nu->bp);
+ nu->bp= newbp;
+ nu->pntsv++;
+ makeknots(nu, 2);
+ }
+ else if(v==0 || v== nu->pntsu-1) { /* collumn in v-direction selected */
+ ok= 1;
+ bpn = newbp =
+ (BPoint*) MEM_mallocN((nu->pntsu + 1) * nu->pntsv * sizeof(BPoint), "extrudeNurb1");
+ bp= nu->bp;
+
+ for(a=0; a<nu->pntsv; a++) {
+ if(v==0) {
+ *bpn= *bp;
+ bpn->f1 |= flag;
+ bpn++;
+ }
+ memcpy(bpn, bp, nu->pntsu*sizeof(BPoint));
+ bp+= nu->pntsu;
+ bpn+= nu->pntsu;
+ if(v== nu->pntsu-1) {
+ *bpn= *(bp-1);
+ bpn->f1 |= flag;
+ bpn++;
+ }
+ }
+
+ MEM_freeN(nu->bp);
+ nu->bp= newbp;
+ nu->pntsu++;
+ makeknots(nu, 1);
+ }
+ }
+ }
+ nu= nu->next;
+ }
+
+ return ok;
+}
+
+static void adduplicateflagNurb(Object *obedit, short flag)
+{
+ ListBase *editnurb= curve_get_editcurve(obedit);
+ Nurb *nu, *newnu;
+ BezTriple *bezt, *bezt1;
+ BPoint *bp, *bp1;
+ int a, b, starta, enda, newu, newv;
+ char *usel;
+
+ nu= editnurb->last;
+ while(nu) {
+ if( (nu->type & 7)==CU_BEZIER) {
+ bezt= nu->bezt;
+ for(a=0; a<nu->pntsu; a++) {
+ enda= -1;
+ starta= a;
+ while( (bezt->f1 & flag) || (bezt->f2 & flag) || (bezt->f3 & flag) ) {
+ select_beztriple(bezt, DESELECT, flag, HIDDEN);
+ enda=a;
+ if(a>=nu->pntsu-1) break;
+ a++;
+ bezt++;
+ }
+ if(enda>=starta) {
+ newnu = (Nurb*)MEM_mallocN(sizeof(Nurb), "adduplicateN");
+ memcpy(newnu, nu, sizeof(Nurb));
+ BLI_addtail(editnurb, newnu);
+ set_actNurb(obedit, newnu);
+ newnu->pntsu= enda-starta+1;
+ newnu->bezt=
+ (BezTriple*)MEM_mallocN((enda - starta + 1) * sizeof(BezTriple), "adduplicateN");
+ memcpy(newnu->bezt, nu->bezt+starta, newnu->pntsu*sizeof(BezTriple));
+
+ b= newnu->pntsu;
+ bezt1= newnu->bezt;
+ while(b--) {
+ select_beztriple(bezt1, SELECT, flag, HIDDEN);
+ bezt1++;
+ }
+
+ if(nu->flagu & CU_CYCLIC) {
+ if(starta!=0 || enda!=nu->pntsu-1) {
+ newnu->flagu &= ~CU_CYCLIC;
+ }
+ }
+ }
+ bezt++;
+ }
+ }
+ else if(nu->pntsv==1) { /* because UV Nurb has a different method for dupli */
+ bp= nu->bp;
+ for(a=0; a<nu->pntsu; a++) {
+ enda= -1;
+ starta= a;
+ while(bp->f1 & flag) {
+ select_bpoint(bp, DESELECT, flag, HIDDEN);
+ enda= a;
+ if(a>=nu->pntsu-1) break;
+ a++;
+ bp++;
+ }
+ if(enda>=starta) {
+ newnu = (Nurb*)MEM_mallocN(sizeof(Nurb), "adduplicateN3");
+ memcpy(newnu, nu, sizeof(Nurb));
+ set_actNurb(obedit, newnu);
+ BLI_addtail(editnurb, newnu);
+ newnu->pntsu= enda-starta+1;
+ newnu->bp = (BPoint*)MEM_mallocN((enda-starta+1) * sizeof(BPoint), "adduplicateN4");
+ memcpy(newnu->bp, nu->bp+starta, newnu->pntsu*sizeof(BPoint));
+
+ b= newnu->pntsu;
+ bp1= newnu->bp;
+ while(b--) {
+ select_bpoint(bp1, SELECT, flag, HIDDEN);
+ bp1++;
+ }
+
+ if(nu->flagu & CU_CYCLIC) {
+ if(starta!=0 || enda!=nu->pntsu-1) {
+ newnu->flagu &= ~CU_CYCLIC;
+ }
+ }
+
+ /* knots */
+ newnu->knotsu= NULL;
+ makeknots(newnu, 1);
+ }
+ bp++;
+ }
+ }
+ else {
+ /* a rectangular area in nurb has to be selected */
+ if(isNurbsel(nu)) {
+ usel= MEM_callocN(nu->pntsu, "adduplicateN4");
+ bp= nu->bp;
+ for(a=0; a<nu->pntsv; a++) {
+ for(b=0; b<nu->pntsu; b++, bp++) {
+ if(bp->f1 & flag) usel[b]++;
+ }
+ }
+ newu= 0;
+ newv= 0;
+ for(a=0; a<nu->pntsu; a++) {
+ if(usel[a]) {
+ if(newv==0 || usel[a]==newv) {
+ newv= usel[a];
+ newu++;
+ }
+ else {
+ newv= 0;
+ break;
+ }
+ }
+ }
+ if(newu==0 || newv==0) {
+ printf("Can't duplicate Nurb\n");
+ }
+ else {
+
+ if(newu==1) SWAP(short, newu, newv);
+
+ newnu = (Nurb*)MEM_mallocN(sizeof(Nurb), "adduplicateN5");
+ memcpy(newnu, nu, sizeof(Nurb));
+ BLI_addtail(editnurb, newnu);
+ set_actNurb(obedit, newnu);
+ newnu->pntsu= newu;
+ newnu->pntsv= newv;
+ newnu->bp =
+ (BPoint*)MEM_mallocN(newu * newv * sizeof(BPoint), "adduplicateN6");
+ clamp_nurb_order_u(newnu);
+ clamp_nurb_order_v(newnu);
+
+ newnu->knotsu= newnu->knotsv= NULL;
+
+ bp= newnu->bp;
+ bp1= nu->bp;
+ for(a=0; a<nu->pntsv; a++) {
+ for(b=0; b<nu->pntsu; b++, bp1++) {
+ if(bp1->f1 & flag) {
+ memcpy(bp, bp1, sizeof(BPoint));
+ select_bpoint(bp1, DESELECT, flag, HIDDEN);
+ bp++;
+ }
+ }
+ }
+ if (check_valid_nurb_u(newnu)) {
+ if(nu->pntsu==newnu->pntsu && nu->knotsu) {
+ newnu->knotsu= MEM_dupallocN( nu->knotsu );
+ } else {
+ makeknots(newnu, 1);
+ }
+ }
+ if (check_valid_nurb_v(newnu)) {
+ if(nu->pntsv==newnu->pntsv && nu->knotsv) {
+ newnu->knotsv= MEM_dupallocN( nu->knotsv );
+ } else {
+ makeknots(newnu, 2);
+ }
+ }
+ }
+ MEM_freeN(usel);
+ }
+ }
+
+ nu= nu->prev;
+ }
+
+ /* actnu changed */
+}
+
+/**************** switch direction operator ***************/
+
+static int switch_direction_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ ListBase *editnurb= curve_get_editcurve(obedit);
+ Nurb *nu;
+
+ for(nu= editnurb->first; nu; nu= nu->next)
+ if(isNurbsel(nu))
+ switchdirectionNurb(nu);
+
+ DAG_object_flush_update(CTX_data_scene(C), obedit, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_switch_direction(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Switch Direction";
+ ot->idname= "CURVE_OT_switch_direction";
+
+ /* api callbacks */
+ ot->exec= switch_direction_exec;
+ ot->poll= ED_operator_editsurfcurve;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/****************** set weight operator *******************/
+
+static int set_weight_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ ListBase *editnurb= curve_get_editcurve(obedit);
+ Nurb *nu;
+ BezTriple *bezt;
+ BPoint *bp;
+ float weight= RNA_float_get(op->ptr, "weight");
+ int a;
+
+ for(nu= editnurb->first; nu; nu= nu->next) {
+ if(nu->bezt) {
+ for(bezt=nu->bezt, a=0; a<nu->pntsu; a++, bezt++) {
+ if(bezt->f2 & SELECT)
+ bezt->weight= weight;
+ }
+ }
+ else if(nu->bp) {
+ for(bp=nu->bp, a=0; a<nu->pntsu*nu->pntsv; a++, bp++) {
+ if(bp->f1 & SELECT)
+ bp->weight= weight;
+ }
+ }
+ }
+
+ DAG_object_flush_update(CTX_data_scene(C), obedit, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_spline_weight_set(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Set Curve Weight";
+ ot->idname= "CURVE_OT_spline_weight_set";
+
+ /* api callbacks */
+ ot->exec= set_weight_exec;
+ ot->invoke= WM_operator_redo;
+ ot->poll= ED_operator_editsurfcurve;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_float_percentage(ot->srna, "weight", 1.0f, 0.0f, 1.0f, "Weight", "", 0.0f, 1.0f);
+}
+
+/******************* set radius operator ******************/
+
+static int set_radius_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ ListBase *editnurb= curve_get_editcurve(obedit);
+ Nurb *nu;
+ BezTriple *bezt;
+ BPoint *bp;
+ float radius= RNA_float_get(op->ptr, "radius");
+ int a;
+
+ for(nu= editnurb->first; nu; nu= nu->next) {
+ if(nu->bezt) {
+ for(bezt=nu->bezt, a=0; a<nu->pntsu; a++, bezt++) {
+ if(bezt->f2 & SELECT)
+ bezt->radius= radius;
+ }
+ }
+ else if(nu->bp) {
+ for(bp=nu->bp, a=0; a<nu->pntsu*nu->pntsv; a++, bp++) {
+ if(bp->f1 & SELECT)
+ bp->radius= radius;
+ }
+ }
+ }
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit);
+ DAG_object_flush_update(CTX_data_scene(C), obedit, OB_RECALC_DATA);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_radius_set(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Set Curve Radius";
+ ot->idname= "CURVE_OT_radius_set";
+
+ /* api callbacks */
+ ot->exec= set_radius_exec;
+ ot->invoke= WM_operator_redo;
+ ot->poll= ED_operator_editsurfcurve;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_float(ot->srna, "radius", 1.0f, 0.0f, FLT_MAX, "Radius", "", 0.0001f, 10.0f);
+}
+
+/********************* smooth operator ********************/
+
+static int smooth_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ ListBase *editnurb= curve_get_editcurve(obedit);
+ Nurb *nu;
+ BezTriple *bezt, *beztOrig;
+ BPoint *bp, *bpOrig;
+ float val, newval, offset;
+ int a, i, change = 0;
+
+ for(nu= editnurb->first; nu; nu= nu->next) {
+ if(nu->bezt) {
+ change = 0;
+ beztOrig = MEM_dupallocN( nu->bezt );
+ for(bezt=nu->bezt+1, a=1; a<nu->pntsu-1; a++, bezt++) {
+ if(bezt->f2 & SELECT) {
+ for(i=0; i<3; i++) {
+ val = bezt->vec[1][i];
+ newval = ((beztOrig+(a-1))->vec[1][i] * 0.5) + ((beztOrig+(a+1))->vec[1][i] * 0.5);
+ offset = (val*((1.0/6.0)*5)) + (newval*(1.0/6.0)) - val;
+ /* offset handles */
+ bezt->vec[1][i] += offset;
+ bezt->vec[0][i] += offset;
+ bezt->vec[2][i] += offset;
+ }
+ change = 1;
+ }
+ }
+ MEM_freeN(beztOrig);
+ if (change)
+ calchandlesNurb(nu);
+ } else if (nu->bp) {
+ bpOrig = MEM_dupallocN( nu->bp );
+ /* Same as above, keep these the same! */
+ for(bp=nu->bp+1, a=1; a<nu->pntsu-1; a++, bp++) {
+ if(bp->f1 & SELECT) {
+ for(i=0; i<3; i++) {
+ val = bp->vec[i];
+ newval = ((bpOrig+(a-1))->vec[i] * 0.5) + ((bpOrig+(a+1))->vec[i] * 0.5);
+ offset = (val*((1.0/6.0)*5)) + (newval*(1.0/6.0)) - val;
+
+ bp->vec[i] += offset;
+ }
+ }
+ }
+ MEM_freeN(bpOrig);
+ }
+ }
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit);
+ DAG_object_flush_update(CTX_data_scene(C), obedit, OB_RECALC_DATA);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_smooth(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Smooth";
+ ot->idname= "CURVE_OT_smooth";
+
+ /* api callbacks */
+ ot->exec= smooth_exec;
+ ot->poll= ED_operator_editsurfcurve;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/**************** smooth curve radius operator *************/
+
+/* TODO, make smoothing distance based */
+static int smooth_radius_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ ListBase *editnurb= curve_get_editcurve(obedit);
+ Nurb *nu;
+ BezTriple *bezt;
+ BPoint *bp;
+ int a;
+
+ /* use for smoothing */
+ int last_sel;
+ int start_sel, end_sel; /* selection indicies, inclusive */
+ float start_rad, end_rad, fac, range;
+
+ for(nu= editnurb->first; nu; nu= nu->next) {
+ if(nu->bezt) {
+
+ for (last_sel=0; last_sel < nu->pntsu; last_sel++) {
+ /* loop over selection segments of a curve, smooth each */
+
+ /* Start BezTriple code, this is duplicated below for points, make sure these functions stay in sync */
+ start_sel = end_sel = -1;
+ for(bezt=nu->bezt+last_sel, a=last_sel; a<nu->pntsu; a++, bezt++) {
+ if(bezt->f2 & SELECT) {
+ start_sel = a;
+ break;
+ }
+ }
+ /* incase there are no other selected verts */
+ end_sel = start_sel;
+ for(bezt=nu->bezt+(start_sel+1), a=start_sel+1; a<nu->pntsu; a++, bezt++) {
+ if((bezt->f2 & SELECT)==0) {
+ break;
+ }
+ end_sel = a;
+ }
+
+ if (start_sel == -1) {
+ last_sel = nu->pntsu; /* next... */
+ } else {
+ last_sel = end_sel; /* before we modify it */
+
+ /* now blend between start and end sel */
+ start_rad = end_rad = -1.0;
+
+ if (start_sel == end_sel) {
+ /* simple, only 1 point selected */
+ if (start_sel>0) start_rad = (nu->bezt+start_sel-1)->radius;
+ if (end_sel!=-1 && end_sel < nu->pntsu) end_rad = (nu->bezt+start_sel+1)->radius;
+
+ if (start_rad >= 0.0 && end_rad >= 0.0) (nu->bezt+start_sel)->radius = (start_rad + end_rad)/2;
+ else if (start_rad >= 0.0) (nu->bezt+start_sel)->radius = start_rad;
+ else if (end_rad >= 0.0) (nu->bezt+start_sel)->radius = end_rad;
+ } else {
+ /* if endpoints selected, then use them */
+ if (start_sel==0) {
+ start_rad = (nu->bezt+start_sel)->radius;
+ start_sel++; /* we dont want to edit the selected endpoint */
+ } else {
+ start_rad = (nu->bezt+start_sel-1)->radius;
+ }
+ if (end_sel==nu->pntsu-1) {
+ end_rad = (nu->bezt+end_sel)->radius;
+ end_sel--; /* we dont want to edit the selected endpoint */
+ } else {
+ end_rad = (nu->bezt+end_sel+1)->radius;
+ }
+
+ /* Now Blend between the points */
+ range = (float)(end_sel - start_sel) + 2.0f;
+ for(bezt=nu->bezt+start_sel, a=start_sel; a<=end_sel; a++, bezt++) {
+ fac = (float)(1+a-start_sel) / range;
+ bezt->radius = start_rad*(1.0-fac) + end_rad*fac;
+ }
+ }
+ }
+ }
+ } else if (nu->bp) {
+ /* Same as above, keep these the same! */
+ for (last_sel=0; last_sel < nu->pntsu; last_sel++) {
+ /* loop over selection segments of a curve, smooth each */
+
+ /* Start BezTriple code, this is duplicated below for points, make sure these functions stay in sync */
+ start_sel = end_sel = -1;
+ for(bp=nu->bp+last_sel, a=last_sel; a<nu->pntsu; a++, bp++) {
+ if(bp->f1 & SELECT) {
+ start_sel = a;
+ break;
+ }
+ }
+ /* incase there are no other selected verts */
+ end_sel = start_sel;
+ for(bp=nu->bp+(start_sel+1), a=start_sel+1; a<nu->pntsu; a++, bp++) {
+ if((bp->f1 & SELECT)==0) {
+ break;
+ }
+ end_sel = a;
+ }
+
+ if (start_sel == -1) {
+ last_sel = nu->pntsu; /* next... */
+ } else {
+ last_sel = end_sel; /* before we modify it */
+
+ /* now blend between start and end sel */
+ start_rad = end_rad = -1.0;
+
+ if (start_sel == end_sel) {
+ /* simple, only 1 point selected */
+ if (start_sel>0) start_rad = (nu->bp+start_sel-1)->radius;
+ if (end_sel!=-1 && end_sel < nu->pntsu) end_rad = (nu->bp+start_sel+1)->radius;
+
+ if (start_rad >= 0.0 && end_rad >= 0.0) (nu->bp+start_sel)->radius = (start_rad + end_rad)/2;
+ else if (start_rad >= 0.0) (nu->bp+start_sel)->radius = start_rad;
+ else if (end_rad >= 0.0) (nu->bp+start_sel)->radius = end_rad;
+ } else {
+ /* if endpoints selected, then use them */
+ if (start_sel==0) {
+ start_rad = (nu->bp+start_sel)->radius;
+ start_sel++; /* we dont want to edit the selected endpoint */
+ } else {
+ start_rad = (nu->bp+start_sel-1)->radius;
+ }
+ if (end_sel==nu->pntsu-1) {
+ end_rad = (nu->bp+end_sel)->radius;
+ end_sel--; /* we dont want to edit the selected endpoint */
+ } else {
+ end_rad = (nu->bp+end_sel+1)->radius;
+ }
+
+ /* Now Blend between the points */
+ range = (float)(end_sel - start_sel) + 2.0f;
+ for(bp=nu->bp+start_sel, a=start_sel; a<=end_sel; a++, bp++) {
+ fac = (float)(1+a-start_sel) / range;
+ bp->radius = start_rad*(1.0-fac) + end_rad*fac;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit);
+ DAG_object_flush_update(CTX_data_scene(C), obedit, OB_RECALC_DATA);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_smooth_radius(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Smooth Curve Radius";
+ ot->idname= "CURVE_OT_smooth_radius";
+
+ /* api clastbacks */
+ ot->exec= smooth_radius_exec;
+ ot->poll= ED_operator_editsurfcurve;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/***************** selection utility *************************/
+
+/* next == 1 -> select next */
+/* next == -1 -> select previous */
+/* cont == 1 -> select continuously */
+/* selstatus, inverts behaviour */
+static void select_adjacent_cp(ListBase *editnurb, short next, short cont, short selstatus)
+{
+ Nurb *nu;
+ BezTriple *bezt;
+ BPoint *bp;
+ int a;
+ short lastsel= 0, sel=0;
+
+ if(next==0) return;
+
+ for(nu= editnurb->first; nu; nu= nu->next) {
+ lastsel=0;
+ if((nu->type & 7)==CU_BEZIER) {
+ a= nu->pntsu;
+ bezt= nu->bezt;
+ if(next < 0) bezt= (nu->bezt + (a-1));
+ while(a--) {
+ if(a-abs(next) < 0) break;
+ sel= 0;
+ if((lastsel==0) && (bezt->hide==0) && ((bezt->f2 & SELECT) || (selstatus==0))) {
+ bezt+=next;
+ if(!(bezt->f2 & SELECT) || (selstatus==0)) {
+ sel= select_beztriple(bezt, selstatus, 1, VISIBLE);
+ if((sel==1) && (cont==0)) lastsel= 1;
+ }
+ }
+ else {
+ bezt+=next;
+ lastsel= 0;
+ }
+ /* move around in zigzag way so that we go through each */
+ bezt-=(next-next/abs(next));
+ }
+ }
+ else {
+ a= nu->pntsu*nu->pntsv;
+ bp= nu->bp;
+ if(next < 0) bp= (nu->bp + (a-1));
+ while(a--) {
+ if(a-abs(next) < 0) break;
+ sel=0;
+ if((lastsel==0) && (bp->hide==0) && ((bp->f1 & SELECT) || (selstatus==0))) {
+ bp+=next;
+ if(!(bp->f1 & SELECT) || (selstatus==0)) {
+ sel= select_bpoint(bp, selstatus, 1, VISIBLE);
+ if((sel==1) && (cont==0)) lastsel= 1;
+ }
+ }
+ else {
+ bp+=next;
+ lastsel= 0;
+ }
+ /* move around in zigzag way so that we go through each */
+ bp-=(next-next/abs(next));
+ }
+ }
+ }
+}
+
+/**************** select start/end operators **************/
+
+/* (de)selects first or last of visible part of each Nurb depending on selFirst */
+/* selFirst: defines the end of which to select */
+/* doswap: defines if selection state of each first/last control point is swapped */
+/* selstatus: selection status in case doswap is false */
+void selectend_nurb(Object *obedit, short selfirst, short doswap, short selstatus)
+{
+ ListBase *editnurb= curve_get_editcurve(obedit);
+ Nurb *nu;
+ BPoint *bp;
+ BezTriple *bezt;
+ int a;
+ short sel;
+
+ if(obedit==0) return;
+
+ for(nu= editnurb->first; nu; nu= nu->next) {
+ sel= 0;
+ if((nu->type & 7)==CU_BEZIER) {
+ a= nu->pntsu;
+
+ /* which point? */
+ if(selfirst==0) { /* select last */
+ bezt= (nu->bezt + (a-1));
+ }
+ else { /* select first */
+ bezt= nu->bezt;
+ }
+
+ while(a--) {
+ if(doswap) sel= swap_selection_beztriple(bezt);
+ else sel= select_beztriple(bezt, selstatus, 1, VISIBLE);
+
+ if(sel==1) break;
+ }
+ }
+ else {
+ a= nu->pntsu*nu->pntsv;
+
+ /* which point? */
+ if(selfirst==0) { /* select last */
+ bp= (nu->bp + (a-1));
+ }
+ else{ /* select first */
+ bp= nu->bp;
+ }
+
+ while(a--) {
+ if (bp->hide == 0) {
+ if(doswap) sel= swap_selection_bpoint(bp);
+ else sel= select_bpoint(bp, selstatus, 1, VISIBLE);
+
+ if(sel==1) break;
+ }
+ }
+ }
+ }
+}
+
+static int de_select_first_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit= CTX_data_edit_object(C);
+
+ selectend_nurb(obedit, FIRST, 1, DESELECT);
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_de_select_first(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Select or Deselect First";
+ ot->idname= "CURVE_OT_de_select_first";
+
+ /* api cfirstbacks */
+ ot->exec= de_select_first_exec;
+ ot->poll= ED_operator_editcurve;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+static int de_select_last_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit= CTX_data_edit_object(C);
+
+ selectend_nurb(obedit, LAST, 1, DESELECT);
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_de_select_last(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Select or Deselect Last";
+ ot->idname= "CURVE_OT_de_select_last";
+
+ /* api clastbacks */
+ ot->exec= de_select_last_exec;
+ ot->poll= ED_operator_editcurve;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/******************* de select all operator ***************/
+
+static short nurb_has_selected_cps(ListBase *editnurb)
+{
+ Nurb *nu;
+ BezTriple *bezt;
+ BPoint *bp;
+ int a;
+
+ for(nu= editnurb->first; nu; nu= nu->next) {
+ if((nu->type & 7)==CU_BEZIER) {
+ a= nu->pntsu;
+ bezt= nu->bezt;
+ while(a--) {
+ if(bezt->hide==0) {
+ if((bezt->f1 & SELECT)
+ || (bezt->f2 & SELECT)
+ || (bezt->f3 & SELECT)) return 1;
+ }
+ bezt++;
+ }
+ }
+ else {
+ a= nu->pntsu*nu->pntsv;
+ bp= nu->bp;
+ while(a--) {
+ if((bp->hide==0) && (bp->f1 & SELECT)) return 1;
+ bp++;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int de_select_all_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ ListBase *editnurb= curve_get_editcurve(obedit);
+
+ if(nurb_has_selected_cps(editnurb)) { /* deselect all */
+ selectend_nurb(obedit, FIRST, 0, DESELECT); /* set first control points as unselected */
+ select_adjacent_cp(editnurb, 1, 1, DESELECT); /* cascade selection */
+ }
+ else { /* select all */
+ selectend_nurb(obedit, FIRST, 0, SELECT); /* set first control points as selected */
+ select_adjacent_cp(editnurb, 1, 1, SELECT); /* cascade selection */
+ }
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_select_all_toggle(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Select or Deselect All";
+ ot->idname= "CURVE_OT_select_all_toggle";
+
+ /* api callbacks */
+ ot->exec= de_select_all_exec;
+ ot->poll= ED_operator_editsurfcurve;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/********************** hide operator *********************/
+
+static int hide_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ ListBase *editnurb= curve_get_editcurve(obedit);
+ Nurb *nu;
+ BPoint *bp;
+ BezTriple *bezt;
+ int a, sel, invert= RNA_boolean_get(op->ptr, "unselected");
+
+ for(nu= editnurb->first; nu; nu= nu->next) {
+ if((nu->type & 7)==CU_BEZIER) {
+ bezt= nu->bezt;
+ a= nu->pntsu;
+ sel= 0;
+ while(a--) {
+ if(invert == 0 && BEZSELECTED_HIDDENHANDLES(bezt)) {
+ select_beztriple(bezt, DESELECT, 1, HIDDEN);
+ bezt->hide= 1;
+ }
+ else if(invert && !BEZSELECTED_HIDDENHANDLES(bezt)) {
+ select_beztriple(bezt, DESELECT, 1, HIDDEN);
+ bezt->hide= 1;
+ }
+ if(bezt->hide) sel++;
+ bezt++;
+ }
+ if(sel==nu->pntsu) nu->hide= 1;
+ }
+ else {
+ bp= nu->bp;
+ a= nu->pntsu*nu->pntsv;
+ sel= 0;
+ while(a--) {
+ if(invert==0 && (bp->f1 & SELECT)) {
+ select_bpoint(bp, DESELECT, 1, HIDDEN);
+ bp->hide= 1;
+ }
+ else if(invert && (bp->f1 & SELECT)==0) {
+ select_bpoint(bp, DESELECT, 1, HIDDEN);
+ bp->hide= 1;
+ }
+ if(bp->hide) sel++;
+ bp++;
+ }
+ if(sel==nu->pntsu*nu->pntsv) nu->hide= 1;
+ }
+ }
+
+ DAG_object_flush_update(CTX_data_scene(C), obedit, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_hide(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Hide Selected";
+ ot->idname= "CURVE_OT_hide";
+
+ /* api callbacks */
+ ot->exec= hide_exec;
+ ot->poll= ED_operator_editsurfcurve;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* props */
+ RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected.");
+}
+
+/********************** reveal operator *********************/
+
+static int reveal_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ ListBase *editnurb= curve_get_editcurve(obedit);
+ Nurb *nu;
+ BPoint *bp;
+ BezTriple *bezt;
+ int a;
+
+ for(nu= editnurb->first; nu; nu= nu->next) {
+ nu->hide= 0;
+ if((nu->type & 7)==CU_BEZIER) {
+ bezt= nu->bezt;
+ a= nu->pntsu;
+ while(a--) {
+ if(bezt->hide) {
+ select_beztriple(bezt, SELECT, 1, HIDDEN);
+ bezt->hide= 0;
+ }
+ bezt++;
+ }
+ }
+ else {
+ bp= nu->bp;
+ a= nu->pntsu*nu->pntsv;
+ while(a--) {
+ if(bp->hide) {
+ select_bpoint(bp, SELECT, 1, HIDDEN);
+ bp->hide= 0;
+ }
+ bp++;
+ }
+ }
+ }
+
+ DAG_object_flush_update(CTX_data_scene(C), obedit, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_reveal(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Reveal Hidden";
+ ot->idname= "CURVE_OT_reveal";
+
+ /* api callbacks */
+ ot->exec= reveal_exec;
+ ot->poll= ED_operator_editsurfcurve;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/********************** select invert operator *********************/
+
+static int select_invert_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ ListBase *editnurb= curve_get_editcurve(obedit);
+ Nurb *nu;
+ BPoint *bp;
+ BezTriple *bezt;
+ int a;
+
+ for(nu= editnurb->first; nu; nu= nu->next) {
+ if((nu->type & 7)==CU_BEZIER) {
+ bezt= nu->bezt;
+ a= nu->pntsu;
+ while(a--) {
+ if(bezt->hide==0) {
+ bezt->f2 ^= SELECT; /* always do the center point */
+ if ((G.f & G_HIDDENHANDLES)==0) {
+ bezt->f1 ^= SELECT;
+ bezt->f3 ^= SELECT;
+ }
+ }
+ bezt++;
+ }
+ }
+ else {
+ bp= nu->bp;
+ a= nu->pntsu*nu->pntsv;
+ while(a--) {
+ swap_selection_bpoint(bp);
+ bp++;
+ }
+ }
+ }
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_select_invert(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Select Invert";
+ ot->idname= "CURVE_OT_select_invert";
+
+ /* api callbacks */
+ ot->exec= select_invert_exec;
+ ot->poll= ED_operator_editsurfcurve;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/********************** subdivide operator *********************/
+
+/** Divide the line segments associated with the currently selected
+ * curve nodes (Bezier or NURB). If there are no valid segment
+ * selections within the current selection, nothing happens.
+ *
+ * @deffunc subdividenurb subdivideNurb(void)
+ * @return Nothing
+ * @param None
+*/
+
+static int subdivide_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ ListBase *editnurb= curve_get_editcurve(obedit);
+ Nurb *nu;
+ BezTriple *prevbezt, *bezt, *beztnew, *beztn;
+ BPoint *bp, *prevbp, *bpnew, *bpn;
+ float vec[15];
+ int a, b, sel, amount, *usel, *vsel;
+
+ // printf("*** subdivideNurb: entering subdivide\n");
+
+ for(nu= editnurb->first; nu; nu= nu->next) {
+ amount= 0;
+ if((nu->type & 7)==CU_BEZIER) {
+ /*
+ Insert a point into a 2D Bezier curve.
+ Endpoints are preserved. Otherwise, all selected and inserted points are
+ newly created. Old points are discarded.
+ */
+ /* count */
+ if(nu->flagu & CU_CYCLIC) {
+ a= nu->pntsu;
+ bezt= nu->bezt;
+ prevbezt= bezt+(a-1);
+ }
+ else {
+ a= nu->pntsu-1;
+ prevbezt= nu->bezt;
+ bezt= prevbezt+1;
+ }
+ while(a--) {
+ if( BEZSELECTED_HIDDENHANDLES(prevbezt) && BEZSELECTED_HIDDENHANDLES(bezt) ) amount++;
+ prevbezt= bezt;
+ bezt++;
+ }
+
+ if(amount) {
+ /* insert */
+ beztnew =
+ (BezTriple*)MEM_mallocN((amount + nu->pntsu) * sizeof(BezTriple), "subdivNurb");
+ beztn= beztnew;
+ if(nu->flagu & CU_CYCLIC) {
+ a= nu->pntsu;
+ bezt= nu->bezt;
+ prevbezt= bezt+(a-1);
+ }
+ else {
+ a= nu->pntsu-1;
+ prevbezt= nu->bezt;
+ bezt= prevbezt+1;
+ }
+ while(a--) {
+ memcpy(beztn, prevbezt, sizeof(BezTriple));
+ beztn++;
+
+ if( BEZSELECTED_HIDDENHANDLES(prevbezt) && BEZSELECTED_HIDDENHANDLES(bezt) ) {
+ memcpy(beztn, bezt, sizeof(BezTriple));
+
+ /* midpoint subdividing */
+ VecMidf(vec, prevbezt->vec[1], prevbezt->vec[2]);
+ VecMidf(vec+3, prevbezt->vec[2], bezt->vec[0]);
+ VecMidf(vec+6, bezt->vec[0], bezt->vec[1]);
+
+ VecMidf(vec+9, vec, vec+3);
+ VecMidf(vec+12, vec+3, vec+6);
+
+ /* change handle of prev beztn */
+ VECCOPY((beztn-1)->vec[2], vec);
+ /* new point */
+ VECCOPY(beztn->vec[0], vec+9);
+ VecMidf(beztn->vec[1], vec+9, vec+12);
+ VECCOPY(beztn->vec[2], vec+12);
+ /* handle of next bezt */
+ if(a==0 && (nu->flagu & CU_CYCLIC)) {VECCOPY(beztnew->vec[0], vec+6);}
+ else {VECCOPY(bezt->vec[0], vec+6);}
+
+ beztn->radius = (prevbezt->radius + bezt->radius)/2.0f;
+ beztn->weight = (prevbezt->weight + bezt->weight)/2.0f;
+
+ beztn++;
+ }
+
+ prevbezt= bezt;
+ bezt++;
+ }
+ /* last point */
+ if((nu->flagu & CU_CYCLIC)==0) memcpy(beztn, prevbezt, sizeof(BezTriple));
+
+ MEM_freeN(nu->bezt);
+ nu->bezt= beztnew;
+ nu->pntsu+= amount;
+
+ calchandlesNurb(nu);
+ }
+ } /* End of 'if((nu->type & 7)==CU_BEZIER)' */
+ else if (nu->pntsv==1) {
+ /*
+ All flat lines (ie. co-planar), except flat Nurbs. Flat NURB curves
+ are handled together with the regular NURB plane division, as it
+ should be. I split it off just now, let's see if it is
+ stable... nzc 30-5-'00
+ */
+ /* count */
+ if(nu->flagu & CU_CYCLIC) {
+ a= nu->pntsu;
+ bp= nu->bp;
+ prevbp= bp+(a-1);
+ }
+ else {
+ a= nu->pntsu-1;
+ prevbp= nu->bp;
+ bp= prevbp+1;
+ }
+ while(a--) {
+ if( (bp->f1 & SELECT) && (prevbp->f1 & SELECT) ) amount++;
+ prevbp= bp;
+ bp++;
+ }
+
+ if(amount) {
+ /* insert */
+ bpnew =
+ (BPoint*)MEM_mallocN((amount + nu->pntsu) * sizeof(BPoint), "subdivNurb2");
+ bpn= bpnew;
+
+ if(nu->flagu & CU_CYCLIC) {
+ a= nu->pntsu;
+ bp= nu->bp;
+ prevbp= bp+(a-1);
+ }
+ else {
+ a= nu->pntsu-1;
+ prevbp= nu->bp;
+ bp= prevbp+1;
+ }
+ while(a--) {
+ memcpy(bpn, prevbp, sizeof(BPoint));
+ bpn++;
+
+ if( (bp->f1 & SELECT) && (prevbp->f1 & SELECT) ) {
+ // printf("*** subdivideNurb: insert 'linear' point\n");
+ memcpy(bpn, bp, sizeof(BPoint));
+ bpn->vec[0]= (prevbp->vec[0]+bp->vec[0])/2.0;
+ bpn->vec[1]= (prevbp->vec[1]+bp->vec[1])/2.0;
+ bpn->vec[2]= (prevbp->vec[2]+bp->vec[2])/2.0;
+ bpn->vec[3]= (prevbp->vec[3]+bp->vec[3])/2.0;
+ bpn++;
+
+ }
+ prevbp= bp;
+ bp++;
+ }
+ if((nu->flagu & CU_CYCLIC)==0) memcpy(bpn, prevbp, sizeof(BPoint)); /* last point */
+
+ MEM_freeN(nu->bp);
+ nu->bp= bpnew;
+ nu->pntsu+= amount;
+
+ if(nu->type & CU_NURBS) {
+ makeknots(nu, 1);
+ }
+ }
+ } /* End of 'else if(nu->pntsv==1)' */
+ else if((nu->type & 7)==CU_NURBS) {
+ /* This is a very strange test ... */
+ /**
+ Subdivide NURB surfaces - nzc 30-5-'00 -
+
+ Subdivision of a NURB curve can be effected by adding a
+ control point (insertion of a knot), or by raising the
+ degree of the functions used to build the NURB. The
+ expression
+
+ degree = #knots - #controlpoints + 1 (J Walter piece)
+ degree = #knots - #controlpoints (Blender
+ implementation)
+ ( this is confusing.... what is true? Another concern
+ is that the JW piece allows the curve to become
+ explicitly 1st order derivative discontinuous, while
+ this is not what we want here... )
+
+ is an invariant for a single NURB curve. Raising the degree
+ of the NURB is done elsewhere; the degree is assumed
+ constant during this opration. Degree is a property shared
+ by all controlpoints in a curve (even though it is stored
+ per control point - this can be misleading).
+ Adding a knot is done by searching for the place in the
+ knot vector where a certain knot value must be inserted, or
+ by picking an appropriate knot value between two existing
+ ones. The number of controlpoints that is influenced by the
+ insertion depends on the order of the curve. A certain
+ minimum number of knots is needed to form high-order
+ curves, as can be seen from the equation above. In Blender,
+ currently NURBs may be up to 6th order, so we modify at
+ most 6 points. One point is added. For an n-degree curve,
+ n points are discarded, and n+1 points inserted
+ (so effectively, n points are modified). (that holds for
+ the JW piece, but it seems not for our NURBs)
+ In practice, the knot spacing is copied, but the tail
+ (the points following the insertion point) need to be
+ offset to keep the knot series ascending. The knot series
+ is always a series of monotonically ascending integers in
+ Blender. When not enough control points are available to
+ fit the order, duplicates of the endpoints are added as
+ needed.
+ */
+ /* selection-arrays */
+ usel= MEM_callocN(sizeof(int)*nu->pntsu, "subivideNurb3");
+ vsel= MEM_callocN(sizeof(int)*nu->pntsv, "subivideNurb3");
+ sel= 0;
+
+ /* Count the number of selected points. */
+ bp= nu->bp;
+ for(a=0; a<nu->pntsv; a++) {
+ for(b=0; b<nu->pntsu; b++) {
+ if(bp->f1 & SELECT) {
+ usel[b]++;
+ vsel[a]++;
+ sel++;
+ }
+ bp++;
+ }
+ }
+ if( sel == (nu->pntsu*nu->pntsv) ) { /* subdivide entire nurb */
+ /* Global subdivision is a special case of partial
+ subdivision. Strange it is considered separately... */
+ bpn=bpnew= MEM_mallocN( (2*nu->pntsu-1)*(2*nu->pntsv-1)*sizeof(BPoint), "subdivideNurb4");
+ bp= nu->bp;
+ /* first subdivide rows */
+ for(a=0; a<nu->pntsv; a++) {
+ for(b=0; b<nu->pntsu; b++) {
+ *bpn= *bp;
+ bpn++;
+ bp++;
+ if(b<nu->pntsu-1) {
+ *bpn= *bp;
+ prevbp= bp-1;
+ bpn->vec[0]= (prevbp->vec[0]+bp->vec[0])/2.0;
+ bpn->vec[1]= (prevbp->vec[1]+bp->vec[1])/2.0;
+ bpn->vec[2]= (prevbp->vec[2]+bp->vec[2])/2.0;
+ bpn->vec[3]= (prevbp->vec[3]+bp->vec[3])/2.0;
+ bpn++;
+ }
+ }
+ bpn+= (2*nu->pntsu-1);
+ }
+ /* now insert new */
+ bpn= bpnew+(2*nu->pntsu-1);
+ bp= bpnew+(4*nu->pntsu-2);
+ prevbp= bpnew;
+ for(a=1; a<nu->pntsv; a++) {
+
+ for(b=0; b<2*nu->pntsu-1; b++) {
+ *bpn= *bp;
+ bpn->vec[0]= (prevbp->vec[0]+bp->vec[0])/2.0;
+ bpn->vec[1]= (prevbp->vec[1]+bp->vec[1])/2.0;
+ bpn->vec[2]= (prevbp->vec[2]+bp->vec[2])/2.0;
+ bpn->vec[3]= (prevbp->vec[3]+bp->vec[3])/2.0;
+ bpn++;
+ bp++;
+ prevbp++;
+ }
+ bp+= (2*nu->pntsu-1);
+ bpn+= (2*nu->pntsu-1);
+ prevbp+= (2*nu->pntsu-1);
+ }
+ MEM_freeN(nu->bp);
+ nu->bp= bpnew;
+ nu->pntsu= 2*nu->pntsu-1;
+ nu->pntsv= 2*nu->pntsv-1;
+ makeknots(nu, 1);
+ makeknots(nu, 2);
+ } /* End of 'if(sel== nu->pntsu*nu->pntsv)' (subdivide entire NURB) */
+ else {
+ /* subdivide in v direction? */
+ sel= 0;
+ for(a=0; a<nu->pntsv-1; a++) {
+ if(vsel[a]==nu->pntsu && vsel[a+1]==nu->pntsu) sel++;
+ }
+
+ if(sel) { /* V ! */
+ bpn=bpnew= MEM_mallocN( (sel+nu->pntsv)*nu->pntsu*sizeof(BPoint), "subdivideNurb4");
+ bp= nu->bp;
+ for(a=0; a<nu->pntsv; a++) {
+ for(b=0; b<nu->pntsu; b++) {
+ *bpn= *bp;
+ bpn++;
+ bp++;
+ }
+ if( (a<nu->pntsv-1) && vsel[a]==nu->pntsu && vsel[a+1]==nu->pntsu ) {
+ prevbp= bp- nu->pntsu;
+ for(b=0; b<nu->pntsu; b++) {
+ /*
+ This simple bisection must be replaces by a
+ subtle resampling of a number of points. Our
+ task is made slightly easier because each
+ point in our curve is a separate data
+ node. (is it?)
+ */
+ *bpn= *prevbp;
+ bpn->vec[0]= (prevbp->vec[0]+bp->vec[0])/2.0;
+ bpn->vec[1]= (prevbp->vec[1]+bp->vec[1])/2.0;
+ bpn->vec[2]= (prevbp->vec[2]+bp->vec[2])/2.0;
+ bpn->vec[3]= (prevbp->vec[3]+bp->vec[3])/2.0;
+ bpn++;
+ prevbp++;
+ bp++;
+ }
+ bp-= nu->pntsu;
+ }
+ }
+ MEM_freeN(nu->bp);
+ nu->bp= bpnew;
+ nu->pntsv+= sel;
+ makeknots(nu, 2);
+ }
+ else {
+ /* or in u direction? */
+ sel= 0;
+ for(a=0; a<nu->pntsu-1; a++) {
+ if(usel[a]==nu->pntsv && usel[a+1]==nu->pntsv) sel++;
+ }
+
+ if(sel) { /* U ! */
+ /* Inserting U points is sort of 'default' Flat curves only get */
+ /* U points inserted in them. */
+ bpn=bpnew= MEM_mallocN( (sel+nu->pntsu)*nu->pntsv*sizeof(BPoint), "subdivideNurb4");
+ bp= nu->bp;
+ for(a=0; a<nu->pntsv; a++) {
+ for(b=0; b<nu->pntsu; b++) {
+ *bpn= *bp;
+ bpn++;
+ bp++;
+ if( (b<nu->pntsu-1) && usel[b]==nu->pntsv && usel[b+1]==nu->pntsv ) {
+ /*
+ One thing that bugs me here is that the
+ orders of things are not the same as in
+ the JW piece. Also, this implies that we
+ handle at most 3rd order curves? I miss
+ some symmetry here...
+ */
+ prevbp= bp- 1;
+ *bpn= *prevbp;
+ bpn->vec[0]= (prevbp->vec[0]+bp->vec[0])/2.0;
+ bpn->vec[1]= (prevbp->vec[1]+bp->vec[1])/2.0;
+ bpn->vec[2]= (prevbp->vec[2]+bp->vec[2])/2.0;
+ bpn->vec[3]= (prevbp->vec[3]+bp->vec[3])/2.0;
+ bpn++;
+ }
+ }
+ }
+ MEM_freeN(nu->bp);
+ nu->bp= bpnew;
+ nu->pntsu+= sel;
+ makeknots(nu, 1); /* shift knots
+ forward */
+ }
+ }
+ }
+ MEM_freeN(usel);
+ MEM_freeN(vsel);
+
+ } /* End of 'if((nu->type & 7)==CU_NURBS)' */
+ }
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit);
+ DAG_object_flush_update(CTX_data_scene(C), obedit, OB_RECALC_DATA);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_subdivide(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Subdivide";
+ ot->idname= "CURVE_OT_subdivide";
+
+ /* api callbacks */
+ ot->exec= subdivide_exec;
+ ot->poll= ED_operator_editsurfcurve;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/******************** find nearest ************************/
+
+static void findnearestNurbvert__doClosest(void *userData, Nurb *nu, BPoint *bp, BezTriple *bezt, int beztindex, int x, int y)
+{
+ struct { BPoint *bp; BezTriple *bezt; Nurb *nurb; int dist, hpoint, select, mval[2]; } *data = userData;
+
+ short flag;
+ short temp;
+
+ if (bp) {
+ flag = bp->f1;
+ } else {
+ if (beztindex==0) {
+ flag = bezt->f1;
+ } else if (beztindex==1) {
+ flag = bezt->f2;
+ } else {
+ flag = bezt->f3;
+ }
+ }
+
+ temp = abs(data->mval[0]-x) + abs(data->mval[1]-y);
+ if ((flag&1)==data->select) temp += 5;
+ if (bezt && beztindex==1) temp += 3; /* middle points get a small disadvantage */
+
+ if (temp<data->dist) {
+ data->dist = temp;
+
+ data->bp = bp;
+ data->bezt = bezt;
+ data->nurb = nu;
+ data->hpoint = bezt?beztindex:0;
+ }
+}
+
+static short findnearestNurbvert(ViewContext *vc, short sel, int mval[2], Nurb **nurb, BezTriple **bezt, BPoint **bp)
+{
+ /* sel==1: selected gets a disadvantage */
+ /* in nurb and bezt or bp the nearest is written */
+ /* return 0 1 2: handlepunt */
+ struct { BPoint *bp; BezTriple *bezt; Nurb *nurb; int dist, hpoint, select, mval[2]; } data = {0};
+
+ data.dist = 100;
+ data.hpoint = 0;
+ data.select = sel;
+ data.mval[0] = mval[0];
+ data.mval[1] = mval[1];
+
+ nurbs_foreachScreenVert(vc, findnearestNurbvert__doClosest, &data);
+
+ *nurb = data.nurb;
+ *bezt = data.bezt;
+ *bp = data.bp;
+
+ return data.hpoint;
+}
+
+static void findselectedNurbvert(ListBase *editnurb, Nurb **nu, BezTriple **bezt, BPoint **bp)
+{
+ /* in nu and (bezt or bp) selected are written if there's 1 sel. */
+ /* if more points selected in 1 spline: return only nu, bezt and bp are 0 */
+ Nurb *nu1;
+ BezTriple *bezt1;
+ BPoint *bp1;
+ int a;
+
+ *nu= 0;
+ *bezt= 0;
+ *bp= 0;
+ for(nu1= editnurb->first; nu1; nu1= nu1->next) {
+ if((nu1->type & 7)==CU_BEZIER) {
+ bezt1= nu1->bezt;
+ a= nu1->pntsu;
+ while(a--) {
+ if( (bezt1->f1 & SELECT) || (bezt1->f2 & SELECT) || (bezt1->f3 & SELECT) ) {
+ if(*nu!=0 && *nu!= nu1) {
+ *nu= 0;
+ *bp= 0;
+ *bezt= 0;
+ return;
+ }
+ else if(*bezt || *bp) {
+ *bp= 0;
+ *bezt= 0;
+ }
+ else {
+ *bezt= bezt1;
+ *nu= nu1;
+ }
+ }
+ bezt1++;
+ }
+ }
+ else {
+ bp1= nu1->bp;
+ a= nu1->pntsu*nu1->pntsv;
+ while(a--) {
+ if( bp1->f1 & 1 ) {
+ if(*nu!=0 && *nu!= nu1) {
+ *bp= 0;
+ *bezt= 0;
+ *nu= 0;
+ return;
+ }
+ else if(*bezt || *bp) {
+ *bp= 0;
+ *bezt= 0;
+ }
+ else {
+ *bp= bp1;
+ *nu= nu1;
+ }
+ }
+ bp1++;
+ }
+ }
+ }
+}
+
+/***************** set spline type operator *******************/
+
+static int convertspline(short type, Nurb *nu)
+{
+ BezTriple *bezt;
+ BPoint *bp;
+ int a, c, nr;
+
+ if((nu->type & 7)==CU_POLY) {
+ if(type==CU_BEZIER) { /* to Bezier with vecthandles */
+ nr= nu->pntsu;
+ bezt =
+ (BezTriple*)MEM_callocN(nr * sizeof(BezTriple), "setsplinetype2");
+ nu->bezt= bezt;
+ a= nr;
+ bp= nu->bp;
+ while(a--) {
+ VECCOPY(bezt->vec[1], bp->vec);
+ bezt->f1=bezt->f2=bezt->f3= bp->f1;
+ bezt->h1= bezt->h2= HD_VECT;
+ bezt->weight= bp->weight;
+ bezt->radius= bp->radius;
+ bp++;
+ bezt++;
+ }
+ MEM_freeN(nu->bp);
+ nu->bp= 0;
+ nu->pntsu= nr;
+ nu->type &= ~7;
+ nu->type |= CU_BEZIER;
+ calchandlesNurb(nu);
+ }
+ else if(type==CU_NURBS) {
+ nu->type &= ~7;
+ nu->type |= CU_NURBS;
+ nu->orderu= 4;
+ nu->flagu &= CU_CYCLIC; /* disable all flags except for cyclic */
+ nu->flagu += 4;
+ makeknots(nu, 1);
+ a= nu->pntsu*nu->pntsv;
+ bp= nu->bp;
+ while(a--) {
+ bp->vec[3]= 1.0;
+ bp++;
+ }
+ }
+ }
+ else if((nu->type & 7)==CU_BEZIER) { /* Bezier */
+ if(type==0 || type==4) { /* to Poly or Nurb */
+ nr= 3*nu->pntsu;
+ nu->bp = MEM_callocN(nr * sizeof(BPoint), "setsplinetype");
+ a= nu->pntsu;
+ bezt= nu->bezt;
+ bp= nu->bp;
+ while(a--) {
+ if(type==0 && bezt->h1==HD_VECT && bezt->h2==HD_VECT) {
+ /* vector handle becomes 1 poly vertice */
+ VECCOPY(bp->vec, bezt->vec[1]);
+ bp->vec[3]= 1.0;
+ bp->f1= bezt->f2;
+ nr-= 2;
+ bp->radius= bezt->radius;
+ bp->weight= bezt->weight;
+ bp++;
+ }
+ else {
+ for(c=0;c<3;c++) {
+ VECCOPY(bp->vec, bezt->vec[c]);
+ bp->vec[3]= 1.0;
+ if(c==0) bp->f1= bezt->f1;
+ else if(c==1) bp->f1= bezt->f2;
+ else bp->f1= bezt->f3;
+ bp->radius= bezt->radius;
+ bp->weight= bezt->weight;
+ bp++;
+ }
+ }
+ bezt++;
+ }
+ MEM_freeN(nu->bezt);
+ nu->bezt= 0;
+ nu->pntsu= nr;
+ nu->pntsv= 1;
+ nu->orderu= 4;
+ nu->orderv= 1;
+ nu->type &= ~7;
+ nu->type+= type;
+ if(nu->flagu & CU_CYCLIC) c= nu->orderu-1;
+ else c= 0;
+ if(type== 4) {
+ nu->flagu &= CU_CYCLIC; /* disable all flags except for cyclic */
+ nu->flagu += 4;
+ makeknots(nu, 1);
+ }
+ }
+ }
+ else if((nu->type & 7)==CU_NURBS) {
+ if(type==0) { /* to Poly */
+ nu->type &= ~7;
+ if(nu->knotsu) MEM_freeN(nu->knotsu); /* python created nurbs have a knotsu of zero */
+ nu->knotsu= NULL;
+ if(nu->knotsv) MEM_freeN(nu->knotsv);
+ nu->knotsv= NULL;
+ }
+ else if(type==CU_BEZIER) { /* to Bezier */
+ nr= nu->pntsu/3;
+
+ if(nr<2)
+ return 1; /* conversion impossible */
+ else {
+ bezt = MEM_callocN(nr * sizeof(BezTriple), "setsplinetype2");
+ nu->bezt= bezt;
+ a= nr;
+ bp= nu->bp;
+ while(a--) {
+ VECCOPY(bezt->vec[0], bp->vec);
+ bezt->f1= bp->f1;
+ bp++;
+ VECCOPY(bezt->vec[1], bp->vec);
+ bezt->f2= bp->f1;
+ bp++;
+ VECCOPY(bezt->vec[2], bp->vec);
+ bezt->f3= bp->f1;
+ bezt->radius= bp->radius;
+ bezt->weight= bp->weight;
+ bp++;
+ bezt++;
+ }
+ MEM_freeN(nu->bp);
+ nu->bp= 0;
+ MEM_freeN(nu->knotsu);
+ nu->knotsu= NULL;
+ nu->pntsu= nr;
+ nu->type &= ~7;
+ nu->type |= CU_BEZIER;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int set_spline_type_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ ListBase *editnurb= curve_get_editcurve(obedit);
+ Nurb *nu;
+ int changed=0, type= RNA_enum_get(op->ptr, "type");
+
+ if(type==CU_CARDINAL || type==CU_BSPLINE) {
+ BKE_report(op->reports, RPT_ERROR, "Not implemented yet");
+ return OPERATOR_CANCELLED;
+ }
+
+ for(nu= editnurb->first; nu; nu= nu->next) {
+ if(isNurbsel(nu)) {
+ if(convertspline(type, nu))
+ BKE_report(op->reports, RPT_ERROR, "No conversion possible");
+ else
+ changed= 1;
+ }
+ }
+
+ return (changed)? OPERATOR_FINISHED: OPERATOR_CANCELLED;
+}
+
+void CURVE_OT_spline_type_set(wmOperatorType *ot)
+{
+ static EnumPropertyItem type_items[]= {
+ {CU_POLY, "POLY", "Poly", ""},
+ {CU_BEZIER, "BEZIER", "Bezier", ""},
+ {CU_CARDINAL, "CARDINAL", "Cardinal", ""},
+ {CU_BSPLINE, "B_SPLINE", "B-Spline", ""},
+ {CU_NURBS, "NURBS", "NURBS", ""},
+ {0, NULL, NULL, NULL}};
+
+ /* identifiers */
+ ot->name= "Set Spline Type";
+ ot->idname= "CURVE_OT_spline_type_set";
+
+ /* api callbacks */
+ ot->exec= set_spline_type_exec;
+ ot->poll= ED_operator_editcurve;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_enum(ot->srna, "type", type_items, CU_POLY, "Type", "Spline type");
+}
+
+/***************** set handle type operator *******************/
+
+static int set_handle_type_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+ ListBase *editnurb= curve_get_editcurve(obedit);
+
+ sethandlesNurb(editnurb, RNA_enum_get(op->ptr, "type"));
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit);
+ DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_handle_type_set(wmOperatorType *ot)
+{
+ static EnumPropertyItem type_items[]= {
+ {1, "AUTOMATIC", "Automatic", ""},
+ {2, "VECTOR", "Vector", ""},
+ {3, "TOGGLE_FREE_ALIGN", "Toggle Free/Align", ""},
+ {5, "ALIGN", "Align", ""},
+ {6, "FREE_ALIGN", "Free Align", ""},
+ {0, NULL, NULL, NULL}};
+
+ /* identifiers */
+ ot->name= "Set Handle Type";
+ ot->idname= "CURVE_OT_handle_type_set";
+
+ /* api callbacks */
+ ot->exec= set_handle_type_exec;
+ ot->poll= ED_operator_editcurve;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_enum(ot->srna, "type", type_items, CU_POLY, "Type", "Spline type");
+}
+
+/***************** make segment operator **********************/
+
+/* ******************** SKINNING LOFTING!!! ******************** */
+
+static void switchdirection_knots(float *base, int tot)
+{
+ float *fp1, *fp2, *tempf;
+ int a;
+
+ if(base==NULL || tot==0) return;
+
+ /* reverse knots */
+ a= tot;
+ fp1= base;
+ fp2= fp1+(a-1);
+ a/= 2;
+ while(fp1!=fp2 && a>0) {
+ SWAP(float, *fp1, *fp2);
+ a--;
+ fp1++;
+ fp2--;
+ }
+ /* and make in increasing order again */
+ a= tot;
+ fp1= base;
+ fp2=tempf= MEM_mallocN(sizeof(float)*a, "switchdirect");
+ while(a--) {
+ fp2[0]= fabs(fp1[1]-fp1[0]);
+ fp1++;
+ fp2++;
+ }
+
+ a= tot-1;
+ fp1= base;
+ fp2= tempf;
+ fp1[0]= 0.0;
+ fp1++;
+ while(a--) {
+ fp1[0]= fp1[-1]+fp2[0];
+ fp1++;
+ fp2++;
+ }
+ MEM_freeN(tempf);
+}
+
+static void rotate_direction_nurb(Nurb *nu)
+{
+ BPoint *bp1, *bp2, *temp;
+ int u, v;
+
+ SWAP(short, nu->pntsu, nu->pntsv);
+ SWAP(short, nu->orderu, nu->orderv);
+ SWAP(short, nu->resolu, nu->resolv);
+ SWAP(short, nu->flagu, nu->flagv);
+
+ SWAP(float *, nu->knotsu, nu->knotsv);
+ switchdirection_knots(nu->knotsv, KNOTSV(nu) );
+
+ temp= MEM_dupallocN(nu->bp);
+ bp1= nu->bp;
+ for(v=0; v<nu->pntsv; v++) {
+ for(u=0; u<nu->pntsu; u++, bp1++) {
+ bp2= temp + (nu->pntsu-u-1)*(nu->pntsv) + v;
+ *bp1= *bp2;
+ }
+ }
+
+ MEM_freeN(temp);
+}
+
+static int is_u_selected(Nurb *nu, int u)
+{
+ BPoint *bp;
+ int v;
+
+ /* what about resolu == 2? */
+ bp= nu->bp+u;
+ for(v=0; v<nu->pntsv-1; v++, bp+=nu->pntsu) {
+ if(v) if(bp->f1 & SELECT) return 1;
+ }
+
+ return 0;
+}
+
+typedef struct NurbSort {
+ struct NurbSort *next, *prev;
+ Nurb *nu;
+ float vec[3];
+} NurbSort;
+
+static ListBase nsortbase= {0, 0};
+/* static NurbSort *nusmain; */ /* this var seems to go unused... at least in this file */
+
+static void make_selection_list_nurb(ListBase *editnurb)
+{
+ ListBase nbase= {0, 0};
+ NurbSort *nus, *nustest, *headdo, *taildo;
+ Nurb *nu;
+ BPoint *bp;
+ float dist, headdist, taildist;
+ int a;
+
+ for(nu= editnurb->first; nu; nu= nu->next) {
+ if( isNurbsel(nu) ) {
+
+ nus = (NurbSort*)MEM_callocN(sizeof(NurbSort), "sort");
+ BLI_addhead(&nbase, nus);
+ nus->nu= nu;
+
+ bp= nu->bp;
+ a= nu->pntsu;
+ while(a--) {
+ VecAddf(nus->vec, nus->vec, bp->vec);
+ bp++;
+ }
+ VecMulf(nus->vec, 1.0/(float)nu->pntsu);
+
+
+ }
+ }
+
+ /* just add the first one */
+ nus= nbase.first;
+ BLI_remlink(&nbase, nus);
+ BLI_addtail( &nsortbase, nus);
+
+ /* now add, either at head or tail, the closest one */
+ while(nbase.first) {
+
+ headdist= taildist= 1.0e30;
+ headdo= taildo= 0;
+
+ nustest= nbase.first;
+ while(nustest) {
+ dist= VecLenf(nustest->vec, ((NurbSort *)nsortbase.first)->vec);
+
+ if(dist<headdist) {
+ headdist= dist;
+ headdo= nustest;
+ }
+ dist= VecLenf(nustest->vec, ((NurbSort *)nsortbase.last)->vec);
+
+ if(dist<taildist) {
+ taildist= dist;
+ taildo= nustest;
+ }
+ nustest= nustest->next;
+ }
+
+ if(headdist<taildist) {
+ BLI_remlink(&nbase, headdo);
+ BLI_addhead(&nsortbase, headdo);
+ }
+ else {
+ BLI_remlink(&nbase, taildo);
+ BLI_addtail(&nsortbase, taildo);
+ }
+ }
+}
+
+static void merge_2_nurb(wmOperator *op, ListBase *editnurb, Nurb *nu1, Nurb *nu2)
+{
+ BPoint *bp, *bp1, *bp2, *temp;
+ float len1, len2;
+ int origu, u, v;
+
+ /* first nurbs will be changed to make u = resolu-1 selected */
+ /* 2nd nurbs will be changed to make u = 0 selected */
+
+ /* first nurbs: u = resolu-1 selected */
+
+ if( is_u_selected(nu1, nu1->pntsu-1) );
+ else {
+ rotate_direction_nurb(nu1);
+ if( is_u_selected(nu1, nu1->pntsu-1) );
+ else {
+ rotate_direction_nurb(nu1);
+ if( is_u_selected(nu1, nu1->pntsu-1) );
+ else {
+ rotate_direction_nurb(nu1);
+ if( is_u_selected(nu1, nu1->pntsu-1) );
+ else {
+ /* rotate again, now its OK! */
+ if(nu1->pntsv!=1) rotate_direction_nurb(nu1);
+ return;
+ }
+ }
+ }
+ }
+
+ /* 2nd nurbs: u = 0 selected */
+ if( is_u_selected(nu2, 0) );
+ else {
+ rotate_direction_nurb(nu2);
+ if( is_u_selected(nu2, 0) );
+ else {
+ rotate_direction_nurb(nu2);
+ if( is_u_selected(nu2, 0) );
+ else {
+ rotate_direction_nurb(nu2);
+ if( is_u_selected(nu2, 0) );
+ else {
+ /* rotate again, now its OK! */
+ if(nu1->pntsu==1) rotate_direction_nurb(nu1);
+ if(nu2->pntsv!=1) rotate_direction_nurb(nu2);
+ return;
+ }
+ }
+ }
+ }
+
+ if( nu1->pntsv != nu2->pntsv ) {
+ BKE_report(op->reports, RPT_ERROR, "Resolution doesn't match");
+ return;
+ }
+
+ /* ok, now nu1 has the rightmost collumn and nu2 the leftmost collumn selected */
+ /* maybe we need a 'v' flip of nu2? */
+
+ bp1= nu1->bp+nu1->pntsu-1;
+ bp2= nu2->bp;
+ len1= 0.0;
+
+ for(v=0; v<nu1->pntsv; v++, bp1+=nu1->pntsu, bp2+=nu2->pntsu) {
+ len1+= VecLenf(bp1->vec, bp2->vec);
+ }
+
+ bp1= nu1->bp + nu1->pntsu-1;
+ bp2= nu2->bp + nu2->pntsu*(nu2->pntsv-1);
+ len2= 0.0;
+
+ for(v=0; v<nu1->pntsv; v++, bp1+=nu1->pntsu, bp2-=nu2->pntsu) {
+ len2+= VecLenf(bp1->vec, bp2->vec);
+ }
+
+ /* merge */
+ origu= nu1->pntsu;
+ nu1->pntsu+= nu2->pntsu;
+ if(nu1->orderu<3) nu1->orderu++;
+ if(nu1->orderv<3) nu1->orderv++;
+ temp= nu1->bp;
+ nu1->bp= MEM_mallocN(nu1->pntsu*nu1->pntsv*sizeof(BPoint), "mergeBP");
+
+ bp= nu1->bp;
+ bp1= temp;
+
+ for(v=0; v<nu1->pntsv; v++) {
+
+ /* switch direction? */
+ if(len1<len2) bp2= nu2->bp + v*nu2->pntsu;
+ else bp2= nu2->bp + (nu1->pntsv-v-1)*nu2->pntsu;
+
+ for(u=0; u<nu1->pntsu; u++, bp++) {
+ if(u<origu) {
+ *bp= *bp1; bp1++;
+ select_bpoint(bp, SELECT, 1, HIDDEN);
+ }
+ else {
+ *bp= *bp2; bp2++;
+ }
+ }
+ }
+
+ if((nu1->type & 7)==CU_NURBS) {
+ /* merge knots */
+ makeknots(nu1, 1);
+
+ /* make knots, for merged curved for example */
+ makeknots(nu1, 2);
+ }
+
+ MEM_freeN(temp);
+ BLI_remlink(editnurb, nu2);
+ freeNurb(nu2);
+}
+
+static int merge_nurb(bContext *C, wmOperator *op)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ ListBase *editnurb= curve_get_editcurve(obedit);
+ NurbSort *nus1, *nus2;
+ int ok= 1;
+
+ make_selection_list_nurb(editnurb);
+
+ if(nsortbase.first == nsortbase.last) {
+ BLI_freelistN(&nsortbase);
+ BKE_report(op->reports, RPT_ERROR, "Too few selections to merge.");
+ return OPERATOR_CANCELLED;
+ }
+
+ nus1= nsortbase.first;
+ nus2= nus1->next;
+
+ /* resolution match, to avoid uv rotations */
+ if(nus1->nu->pntsv==1) {
+ if(nus1->nu->pntsu==nus2->nu->pntsu || nus1->nu->pntsu==nus2->nu->pntsv);
+ else ok= 0;
+ }
+ else if(nus2->nu->pntsv==1) {
+ if(nus2->nu->pntsu==nus1->nu->pntsu || nus2->nu->pntsu==nus1->nu->pntsv);
+ else ok= 0;
+ }
+ else if( nus1->nu->pntsu==nus2->nu->pntsu || nus1->nu->pntsv==nus2->nu->pntsv);
+ else if( nus1->nu->pntsu==nus2->nu->pntsv || nus1->nu->pntsv==nus2->nu->pntsu);
+ else {
+ ok= 0;
+ }
+
+ if(ok==0) {
+ BKE_report(op->reports, RPT_ERROR, "Resolution doesn't match");
+ BLI_freelistN(&nsortbase);
+ return OPERATOR_CANCELLED;
+ }
+
+ while(nus2) {
+ merge_2_nurb(op, editnurb, nus1->nu, nus2->nu);
+ nus2= nus2->next;
+ }
+
+ BLI_freelistN(&nsortbase);
+
+ set_actNurb(obedit, NULL);
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit);
+ DAG_object_flush_update(CTX_data_scene(C), obedit, OB_RECALC_DATA);
+
+ return OPERATOR_FINISHED;
+}
+
+static int make_segment_exec(bContext *C, wmOperator *op)
+{
+ /* joins 2 curves */
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+ ListBase *editnurb= curve_get_editcurve(obedit);
+ Nurb *nu, *nu1=0, *nu2=0;
+ BezTriple *bezt;
+ BPoint *bp;
+ float *fp, offset;
+ int a;
+
+ /* first decide if this is a surface merge! */
+ if(obedit->type==OB_SURF) nu= editnurb->first;
+ else nu= NULL;
+
+ while(nu) {
+ if( isNurbsel(nu) ) {
+
+ if(nu->pntsu>1 && nu->pntsv>1) break;
+ if(isNurbsel_count(nu)>1) break;
+ if(isNurbsel_count(nu)==1) {
+ /* only 1 selected, not first or last, a little complex, but intuitive */
+ if(nu->pntsv==1) {
+ if( (nu->bp->f1 & SELECT) || ((nu->bp+nu->pntsu-1)->f1 & SELECT));
+ else break;
+ }
+ }
+ }
+ nu= nu->next;
+ }
+
+ if(nu)
+ return merge_nurb(C, op);
+
+ /* find both nurbs and points, nu1 will be put behind nu2 */
+ for(nu= editnurb->first; nu; nu= nu->next) {
+ if((nu->flagu & CU_CYCLIC)==0) { /* not cyclic */
+ if( (nu->type & 7)==CU_BEZIER ) {
+ bezt= nu->bezt;
+ if(nu1==0) {
+ if( BEZSELECTED_HIDDENHANDLES(bezt) ) nu1= nu;
+ else {
+ bezt= bezt+(nu->pntsu-1);
+ if( BEZSELECTED_HIDDENHANDLES(bezt) ) {
+ nu1= nu;
+ switchdirectionNurb(nu);
+ }
+ }
+ }
+ else if(nu2==0) {
+ if( BEZSELECTED_HIDDENHANDLES(bezt) ) {
+ nu2= nu;
+ switchdirectionNurb(nu);
+ }
+ else {
+ bezt= bezt+(nu->pntsu-1);
+ if( BEZSELECTED_HIDDENHANDLES(bezt) ) {
+ nu2= nu;
+ }
+ }
+ }
+ else break;
+ }
+ else if(nu->pntsv==1) {
+ bp= nu->bp;
+ if(nu1==0) {
+ if( bp->f1 & SELECT) nu1= nu;
+ else {
+ bp= bp+(nu->pntsu-1);
+ if( bp->f1 & SELECT ) {
+ nu1= nu;
+ switchdirectionNurb(nu);
+ }
+ }
+ }
+ else if(nu2==0) {
+ if( bp->f1 & SELECT ) {
+ nu2= nu;
+ switchdirectionNurb(nu);
+ }
+ else {
+ bp= bp+(nu->pntsu-1);
+ if( bp->f1 & SELECT ) {
+ nu2= nu;
+ }
+ }
+ }
+ else break;
+ }
+ }
+ }
+
+ if((nu1 && nu2) && (nu1!=nu2)) {
+ if( nu1->type==nu2->type) {
+ if((nu1->type & 7)==CU_BEZIER) {
+ bezt =
+ (BezTriple*)MEM_mallocN((nu1->pntsu+nu2->pntsu) * sizeof(BezTriple), "addsegmentN");
+ memcpy(bezt, nu2->bezt, nu2->pntsu*sizeof(BezTriple));
+ memcpy(bezt+nu2->pntsu, nu1->bezt, nu1->pntsu*sizeof(BezTriple));
+ MEM_freeN(nu1->bezt);
+ nu1->bezt= bezt;
+ nu1->pntsu+= nu2->pntsu;
+ BLI_remlink(editnurb, nu2);
+ freeNurb(nu2); nu2= NULL;
+ calchandlesNurb(nu1);
+ }
+ else {
+ bp =
+ (BPoint*)MEM_mallocN((nu1->pntsu+nu2->pntsu) * sizeof(BPoint), "addsegmentN2");
+ memcpy(bp, nu2->bp, nu2->pntsu*sizeof(BPoint) );
+ memcpy(bp+nu2->pntsu, nu1->bp, nu1->pntsu*sizeof(BPoint));
+ MEM_freeN(nu1->bp);
+ nu1->bp= bp;
+
+ a= nu1->pntsu+nu1->orderu;
+
+ nu1->pntsu+= nu2->pntsu;
+ BLI_remlink(editnurb, nu2);
+
+ /* now join the knots */
+ if((nu1->type & 7)==CU_NURBS) {
+ if(nu1->knotsu==NULL) {
+ makeknots(nu1, 1);
+ }
+ else {
+ fp= MEM_mallocN(sizeof(float)*KNOTSU(nu1), "addsegment3");
+ memcpy(fp, nu1->knotsu, sizeof(float)*a);
+ MEM_freeN(nu1->knotsu);
+ nu1->knotsu= fp;
+
+
+ offset= nu1->knotsu[a-1] +1.0;
+ fp= nu1->knotsu+a;
+ for(a=0; a<nu2->pntsu; a++, fp++) {
+ if(nu2->knotsu)
+ *fp= offset+nu2->knotsu[a+1];
+ else
+ *fp = offset;
+ }
+ }
+ }
+ freeNurb(nu2); nu2= NULL;
+ }
+ }
+
+ set_actNurb(obedit, NULL); /* for selected */
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit);
+ DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
+
+ return OPERATOR_FINISHED;
+ }
+ else {
+ BKE_report(op->reports, RPT_ERROR, "Can't make segment");
+ return OPERATOR_CANCELLED;
+ }
+}
+
+void CURVE_OT_make_segment(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Make Segment";
+ ot->idname= "CURVE_OT_make_segment";
+
+ /* api callbacks */
+ ot->exec= make_segment_exec;
+ ot->poll= ED_operator_editsurfcurve;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/***************** pick select from 3d view **********************/
+
+void mouse_nurb(bContext *C, short mval[2], int extend)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ ListBase *editnurb= curve_get_editcurve(obedit);
+ Curve *cu= obedit->data;
+ ViewContext vc;
+ Nurb *nu;
+ BezTriple *bezt=0;
+ BPoint *bp=0;
+ int location[2];
+ short hand;
+
+ view3d_set_viewcontext(C, &vc);
+
+ location[0]= mval[0];
+ location[1]= mval[1];
+ hand= findnearestNurbvert(&vc, 1, location, &nu, &bezt, &bp);
+
+ if(bezt || bp) {
+ if(extend==0) {
+
+ setflagsNurb(editnurb, 0);
+
+ if(bezt) {
+
+ if(hand==1) select_beztriple(bezt, SELECT, 1, HIDDEN);
+ else if(hand==0) bezt->f1|= SELECT;
+ else bezt->f3|= SELECT;
+ }
+ else {
+ cu->lastselbp= bp;
+ select_bpoint(bp, SELECT, 1, HIDDEN);
+ }
+
+ }
+ else {
+ if(bezt) {
+ if(hand==1) {
+ if(bezt->f2 & SELECT) select_beztriple(bezt, DESELECT, 1, HIDDEN);
+ else select_beztriple(bezt, SELECT, 1, HIDDEN);
+ } else if(hand==0) {
+ bezt->f1 ^= SELECT;
+ } else {
+ bezt->f3 ^= SELECT;
+ }
+ }
+ else {
+ if(bp->f1 & SELECT) select_bpoint(bp, DESELECT, 1, HIDDEN);
+ else {
+ select_bpoint(bp, SELECT, 1, HIDDEN);
+ cu->lastselbp= bp;
+ }
+ }
+
+ }
+
+ }
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
+
+ if(nu!=get_actNurb(obedit))
+ set_actNurb(obedit, nu);
+}
+
+/******************** spin operator ***********************/
+
+/* from what I can gather, the mode==0 magic number spins and bridges the nurbs based on the
+ * orientation of the global 3d view (yuck yuck!) mode==1 does the same, but doesn't bridge up
+ * up the new geometry, mode==2 now does the same as 0, but aligned to world axes, not the view.
+*/
+static int spin_nurb(bContext *C, Scene *scene, Object *obedit, float *dvec, short mode)
+{
+ ListBase *editnurb= curve_get_editcurve(obedit);
+ View3D *v3d= CTX_wm_view3d(C);
+ RegionView3D *rv3d= CTX_wm_region_view3d(C);
+ Nurb *nu;
+ float *curs, si,phi,n[3],q[4],cmat[3][3],tmat[3][3],imat[3][3];
+ float cent[3],bmat[3][3], rotmat[3][3], scalemat1[3][3], scalemat2[3][3];
+ float persmat[3][3], persinv[3][3];
+ short a,ok, changed= 0;
+
+ if(mode != 2 && rv3d) Mat3CpyMat4(persmat, rv3d->viewmat);
+ else Mat3One(persmat);
+ Mat3Inv(persinv, persmat);
+
+ /* imat and center and size */
+ Mat3CpyMat4(bmat, obedit->obmat);
+ Mat3Inv(imat, bmat);
+
+ if(v3d) {
+ curs= give_cursor(scene, v3d);
+ VECCOPY(cent, curs);
+ }
+ else
+ cent[0]= cent[1]= cent[2]= 0.0f;
+
+ VecSubf(cent, cent, obedit->obmat[3]);
+ Mat3MulVecfl(imat,cent);
+
+ if(dvec || mode==2 || !rv3d) {
+ n[0]=n[1]= 0.0;
+ n[2]= 1.0;
+ } else {
+ n[0]= rv3d->viewinv[2][0];
+ n[1]= rv3d->viewinv[2][1];
+ n[2]= rv3d->viewinv[2][2];
+ Normalize(n);
+ }
+
+ phi= M_PI/8.0;
+ q[0]= cos(phi);
+ si= sin(phi);
+ q[1]= n[0]*si;
+ q[2]= n[1]*si;
+ q[3]= n[2]*si;
+ QuatToMat3(q, cmat);
+ Mat3MulMat3(tmat, cmat, bmat);
+ Mat3MulMat3(rotmat, imat, tmat);
+
+ Mat3One(scalemat1);
+ scalemat1[0][0]= sqrt(2.0);
+ scalemat1[1][1]= sqrt(2.0);
+
+ Mat3MulMat3(tmat,persmat,bmat);
+ Mat3MulMat3(cmat,scalemat1,tmat);
+ Mat3MulMat3(tmat,persinv,cmat);
+ Mat3MulMat3(scalemat1,imat,tmat);
+
+ Mat3One(scalemat2);
+ scalemat2[0][0]/= sqrt(2.0);
+ scalemat2[1][1]/= sqrt(2.0);
+
+ Mat3MulMat3(tmat,persmat,bmat);
+ Mat3MulMat3(cmat,scalemat2,tmat);
+ Mat3MulMat3(tmat,persinv,cmat);
+ Mat3MulMat3(scalemat2,imat,tmat);
+
+ ok= 1;
+
+ for(a=0;a<7;a++) {
+ if(mode==0 || mode==2) ok= extrudeflagNurb(editnurb, 1);
+ else adduplicateflagNurb(obedit, 1);
+
+ if(ok==0)
+ return changed;
+
+ changed= 1;
+
+ rotateflagNurb(editnurb, 1,cent,rotmat);
+
+ if(mode==0 || mode==2) {
+ if( (a & 1)==0 ) {
+ rotateflagNurb(editnurb, 1,cent,scalemat1);
+ weightflagNurb(editnurb, 1, 0.25*sqrt(2.0), 1);
+ }
+ else {
+ rotateflagNurb(editnurb, 1,cent,scalemat2);
+ weightflagNurb(editnurb, 1, 4.0/sqrt(2.0), 1);
+ }
+ }
+ if(dvec) {
+ Mat3MulVecfl(bmat,dvec);
+ translateflagNurb(editnurb, 1,dvec);
+ }
+ }
+
+ if(ok) {
+ for(nu= editnurb->first; nu; nu= nu->next) {
+ if(isNurbsel(nu)) {
+ nu->orderv= 4;
+ nu->flagv |= CU_CYCLIC;
+ makeknots(nu, 2);
+ }
+ }
+ }
+
+ return changed;
+}
+
+static int spin_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+
+ if(!spin_nurb(C, scene, obedit, 0, 0)) {
+ BKE_report(op->reports, RPT_ERROR, "Can't spin");
+ return OPERATOR_CANCELLED;
+ }
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit);
+ DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_spin(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Spin";
+ ot->idname= "CURVE_OT_spin";
+
+ /* api callbacks */
+ ot->exec= spin_exec;
+ ot->poll= ED_operator_editsurf;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/***************** add vertex operator **********************/
+
+static int addvert_Nurb(bContext *C, short mode, float location[3])
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+ ListBase *editnurb= curve_get_editcurve(obedit);
+ Nurb *nu;
+ BezTriple *bezt, *newbezt = NULL;
+ BPoint *bp, *newbp = NULL;
+ float mat[3][3],imat[3][3], temp[3];
+
+ Mat3CpyMat4(mat, obedit->obmat);
+ Mat3Inv(imat,mat);
+
+ findselectedNurbvert(editnurb, &nu, &bezt, &bp);
+ if(bezt==0 && bp==0) return OPERATOR_CANCELLED;
+
+ if((nu->type & 7)==CU_BEZIER) {
+ /* which bezpoint? */
+ if(bezt== nu->bezt) { /* first */
+ BEZ_DESEL(bezt);
+ newbezt =
+ (BezTriple*)MEM_callocN((nu->pntsu+1) * sizeof(BezTriple), "addvert_Nurb");
+ memcpy(newbezt+1, bezt, nu->pntsu*sizeof(BezTriple));
+ *newbezt= *bezt;
+ BEZ_SEL(newbezt);
+ newbezt->h2= newbezt->h1;
+ VECCOPY(temp, bezt->vec[1]);
+ MEM_freeN(nu->bezt);
+ nu->bezt= newbezt;
+ bezt= newbezt+1;
+ }
+ else if(bezt== (nu->bezt+nu->pntsu-1)) { /* last */
+ BEZ_DESEL(bezt);
+ newbezt =
+ (BezTriple*)MEM_callocN((nu->pntsu+1) * sizeof(BezTriple), "addvert_Nurb");
+ memcpy(newbezt, nu->bezt, nu->pntsu*sizeof(BezTriple));
+ *(newbezt+nu->pntsu)= *bezt;
+ VECCOPY(temp, bezt->vec[1]);
+ MEM_freeN(nu->bezt);
+ nu->bezt= newbezt;
+ newbezt+= nu->pntsu;
+ BEZ_SEL(newbezt);
+ newbezt->h2= newbezt->h1;
+ bezt= nu->bezt+nu->pntsu-1;
+ }
+ else bezt= 0;
+
+ if(bezt) {
+ nu->pntsu++;
+
+ if(mode=='e') {
+ VECCOPY(newbezt->vec[0], bezt->vec[0]);
+ VECCOPY(newbezt->vec[1], bezt->vec[1]);
+ VECCOPY(newbezt->vec[2], bezt->vec[2]);
+ }
+ else {
+ VECCOPY(newbezt->vec[1], location);
+ VecSubf(newbezt->vec[1],newbezt->vec[1], obedit->obmat[3]);
+ Mat3MulVecfl(imat,newbezt->vec[1]);
+ VecSubf(temp, newbezt->vec[1],temp);
+ VecAddf(newbezt->vec[0], bezt->vec[0],temp);
+ VecAddf(newbezt->vec[2], bezt->vec[2],temp);
+ calchandlesNurb(nu);
+ }
+ }
+ }
+ else if(nu->pntsv==1) {
+ /* which b-point? */
+ if(bp== nu->bp) { /* first */
+ bp->f1= 0;
+ newbp =
+ (BPoint*)MEM_callocN((nu->pntsu+1) * sizeof(BPoint), "addvert_Nurb3");
+ memcpy(newbp+1, bp, nu->pntsu*sizeof(BPoint));
+ *newbp= *bp;
+ newbp->f1= 1;
+ MEM_freeN(nu->bp);
+ nu->bp= newbp;
+ bp= newbp + 1;
+ }
+ else if(bp== (nu->bp+nu->pntsu-1)) { /* last */
+ bp->f1= 0;
+ newbp =
+ (BPoint*)MEM_callocN((nu->pntsu+1) * sizeof(BPoint), "addvert_Nurb4");
+ memcpy(newbp, nu->bp, nu->pntsu*sizeof(BPoint));
+ *(newbp+nu->pntsu)= *bp;
+ MEM_freeN(nu->bp);
+ nu->bp= newbp;
+ newbp+= nu->pntsu;
+ newbp->f1= 1;
+ bp= newbp - 1;
+ }
+ else bp= 0;
+
+ if(bp) {
+ nu->pntsu++;
+
+ makeknots(nu, 1);
+
+ if(mode=='e') {
+ VECCOPY(newbp->vec, bp->vec);
+ }
+ else {
+ VECCOPY(newbp->vec, location);
+ VecSubf(newbp->vec, newbp->vec, obedit->obmat[3]);
+ Mat3MulVecfl(imat,newbp->vec);
+ newbp->vec[3]= 1.0;
+ }
+ }
+ }
+
+ // XXX retopo_do_all();
+
+ test2DNurb(nu);
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit);
+ DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
+
+ return OPERATOR_FINISHED;
+}
+
+static int add_vertex_exec(bContext *C, wmOperator *op)
+{
+ float location[3];
+
+ RNA_float_get_array(op->ptr, "location", location);
+ return addvert_Nurb(C, 0, location);
+}
+
+static int add_vertex_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ RegionView3D *rv3d= CTX_wm_region_view3d(C);
+ ViewContext vc;
+ float location[3];
+ short mval[2];
+
+ if(rv3d && !RNA_property_is_set(op->ptr, "location")) {
+ view3d_set_viewcontext(C, &vc);
+
+ mval[0]= event->x - vc.ar->winrct.xmin;
+ mval[1]= event->y - vc.ar->winrct.ymin;
+
+ view3d_get_view_aligned_coordinate(&vc, location, mval);
+ RNA_float_set_array(op->ptr, "location", location);
+ }
+
+ return add_vertex_exec(C, op);
+}
+
+void CURVE_OT_vertex_add(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Add Vertex";
+ ot->idname= "CURVE_OT_vertex_add";
+
+ /* api callbacks */
+ ot->exec= add_vertex_exec;
+ ot->invoke= add_vertex_invoke;
+ ot->poll= ED_operator_editcurve;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_float_vector(ot->srna, "location", 3, NULL, -FLT_MAX, FLT_MAX, "Location", "Location to add new vertex at.", -1e4, 1e4);
+}
+
+/***************** extrude operator **********************/
+
+static int extrude_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+ ListBase *editnurb= curve_get_editcurve(obedit);
+ Nurb *nu;
+
+ /* first test: curve? */
+ for(nu= editnurb->first; nu; nu= nu->next)
+ if(nu->pntsv==1 && isNurbsel_count(nu)==1)
+ break;
+
+ if(obedit->type==OB_CURVE || nu) {
+ addvert_Nurb(C, 'e', NULL);
+ }
+ else {
+ if(extrudeflagNurb(editnurb, 1)) { /* '1'= flag */
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit);
+ DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
+ }
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+static int extrude_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ if(extrude_exec(C, op) == OPERATOR_FINISHED) {
+ RNA_int_set(op->ptr, "mode", TFM_TRANSLATION);
+ WM_operator_name_call(C, "TFM_OT_transform", WM_OP_INVOKE_REGION_WIN, op->ptr);
+
+ return OPERATOR_FINISHED;
+ }
+
+ return OPERATOR_CANCELLED;
+}
+
+void CURVE_OT_extrude(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Extrude";
+ ot->idname= "CURVE_OT_extrude";
+
+ /* api callbacks */
+ ot->exec= extrude_exec;
+ ot->invoke= extrude_invoke;
+ ot->poll= ED_operator_editsurfcurve;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* to give to transform */
+ RNA_def_int(ot->srna, "mode", TFM_TRANSLATION, 0, INT_MAX, "Mode", "", 0, INT_MAX);
+}
+
+/***************** make cyclic operator **********************/
+
+static int toggle_cyclic_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ ListBase *editnurb= curve_get_editcurve(obedit);
+ Nurb *nu;
+ BezTriple *bezt;
+ BPoint *bp;
+ int a, direction= RNA_enum_get(op->ptr, "direction");
+
+ for(nu= editnurb->first; nu; nu= nu->next) {
+ if( nu->pntsu>1 || nu->pntsv>1) {
+ if( (nu->type & 7)==CU_POLY ) {
+ a= nu->pntsu;
+ bp= nu->bp;
+ while(a--) {
+ if( bp->f1 & SELECT ) {
+ nu->flagu ^= CU_CYCLIC;
+ break;
+ }
+ bp++;
+ }
+ }
+ else if( (nu->type & 7)==CU_BEZIER ) {
+ a= nu->pntsu;
+ bezt= nu->bezt;
+ while(a--) {
+ if( BEZSELECTED_HIDDENHANDLES(bezt) ) {
+ nu->flagu ^= CU_CYCLIC;
+ break;
+ }
+ bezt++;
+ }
+ calchandlesNurb(nu);
+ }
+ else if(nu->pntsv==1 && (nu->type & 7)==CU_NURBS) {
+ if (nu->knotsu) { /* if check_valid_nurb_u fails the knotsu can be NULL */
+ a= nu->pntsu;
+ bp= nu->bp;
+ while(a--) {
+ if( bp->f1 & SELECT ) {
+ nu->flagu ^= CU_CYCLIC;
+ makeknots(nu, 1); /* 1==u type is ignored for cyclic curves */
+ break;
+ }
+ bp++;
+ }
+ }
+ }
+ else if(nu->type==CU_NURBS) {
+ a= nu->pntsu*nu->pntsv;
+ bp= nu->bp;
+ while(a--) {
+
+ if( bp->f1 & SELECT) {
+ if(direction==0 && nu->pntsu>1) {
+ nu->flagu ^= CU_CYCLIC;
+ makeknots(nu, 1); /* 1==u type is ignored for cyclic curves */
+ }
+ if(direction==1 && nu->pntsv>1) {
+ nu->flagv ^= CU_CYCLIC;
+ makeknots(nu, 2); /* 2==v type is ignored for cyclic curves */
+ }
+ break;
+ }
+ bp++;
+ }
+
+ }
+ }
+ }
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit);
+ DAG_object_flush_update(CTX_data_scene(C), obedit, OB_RECALC_DATA);
+
+ return OPERATOR_FINISHED;
+}
+
+static int toggle_cyclic_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ ListBase *editnurb= curve_get_editcurve(obedit);
+ uiPopupMenu *pup;
+ uiLayout *layout;
+ Nurb *nu;
+
+ for(nu= editnurb->first; nu; nu= nu->next) {
+ if(nu->pntsu>1 || nu->pntsv>1) {
+ if(nu->type==CU_NURBS) {
+ pup= uiPupMenuBegin(C, "Direction", 0);
+ layout= uiPupMenuLayout(pup);
+ uiItemsEnumO(layout, op->type->idname, "direction");
+ uiPupMenuEnd(C, pup);
+ return OPERATOR_CANCELLED;
+ }
+ }
+ }
+
+ return toggle_cyclic_exec(C, op);
+}
+
+void CURVE_OT_cyclic_toggle(wmOperatorType *ot)
+{
+ static EnumPropertyItem direction_items[]= {
+ {0, "CYCLIC_U", "Cyclic U", ""},
+ {1, "CYCLIC_V", "Cyclic V", ""},
+ {0, NULL, NULL, NULL}};
+
+ /* identifiers */
+ ot->name= "Toggle Cyclic";
+ ot->idname= "CURVE_OT_cyclic_toggle";
+
+ /* api callbacks */
+ ot->exec= toggle_cyclic_exec;
+ ot->invoke= toggle_cyclic_invoke;
+ ot->poll= ED_operator_editsurfcurve;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_enum(ot->srna, "direction", direction_items, 0, "Direction", "Direction to make surface cyclic in.");
+}
+
+/***************** select linked operator ******************/
+
+static int select_linked_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ RegionView3D *rv3d= CTX_wm_region_view3d(C);
+ ViewContext vc;
+ Nurb *nu;
+ BezTriple *bezt;
+ BPoint *bp;
+ int a, location[2], deselect;
+
+ if(!rv3d)
+ return OPERATOR_CANCELLED;
+
+ deselect= RNA_boolean_get(op->ptr, "deselect");
+ RNA_int_get_array(op->ptr, "location", location);
+
+ view3d_set_viewcontext(C, &vc);
+ findnearestNurbvert(&vc, 1, location, &nu, &bezt, &bp);
+
+ if(bezt) {
+ a= nu->pntsu;
+ bezt= nu->bezt;
+ while(a--) {
+ if(deselect) select_beztriple(bezt, DESELECT, 1, VISIBLE);
+ else select_beztriple(bezt, SELECT, 1, VISIBLE);
+ bezt++;
+ }
+ }
+ else if(bp) {
+ a= nu->pntsu*nu->pntsv;
+ bp= nu->bp;
+ while(a--) {
+ if(deselect) select_bpoint(bp, DESELECT, 1, VISIBLE);
+ else select_bpoint(bp, SELECT, 1, VISIBLE);
+ bp++;
+ }
+ }
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
+
+ return OPERATOR_FINISHED;
+}
+
+static int select_linked_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ ARegion *ar= CTX_wm_region(C);
+ int location[2];
+
+ location[0]= event->x - ar->winrct.xmin;
+ location[1]= event->y - ar->winrct.ymin;
+ RNA_int_set_array(op->ptr, "location", location);
+
+ return select_linked_exec(C, op);
+}
+
+void CURVE_OT_select_linked(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Select Linked";
+ ot->idname= "CURVE_OT_select_linked";
+
+ /* api callbacks */
+ ot->exec= select_linked_exec;
+ ot->invoke= select_linked_invoke;
+ ot->poll= ED_operator_editsurfcurve;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Deselect linked control points rather than selecting them.");
+ RNA_def_int_vector(ot->srna, "location", 2, NULL, 0, INT_MAX, "Location", "", 0, 16384);
+}
+
+/***************** select row operator **********************/
+
+static int select_row_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ ListBase *editnurb= curve_get_editcurve(obedit);
+ Curve *cu= obedit->data;
+ static BPoint *last=0;
+ static int direction=0;
+ Nurb *nu;
+ BPoint *bp;
+ int u = 0, v = 0, a, b, ok=0;
+
+ if(editnurb->first==0)
+ return OPERATOR_CANCELLED;
+ if(cu->lastselbp==NULL)
+ return OPERATOR_CANCELLED;
+
+ /* find the correct nurb and toggle with u of v */
+ for(nu= editnurb->first; nu; nu= nu->next) {
+ bp= nu->bp;
+ for(v=0; v<nu->pntsv; v++) {
+ for(u=0; u<nu->pntsu; u++, bp++) {
+ if(bp==cu->lastselbp) {
+ if(bp->f1 & SELECT) {
+ ok= 1;
+ break;
+ }
+ }
+ }
+ if(ok) break;
+ }
+
+ if(ok) {
+ if(last==cu->lastselbp) {
+ direction= 1-direction;
+ setflagsNurb(editnurb, 0);
+ }
+ last= cu->lastselbp;
+
+ bp= nu->bp;
+ for(a=0; a<nu->pntsv; a++) {
+ for(b=0; b<nu->pntsu; b++, bp++) {
+ if(direction) {
+ if(a==v) select_bpoint(bp, SELECT, 1, VISIBLE);
+ }
+ else {
+ if(b==u) select_bpoint(bp, SELECT, 1, VISIBLE);
+ }
+ }
+ }
+
+ break;
+ }
+ }
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_select_row(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Select Control Point Row";
+ ot->idname= "CURVE_OT_select_row";
+
+ /* api callbacks */
+ ot->exec= select_row_exec;
+ ot->poll= ED_operator_editsurf;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/***************** select next operator **********************/
+
+static int select_next_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ ListBase *editnurb= curve_get_editcurve(obedit);
+
+ select_adjacent_cp(editnurb, 1, 0, SELECT);
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_select_next(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Select Next";
+ ot->idname= "CURVE_OT_select_next";
+
+ /* api callbacks */
+ ot->exec= select_next_exec;
+ ot->poll= ED_operator_editcurve;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/***************** select previous operator **********************/
+
+static int select_previous_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ ListBase *editnurb= curve_get_editcurve(obedit);
+
+ select_adjacent_cp(editnurb, -1, 0, SELECT);
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_select_previous(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Select Previous";
+ ot->idname= "CURVE_OT_select_previous";
+
+ /* api callbacks */
+ ot->exec= select_previous_exec;
+ ot->poll= ED_operator_editcurve;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/***************** select more operator **********************/
+
+static int select_more_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ ListBase *editnurb= curve_get_editcurve(obedit);
+ Nurb *nu;
+ BPoint *bp, *tempbp;
+ int a;
+ short sel= 0;
+ short *selbpoints;
+
+ /* note that NURBS surface is a special case because we mimic */
+ /* the behaviour of "select more" of mesh tools. */
+ /* The algorithm is designed to work in planar cases so it */
+ /* may not be optimal always (example: end of NURBS sphere) */
+ if(obedit->type==OB_SURF) {
+ for(nu= editnurb->first; nu; nu= nu->next) {
+ a= nu->pntsu*nu->pntsv;
+ bp= nu->bp;
+ selbpoints= MEM_callocN(sizeof(short)*a-nu->pntsu, "selectlist");
+ while(a > 0) {
+ if((selbpoints[a]!=1) && (bp->hide==0) && (bp->f1 & SELECT)) {
+ /* upper control point */
+ if(a%nu->pntsu != 0) {
+ tempbp= bp-1;
+ if(!(tempbp->f1 & SELECT)) select_bpoint(tempbp, SELECT, 1, VISIBLE);
+ }
+
+ /* left control point. select only if it is not selected already */
+ if(a-nu->pntsu > 0) {
+ sel= 0;
+ tempbp= bp+nu->pntsu;
+ if(!(tempbp->f1 & SELECT)) sel= select_bpoint(tempbp, SELECT, 1, VISIBLE);
+ /* make sure selected bpoint is discarded */
+ if(sel == 1) selbpoints[a-nu->pntsu]= 1;
+ }
+
+ /* right control point */
+ if(a+nu->pntsu < nu->pntsu*nu->pntsv) {
+ tempbp= bp-nu->pntsu;
+ if(!(tempbp->f1 & SELECT)) select_bpoint(tempbp, SELECT, 1, VISIBLE);
+ }
+
+ /* lower control point. skip next bp in case selection was made */
+ if(a%nu->pntsu != 1) {
+ sel= 0;
+ tempbp= bp+1;
+ if(!(tempbp->f1 & 1)) sel= select_bpoint(tempbp, SELECT, 1, VISIBLE);
+ if(sel) {
+ bp++;
+ a--;
+ }
+ }
+ }
+
+ bp++;
+ a--;
+ }
+
+ MEM_freeN(selbpoints);
+ }
+ }
+ else {
+ select_adjacent_cp(editnurb, 1, 0, SELECT);
+ select_adjacent_cp(editnurb, -1, 0, SELECT);
+ }
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_select_more(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Select More";
+ ot->idname= "CURVE_OT_select_more";
+
+ /* api callbacks */
+ ot->exec= select_more_exec;
+ ot->poll= ED_operator_editsurfcurve;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/******************** select less operator *****************/
+
+/* basic method: deselect if control point doesn't have all neighbours selected */
+static int select_less_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ ListBase *editnurb= curve_get_editcurve(obedit);
+ Nurb *nu;
+ BPoint *bp;
+ BezTriple *bezt;
+ int a;
+ short sel= 0, lastsel= 0;
+ short *selbpoints;
+
+ if(obedit->type==OB_SURF) {
+ for(nu= editnurb->first; nu; nu= nu->next) {
+ a= nu->pntsu*nu->pntsv;
+ bp= nu->bp;
+ selbpoints= MEM_callocN(sizeof(short)*a, "selectlist");
+ while(a--) {
+ if((bp->hide==0) && (bp->f1 & SELECT)) {
+ sel= 0;
+
+ /* check if neighbours have been selected */
+ /* edges of surface are an exception */
+ if((a+1)%nu->pntsu==0) sel++;
+ else {
+ bp--;
+ if((selbpoints[a+1]==1) || ((bp->hide==0) && (bp->f1 & SELECT))) sel++;
+ bp++;
+ }
+
+ if((a+1)%nu->pntsu==1) sel++;
+ else {
+ bp++;
+ if((bp->hide==0) && (bp->f1 & SELECT)) sel++;
+ bp--;
+ }
+
+ if(a+1 > nu->pntsu*nu->pntsv-nu->pntsu) sel++;
+ else {
+ bp-=nu->pntsu;
+ if((selbpoints[a+nu->pntsu]==1) || ((bp->hide==0) && (bp->f1 & SELECT))) sel++;
+ bp+=nu->pntsu;
+ }
+
+ if(a < nu->pntsu) sel++;
+ else {
+ bp+=nu->pntsu;
+ if((bp->hide==0) && (bp->f1 & SELECT)) sel++;
+ bp-=nu->pntsu;
+ }
+
+ if(sel!=4) {
+ select_bpoint(bp, DESELECT, 1, VISIBLE);
+ selbpoints[a]= 1;
+ }
+ }
+ else lastsel= 0;
+
+ bp++;
+ }
+
+ MEM_freeN(selbpoints);
+ }
+ }
+ else {
+ for(nu= editnurb->first; nu; nu= nu->next) {
+ lastsel=0;
+ /* check what type of curve/nurb it is */
+ if((nu->type & 7)==CU_BEZIER) {
+ a= nu->pntsu;
+ bezt= nu->bezt;
+ while(a--) {
+ if((bezt->hide==0) && (bezt->f2 & SELECT)) {
+ if(lastsel==1) sel= 1;
+ else sel= 0;
+
+ /* check if neighbours have been selected */
+ /* first and last are exceptions */
+ if(a==nu->pntsu-1) sel++;
+ else {
+ bezt--;
+ if((bezt->hide==0) && (bezt->f2 & SELECT)) sel++;
+ bezt++;
+ }
+
+ if(a==0) sel++;
+ else {
+ bezt++;
+ if((bezt->hide==0) && (bezt->f2 & SELECT)) sel++;
+ bezt--;
+ }
+
+ if(sel!=2) {
+ select_beztriple(bezt, DESELECT, 1, VISIBLE);
+ lastsel= 1;
+ }
+ else lastsel= 0;
+ }
+ else lastsel= 0;
+
+ bezt++;
+ }
+ }
+ else {
+ a= nu->pntsu*nu->pntsv;
+ bp= nu->bp;
+ while(a--) {
+ if((lastsel==0) && (bp->hide==0) && (bp->f1 & SELECT)) {
+ if(lastsel!=0) sel= 1;
+ else sel= 0;
+
+ /* first and last are exceptions */
+ if(a==nu->pntsu*nu->pntsv-1) sel++;
+ else {
+ bp--;
+ if((bp->hide==0) && (bp->f1 & SELECT)) sel++;
+ bp++;
+ }
+
+ if(a==0) sel++;
+ else {
+ bp++;
+ if((bp->hide==0) && (bp->f1 & SELECT)) sel++;
+ bp--;
+ }
+
+ if(sel!=2) {
+ select_bpoint(bp, DESELECT, 1, VISIBLE);
+ lastsel= 1;
+ }
+ else lastsel= 0;
+ }
+ else lastsel= 0;
+
+ bp++;
+ }
+ }
+ }
+ }
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_select_less(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Select Less";
+ ot->idname= "CURVE_OT_select_less";
+
+ /* api callbacks */
+ ot->exec= select_less_exec;
+ ot->poll= ED_operator_editsurfcurve;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/********************** select random *********************/
+
+/* this function could be moved elsewhere as it can be reused in other parts of the source needing randomized list */
+/* returns list containing -1 in indices that have been left out of the list. otherwise index contains reference */
+/* to next index. basically *list contains a linked list */
+static void generate_pickable_list(int *list, int size, int pickamount)
+{
+ int i, j, removable;
+
+ BLI_srand( BLI_rand() ); /* random seed */
+
+ /* generate list in form 0->1, 1->2, 2->3, ... i-2->i-1, i->0 */
+ for(i=0; i<size; i++) {
+ if(i == size-1) list[i]= 0;
+ else list[i]= i+1;
+ }
+
+ for(i=0; i<size-pickamount; i++) {
+ removable= floor(BLI_frand()*(size-1)+0.5); /* with rounding. frand returns [0,1] */
+
+ /* seek proper item as the one randomly selected might not be appropriate */
+ for(j=0; j<size; j++, removable++) {
+ if(list[removable] != -1) break;
+ if(removable == size-1) removable= -1;
+ }
+
+ /* pick unwanted item out of the list */
+ list[list[removable]]= -1; /* mark former last as invalid */
+
+ if(list[removable] == size-1) list[removable]= 0;
+ else list[removable]= list[removable]+1;
+ }
+}
+
+static int select_random_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ ListBase *editnurb= curve_get_editcurve(obedit);
+ Nurb *nu;
+ BezTriple *bezt;
+ BPoint *bp;
+ float percent= RNA_float_get(op->ptr, "percent");
+ int amounttoselect, amountofcps, a, i, k= 0;
+ int *itemstobeselected;
+
+ if(percent == 0.0f)
+ return OPERATOR_CANCELLED;
+
+ amountofcps= count_curveverts_without_handles(editnurb);
+ itemstobeselected= MEM_callocN(sizeof(int) * amountofcps, "selectitems");
+ amounttoselect= floor(percent * amountofcps + 0.5);
+ generate_pickable_list(itemstobeselected, amountofcps, amounttoselect);
+
+ /* select elements */
+ for(i=1, nu= editnurb->first; nu; nu= nu->next) {
+ if((nu->type & 7)==CU_BEZIER) {
+ bezt= nu->bezt;
+ a= nu->pntsu;
+ while(a--) {
+ if(itemstobeselected[k] != -1) select_beztriple(bezt, SELECT, 1, VISIBLE);
+ k++;
+ bezt++;
+ }
+ }
+ else {
+ bp= nu->bp;
+ a= nu->pntsu*nu->pntsv;
+ while(a--) {
+ if(itemstobeselected[k] != -1) select_bpoint(bp, SELECT, 1, VISIBLE);
+ k++;
+ bp++;
+ }
+ }
+ }
+
+ MEM_freeN(itemstobeselected);
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_select_random(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Select Random";
+ ot->idname= "CURVE_OT_select_random";
+
+ /* api callbacks */
+ ot->exec= select_random_exec;
+ ot->invoke= WM_operator_redo;
+ ot->poll= ED_operator_editsurfcurve;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_float_percentage(ot->srna, "percent", 0.5f, 0.0f, 1.0f, "Percent", "Percentage of vertices to select randomly.", 0.0001f, 1.0f);
+}
+
+/********************** select every nth *********************/
+
+static int select_every_nth_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ ListBase *editnurb= curve_get_editcurve(obedit);
+ int n= RNA_int_get(op->ptr, "n");
+
+ select_adjacent_cp(editnurb, n, 1, SELECT);
+ select_adjacent_cp(editnurb, -n, 1, SELECT);
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_select_every_nth(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Select Every Nth";
+ ot->idname= "CURVE_OT_select_every_nth";
+
+ /* api callbacks */
+ ot->exec= select_every_nth_exec;
+ ot->invoke= WM_operator_redo;
+ ot->poll= ED_operator_editsurfcurve;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_int(ot->srna, "n", 2, 2, INT_MAX, "N", "Select every Nth element", 2, 25);
+}
+
+/********************** add duplicate operator *********************/
+
+static int duplicate_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit= CTX_data_edit_object(C);
+
+ adduplicateflagNurb(obedit, 1);
+
+ return OPERATOR_FINISHED;
+}
+
+static int duplicate_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ duplicate_exec(C, op);
+
+ RNA_int_set(op->ptr, "mode", TFM_TRANSLATION);
+ WM_operator_name_call(C, "TFM_OT_transform", WM_OP_INVOKE_REGION_WIN, op->ptr);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_duplicate(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Duplicate";
+ ot->idname= "CURVE_OT_duplicate";
+
+ /* api callbacks */
+ ot->exec= duplicate_exec;
+ ot->invoke= duplicate_invoke;
+ ot->poll= ED_operator_editsurfcurve;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* to give to transform */
+ RNA_def_int(ot->srna, "mode", TFM_TRANSLATION, 0, INT_MAX, "Mode", "", 0, INT_MAX);
+}
+
+/********************** delete operator *********************/
+
+static int delete_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+ ListBase *editnurb= curve_get_editcurve(obedit);
+ Nurb *nu, *next, *nu1;
+ BezTriple *bezt, *bezt1, *bezt2;
+ BPoint *bp, *bp1, *bp2;
+ int a, cut= 0, type= RNA_enum_get(op->ptr, "type");
+
+ if(obedit->type==OB_SURF) {
+ if(type==0) deleteflagNurb(C, op, 1);
+ else freeNurblist(editnurb);
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit);
+ DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
+
+ return OPERATOR_FINISHED;
+ }
+
+ if(type==0) {
+ /* first loop, can we remove entire pieces? */
+ nu= editnurb->first;
+ while(nu) {
+ next= nu->next;
+ if( (nu->type & 7)==CU_BEZIER ) {
+ bezt= nu->bezt;
+ a= nu->pntsu;
+ if(a) {
+ while(a) {
+ if( BEZSELECTED_HIDDENHANDLES(bezt) );
+ else break;
+ a--;
+ bezt++;
+ }
+ if(a==0) {
+ BLI_remlink(editnurb, nu);
+ freeNurb(nu); nu= NULL;
+ }
+ }
+ }
+ else {
+ bp= nu->bp;
+ a= nu->pntsu*nu->pntsv;
+ if(a) {
+ while(a) {
+ if(bp->f1 & SELECT);
+ else break;
+ a--;
+ bp++;
+ }
+ if(a==0) {
+ BLI_remlink(editnurb, nu);
+ freeNurb(nu); nu= NULL;
+ }
+ }
+ }
+
+ /* Never allow the order to exceed the number of points
+ - note, this is ok but changes unselected nurbs, disable for now */
+ /*
+ if ((nu!= NULL) && ((nu->type & 7)==CU_NURBS)) {
+ clamp_nurb_order_u(nu);
+ }
+ */
+ nu= next;
+ }
+ /* 2nd loop, delete small pieces: just for curves */
+ nu= editnurb->first;
+ while(nu) {
+ next= nu->next;
+ type= 0;
+ if( (nu->type & 7)==CU_BEZIER ) {
+ bezt= nu->bezt;
+ for(a=0;a<nu->pntsu;a++) {
+ if( BEZSELECTED_HIDDENHANDLES(bezt) ) {
+ memmove(bezt, bezt+1, (nu->pntsu-a-1)*sizeof(BezTriple));
+ nu->pntsu--;
+ a--;
+ type= 1;
+ }
+ else bezt++;
+ }
+ if(type) {
+ bezt1 =
+ (BezTriple*)MEM_mallocN((nu->pntsu) * sizeof(BezTriple), "delNurb");
+ memcpy(bezt1, nu->bezt, (nu->pntsu)*sizeof(BezTriple) );
+ MEM_freeN(nu->bezt);
+ nu->bezt= bezt1;
+ calchandlesNurb(nu);
+ }
+ }
+ else if(nu->pntsv==1) {
+ bp= nu->bp;
+
+ for(a=0;a<nu->pntsu;a++) {
+ if( bp->f1 & SELECT ) {
+ memmove(bp, bp+1, (nu->pntsu-a-1)*sizeof(BPoint));
+ nu->pntsu--;
+ a--;
+ type= 1;
+ }
+ else {
+ bp++;
+ }
+ }
+ if(type) {
+ bp1 = (BPoint*)MEM_mallocN(nu->pntsu * sizeof(BPoint), "delNurb2");
+ memcpy(bp1, nu->bp, (nu->pntsu)*sizeof(BPoint) );
+ MEM_freeN(nu->bp);
+ nu->bp= bp1;
+
+ /* Never allow the order to exceed the number of points\
+ - note, this is ok but changes unselected nurbs, disable for now */
+ /*
+ if ((nu->type & 7)==CU_NURBS) {
+ clamp_nurb_order_u(nu);
+ }*/
+ }
+ makeknots(nu, 1);
+ }
+ nu= next;
+ }
+ }
+ else if(type==1) { /* erase segment */
+ /* find the 2 selected points */
+ bezt1= bezt2= 0;
+ bp1= bp2= 0;
+ nu= editnurb->first;
+ nu1= 0;
+ while(nu) {
+ next= nu->next;
+ if( (nu->type & 7)==CU_BEZIER ) {
+ bezt= nu->bezt;
+ for(a=0; a<nu->pntsu-1; a++) {
+ if( BEZSELECTED_HIDDENHANDLES(bezt) ) {
+ bezt1= bezt;
+ bezt2= bezt+1;
+ if( (bezt2->f1 & SELECT) || (bezt2->f2 & SELECT) || (bezt2->f3 & SELECT) ) ;
+ else { /* maybe do not make cyclic */
+ if(a==0 && (nu->flagu & CU_CYCLIC) ) {
+ bezt2= bezt+(nu->pntsu-1);
+ if( (bezt2->f1 & SELECT) || (bezt2->f2 & SELECT) || (bezt2->f3 & SELECT) ) {
+ nu->flagu &= ~CU_CYCLIC;
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit);
+ DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
+ }
+ }
+
+ return OPERATOR_FINISHED;
+ }
+ cut= a;
+ nu1= nu;
+ break;
+ }
+ bezt++;
+ }
+ }
+ else if(nu->pntsv==1) {
+ bp= nu->bp;
+ for(a=0; a<nu->pntsu-1; a++) {
+ if( bp->f1 & SELECT ) {
+ bp1= bp;
+ bp2= bp+1;
+ if( bp2->f1 & 1 ) ;
+ else { /* maybe do not make cyclic */
+ if(a==0 && (nu->flagu & CU_CYCLIC) ) {
+ bp2= bp+(nu->pntsu-1);
+ if( bp2->f1 & SELECT ) {
+ nu->flagu &= ~CU_CYCLIC;
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit);
+ DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
+ }
+ }
+
+ return OPERATOR_FINISHED;
+ }
+ cut= a;
+ nu1= nu;
+ break;
+ }
+ bp++;
+ }
+ }
+ if(nu1) break;
+
+ nu= nu->next;
+ }
+ if(nu1) {
+ if(bezt1) {
+ if(nu1->pntsu==2) { /* remove completely */
+ BLI_remlink(editnurb, nu);
+ freeNurb(nu); nu = NULL;
+ }
+ else if(nu1->flagu & CU_CYCLIC) { /* cyclic */
+ bezt =
+ (BezTriple*)MEM_mallocN((cut+1) * sizeof(BezTriple), "delNurb1");
+ memcpy(bezt, nu1->bezt,(cut+1)*sizeof(BezTriple));
+ a= nu1->pntsu-cut-1;
+ memcpy(nu1->bezt, bezt2, a*sizeof(BezTriple));
+ memcpy(nu1->bezt+a, bezt, (cut+1)*sizeof(BezTriple));
+ nu1->flagu &= ~CU_CYCLIC;
+ MEM_freeN(bezt);
+ calchandlesNurb(nu);
+ }
+ else { /* add new curve */
+
+/* seems to be an error here... but where? (a can become zero) */
+
+ nu =
+ (Nurb*)MEM_mallocN(sizeof(Nurb), "delNurb2");
+ memcpy(nu, nu1, sizeof(Nurb));
+ BLI_addtail(editnurb, nu);
+ nu->bezt =
+ (BezTriple*)MEM_mallocN((cut+1) * sizeof(BezTriple), "delNurb3");
+ memcpy(nu->bezt, nu1->bezt,(cut+1)*sizeof(BezTriple));
+ a= nu1->pntsu-cut-1;
+
+ bezt =
+ (BezTriple*)MEM_mallocN(a * sizeof(BezTriple), "delNurb4");
+ memcpy(bezt, nu1->bezt+cut+1,a*sizeof(BezTriple));
+ MEM_freeN(nu1->bezt);
+ nu1->bezt= bezt;
+ nu1->pntsu= a;
+ nu->pntsu= cut+1;
+
+
+ calchandlesNurb(nu);
+ calchandlesNurb(nu1);
+ }
+ }
+ else if(bp1) {
+ if(nu1->pntsu==2) { /* remove completely */
+ BLI_remlink(editnurb, nu);
+ freeNurb(nu); nu= NULL;
+ }
+ else if(nu1->flagu & CU_CYCLIC) { /* cyclic */
+ bp =
+ (BPoint*)MEM_mallocN((cut+1) * sizeof(BPoint), "delNurb5");
+ memcpy(bp, nu1->bp,(cut+1)*sizeof(BPoint));
+ a= nu1->pntsu-cut-1;
+ memcpy(nu1->bp, bp2, a*sizeof(BPoint));
+ memcpy(nu1->bp+a, bp, (cut+1)*sizeof(BPoint));
+ nu1->flagu &= ~CU_CYCLIC;
+ MEM_freeN(bp);
+ }
+ else { /* add new curve */
+ nu = (Nurb*)MEM_mallocN(sizeof(Nurb), "delNurb6");
+ memcpy(nu, nu1, sizeof(Nurb));
+ BLI_addtail(editnurb, nu);
+ nu->bp =
+ (BPoint*)MEM_mallocN((cut+1) * sizeof(BPoint), "delNurb7");
+ memcpy(nu->bp, nu1->bp,(cut+1)*sizeof(BPoint));
+ a= nu1->pntsu-cut-1;
+ bp =
+ (BPoint*)MEM_mallocN(a * sizeof(BPoint), "delNurb8");
+ memcpy(bp, nu1->bp+cut+1,a*sizeof(BPoint));
+ MEM_freeN(nu1->bp);
+ nu1->bp= bp;
+ nu1->pntsu= a;
+ nu->pntsu= cut+1;
+ }
+ }
+ }
+ }
+ else if(type==2)
+ freeNurblist(editnurb);
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit);
+ DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
+
+ return OPERATOR_FINISHED;
+}
+
+static int delete_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ uiPopupMenu *pup;
+ uiLayout *layout;
+
+ if(obedit->type==OB_SURF) {
+ pup= uiPupMenuBegin(C, "Delete", 0);
+ layout= uiPupMenuLayout(pup);
+ uiItemEnumO(layout, NULL, 0, op->type->idname, "type", 0);
+ uiItemEnumO(layout, NULL, 0, op->type->idname, "type", 2);
+ uiPupMenuEnd(C, pup);
+ }
+ else {
+ pup= uiPupMenuBegin(C, "Delete", 0);
+ layout= uiPupMenuLayout(pup);
+ uiItemsEnumO(layout, op->type->idname, "type");
+ uiPupMenuEnd(C, pup);
+ }
+
+ return OPERATOR_CANCELLED;
+}
+
+void CURVE_OT_delete(wmOperatorType *ot)
+{
+ static EnumPropertyItem type_items[] = {
+ {0, "SELECTED", "Selected", ""},
+ {1, "SEGMENT", "Segment", ""},
+ {2, "ALL", "All", ""},
+ {0, NULL, NULL, NULL}};
+
+ /* identifiers */
+ ot->name= "Delete";
+ ot->idname= "CURVE_OT_delete";
+
+ /* api callbacks */
+ ot->exec= delete_exec;
+ ot->invoke= delete_invoke;
+ ot->poll= ED_operator_editsurfcurve;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_enum(ot->srna, "type", type_items, 0, "Type", "Which elements to delete.");
+}
+
+/********************** set smooth operator *********************/
+
+static int set_smooth_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ ListBase *editnurb= curve_get_editcurve(obedit);
+ Nurb *nu;
+ int clear= RNA_boolean_get(op->ptr, "clear");
+
+ if(obedit->type != OB_CURVE)
+ return OPERATOR_CANCELLED;
+
+ for(nu= editnurb->first; nu; nu= nu->next) {
+ if(isNurbsel(nu)) {
+ if(!clear) nu->flag |= CU_SMOOTH;
+ else nu->flag &= ~CU_SMOOTH;
+ }
+ }
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit);
+ DAG_object_flush_update(CTX_data_scene(C), obedit, OB_RECALC_DATA);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_smooth_set(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Set Smooth";
+ ot->idname= "CURVE_OT_smooth_set";
+
+ /* api callbacks */
+ ot->exec= set_smooth_exec;
+ ot->poll= ED_operator_editsurfcurve;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_boolean(ot->srna, "clear", 0, "Clear", "Clear smooth shading to solid for selection instead of enabling it.");
+}
+
+/************** join operator, to be used externally? ****************/
+
+int join_curve(bContext *C, wmOperator *op, int type)
+{
+ View3D *v3d= CTX_wm_view3d(C);
+ Scene *scene= CTX_data_scene(C);
+ Object *ob= CTX_data_edit_object(C);
+ Base *base, *nextb;
+ Curve *cu;
+ Nurb *nu, *newnu;
+ BezTriple *bezt;
+ BPoint *bp;
+ ListBase tempbase;
+ float imat[4][4], cmat[4][4];
+ int a;
+
+ // XXX not integrated yet, to be called by object/ module? */
+
+ if(object_data_is_libdata(ob)) {
+ BKE_report(op->reports, RPT_ERROR, "Can't edit external libdata");
+ return OPERATOR_CANCELLED;
+ }
+
+ if(ob->type!=type)
+ return 0;
+
+ tempbase.first= tempbase.last= 0;
+
+ /* trasnform all selected curves inverse in obact */
+ Mat4Invert(imat, ob->obmat);
+
+ for(base= FIRSTBASE; base; base=nextb) {
+ nextb= base->next;
+
+ if(TESTBASE(v3d, base)) {
+ if(base->object->type==type) {
+ if(base->object != ob) {
+
+ cu= base->object->data;
+
+ if(cu->nurb.first) {
+ /* watch it: switch order here really goes wrong */
+ Mat4MulMat4(cmat, base->object->obmat, imat);
+
+ nu= cu->nurb.first;
+ while(nu) {
+ newnu= duplicateNurb(nu);
+ BLI_addtail(&tempbase, newnu);
+
+ if( (bezt= newnu->bezt) ) {
+ a= newnu->pntsu;
+ while(a--) {
+ Mat4MulVecfl(cmat, bezt->vec[0]);
+ Mat4MulVecfl(cmat, bezt->vec[1]);
+ Mat4MulVecfl(cmat, bezt->vec[2]);
+ bezt++;
+ }
+ }
+ if( (bp= newnu->bp) ) {
+ a= newnu->pntsu*nu->pntsv;
+ while(a--) {
+ Mat4MulVecfl(cmat, bp->vec);
+ bp++;
+ }
+ }
+ nu= nu->next;
+ }
+ }
+
+ ED_base_object_free_and_unlink(scene, base);
+ }
+ }
+ }
+ }
+
+ cu= ob->data;
+ addlisttolist(&cu->nurb, &tempbase);
+
+ DAG_scene_sort(scene); // because we removed object(s), call before editmode!
+
+ ED_object_enter_editmode(C, EM_WAITCURSOR);
+ ED_object_exit_editmode(C, EM_FREEDATA|EM_WAITCURSOR);
+
+ // BIF_undo_push("Join");
+
+ return OPERATOR_FINISHED;
+}
+
+/************ add primitive, used by object/ module ****************/
+
+Nurb *add_nurbs_primitive(bContext *C, int type, int newname)
+{
+ static int xzproj= 0; /* this function calls itself... */
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+ ListBase *editnurb= curve_get_editcurve(obedit);
+ View3D *v3d= CTX_wm_view3d(C);
+ RegionView3D *rv3d= CTX_wm_region_view3d(C);
+ Nurb *nu = NULL;
+ BezTriple *bezt;
+ BPoint *bp;
+ float *curs, cent[3],vec[3],imat[3][3],mat[3][3];
+ float fac,cmat[3][3], grid;
+ int a, b, cutype, stype;
+
+ cutype= type & CU_TYPE; // poly, bezier, nurbs, etc
+ stype= type & CU_PRIMITIVE;
+
+ if (v3d) grid = v3d->grid;
+ else grid = 1.0;
+
+ /* imat and center and size */
+ if(obedit) {
+
+ Mat3CpyMat4(mat, obedit->obmat);
+ if(v3d) {
+ curs= give_cursor(scene, v3d);
+ VECCOPY(cent, curs);
+ }
+ else
+ cent[0]= cent[1]= cent[2]= 0.0f;
+
+ cent[0]-= obedit->obmat[3][0];
+ cent[1]-= obedit->obmat[3][1];
+ cent[2]-= obedit->obmat[3][2];
+
+ if(rv3d) {
+ if (!(newname) || U.flag & USER_ADD_VIEWALIGNED || !rv3d)
+ Mat3CpyMat4(imat, rv3d->viewmat);
+ else
+ Mat3One(imat);
+
+ Mat3MulVecfl(imat, cent);
+ Mat3MulMat3(cmat, imat, mat);
+ Mat3Inv(imat, cmat);
+ }
+ setflagsNurb(editnurb, 0);
+ }
+ else {
+ return NULL;
+ }
+
+ /* these types call this function to return a Nurb */
+ if (stype!=CU_PRIM_TUBE && stype!=CU_PRIM_DONUT) {
+ nu = (Nurb*)MEM_callocN(sizeof(Nurb), "addNurbprim");
+ nu->type= type;
+ nu->resolu= 4;
+ nu->resolv= 4;
+ }
+
+ switch(stype) {
+ case CU_PRIM_CURVE: /* curve */
+ nu->resolu= 12; /* set as 4 above */
+ if(newname) {
+ rename_id((ID *)obedit, "Curve");
+ rename_id((ID *)obedit->data, "Curve");
+ }
+ if(cutype==CU_BEZIER) {
+ nu->pntsu= 2;
+ nu->bezt =
+ (BezTriple*)MEM_callocN(2 * sizeof(BezTriple), "addNurbprim1");
+ bezt= nu->bezt;
+ bezt->h1= bezt->h2= HD_ALIGN;
+ bezt->f1= bezt->f2= bezt->f3= SELECT;
+ bezt->radius = 1.0;
+
+ for(a=0;a<3;a++) {
+ VECCOPY(bezt->vec[a], cent);
+ }
+ bezt->vec[1][0]+= -grid;
+ bezt->vec[0][0]+= -1.5*grid;
+ bezt->vec[0][1]+= -0.5*grid;
+ bezt->vec[2][0]+= -0.5*grid;
+ bezt->vec[2][1]+= 0.5*grid;
+ for(a=0;a<3;a++) Mat3MulVecfl(imat, bezt->vec[a]);
+
+ bezt++;
+ bezt->h1= bezt->h2= HD_ALIGN;
+ bezt->f1= bezt->f2= bezt->f3= SELECT;
+ bezt->radius = bezt->weight = 1.0;
+
+ for(a=0;a<3;a++) {
+ VECCOPY(bezt->vec[a], cent);
+ }
+ bezt->vec[1][0]+= grid;
+ for(a=0;a<3;a++) Mat3MulVecfl(imat, bezt->vec[a]);
+
+ calchandlesNurb(nu);
+ }
+ else {
+
+ nu->pntsu= 4;
+ nu->pntsv= 1;
+ nu->orderu= 4;
+ nu->bp= callocstructN(BPoint, 4, "addNurbprim3");
+
+ bp= nu->bp;
+ for(a=0;a<4;a++, bp++) {
+ VECCOPY(bp->vec, cent);
+ bp->vec[3]= 1.0;
+ bp->f1= SELECT;
+ bp->radius = bp->weight = 1.0;
+ }
+
+ bp= nu->bp;
+ bp->vec[0]+= -1.5*grid;
+ bp++;
+ bp->vec[0]+= -grid;
+ bp->vec[1]+= grid;
+ bp++;
+ bp->vec[0]+= grid;
+ bp->vec[1]+= grid;
+ bp++;
+ bp->vec[0]+= 1.5*grid;
+
+ bp= nu->bp;
+ for(a=0;a<4;a++, bp++) Mat3MulVecfl(imat,bp->vec);
+
+ if(cutype==CU_NURBS) {
+ nu->knotsu= 0; /* makeknots allocates */
+ makeknots(nu, 1);
+ }
+
+ }
+ break;
+ case CU_PRIM_PATH: /* 5 point path */
+ nu->pntsu= 5;
+ nu->pntsv= 1;
+ nu->orderu= 5;
+ nu->flagu= 2; /* endpoint */
+ nu->resolu= 8;
+ nu->bp= callocstructN(BPoint, 5, "addNurbprim3");
+
+ bp= nu->bp;
+ for(a=0;a<5;a++, bp++) {
+ VECCOPY(bp->vec, cent);
+ bp->vec[3]= 1.0;
+ bp->f1= SELECT;
+ bp->radius = bp->weight = 1.0;
+ }
+
+ bp= nu->bp;
+ bp->vec[0]+= -2.0*grid;
+ bp++;
+ bp->vec[0]+= -grid;
+ bp++; bp++;
+ bp->vec[0]+= grid;
+ bp++;
+ bp->vec[0]+= 2.0*grid;
+
+ bp= nu->bp;
+ for(a=0;a<5;a++, bp++) Mat3MulVecfl(imat,bp->vec);
+
+ if(cutype==CU_NURBS) {
+ nu->knotsu= 0; /* makeknots allocates */
+ makeknots(nu, 1);
+ }
+
+ break;
+ case CU_PRIM_CIRCLE: /* circle */
+ nu->resolu= 12; /* set as 4 above */
+ if(newname) {
+ rename_id((ID *)obedit, "CurveCircle");
+ rename_id((ID *)obedit->data, "CurveCircle");
+ }
+ if(cutype==CU_BEZIER) {
+ nu->pntsu= 4;
+ nu->bezt= callocstructN(BezTriple, 4, "addNurbprim1");
+ nu->flagu= CU_CYCLIC;
+ bezt= nu->bezt;
+
+ for(a=0;a<3;a++) {
+ VECCOPY(bezt->vec[a], cent);
+ }
+ bezt->h1= bezt->h2= HD_AUTO;
+ bezt->f1= bezt->f2= bezt->f3= SELECT;
+ bezt->vec[1][0]+= -grid;
+ for(a=0;a<3;a++) Mat3MulVecfl(imat,bezt->vec[a]);
+ bezt->radius = bezt->weight = 1.0;
+
+ bezt++;
+ for(a=0;a<3;a++) {
+ VECCOPY(bezt->vec[a], cent);
+ }
+ bezt->h1= bezt->h2= HD_AUTO;
+ bezt->f1= bezt->f2= bezt->f3= SELECT;
+ bezt->vec[1][1]+= grid;
+ for(a=0;a<3;a++) Mat3MulVecfl(imat,bezt->vec[a]);
+ bezt->radius = bezt->weight = 1.0;
+
+ bezt++;
+ for(a=0;a<3;a++) {
+ VECCOPY(bezt->vec[a], cent);
+ }
+ bezt->h1= bezt->h2= HD_AUTO;
+ bezt->f1= bezt->f2= bezt->f3= SELECT;
+ bezt->vec[1][0]+= grid;
+ for(a=0;a<3;a++) Mat3MulVecfl(imat,bezt->vec[a]);
+ bezt->radius = bezt->weight = 1.0;
+
+ bezt++;
+ for(a=0;a<3;a++) {
+ VECCOPY(bezt->vec[a], cent);
+ }
+ bezt->h1= bezt->h2= HD_AUTO;
+ bezt->f1= bezt->f2= bezt->f3= SELECT;
+ bezt->vec[1][1]+= -grid;
+ for(a=0;a<3;a++) Mat3MulVecfl(imat,bezt->vec[a]);
+ bezt->radius = bezt->weight = 1.0;
+
+ calchandlesNurb(nu);
+ }
+ else if( cutype==CU_NURBS ) { /* nurb */
+ nu->pntsu= 8;
+ nu->pntsv= 1;
+ nu->orderu= 4;
+ nu->bp= callocstructN(BPoint, 8, "addNurbprim6");
+ nu->flagu= CU_CYCLIC;
+ bp= nu->bp;
+
+ for(a=0; a<8; a++) {
+ bp->f1= SELECT;
+ VECCOPY(bp->vec, cent);
+
+ if(xzproj==0) {
+ bp->vec[0]+= nurbcircle[a][0]*grid;
+ bp->vec[1]+= nurbcircle[a][1]*grid;
+ }
+ else {
+ bp->vec[0]+= 0.25*nurbcircle[a][0]*grid-.75*grid;
+ bp->vec[2]+= 0.25*nurbcircle[a][1]*grid;
+ }
+ if(a & 1) bp->vec[3]= 0.25*sqrt(2.0);
+ else bp->vec[3]= 1.0;
+ Mat3MulVecfl(imat,bp->vec);
+ bp->radius = bp->weight = 1.0;
+
+ bp++;
+ }
+
+ makeknots(nu, 1);
+ }
+ break;
+ case CU_PRIM_PATCH: /* 4x4 patch */
+ if( cutype==CU_NURBS ) { /* nurb */
+ if(newname) {
+ rename_id((ID *)obedit, "Surf");
+ rename_id((ID *)obedit->data, "Surf");
+ }
+
+ nu->pntsu= 4;
+ nu->pntsv= 4;
+ nu->orderu= 4;
+ nu->orderv= 4;
+ nu->flag= CU_SMOOTH;
+ nu->bp= callocstructN(BPoint, 4*4, "addNurbprim6");
+ nu->flagu= 0;
+ nu->flagv= 0;
+ bp= nu->bp;
+
+ for(a=0; a<4; a++) {
+ for(b=0; b<4; b++) {
+ VECCOPY(bp->vec, cent);
+ bp->f1= SELECT;
+ fac= (float)a -1.5;
+ bp->vec[0]+= fac*grid;
+ fac= (float)b -1.5;
+ bp->vec[1]+= fac*grid;
+ if(a==1 || a==2) if(b==1 || b==2) {
+ bp->vec[2]+= grid;
+ }
+ Mat3MulVecfl(imat,bp->vec);
+ bp->vec[3]= 1.0;
+ bp++;
+ }
+ }
+
+ makeknots(nu, 1);
+ makeknots(nu, 2);
+ }
+ break;
+ case CU_PRIM_TUBE: /* tube */
+ if( cutype==CU_NURBS ) {
+ if(newname) {
+ rename_id((ID *)obedit, "SurfTube");
+ rename_id((ID *)obedit->data, "SurfTube");
+ }
+
+ nu= add_nurbs_primitive(C, CU_NURBS|CU_PRIM_CIRCLE, 0); /* circle */
+ nu->resolu= 4;
+ nu->flag= CU_SMOOTH;
+ BLI_addtail(editnurb, nu); /* temporal for extrude and translate */
+ vec[0]=vec[1]= 0.0;
+ vec[2]= -grid;
+ Mat3MulVecfl(imat, vec);
+ translateflagNurb(editnurb, 1, vec);
+ extrudeflagNurb(editnurb, 1);
+ vec[0]= -2*vec[0];
+ vec[1]= -2*vec[1];
+ vec[2]= -2*vec[2];
+ translateflagNurb(editnurb, 1, vec);
+
+ BLI_remlink(editnurb, nu);
+
+ a= nu->pntsu*nu->pntsv;
+ bp= nu->bp;
+ while(a-- >0) {
+ bp->f1 |= SELECT;
+ bp++;
+ }
+ }
+ break;
+ case CU_PRIM_SPHERE: /* sphere */
+ if( cutype==CU_NURBS ) {
+ if(newname) {
+ rename_id((ID *)obedit, "SurfSphere");
+ rename_id((ID *)obedit->data, "SurfSphere");
+ }
+
+ nu->pntsu= 5;
+ nu->pntsv= 1;
+ nu->orderu= 3;
+ nu->resolu= 4;
+ nu->resolv= 4;
+ nu->flag= CU_SMOOTH;
+ nu->bp= callocstructN(BPoint, 5, "addNurbprim6");
+ nu->flagu= 0;
+ bp= nu->bp;
+
+ for(a=0; a<5; a++) {
+ bp->f1= SELECT;
+ VECCOPY(bp->vec, cent);
+ bp->vec[0]+= nurbcircle[a][0]*grid;
+ bp->vec[2]+= nurbcircle[a][1]*grid;
+ if(a & 1) bp->vec[3]= 0.5*sqrt(2.0);
+ else bp->vec[3]= 1.0;
+ Mat3MulVecfl(imat,bp->vec);
+ bp++;
+ }
+ nu->flagu= 4;
+ makeknots(nu, 1);
+
+ BLI_addtail(editnurb, nu); /* temporal for spin */
+ if(newname && (U.flag & USER_ADD_VIEWALIGNED) == 0)
+ spin_nurb(C, scene, obedit, 0, 2);
+ else
+ spin_nurb(C, scene, obedit, 0, 0);
+
+ makeknots(nu, 2);
+
+ a= nu->pntsu*nu->pntsv;
+ bp= nu->bp;
+ while(a-- >0) {
+ bp->f1 |= SELECT;
+ bp++;
+ }
+ BLI_remlink(editnurb, nu);
+ }
+ break;
+ case CU_PRIM_DONUT: /* donut */
+ if( cutype==CU_NURBS ) {
+ if(newname) {
+ rename_id((ID *)obedit, "SurfDonut");
+ rename_id((ID *)obedit->data, "SurfDonut");
+ }
+
+ xzproj= 1;
+ nu= add_nurbs_primitive(C, CU_NURBS|CU_PRIM_CIRCLE, 0); /* circle */
+ xzproj= 0;
+ nu->resolu= 4;
+ nu->resolv= 4;
+ nu->flag= CU_SMOOTH;
+ BLI_addtail(editnurb, nu); /* temporal for extrude and translate */
+ if(newname && (U.flag & USER_ADD_VIEWALIGNED) == 0)
+ spin_nurb(C, scene, obedit, 0, 2);
+ else
+ spin_nurb(C, scene, obedit, 0, 0);
+
+ BLI_remlink(editnurb, nu);
+
+ a= nu->pntsu*nu->pntsv;
+ bp= nu->bp;
+ while(a-- >0) {
+ bp->f1 |= SELECT;
+ bp++;
+ }
+
+ }
+ break;
+ }
+
+ /* always do: */
+ nu->flag= CU_SMOOTH;
+
+ test2DNurb(nu);
+
+ return nu;
+}
+
+/***************** clear tilt operator ********************/
+
+static int clear_tilt_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ ListBase *editnurb= curve_get_editcurve(obedit);
+ Nurb *nu;
+ BezTriple *bezt;
+ BPoint *bp;
+ int a;
+
+ for(nu= editnurb->first; nu; nu= nu->next) {
+ if( nu->bezt ) {
+ bezt= nu->bezt;
+ a= nu->pntsu;
+ while(a--) {
+ if(BEZSELECTED_HIDDENHANDLES(bezt)) bezt->alfa= 0.0;
+ bezt++;
+ }
+ }
+ else if(nu->bp) {
+ bp= nu->bp;
+ a= nu->pntsu*nu->pntsv;
+ while(a--) {
+ if(bp->f1 & SELECT) bp->alfa= 0.0;
+ bp++;
+ }
+ }
+ }
+
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit);
+ DAG_object_flush_update(CTX_data_scene(C), obedit, OB_RECALC_DATA);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_tilt_clear(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Clear Tilt";
+ ot->idname= "CURVE_OT_tilt_clear";
+
+ /* api callbacks */
+ ot->exec= clear_tilt_exec;
+ ot->poll= ED_operator_editcurve;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/****************** undo for curves ****************/
+
+static void undoCurve_to_editCurve(void *lbu, void *lbe)
+{
+ ListBase *lb= lbu;
+ ListBase *editnurb= lbe;
+ Nurb *nu, *newnu;
+
+ freeNurblist(editnurb);
+
+ /* copy */
+ for(nu= lb->first; nu; nu= nu->next) {
+ newnu= duplicateNurb(nu);
+ BLI_addtail(editnurb, newnu);
+ }
+}
+
+static void *editCurve_to_undoCurve(void *lbe)
+{
+ ListBase *editnurb= lbe;
+ ListBase *lb;
+ Nurb *nu, *newnu;
+
+ lb= MEM_callocN(sizeof(ListBase), "listbase undo");
+
+ /* copy */
+ for(nu= editnurb->first; nu; nu= nu->next) {
+ newnu= duplicateNurb(nu);
+ BLI_addtail(lb, newnu);
+ }
+ return lb;
+}
+
+static void free_undoCurve(void *lbv)
+{
+ ListBase *lb= lbv;
+
+ freeNurblist(lb);
+ MEM_freeN(lb);
+}
+
+static void *get_data(bContext *C)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ return curve_get_editcurve(obedit);
+}
+
+/* and this is all the undo system needs to know */
+void undo_push_curve(bContext *C, char *name)
+{
+ undo_editmode_push(C, name, get_data, free_undoCurve, undoCurve_to_editCurve, editCurve_to_undoCurve, NULL);
+}
+
+/***************** XXX old cruft ********************/
+
+void default_curve_ipo(Scene *scene, Curve *cu)
+{
+#if 0 // XXX old animation system
+ IpoCurve *icu;
+ BezTriple *bezt;
+
+ if(cu->ipo) return;
+
+ cu->ipo= add_ipo(scene, "CurveIpo", ID_CU);
+
+ icu= MEM_callocN(sizeof(IpoCurve), "ipocurve");
+
+ icu->blocktype= ID_CU;
+ icu->adrcode= CU_SPEED;
+ icu->flag= IPO_VISIBLE|IPO_SELECT|IPO_AUTO_HORIZ;
+ set_icu_vars(icu);
+
+ BLI_addtail( &(cu->ipo->curve), icu);
+
+ icu->bezt= bezt= MEM_callocN(2*sizeof(BezTriple), "defaultipo");
+ icu->totvert= 2;
+
+ bezt->hide= IPO_BEZ;
+ bezt->f1=bezt->f2= bezt->f3= SELECT;
+ bezt->h1= bezt->h2= HD_AUTO;
+ bezt++;
+ bezt->vec[1][0]= 100.0;
+ bezt->vec[1][1]= 1.0;
+ bezt->hide= IPO_BEZ;
+ bezt->f1=bezt->f2= bezt->f3= SELECT;
+ bezt->h1= bezt->h2= HD_AUTO;
+
+ calchandles_ipocurve(icu);
+#endif // XXX old animation system
+}
+
diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c
new file mode 100644
index 00000000000..1b2c8ea6b11
--- /dev/null
+++ b/source/blender/editors/curve/editfont.c
@@ -0,0 +1,1625 @@
+/**
+ * $Id$
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <wchar.h>
+
+#ifndef WIN32
+#include <unistd.h>
+#else
+#include <io.h>
+#endif
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_arithb.h"
+
+#include "DNA_curve_types.h"
+#include "DNA_object_types.h"
+#include "DNA_vfont_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_text_types.h"
+#include "DNA_view3d_types.h"
+#include "DNA_userdef_types.h"
+
+#include "BKE_context.h"
+#include "BKE_curve.h"
+#include "BKE_depsgraph.h"
+#include "BKE_font.h"
+#include "BKE_global.h"
+#include "BKE_main.h"
+#include "BKE_object.h"
+#include "BKE_report.h"
+#include "BKE_utildefines.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_curve.h"
+#include "ED_object.h"
+#include "ED_screen.h"
+#include "ED_util.h"
+
+#include "curve_intern.h"
+
+#define MAXTEXT 32766
+
+/************************* utilities ******************************/
+
+static char findaccent(char char1, unsigned int code)
+{
+ char new= 0;
+
+ if(char1=='a') {
+ if(code=='`') new= 224;
+ else if(code==39) new= 225;
+ else if(code=='^') new= 226;
+ else if(code=='~') new= 227;
+ else if(code=='"') new= 228;
+ else if(code=='o') new= 229;
+ else if(code=='e') new= 230;
+ else if(code=='-') new= 170;
+ }
+ else if(char1=='c') {
+ if(code==',') new= 231;
+ if(code=='|') new= 162;
+ }
+ else if(char1=='e') {
+ if(code=='`') new= 232;
+ else if(code==39) new= 233;
+ else if(code=='^') new= 234;
+ else if(code=='"') new= 235;
+ }
+ else if(char1=='i') {
+ if(code=='`') new= 236;
+ else if(code==39) new= 237;
+ else if(code=='^') new= 238;
+ else if(code=='"') new= 239;
+ }
+ else if(char1=='n') {
+ if(code=='~') new= 241;
+ }
+ else if(char1=='o') {
+ if(code=='`') new= 242;
+ else if(code==39) new= 243;
+ else if(code=='^') new= 244;
+ else if(code=='~') new= 245;
+ else if(code=='"') new= 246;
+ else if(code=='/') new= 248;
+ else if(code=='-') new= 186;
+ else if(code=='e') new= 143;
+ }
+ else if(char1=='s') {
+ if(code=='s') new= 167;
+ }
+ else if(char1=='u') {
+ if(code=='`') new= 249;
+ else if(code==39) new= 250;
+ else if(code=='^') new= 251;
+ else if(code=='"') new= 252;
+ }
+ else if(char1=='y') {
+ if(code==39) new= 253;
+ else if(code=='"') new= 255;
+ }
+ else if(char1=='A') {
+ if(code=='`') new= 192;
+ else if(code==39) new= 193;
+ else if(code=='^') new= 194;
+ else if(code=='~') new= 195;
+ else if(code=='"') new= 196;
+ else if(code=='o') new= 197;
+ else if(code=='e') new= 198;
+ }
+ else if(char1=='C') {
+ if(code==',') new= 199;
+ }
+ else if(char1=='E') {
+ if(code=='`') new= 200;
+ else if(code==39) new= 201;
+ else if(code=='^') new= 202;
+ else if(code=='"') new= 203;
+ }
+ else if(char1=='I') {
+ if(code=='`') new= 204;
+ else if(code==39) new= 205;
+ else if(code=='^') new= 206;
+ else if(code=='"') new= 207;
+ }
+ else if(char1=='N') {
+ if(code=='~') new= 209;
+ }
+ else if(char1=='O') {
+ if(code=='`') new= 210;
+ else if(code==39) new= 211;
+ else if(code=='^') new= 212;
+ else if(code=='~') new= 213;
+ else if(code=='"') new= 214;
+ else if(code=='/') new= 216;
+ else if(code=='e') new= 141;
+ }
+ else if(char1=='U') {
+ if(code=='`') new= 217;
+ else if(code==39) new= 218;
+ else if(code=='^') new= 219;
+ else if(code=='"') new= 220;
+ }
+ else if(char1=='Y') {
+ if(code==39) new= 221;
+ }
+ else if(char1=='1') {
+ if(code=='4') new= 188;
+ if(code=='2') new= 189;
+ }
+ else if(char1=='3') {
+ if(code=='4') new= 190;
+ }
+ else if(char1==':') {
+ if(code=='-') new= 247;
+ }
+ else if(char1=='-') {
+ if(code==':') new= 247;
+ if(code=='|') new= 135;
+ if(code=='+') new= 177;
+ }
+ else if(char1=='|') {
+ if(code=='-') new= 135;
+ if(code=='=') new= 136;
+ }
+ else if(char1=='=') {
+ if(code=='|') new= 136;
+ }
+ else if(char1=='+') {
+ if(code=='-') new= 177;
+ }
+
+ if(new) return new;
+ else return char1;
+}
+
+
+void update_string(Curve *cu)
+{
+ EditFont *ef= cu->editfont;
+ int len;
+
+ // Free the old curve string
+ MEM_freeN(cu->str);
+
+ // Calculate the actual string length in UTF-8 variable characters
+ len = wcsleninu8(ef->textbuf);
+
+ // Alloc memory for UTF-8 variable char length string
+ cu->str = MEM_callocN(len + sizeof(wchar_t), "str");
+
+ // Copy the wchar to UTF-8
+ wcs2utf8s(cu->str, ef->textbuf);
+}
+
+static int insert_into_textbuf(Object *obedit, uintptr_t c)
+{
+ Curve *cu= obedit->data;
+
+ if(cu->len<MAXTEXT-1) {
+ EditFont *ef= cu->editfont;
+ int x;
+
+ for(x= cu->len; x>cu->pos; x--) ef->textbuf[x]= ef->textbuf[x-1];
+ for(x= cu->len; x>cu->pos; x--) ef->textbufinfo[x]= ef->textbufinfo[x-1];
+ ef->textbuf[cu->pos]= c;
+ ef->textbufinfo[cu->pos] = cu->curinfo;
+ ef->textbufinfo[cu->pos].kern = 0;
+ if(obedit->actcol>0)
+ ef->textbufinfo[cu->pos].mat_nr = obedit->actcol;
+ else
+ ef->textbufinfo[cu->pos].mat_nr = 0;
+
+ cu->pos++;
+ cu->len++;
+ ef->textbuf[cu->len]='\0';
+
+ update_string(cu);
+
+ return 1;
+ }
+ else
+ return 0;
+}
+
+static void text_update_edited(bContext *C, Scene *scene, Object *obedit, int recalc, int mode)
+{
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+
+ if(cu->pos)
+ cu->curinfo = ef->textbufinfo[cu->pos-1];
+ else
+ cu->curinfo = ef->textbufinfo[0];
+
+ if(obedit->totcol>0)
+ obedit->actcol= ef->textbufinfo[cu->pos-1].mat_nr;
+
+ update_string(cu);
+ BKE_text_to_curve(scene, obedit, mode);
+
+ if(recalc)
+ DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit);
+}
+
+/********************** insert lorem operator *********************/
+
+static int insert_lorem_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+ char *p, *p2;
+ int i;
+ static char *lastlorem;
+
+ if(lastlorem)
+ p= lastlorem;
+ else
+ p= ED_lorem;
+
+ i= rand()/(RAND_MAX/6)+4;
+
+ for(p2=p; *p2 && i; p2++) {
+ insert_into_textbuf(obedit, *p2);
+
+ if(*p2=='.')
+ i--;
+ }
+
+ lastlorem = p2+1;
+ if(strlen(lastlorem)<5)
+ lastlorem = ED_lorem;
+
+ insert_into_textbuf(obedit, '\n');
+ insert_into_textbuf(obedit, '\n');
+
+ DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit);
+
+ return OPERATOR_FINISHED;
+}
+
+void FONT_OT_insert_lorem(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Insert Lorem";
+ ot->idname= "FONT_OT_insert_lorem";
+
+ /* api callbacks */
+ ot->exec= insert_lorem_exec;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/******************* paste file operator ********************/
+
+/* note this handles both ascii and utf8 unicode, previously
+ * there were 3 functions that did effectively the same thing. */
+
+static int paste_file(bContext *C, ReportList *reports, char *filename)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+ FILE *fp;
+ int filelen;
+ char *strp;
+
+ fp= fopen(filename, "r");
+
+ if(!fp) {
+ if(reports)
+ BKE_reportf(reports, RPT_ERROR, "Failed to open file %s.", filename);
+ return OPERATOR_CANCELLED;
+ }
+
+ fseek(fp, 0L, SEEK_END);
+ filelen = ftell(fp);
+ fseek(fp, 0L, SEEK_SET);
+
+ strp= MEM_callocN(filelen+4, "tempstr");
+
+ // fread() instead of read(), because windows read() converts text
+ // to DOS \r\n linebreaks, causing double linebreaks in the 3d text
+ filelen = fread(strp, 1, filelen, fp);
+ fclose(fp);
+ strp[filelen]= 0;
+
+ if(cu->len+filelen<MAXTEXT) {
+ int tmplen;
+ wchar_t *mem = MEM_callocN((sizeof(wchar_t)*filelen)+(4*sizeof(wchar_t)), "temporary");
+ tmplen = utf8towchar(mem, strp);
+ wcscat(ef->textbuf, mem);
+ MEM_freeN(mem);
+ cu->len += tmplen;
+ cu->pos= cu->len;
+ }
+ MEM_freeN(strp);
+
+ text_update_edited(C, scene, obedit, 1, 0);
+
+ return OPERATOR_FINISHED;
+}
+
+static int paste_file_exec(bContext *C, wmOperator *op)
+{
+ char *filename;
+ int retval;
+
+ filename= RNA_string_get_alloc(op->ptr, "filename", NULL, 0);
+ retval= paste_file(C, op->reports, filename);
+ MEM_freeN(filename);
+
+ return retval;
+}
+
+static int paste_file_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ if(RNA_property_is_set(op->ptr, "filename"))
+ return paste_file_exec(C, op);
+
+ WM_event_add_fileselect(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+void FONT_OT_file_paste(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Paste File";
+ ot->idname= "FONT_OT_file_paste";
+
+ /* api callbacks */
+ ot->exec= paste_file_exec;
+ ot->invoke= paste_file_invoke;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_string_file_path(ot->srna, "filename", "", 0, "Filename", "File path of text file to load.");
+}
+
+/******************* paste buffer operator ********************/
+
+static int paste_buffer_exec(bContext *C, wmOperator *op)
+{
+ char *filename;
+
+#ifdef WIN32
+ filename= "C:\\windows\\temp\\cutbuf.txt";
+
+// The following is more likely to work on all Win32 installations.
+// suggested by Douglas Toltzman. Needs windows include files...
+/*
+ char tempFileName[MAX_PATH];
+ DWORD pathlen;
+ static const char cutbufname[]="cutbuf.txt";
+
+ if((pathlen=GetTempPath(sizeof(tempFileName),tempFileName)) > 0 &&
+ pathlen + sizeof(cutbufname) <= sizeof(tempFileName))
+ {
+ strcat(tempFileName,cutbufname);
+ filename= tempFilename;
+ }
+*/
+#else
+ filename= "/tmp/.cutbuffer";
+#endif
+
+ return paste_file(C, NULL, filename);
+}
+
+void FONT_OT_buffer_paste(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Paste Buffer";
+ ot->idname= "FONT_OT_buffer_paste";
+
+ /* api callbacks */
+ ot->exec= paste_buffer_exec;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/******************* text to object operator ********************/
+
+static void txt_add_object(bContext *C, TextLine *firstline, int totline, float offset[3])
+{
+ Scene *scene= CTX_data_scene(C);
+ Curve *cu;
+ Object *obedit;
+ Base *base;
+ struct TextLine *tmp;
+ int nchars = 0, a;
+
+ obedit= add_object(scene, OB_FONT);
+ base= scene->basact;
+
+ ED_object_base_init_from_view(C, base);
+ where_is_object(scene, obedit);
+
+ obedit->loc[0] += offset[0];
+ obedit->loc[1] += offset[1];
+ obedit->loc[2] += offset[2];
+
+ cu= obedit->data;
+ cu->vfont= get_builtin_font();
+ cu->vfont->id.us++;
+
+ for(tmp=firstline, a=0; cu->len<MAXTEXT && a<totline; tmp=tmp->next, a++)
+ nchars += strlen(tmp->line) + 1;
+
+ if(cu->str) MEM_freeN(cu->str);
+ if(cu->strinfo) MEM_freeN(cu->strinfo);
+
+ cu->str= MEM_callocN(nchars+4, "str");
+ cu->strinfo= MEM_callocN((nchars+4)*sizeof(CharInfo), "strinfo");
+
+ cu->str[0]= '\0';
+ cu->len= 0;
+ cu->pos= 0;
+
+ for(tmp=firstline, a=0; cu->len<MAXTEXT && a<totline; tmp=tmp->next, a++) {
+ strcat(cu->str, tmp->line);
+ cu->len+= strlen(tmp->line);
+
+ if(tmp->next) {
+ strcat(cu->str, "\n");
+ cu->len++;
+ }
+
+ cu->pos= cu->len;
+ }
+
+ WM_event_add_notifier(C, NC_OBJECT|NA_ADDED, obedit);
+}
+
+void ED_text_to_object(bContext *C, Text *text, int split_lines)
+{
+ RegionView3D *rv3d= CTX_wm_region_view3d(C);
+ TextLine *line;
+ float offset[3];
+ int linenum= 0;
+
+ if(!text || !text->lines.first) return;
+
+ if(split_lines) {
+ for(line=text->lines.first; line; line=line->next) {
+ /* skip lines with no text, but still make space for them */
+ if(line->line[0] == '\0') {
+ linenum++;
+ continue;
+ }
+
+ /* do the translation */
+ offset[0] = 0;
+ offset[1] = -linenum;
+ offset[2] = 0;
+
+ if(rv3d)
+ Mat4Mul3Vecfl(rv3d->viewinv, offset);
+
+ txt_add_object(C, line, 1, offset);
+
+ linenum++;
+ }
+ }
+ else {
+ offset[0]= 0.0f;
+ offset[1]= 0.0f;
+ offset[2]= 0.0f;
+
+ txt_add_object(C, text->lines.first, BLI_countlist(&text->lines), offset);
+ }
+}
+
+/********************** utilities ***************************/
+
+static short next_word(Curve *cu)
+{
+ short s;
+ for(s=cu->pos; (cu->str[s]) && (cu->str[s]!=' ') && (cu->str[s]!='\n') &&
+ (cu->str[s]!=1) && (cu->str[s]!='\r'); s++);
+ if(cu->str[s]) return(s+1); else return(s);
+}
+
+static short prev_word(Curve *cu)
+{
+ short s;
+
+ if(cu->pos==0) return(0);
+ for(s=cu->pos-2; (cu->str[s]) && (cu->str[s]!=' ') && (cu->str[s]!='\n') &&
+ (cu->str[s]!=1) && (cu->str[s]!='\r'); s--);
+ if(cu->str[s]) return(s+1); else return(s);
+}
+
+static int kill_selection(Object *obedit, int ins) /* 1 == new character */
+{
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+ int selend, selstart, direction;
+ int offset = 0;
+ int getfrom;
+
+ direction = BKE_font_getselection(obedit, &selstart, &selend);
+ if(direction) {
+ int size;
+ if(ins) offset = 1;
+ if(cu->pos >= selstart) cu->pos = selstart+offset;
+ if((direction == -1) && ins) {
+ selstart++;
+ selend++;
+ }
+ getfrom = selend+offset;
+ if(ins==0) getfrom++;
+ size = (cu->len * sizeof(wchar_t)) - (selstart * sizeof(wchar_t)) + (offset*sizeof(wchar_t));
+ memmove(ef->textbuf+selstart, ef->textbuf+getfrom, size);
+ memmove(ef->textbufinfo+selstart, ef->textbufinfo+getfrom, ((cu->len-selstart)+offset)*sizeof(CharInfo));
+ cu->len -= (selend-selstart)+offset;
+ cu->selstart = cu->selend = 0;
+ }
+
+ return(direction);
+}
+
+/******************* set style operator ********************/
+
+static EnumPropertyItem style_items[]= {
+ {CU_BOLD, "BOLD", "Bold", ""},
+ {CU_ITALIC, "ITALIC", "Italic", ""},
+ {CU_UNDERLINE, "UNDERLINE", "Underline", ""},
+ {0, NULL, NULL, NULL}};
+
+static int set_style(bContext *C, int style, int clear)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+ int i, selstart, selend;
+
+ if(!BKE_font_getselection(obedit, &selstart, &selend))
+ return OPERATOR_CANCELLED;
+
+ for(i=selstart; i<=selend; i++) {
+ if(clear)
+ ef->textbufinfo[i].flag &= ~style;
+ else
+ ef->textbufinfo[i].flag |= style;
+ }
+
+ DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit);
+
+ return OPERATOR_FINISHED;
+}
+
+static int set_style_exec(bContext *C, wmOperator *op)
+{
+ int style, clear;
+
+ style= RNA_enum_get(op->ptr, "style");
+ clear= RNA_enum_get(op->ptr, "clear");
+
+ return set_style(C, style, clear);
+}
+
+void FONT_OT_style_set(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Set Style";
+ ot->idname= "FONT_OT_style_set";
+
+ /* api callbacks */
+ ot->exec= set_style_exec;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_enum(ot->srna, "style", style_items, CU_BOLD, "Style", "Style to set selection to.");
+ RNA_def_boolean(ot->srna, "clear", 0, "Clear", "Clear style rather than setting it.");
+}
+
+/******************* toggle style operator ********************/
+
+static int toggle_style_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ Curve *cu= obedit->data;
+ int style, clear, selstart, selend;
+
+ if(!BKE_font_getselection(obedit, &selstart, &selend))
+ return OPERATOR_CANCELLED;
+
+ style= RNA_enum_get(op->ptr, "style");
+
+ cu->curinfo.flag ^= style;
+ clear= (cu->curinfo.flag & style) == 0;
+
+ return set_style(C, style, clear);
+}
+
+void FONT_OT_style_toggle(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Toggle Style";
+ ot->idname= "FONT_OT_style_toggle";
+
+ /* api callbacks */
+ ot->exec= toggle_style_exec;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_enum(ot->srna, "style", style_items, CU_BOLD, "Style", "Style to set selection to.");
+}
+
+/******************* set material operator ********************/
+
+static int set_material_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+ int i, mat_nr, selstart, selend;
+
+ if(!BKE_font_getselection(obedit, &selstart, &selend))
+ return OPERATOR_CANCELLED;
+
+ if(RNA_property_is_set(op->ptr, "index"))
+ mat_nr= RNA_int_get(op->ptr, "index");
+ else
+ mat_nr= obedit->actcol;
+
+ for(i=selstart; i<=selend; i++)
+ ef->textbufinfo[i].mat_nr = mat_nr;
+
+ DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit);
+
+ return OPERATOR_FINISHED;
+}
+
+void FONT_OT_material_set(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Set Material";
+ ot->idname= "FONT_OT_material_set";
+
+ /* api callbacks */
+ ot->exec= set_material_exec;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Material Index", "Material slot index.", 0, INT_MAX);
+}
+
+/******************* copy text operator ********************/
+
+static void copy_selection(Object *obedit)
+{
+ int selstart, selend;
+
+ if(BKE_font_getselection(obedit, &selstart, &selend)) {
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+
+ memcpy(ef->copybuf, ef->textbuf+selstart, ((selend-selstart)+1)*sizeof(wchar_t));
+ ef->copybuf[(selend-selstart)+1]=0;
+ memcpy(ef->copybufinfo, ef->textbufinfo+selstart, ((selend-selstart)+1)*sizeof(CharInfo));
+ }
+}
+
+static int copy_text_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit= CTX_data_edit_object(C);
+
+ copy_selection(obedit);
+
+ return OPERATOR_FINISHED;
+}
+
+void FONT_OT_text_copy(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Copy Text";
+ ot->idname= "FONT_OT_text_copy";
+
+ /* api callbacks */
+ ot->exec= copy_text_exec;
+ ot->poll= ED_operator_editfont;
+}
+
+/******************* cut text operator ********************/
+
+static int cut_text_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+ int selstart, selend;
+
+ if(!BKE_font_getselection(obedit, &selstart, &selend))
+ return OPERATOR_CANCELLED;
+
+ copy_selection(obedit);
+ kill_selection(obedit, 0);
+
+ text_update_edited(C, scene, obedit, 1, 0);
+
+ return OPERATOR_FINISHED;
+}
+
+void FONT_OT_text_cut(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Cut Text";
+ ot->idname= "FONT_OT_text_cut";
+
+ /* api callbacks */
+ ot->exec= cut_text_exec;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/******************* paste text operator ********************/
+
+static int paste_selection(Object *obedit, ReportList *reports)
+{
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+ int len= wcslen(ef->copybuf);
+
+ // Verify that the copy buffer => [copy buffer len] + cu->len < MAXTEXT
+ if(cu->len + len <= MAXTEXT) {
+ if(len) {
+ int size = (cu->len * sizeof(wchar_t)) - (cu->pos*sizeof(wchar_t)) + sizeof(wchar_t);
+ memmove(ef->textbuf+cu->pos+len, ef->textbuf+cu->pos, size);
+ memcpy(ef->textbuf+cu->pos, ef->copybuf, len * sizeof(wchar_t));
+
+ memmove(ef->textbufinfo+cu->pos+len, ef->textbufinfo+cu->pos, (cu->len-cu->pos+1)*sizeof(CharInfo));
+ memcpy(ef->textbufinfo+cu->pos, ef->copybufinfo, len*sizeof(CharInfo));
+
+ cu->len += len;
+ cu->pos += len;
+
+ return 1;
+ }
+ }
+ else
+ BKE_report(reports, RPT_WARNING, "Text too long.");
+
+ return 0;
+}
+
+static int paste_text_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+
+ if(!paste_selection(obedit, op->reports))
+ return OPERATOR_CANCELLED;
+
+ text_update_edited(C, scene, obedit, 1, 0);
+
+ return OPERATOR_FINISHED;
+}
+
+void FONT_OT_text_paste(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Paste Text";
+ ot->idname= "FONT_OT_text_paste";
+
+ /* api callbacks */
+ ot->exec= paste_text_exec;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/************************ move operator ************************/
+
+static EnumPropertyItem move_type_items[]= {
+ {LINE_BEGIN, "LINE_BEGIN", "Line Begin", ""},
+ {LINE_END, "LINE_END", "Line End", ""},
+ {PREV_CHAR, "PREVIOUS_CHARACTER", "Previous Character", ""},
+ {NEXT_CHAR, "NEXT_CHARACTER", "Next Character", ""},
+ {PREV_WORD, "PREVIOUS_WORD", "Previous Word", ""},
+ {NEXT_WORD, "NEXT_WORD", "Next Word", ""},
+ {PREV_LINE, "PREVIOUS_LINE", "Previous Line", ""},
+ {NEXT_LINE, "NEXT_LINE", "Next Line", ""},
+ {PREV_PAGE, "PREVIOUS_PAGE", "Previous Page", ""},
+ {NEXT_PAGE, "NEXT_PAGE", "Next Page", ""},
+ {0, NULL, NULL, NULL}};
+
+static int move_cursor(bContext *C, int type, int select)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+ int cursmove= 0;
+
+ switch(type) {
+ case LINE_BEGIN:
+ if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
+ while(cu->pos>0) {
+ if(ef->textbuf[cu->pos-1]=='\n') break;
+ if(ef->textbufinfo[cu->pos-1].flag & CU_WRAP ) break;
+ cu->pos--;
+ }
+ cursmove=FO_CURS;
+ break;
+
+ case LINE_END:
+ if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
+ while(cu->pos<cu->len) {
+ if(ef->textbuf[cu->pos]==0) break;
+ if(ef->textbuf[cu->pos]=='\n') break;
+ if(ef->textbufinfo[cu->pos].flag & CU_WRAP ) break;
+ cu->pos++;
+ }
+ cursmove=FO_CURS;
+ break;
+
+ case PREV_WORD:
+ if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
+ cu->pos= prev_word(cu);
+ cursmove= FO_CURS;
+ break;
+
+ case NEXT_WORD:
+ if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
+ cu->pos= next_word(cu);
+ cursmove= FO_CURS;
+ break;
+
+ case PREV_CHAR:
+ if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
+ cu->pos--;
+ cursmove=FO_CURS;
+ break;
+
+ case NEXT_CHAR:
+ if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
+ cu->pos++;
+ cursmove= FO_CURS;
+
+ break;
+
+ case PREV_LINE:
+ if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
+ cursmove=FO_CURSUP;
+ break;
+
+ case NEXT_LINE:
+ if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
+ cursmove= FO_CURSDOWN;
+ break;
+
+ case PREV_PAGE:
+ if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
+ cursmove=FO_PAGEUP;
+ break;
+
+ case NEXT_PAGE:
+ if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
+ cursmove=FO_PAGEDOWN;
+ break;
+ }
+
+ if(!cursmove)
+ return OPERATOR_CANCELLED;
+
+ if(select == 0) {
+ if(cu->selstart) {
+ cu->selstart = cu->selend = 0;
+ update_string(cu);
+ BKE_text_to_curve(scene, obedit, FO_SELCHANGE);
+ }
+ }
+
+ if(cu->pos>cu->len) cu->pos= cu->len;
+ else if(cu->pos>=MAXTEXT) cu->pos= MAXTEXT;
+ else if(cu->pos<0) cu->pos= 0;
+
+ text_update_edited(C, scene, obedit, select, cursmove);
+
+ if(select)
+ cu->selend = cu->pos;
+
+ return OPERATOR_FINISHED;
+}
+
+static int move_exec(bContext *C, wmOperator *op)
+{
+ int type= RNA_enum_get(op->ptr, "type");
+
+ return move_cursor(C, type, 0);
+}
+
+void FONT_OT_move(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Move Cursor";
+ ot->idname= "FONT_OT_move";
+
+ /* api callbacks */
+ ot->exec= move_exec;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_enum(ot->srna, "type", move_type_items, LINE_BEGIN, "Type", "Where to move cursor to.");
+}
+
+/******************* move select operator ********************/
+
+static int move_select_exec(bContext *C, wmOperator *op)
+{
+ int type= RNA_enum_get(op->ptr, "type");
+
+ return move_cursor(C, type, 1);
+}
+
+void FONT_OT_move_select(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Move Select";
+ ot->idname= "FONT_OT_move_select";
+
+ /* api callbacks */
+ ot->exec= move_select_exec;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_enum(ot->srna, "type", move_type_items, LINE_BEGIN, "Type", "Where to move cursor to, to make a selection.");
+}
+
+/************************* change spacing **********************/
+
+static int change_spacing_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+ int kern, delta= RNA_int_get(op->ptr, "delta");
+
+ kern = ef->textbufinfo[cu->pos-1].kern;
+ kern += delta;
+ CLAMP(kern, -20, 20);
+
+ if(ef->textbufinfo[cu->pos-1].kern == kern)
+ return OPERATOR_CANCELLED;
+
+ ef->textbufinfo[cu->pos-1].kern = kern;
+
+ text_update_edited(C, scene, obedit, 1, 0);
+
+ return OPERATOR_FINISHED;
+}
+
+void FONT_OT_change_spacing(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Change Spacing";
+ ot->idname= "FONT_OT_change_spacing";
+
+ /* api callbacks */
+ ot->exec= change_spacing_exec;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_int(ot->srna, "delta", 1, -20, 20, "Delta", "Amount to decrease or increasing character spacing with.", -20, 20);
+}
+
+/************************* change character **********************/
+
+static int change_character_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+ int character, delta= RNA_int_get(op->ptr, "delta");
+
+ if(cu->pos <= 0)
+ return OPERATOR_CANCELLED;
+
+ character= ef->textbuf[cu->pos - 1];
+ character += delta;
+ CLAMP(character, 0, 255);
+
+ if(character == ef->textbuf[cu->pos - 1])
+ return OPERATOR_CANCELLED;
+
+ ef->textbuf[cu->pos - 1]= character;
+
+ text_update_edited(C, scene, obedit, 1, 0);
+
+ return OPERATOR_FINISHED;
+}
+
+void FONT_OT_change_character(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Change Character";
+ ot->idname= "FONT_OT_change_character";
+
+ /* api callbacks */
+ ot->exec= change_character_exec;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_int(ot->srna, "delta", 1, -255, 255, "Delta", "Number to increase or decrease character code with.", -255, 255);
+}
+
+/******************* line break operator ********************/
+
+static int line_break_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+ int ctrl= RNA_enum_get(op->ptr, "ctrl");
+
+ if(ctrl) {
+ insert_into_textbuf(obedit, 1);
+ if(ef->textbuf[cu->pos]!='\n')
+ insert_into_textbuf(obedit, '\n');
+ }
+ else
+ insert_into_textbuf(obedit, '\n');
+
+ cu->selstart = cu->selend = 0;
+
+ text_update_edited(C, scene, obedit, 1, 0);
+
+ return OPERATOR_FINISHED;
+}
+
+void FONT_OT_line_break(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Line Break";
+ ot->idname= "FONT_OT_line_break";
+
+ /* api callbacks */
+ ot->exec= line_break_exec;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_boolean(ot->srna, "ctrl", 0, "Ctrl", ""); // XXX what is this?
+}
+
+/******************* delete operator **********************/
+
+static EnumPropertyItem delete_type_items[]= {
+ {DEL_ALL, "ALL", "All", ""},
+ {DEL_NEXT_CHAR, "NEXT_CHARACTER", "Next Character", ""},
+ {DEL_PREV_CHAR, "PREVIOUS_CHARACTER", "Previous Character", ""},
+ {DEL_SELECTION, "SELECTION", "Selection", ""},
+ {DEL_NEXT_SEL, "NEXT_OR_SELECTION", "Next or Selection", ""},
+ {DEL_PREV_SEL, "PREVIOUS_OR_SELECTION", "Previous or Selection", ""},
+ {0, NULL, NULL, NULL}};
+
+static int delete_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+ int x, selstart, selend, type= RNA_enum_get(op->ptr, "type");
+
+ if(cu->len == 0)
+ return OPERATOR_CANCELLED;
+
+ if(BKE_font_getselection(obedit, &selstart, &selend)) {
+ if(type == DEL_NEXT_SEL) type= DEL_SELECTION;
+ else if(type == DEL_PREV_SEL) type= DEL_SELECTION;
+ }
+ else {
+ if(type == DEL_NEXT_SEL) type= DEL_NEXT_CHAR;
+ else if(type == DEL_PREV_SEL) type= DEL_PREV_CHAR;
+ }
+
+ switch(type) {
+ case DEL_ALL:
+ cu->len = cu->pos = 0;
+ ef->textbuf[0]= 0;
+ break;
+ case DEL_SELECTION:
+ if(!kill_selection(obedit, 0))
+ return OPERATOR_CANCELLED;
+ break;
+ case DEL_PREV_CHAR:
+ if(cu->pos<=0)
+ return OPERATOR_CANCELLED;
+
+ for(x=cu->pos;x<=cu->len;x++)
+ ef->textbuf[x-1]= ef->textbuf[x];
+ for(x=cu->pos;x<=cu->len;x++)
+ ef->textbufinfo[x-1]= ef->textbufinfo[x];
+
+ cu->pos--;
+ ef->textbuf[--cu->len]='\0';
+ break;
+ case DEL_NEXT_CHAR:
+ if(cu->pos>=cu->len)
+ return OPERATOR_CANCELLED;
+
+ for(x=cu->pos;x<cu->len;x++)
+ ef->textbuf[x]= ef->textbuf[x+1];
+ for(x=cu->pos;x<cu->len;x++)
+ ef->textbufinfo[x]= ef->textbufinfo[x+1];
+
+ ef->textbuf[--cu->len]='\0';
+ break;
+ default:
+ return OPERATOR_CANCELLED;
+ }
+
+ text_update_edited(C, scene, obedit, 1, 0);
+
+ return OPERATOR_FINISHED;
+}
+
+void FONT_OT_delete(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Delete";
+ ot->idname= "FONT_OT_delete";
+
+ /* api callbacks */
+ ot->exec= delete_exec;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_enum(ot->srna, "type", delete_type_items, DEL_ALL, "Type", "Which part of the text to delete.");
+}
+
+/*********************** insert text operator *************************/
+
+static int insert_text_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+ char *inserted_utf8;
+ wchar_t *inserted_text, first;
+ int len;
+
+ if(!RNA_property_is_set(op->ptr, "text"))
+ return OPERATOR_CANCELLED;
+
+ inserted_utf8= RNA_string_get_alloc(op->ptr, "text", NULL, 0);
+ len= strlen(inserted_utf8);
+
+ inserted_text= MEM_callocN(sizeof(wchar_t)*(len+1), "FONT_insert_text");
+ utf8towchar(inserted_text, inserted_utf8);
+ first= inserted_text[0];
+
+ MEM_freeN(inserted_text);
+ MEM_freeN(inserted_utf8);
+
+ if(!first)
+ return OPERATOR_CANCELLED;
+
+ insert_into_textbuf(obedit, first);
+ kill_selection(obedit, 1);
+ text_update_edited(C, scene, obedit, 1, 0);
+
+ return OPERATOR_FINISHED;
+}
+
+static int insert_text_invoke(bContext *C, wmOperator *op, wmEvent *evt)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+ static int accentcode= 0;
+ uintptr_t ascii = evt->ascii;
+ int alt= evt->alt, shift= evt->shift, ctrl= evt->ctrl;
+ int event= evt->type, val= evt->val;
+ wchar_t inserted_text[2]= {0};
+
+ if(RNA_property_is_set(op->ptr, "text"))
+ return insert_text_exec(C, op);
+
+ /* tab should exit editmode, but we allow it to be typed using modifier keys */
+ if(event==TABKEY) {
+ if((alt||ctrl||shift) == 0)
+ return OPERATOR_PASS_THROUGH;
+ else
+ ascii= 9;
+ }
+ else if(event==BACKSPACEKEY)
+ ascii= 0;
+
+ if(val && ascii) {
+ /* handle case like TAB (== 9) */
+ if((ascii > 31 && ascii < 254 && ascii != 127) || (ascii==13) || (ascii==10) || (ascii==8)) {
+ if(accentcode) {
+ if(cu->pos>0) {
+ inserted_text[0]= findaccent(ef->textbuf[cu->pos-1], ascii);
+ ef->textbuf[cu->pos-1]= inserted_text[0];
+ }
+ accentcode= 0;
+ }
+ else if(cu->len<MAXTEXT-1) {
+ if(alt) {
+ /* might become obsolete, apple has default values for this, other OS's too? */
+ if(ascii=='t') ascii= 137;
+ else if(ascii=='c') ascii= 169;
+ else if(ascii=='f') ascii= 164;
+ else if(ascii=='g') ascii= 176;
+ else if(ascii=='l') ascii= 163;
+ else if(ascii=='r') ascii= 174;
+ else if(ascii=='s') ascii= 223;
+ else if(ascii=='y') ascii= 165;
+ else if(ascii=='.') ascii= 138;
+ else if(ascii=='1') ascii= 185;
+ else if(ascii=='2') ascii= 178;
+ else if(ascii=='3') ascii= 179;
+ else if(ascii=='%') ascii= 139;
+ else if(ascii=='?') ascii= 191;
+ else if(ascii=='!') ascii= 161;
+ else if(ascii=='x') ascii= 215;
+ else if(ascii=='>') ascii= 187;
+ else if(ascii=='<') ascii= 171;
+ }
+
+ inserted_text[0]= ascii;
+ insert_into_textbuf(obedit, ascii);
+ }
+
+ kill_selection(obedit, 1);
+ text_update_edited(C, scene, obedit, 1, 0);
+ }
+ else {
+ inserted_text[0]= ascii;
+ insert_into_textbuf(obedit, ascii);
+ text_update_edited(C, scene, obedit, 1, 0);
+ }
+ }
+ else if(val && event == BACKSPACEKEY) {
+ if(alt && cu->len!=0 && cu->pos>0)
+ accentcode= 1;
+
+ return OPERATOR_PASS_THROUGH;
+ }
+ else
+ return OPERATOR_PASS_THROUGH;
+
+ if(inserted_text[0]) {
+ /* store as utf8 in RNA string */
+ char inserted_utf8[8] = {0};
+
+ wcs2utf8s(inserted_utf8, inserted_text);
+ RNA_string_set(op->ptr, "text", inserted_utf8);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void FONT_OT_text_insert(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Insert Text";
+ ot->idname= "FONT_OT_text_insert";
+
+ /* api callbacks */
+ ot->exec= insert_text_exec;
+ ot->invoke= insert_text_invoke;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_string(ot->srna, "text", "", 0, "Text", "Text to insert at the cursor position.");
+}
+
+/***************** editmode enter/exit ********************/
+
+void make_editText(Object *obedit)
+{
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+
+ if(ef==NULL) {
+ ef= cu->editfont= MEM_callocN(sizeof(EditFont), "editfont");
+
+ ef->textbuf= MEM_callocN((MAXTEXT+4)*sizeof(wchar_t), "texteditbuf");
+ ef->textbufinfo= MEM_callocN((MAXTEXT+4)*sizeof(CharInfo), "texteditbufinfo");
+ ef->copybuf= MEM_callocN((MAXTEXT+4)*sizeof(wchar_t), "texteditcopybuf");
+ ef->copybufinfo= MEM_callocN((MAXTEXT+4)*sizeof(CharInfo), "texteditcopybufinfo");
+ ef->oldstr= MEM_callocN((MAXTEXT+4)*sizeof(wchar_t), "oldstrbuf");
+ ef->oldstrinfo= MEM_callocN((MAXTEXT+4)*sizeof(wchar_t), "oldstrbuf");
+ }
+
+ // Convert the original text to wchar_t
+ utf8towchar(ef->textbuf, cu->str);
+ wcscpy(ef->oldstr, ef->textbuf);
+
+ cu->len= wcslen(ef->textbuf);
+
+ memcpy(ef->textbufinfo, cu->strinfo, (cu->len)*sizeof(CharInfo));
+ memcpy(ef->oldstrinfo, cu->strinfo, (cu->len)*sizeof(CharInfo));
+
+ if(cu->pos>cu->len) cu->pos= cu->len;
+
+ if(cu->pos)
+ cu->curinfo = ef->textbufinfo[cu->pos-1];
+ else
+ cu->curinfo = ef->textbufinfo[0];
+
+ // Convert to UTF-8
+ update_string(cu);
+}
+
+void load_editText(Object *obedit)
+{
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+
+ MEM_freeN(ef->oldstr);
+ ef->oldstr= NULL;
+ MEM_freeN(ef->oldstrinfo);
+ ef->oldstrinfo= NULL;
+
+ update_string(cu);
+
+ if(cu->strinfo)
+ MEM_freeN(cu->strinfo);
+ cu->strinfo= MEM_callocN((cu->len+4)*sizeof(CharInfo), "texteditinfo");
+ memcpy(cu->strinfo, ef->textbufinfo, (cu->len)*sizeof(CharInfo));
+
+ cu->len= strlen(cu->str);
+
+ /* this memory system is weak... */
+
+ if(cu->selboxes) {
+ MEM_freeN(cu->selboxes);
+ cu->selboxes= NULL;
+ }
+}
+
+void free_editText(Object *obedit)
+{
+ BKE_free_editfont((Curve *)obedit->data);
+}
+
+/********************** set case operator *********************/
+
+static EnumPropertyItem case_items[]= {
+ {CASE_LOWER, "LOWER", "Lower", ""},
+ {CASE_UPPER, "UPPER", "Upper", ""},
+ {0, NULL, NULL, NULL}};
+
+static int set_case(bContext *C, int ccase)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+ wchar_t *str;
+ int len;
+
+ len= wcslen(ef->textbuf);
+ str= ef->textbuf;
+ while(len) {
+ if(*str>='a' && *str<='z')
+ *str-= 32;
+ len--;
+ str++;
+ }
+
+ if(ccase == CASE_LOWER) {
+ len= wcslen(ef->textbuf);
+ str= ef->textbuf;
+ while(len) {
+ if(*str>='A' && *str<='Z') {
+ *str+= 32;
+ }
+ len--;
+ str++;
+ }
+ }
+
+ text_update_edited(C, scene, obedit, 1, 0);
+
+ return OPERATOR_FINISHED;
+}
+
+static int set_case_exec(bContext *C, wmOperator *op)
+{
+ return set_case(C, RNA_enum_get(op->ptr, "case"));
+}
+
+void FONT_OT_case_set(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Set Case";
+ ot->idname= "FONT_OT_case_set";
+
+ /* api callbacks */
+ ot->exec= set_case_exec;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_enum(ot->srna, "case", case_items, CASE_LOWER, "Case", "Lower or upper case.");
+}
+
+/********************** toggle case operator *********************/
+
+static int toggle_case_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+ wchar_t *str;
+ int len, ccase= CASE_UPPER;
+
+ len= wcslen(ef->textbuf);
+ str= ef->textbuf;
+ while(len) {
+ if(*str>='a' && *str<='z') {
+ ccase= CASE_LOWER;
+ break;
+ }
+
+ len--;
+ str++;
+ }
+
+ return set_case(C, ccase);
+}
+
+void FONT_OT_case_toggle(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Toggle Case";
+ ot->idname= "FONT_OT_case_toggle";
+
+ /* api callbacks */
+ ot->exec= toggle_case_exec;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/* **************** undo for font object ************** */
+
+static void undoFont_to_editFont(void *strv, void *ecu)
+{
+ Curve *cu= (Curve *)ecu;
+ EditFont *ef= cu->editfont;
+ char *str= strv;
+
+ cu->pos= *((short *)str);
+ cu->len= *((short *)(str+2));
+
+ memcpy(ef->textbuf, str+4, (cu->len+1)*sizeof(wchar_t));
+ memcpy(ef->textbufinfo, str+4 + (cu->len+1)*sizeof(wchar_t), cu->len*sizeof(CharInfo));
+
+ cu->selstart = cu->selend = 0;
+
+ update_string(cu);
+}
+
+static void *editFont_to_undoFont(void *ecu)
+{
+ Curve *cu= (Curve *)ecu;
+ EditFont *ef= cu->editfont;
+ char *str;
+
+ // The undo buffer includes [MAXTEXT+6]=actual string and [MAXTEXT+4]*sizeof(CharInfo)=charinfo
+ str= MEM_callocN((MAXTEXT+6)*sizeof(wchar_t) + (MAXTEXT+4)*sizeof(CharInfo), "string undo");
+
+ // Copy the string and string information
+ memcpy(str+4, ef->textbuf, (cu->len+1)*sizeof(wchar_t));
+ memcpy(str+4 + (cu->len+1)*sizeof(wchar_t), ef->textbufinfo, cu->len*sizeof(CharInfo));
+
+ *((short *)str)= cu->pos;
+ *((short *)(str+2))= cu->len;
+
+ return str;
+}
+
+static void free_undoFont(void *strv)
+{
+ MEM_freeN(strv);
+}
+
+static void *get_undoFont(bContext *C)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ if(obedit && obedit->type==OB_FONT) {
+ return obedit->data;
+ }
+ return NULL;
+}
+
+/* and this is all the undo system needs to know */
+void undo_push_font(bContext *C, char *name)
+{
+ undo_editmode_push(C, name, get_undoFont, free_undoFont, undoFont_to_editFont, editFont_to_undoFont, NULL);
+}
+
diff --git a/source/blender/editors/curve/lorem.c b/source/blender/editors/curve/lorem.c
new file mode 100644
index 00000000000..e552f99d0e8
--- /dev/null
+++ b/source/blender/editors/curve/lorem.c
@@ -0,0 +1,513 @@
+/**
+ * $Id$
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "curve_intern.h"
+
+char *ED_lorem =
+"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. "
+"Aliquam tristique interdum sem. "
+"Nullam pretium, tortor non euismod varius, nulla odio sodales nulla, at bibendum lorem metus sed nulla. "
+"Vestibulum in lectus at pede blandit viverra. "
+"Fusce scelerisque ipsum nec enim. "
+"Fusce euismod nunc id enim. "
+"In venenatis cursus arcu. "
+"Aenean quis dui. "
+"Maecenas laoreet. "
+"Nulla tempor, arcu pulvinar pretium suscipit, tortor wisi dapibus libero, id ornare felis ipsum suscipit purus. "
+"Maecenas ipsum. "
+"Morbi cursus. "
+"Vestibulum diam purus, commodo et, convallis eu, posuere at, ligula. "
+"Nulla aliquam aliquet lorem. "
+"Nunc et mauris hendrerit est bibendum suscipit. "
+"Donec pellentesque libero eu nisl. "
+"Pellentesque eget libero. "
+"Donec tempus ipsum sed quam. "
+"Sed blandit nunc quis enim. "
+"Quisque lectus diam, adipiscing hendrerit, placerat non, pulvinar id, felis. "
+"In congue magna sit amet urna. "
+"Nunc non augue sed nisl dictum laoreet. "
+"Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. "
+"In venenatis dapibus massa. "
+"Nulla hendrerit sapien et quam. "
+"Nunc ac magna lobortis tellus tincidunt posuere. "
+"Cras augue mauris, mattis lobortis, fermentum at, semper ac, tellus. "
+"Cras vitae ligula sit amet sem posuere iaculis. "
+"Aliquam condimentum eleifend felis. "
+"Ut sit amet sapien. "
+"Suspendisse potenti. "
+"Mauris urna. "
+"Ut eu enim eu ante porta vestibulum. "
+"Aenean scelerisque est ac felis. "
+"Suspendisse auctor. "
+"Nunc pellentesque. "
+"Morbi laoreet ante et nibh. "
+"Donec feugiat arcu eget enim. "
+"Morbi vehicula tortor ac ipsum. "
+"Quisque lacus arcu, elementum ac, faucibus vel, posuere id, est. "
+"Proin commodo gravida sem. "
+"Vivamus tincidunt vehicula libero. "
+"Phasellus wisi. "
+"Maecenas pretium tellus eu sapien. "
+"Nunc sit amet nunc. "
+"In hac habitasse platea dictumst. "
+"Aenean dictum neque sed tortor. "
+"Donec et erat. "
+"Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed justo turpis, scelerisque ut, mattis sit amet, ornare rutrum, massa. "
+"Vestibulum bibendum enim sit amet velit. "
+"Vivamus tellus ipsum, luctus ut, consectetuer vitae, dignissim non, ligula. "
+"Phasellus lacinia wisi at est. "
+"Donec elit wisi, commodo non, placerat in, convallis id, elit. "
+"Nunc dolor dolor, vestibulum id, bibendum vitae, lacinia id, erat. "
+"Cras sit amet eros. "
+"Suspendisse suscipit lobortis lectus. "
+"Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. "
+"Cras orci. "
+"Praesent massa urna, lobortis semper, auctor a, pretium vitae, tellus. "
+"Curabitur at purus. "
+"Morbi tortor quam, imperdiet venenatis, egestas a, cursus eu, est. "
+"Nunc interdum lectus sit amet libero. "
+"Quisque dignissim placerat ligula. "
+"Nunc porttitor posuere arcu. "
+"Mauris faucibus quam at massa. "
+"Vivamus sodales aliquet mauris. "
+"In id ante. "
+"Pellentesque varius ipsum in arcu. "
+"Fusce mauris lacus, tristique ac, lobortis quis, lobortis luctus, pede. "
+"Sed wisi. "
+"Vestibulum mattis. "
+"Maecenas hendrerit sem nec purus. "
+"Proin id quam. "
+"Cras nec mauris. "
+"Integer orci. "
+"Nullam dui sem, molestie sed, egestas quis, cursus in, magna. "
+"Mauris neque lacus, consectetuer nec, sagittis eu, porttitor eget, dui. "
+"Fusce consectetuer. "
+"Donec nec tellus quis leo lobortis ullamcorper. "
+"Etiam metus urna, aliquet pretium, ultrices eu, cursus ut, turpis. "
+"Morbi bibendum vehicula lectus. "
+"Sed non ante vitae arcu pellentesque tempor. "
+"Fusce sed ligula in sem tempor imperdiet. "
+"Aliquam vel est. "
+"Phasellus sollicitudin sollicitudin nibh. "
+"Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. "
+"Sed vel leo nec eros blandit imperdiet. "
+"Nulla facilisi. "
+"Suspendisse lobortis, dui ut fringilla hendrerit, justo purus ullamcorper ligula, ultricies ultrices dolor enim in libero. "
+"Sed elementum, pede eget porta convallis, dui nulla dignissim pede, eget vehicula odio ante at sem. "
+"Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. "
+"Sed ullamcorper tincidunt ipsum. "
+"Fusce risus nibh, accumsan sit amet, tempus eget, tristique ac, neque. "
+"Sed quis lorem ut tortor facilisis fermentum. "
+"Fusce pulvinar quam sit amet ipsum. "
+"Morbi ac elit quis tellus malesuada blandit. "
+"Maecenas suscipit sollicitudin sem. "
+"Nam sed eros vel lacus lobortis congue. "
+"Proin interdum nunc lobortis orci. "
+"Donec egestas enim eu odio. "
+"Vestibulum id metus. "
+"Pellentesque auctor, sem varius luctus tempus, libero magna cursus neque, et porttitor diam diam quis purus. "
+"Vestibulum sit amet dolor. "
+"Nulla in magna. "
+"Cras id diam at lectus faucibus placerat. "
+"Nunc porta posuere sapien. "
+"Etiam scelerisque. "
+"Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. "
+"Aliquam pellentesque mi sed tortor. "
+"Maecenas eleifend diam non urna. "
+"Donec luctus pharetra tellus. "
+"Proin ac ipsum eget libero bibendum volutpat. "
+"Cras id tellus. "
+"Nulla non risus. "
+"In dolor. "
+"Fusce scelerisque quam in massa ultrices porta. "
+"Morbi ut dolor eu massa egestas condimentum. "
+"Mauris vulputate. "
+"In hac habitasse platea dictumst. "
+"Suspendisse potenti. "
+"Mauris vehicula leo in tortor. "
+"Mauris vel erat a urna laoreet semper. "
+"Pellentesque ut metus ac tellus commodo eleifend. "
+"Suspendisse quis urna. "
+"Curabitur lacinia dignissim dui. "
+"Nam nec ante. "
+"In id enim. "
+"Aenean mattis enim. "
+"In ut neque porttitor risus hendrerit tincidunt. "
+"Suspendisse potenti. "
+"Ut vestibulum lectus vitae tortor. "
+"Duis velit. "
+"Nulla facilisi. "
+"Integer sit amet urna. "
+"Cras varius tortor in pede. "
+"Sed facilisis. "
+"Praesent lacinia libero nec nibh. "
+"Donec aliquam risus non nisl. "
+"Nam a nunc et felis tempor feugiat. "
+"Nunc metus. "
+"Vestibulum euismod, metus in semper laoreet, urna ipsum pharetra lorem, sed ultricies magna lorem sit amet wisi. "
+"Sed wisi. "
+"Nullam facilisis elit sed nisl. "
+"Phasellus mattis leo nec massa. "
+"Aenean malesuada. "
+"Cras wisi erat, lobortis nec, cursus eget, lobortis at, libero. "
+"In massa nisl, rutrum non, cursus nec, faucibus sed, lacus. "
+"Pellentesque malesuada. "
+"Cras euismod, neque ac suscipit tempus, velit lorem luctus elit, dapibus rhoncus wisi est ut sem. "
+"Proin vulputate enim in eros elementum accumsan. "
+"Ut lorem nisl, hendrerit et, interdum nec, lacinia in, dolor. "
+"Duis nec quam. "
+"Praesent velit felis, posuere id, luctus quis, laoreet ut, ipsum. "
+"Praesent eget arcu. "
+"Mauris massa felis, ornare non, ultrices id, tristique in, elit. "
+"Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. "
+"Phasellus vulputate, mi ac bibendum facilisis, libero enim suscipit leo, non nonummy pede diam eget nibh. "
+"Sed tempus. "
+"Aenean interdum suscipit dui. "
+"Aliquam erat volutpat. "
+"Ut malesuada. "
+"Nam commodo, nulla ut fringilla rutrum, orci elit viverra diam, vitae mollis odio odio et purus. "
+"Aliquam erat volutpat. "
+"Sed aliquet lobortis ipsum. "
+"In ut est. "
+"Etiam condimentum. "
+"Vestibulum pellentesque tortor pulvinar lacus. "
+"Aenean orci. "
+"Suspendisse lacus nulla, nonummy a, dictum vitae, egestas quis, eros. "
+"Donec auctor gravida nisl. "
+"Cras ac est rutrum augue pulvinar ornare. "
+"Phasellus mauris nibh, vulputate in, rhoncus imperdiet, dapibus eget, lacus. "
+"Nam nunc mauris, suscipit at, ultricies a, facilisis at, tellus. "
+"Nam accumsan mollis libero. "
+"Vivamus condimentum mattis est. "
+"Donec lacus. "
+"Nullam ac sapien id massa lobortis molestie. "
+"Pellentesque elementum. "
+"Proin ut purus. "
+"Integer et sapien quis turpis commodo mollis. "
+"Nulla consequat. "
+"Proin a wisi ut tellus blandit elementum. "
+"Aliquam nulla lorem, bibendum ac, malesuada vel, elementum et, metus. "
+"Phasellus egestas nibh et ligula. "
+"Vivamus diam odio, lacinia quis, malesuada quis, sollicitudin ut, eros. "
+"Phasellus aliquet lorem ac ipsum. "
+"Sed cursus tellus ac orci. "
+"Phasellus at nulla. "
+"Donec porta sodales ante. "
+"Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. "
+"Sed id lectus at massa ullamcorper tristique. "
+"Suspendisse porttitor lacus. "
+"In hac habitasse platea dictumst. "
+"Nunc non turpis. "
+"Sed sagittis. "
+"Morbi laoreet scelerisque dui. "
+"Nam arcu tellus, tempor vitae, vestibulum et, imperdiet ut, velit. "
+"In sit amet augue a arcu volutpat suscipit. "
+"Donec dictum ultrices lectus. "
+"Phasellus a purus et orci dictum lacinia. "
+"Ut leo. "
+"Cras semper, lorem sit amet tincidunt congue, justo eros varius pede, in bibendum turpis lectus non eros. "
+"Phasellus quam. "
+"Suspendisse mattis sollicitudin magna. "
+"Aenean facilisis diam vel nisl nonummy condimentum. "
+"Suspendisse vel dolor. "
+"Vestibulum nonummy. "
+"Mauris imperdiet semper ante. "
+"Maecenas vulputate eros. "
+"Vestibulum ac dolor. "
+"Fusce risus metus, aliquet eget, facilisis et, feugiat vel, orci. "
+"Ut at nunc id ante sodales vulputate. "
+"Duis tristique mattis ante. "
+"Vestibulum neque mauris, laoreet id, congue interdum, aliquet ut, diam. "
+"Nulla volutpat blandit magna. "
+"Donec accumsan congue diam. "
+"Etiam vel dui eget nisl tempor varius. "
+"Cras dictum massa sed enim. "
+"Cras urna tortor, fringilla ac, ullamcorper in, euismod vel, arcu. "
+"Morbi posuere luctus augue. "
+"Aliquam dui dui, adipiscing in, lobortis eu, luctus eu, nulla. "
+"Cras velit pede, ullamcorper sit amet, feugiat in, auctor vitae, ante. "
+"Suspendisse dictum fringilla mauris. "
+"In a nibh. "
+"Donec ac ligula. "
+"In quam. "
+"Praesent vitae urna ultricies sem aliquam placerat. "
+"Aliquam erat volutpat. "
+"Nam est. "
+"Donec faucibus sodales metus. "
+"Ut congue. "
+"Donec arcu tellus, pharetra ac, vulputate ac, mollis sed, lectus. "
+"Nulla facilisi. "
+"Nullam volutpat nunc et felis. "
+"Sed pede odio, tincidunt in, volutpat malesuada, feugiat gravida, nulla. "
+"Nulla aliquam pede vitae arcu. "
+"Proin velit elit, nonummy sit amet, elementum vitae, varius vitae, dolor. "
+"Donec rutrum ipsum eu mi. "
+"Aliquam et sem. "
+"In adipiscing rhoncus velit. "
+"Nam viverra scelerisque arcu. "
+"Aliquam sem. "
+"Sed tincidunt nulla quis massa. "
+"Mauris faucibus tempus nunc. "
+"Phasellus condimentum. "
+"Curabitur aliquet iaculis sapien. "
+"Nunc rhoncus, odio vitae bibendum dignissim, tellus libero commodo ipsum, ut sollicitudin nisl nisl vel justo. "
+"Nulla facilisi. "
+"Praesent blandit enim ut justo. "
+"Proin elementum, elit eget accumsan pulvinar, orci quam auctor neque, sed convallis diam purus vel felis. "
+"Sed orci leo, eleifend vel, blandit non, semper eu, purus. "
+"Proin bibendum, libero ac consectetuer commodo, eros sapien blandit nisl, eu eleifend nibh nibh vel lectus. "
+"Vivamus placerat. "
+"Integer odio dolor, pharetra non, sodales id, viverra eget, diam. "
+"Nunc mauris magna, egestas quis, feugiat id, fermentum viverra, mi. "
+"Aenean suscipit nisl non nunc. "
+"Proin quis lectus ac tellus nonummy commodo. "
+"Nunc eget diam ac elit vestibulum auctor. "
+"Etiam vulputate, odio sed lacinia consequat, justo mi vulputate purus, sit amet euismod libero metus sed tortor. "
+"Maecenas ac elit sed lorem vulputate gravida. "
+"Proin lectus eros, ullamcorper id, volutpat quis, condimentum tincidunt, sapien. "
+"Sed et massa eget lorem aliquet tempus. "
+"Duis porttitor nisl non risus. "
+"Nam id quam. "
+"Nullam est. "
+"Proin orci diam, posuere et, pharetra commodo, dictum vel, enim. "
+"Proin eget erat. "
+"Donec nisl. "
+"Maecenas auctor velit ut pede. "
+"Nunc vitae lectus nec libero tincidunt hendrerit. "
+"Quisque varius, erat ultrices ultrices euismod, purus lacus dictum eros, at condimentum enim dui nec magna. "
+"Morbi diam. "
+"Phasellus sed est. "
+"Phasellus nec libero in arcu fringilla sollicitudin. "
+"In rutrum nisl at arcu. "
+"Nulla facilisi. "
+"Mauris dignissim. "
+"Etiam est mauris, pharetra sed, viverra et, tincidunt sed, neque. "
+"Ut at lectus id nibh luctus ornare. "
+"Mauris varius porttitor risus. "
+"Ut vulputate aliquet risus. "
+"Vestibulum luctus neque sit amet nunc. "
+"Duis fermentum nibh. "
+"Pellentesque dapibus. "
+"Proin eros libero, aliquam non, condimentum a, sodales ut, turpis. "
+"Integer accumsan mi sed lorem. "
+"Vestibulum pellentesque sodales nisl. "
+"Nulla eu justo quis dui pretium rhoncus. "
+"Praesent viverra commodo mi. "
+"Maecenas dolor libero, viverra a, elementum vitae, aliquet vitae, dui. "
+"Mauris convallis lectus et mi. "
+"Mauris sagittis. "
+"Sed arcu. "
+"Pellentesque auctor. "
+"Donec pellentesque purus non tellus. "
+"Ut leo wisi, ultrices sit amet, ultrices eu, gravida ac, libero. "
+"Mauris fermentum dapibus diam. "
+"Integer quis lacus dapibus odio pellentesque varius. "
+"Fusce pede quam, vehicula ut, pulvinar et, tincidunt sed, felis. "
+"Curabitur eros enim, vulputate sed, aliquam ac, euismod ac, erat. "
+"Ut dignissim, lacus a interdum iaculis, enim orci posuere nunc, nec ultricies lectus risus in odio. "
+"Etiam et massa id dui commodo vehicula. "
+"Nunc blandit tortor quis dui. "
+"Quisque nisl. "
+"Sed venenatis blandit ligula. "
+"Fusce viverra imperdiet magna. "
+"Donec eget nunc quis est pharetra lobortis. "
+"Vestibulum quis lectus. "
+"Mauris vel orci lobortis nunc fermentum bibendum. "
+"Pellentesque eget leo. "
+"Morbi vel urna sit amet erat fermentum facilisis. "
+"Sed vulputate, libero et sollicitudin congue, wisi lectus sodales dolor, eget molestie magna orci vel tellus. "
+"Sed tempor ante et enim. "
+"Mauris elit. "
+"Curabitur ullamcorper vehicula massa. "
+"Sed viverra. "
+"Duis nulla. "
+"Nam bibendum. "
+"Nam tortor lorem, ullamcorper vitae, dictum sed, posuere eu, justo. "
+"Aliquam adipiscing arcu vitae turpis. "
+"Donec malesuada posuere libero. "
+"Ut sed tellus. "
+"Fusce sed nunc eget nisl dapibus malesuada. "
+"Suspendisse potenti. "
+"Integer tristique libero et metus. "
+"Vivamus posuere. "
+"Maecenas non sem non quam fermentum blandit. "
+"Duis risus tellus, rutrum vitae, imperdiet nec, malesuada nec, ipsum. "
+"Nunc quam dolor, luctus eget, placerat non, rhoncus at, tellus. "
+"Duis pede lectus, mattis adipiscing, tempor ut, porta at, mi. "
+"Pellentesque risus nulla, sodales sed, interdum id, nonummy vitae, ligula. "
+"Morbi pulvinar pede ut massa. "
+"Nunc risus mauris, tincidunt et, faucibus eu, suscipit vel, orci. "
+"In faucibus felis in arcu. "
+"Nulla sit amet elit. "
+"Nulla erat sapien, sagittis eget, dignissim eget, viverra eu, felis. "
+"Nam ac ipsum. "
+"Suspendisse vulputate turpis vel sem lacinia ullamcorper. "
+"Mauris ornare ipsum sed ligula. "
+"Duis facilisis neque quis orci. "
+"Nullam et erat et orci lacinia pellentesque. "
+"Donec ac ipsum. "
+"Duis molestie ipsum ac arcu. "
+"Aenean congue accumsan ante. "
+"Integer bibendum, leo ut ornare aliquam, nunc erat condimentum arcu, ut pulvinar mi augue et nulla. "
+"Quisque lacinia aliquet wisi. "
+"Vivamus nec dui. "
+"Etiam wisi leo, euismod vitae, vulputate a, dictum vitae, quam. "
+"Quisque quis tortor. "
+"Etiam interdum. "
+"In massa erat, porttitor sed, tincidunt vel, vehicula fringilla, augue. "
+"Nulla vel urna. "
+"In libero mi, pretium sed, mattis tempus, sagittis sed, massa. "
+"Suspendisse quam wisi, fermentum quis, sagittis at, consequat eget, odio. "
+"Nullam imperdiet, purus quis aliquam cursus, turpis odio egestas justo, placerat gravida turpis wisi vel tortor. "
+"Nunc ultricies porta purus. "
+"Proin elementum erat ac orci. "
+"Ut vel magna nec mi feugiat tincidunt. "
+"Ut ligula. "
+"Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec et magna in diam porta nonummy. "
+"Maecenas ut sem in turpis fermentum viverra. "
+"Suspendisse at orci. "
+"Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. "
+"Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Pellentesque rutrum eleifend justo. "
+"Nullam vitae pede. "
+"Donec condimentum nibh et odio. "
+"Sed et metus. "
+"Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. "
+"Nam tempus. "
+"Sed ac wisi. "
+"In hac habitasse platea dictumst. "
+"Sed sed wisi. "
+"Ut facilisis tellus non ligula. "
+"Integer metus. "
+"In lacinia dui. "
+"Curabitur ornare. "
+"Mauris vel urna. "
+"Nam consectetuer dignissim urna. "
+"Nunc elementum porttitor erat. "
+"Sed blandit, risus non commodo nonummy, ligula erat fermentum nibh, eu facilisis ante neque sed sem. "
+"Etiam scelerisque justo eget wisi. "
+"Nunc dignissim. "
+"Proin pulvinar quam non lectus. "
+"Proin ut turpis quis augue pellentesque dictum. "
+"Fusce et lorem. "
+"Aliquam urna lacus, blandit sed, vestibulum sit amet, placerat et, dolor. "
+"Curabitur auctor erat nec lorem. "
+"Phasellus urna wisi, lacinia ut, molestie tincidunt, condimentum id, odio. "
+"Curabitur convallis ullamcorper justo. "
+"Donec vestibulum est ac quam. "
+"Nullam vitae elit eu massa varius vulputate. "
+"Nulla facilisi. "
+"Suspendisse potenti. "
+"Praesent non libero. "
+"Nullam tristique massa id magna viverra commodo. "
+"Vestibulum libero tortor, luctus ac, viverra congue, consectetuer vel, libero. "
+"Aenean arcu augue, luctus id, laoreet pulvinar, dictum sed, lectus. "
+"Donec vestibulum volutpat dolor. "
+"Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. "
+"Pellentesque augue turpis, laoreet nec, malesuada at, nonummy vitae, nibh. "
+"Etiam orci sapien, congue in, porttitor sit amet, rutrum vel, nibh. "
+"Integer eu lorem. "
+"Mauris pretium leo et elit. "
+"In nonummy ultricies sapien. "
+"Mauris varius. "
+"Mauris sed libero. "
+"Curabitur ullamcorper elit eu purus. "
+"Vestibulum velit pede, semper sit amet, lobortis vitae, tincidunt vel, dui. "
+"Nulla neque ante, sagittis eu, vestibulum et, lacinia a, libero. "
+"Morbi sit amet wisi. "
+"Pellentesque non felis quis arcu bibendum ornare. "
+"Aenean enim metus, commodo eu, hendrerit nonummy, euismod ut, quam. "
+"Nulla eleifend nisl quis dolor. "
+"Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. "
+"Maecenas pellentesque massa in erat molestie molestie. "
+"Mauris dignissim dapibus libero. "
+"Sed sed risus id neque dictum ornare. "
+"Sed eu ligula at felis sodales accumsan. "
+"Sed interdum, urna non pharetra hendrerit, quam mi ornare libero, id fringilla tortor orci non velit. "
+"Aliquam nec risus. "
+"Donec at nunc vitae tellus molestie vestibulum. "
+"Pellentesque vel justo. "
+"Duis ligula libero, vulputate quis, adipiscing bibendum, feugiat vitae, velit. "
+"Vivamus et arcu. "
+"Fusce eget quam. "
+"Ut ante. "
+"Suspendisse feugiat metus non ipsum. "
+"Nulla tempus leo ut mi. "
+"Curabitur vitae nisl. "
+"Vivamus elementum. "
+"Etiam a orci. "
+"Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Vivamus urna quam, tincidunt at, ultrices vel, feugiat eget, nulla. "
+"Maecenas lacus magna, nonummy eu, iaculis sed, consectetuer quis, enim. "
+"Praesent a eros. "
+"Aliquam nonummy dignissim neque. "
+"Nulla enim. "
+"Praesent molestie, orci quis tristique volutpat, lacus metus luctus sapien, et facilisis eros neque id sapien. "
+"Nunc condimentum dolor vel orci. "
+"Integer wisi diam, porttitor sit amet, feugiat in, dapibus in, lectus. "
+"Aliquam erat volutpat. "
+"Quisque mollis turpis vitae tortor. "
+"Mauris turpis mi, pretium ut, ultrices sed, porta in, justo. "
+"Suspendisse posuere. "
+"Quisque ultricies lacus vitae enim. "
+"Donec lacus. "
+"Suspendisse potenti. "
+"Donec molestie, magna sed euismod dictum, magna magna interdum diam, vitae sagittis leo lorem ac neque. "
+"Cras metus. "
+"Quisque nunc. "
+"Duis consectetuer. "
+"Vestibulum gravida sollicitudin urna. "
+"Integer volutpat, massa quis ultrices pulvinar, eros purus dignissim nunc, eget rhoncus enim lectus quis tortor. "
+"Integer lacinia quam quis erat convallis mattis. "
+"Suspendisse iaculis posuere velit. "
+"Etiam tellus enim, aliquet nec, laoreet a, molestie non, velit. "
+"Quisque lacus velit, eleifend imperdiet, fringilla id, dapibus scelerisque, lectus. "
+"Nulla quis lorem. "
+"Nulla malesuada neque et dui. "
+"Phasellus malesuada ultricies odio. "
+"Phasellus vitae ligula. "
+"Pellentesque feugiat arcu at erat. "
+"Vivamus ut eros ut lorem pulvinar iaculis. "
+"Proin lobortis ipsum id nunc. "
+"Curabitur vel massa. "
+"Suspendisse nulla ipsum, malesuada vel, posuere eget, mollis at, risus. "
+"Vestibulum sed diam id est dapibus ultrices. "
+"Proin tempus, eros a scelerisque vestibulum, ipsum arcu aliquam mi, ut feugiat libero odio in nisl. "
+"Quisque et massa a mauris luctus congue. "
+"Ut id eros. "
+"Fusce ante eros, pharetra non, molestie tristique, bibendum sit amet, wisi. "
+"Phasellus rutrum, dolor et semper elementum, eros ante malesuada massa, sed sollicitudin lectus velit et massa. "
+"In auctor. "
+"Aliquam erat volutpat. "
+"Etiam risus leo, vulputate suscipit, sollicitudin et, sodales eget, nisl. "
+"Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Curabitur lobortis, libero ac laoreet mollis, ligula leo porta wisi, ut euismod felis ligula id elit. "
+"Vivamus malesuada nulla eu enim. "
+"Donec accumsan faucibus orci. "
+"Nulla lacinia ante. "
+"Praesent at nibh. "
+"Mauris porta dignissim wisi. "
+"Ut lacinia tortor nec nunc. "
+"Phasellus et augue. "
+"Integer rhoncus, libero a pellentesque rhoncus, tortor sapien lobortis pede, eget condimentum sapien risus vitae elit. "
+"Suspendisse sed turpis ut dolor placerat dignissim. "
+"Quisque quis leo. "
+"Cras ultrices. "
+"Maecenas hendrerit auctor tortor. "
+"Etiam sit amet arcu. ";