Merge branch 'master' into blender2.8
- Undo that changes modes currently asserts, since undo is now screen data. Most likely we will change how object mode and workspaces work since it's not practical/maintainable at the moment. - Removed view_layer from particle settings (wasn't needed and complicated undo).
This commit is contained in:
commit
b65ea517eb
@ -27,7 +27,7 @@ else()
|
||||
set(SNDFILE_OPTIONS --enable-static --disable-shared )
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
if(UNIX)
|
||||
set(SNDFILE_PATCH_CMD ${PATCH_CMD} --verbose -p 0 -d ${BUILD_DIR}/sndfile/src/external_sndfile < ${PATCH_DIR}/sndfile.diff)
|
||||
else()
|
||||
set(SNDFILE_PATCH_CMD)
|
||||
|
@ -1650,7 +1650,6 @@ compile_OIIO() {
|
||||
cmake_d="$cmake_d -D LINKSTATIC=OFF"
|
||||
cmake_d="$cmake_d -D USE_SIMD=sse2"
|
||||
|
||||
cmake_d="$cmake_d -D ILMBASE_VERSION=$ILMBASE_VERSION"
|
||||
cmake_d="$cmake_d -D OPENEXR_VERSION=$OPENEXR_VERSION"
|
||||
|
||||
if [ "$_with_built_openexr" = true ]; then
|
||||
@ -1669,6 +1668,7 @@ compile_OIIO() {
|
||||
cmake_d="$cmake_d -D BUILD_TESTING=OFF"
|
||||
cmake_d="$cmake_d -D OIIO_BUILD_TESTS=OFF"
|
||||
cmake_d="$cmake_d -D OIIO_BUILD_TOOLS=OFF"
|
||||
cmake_d="$cmake_d -D TXT2MAN="
|
||||
#cmake_d="$cmake_d -D CMAKE_EXPORT_COMPILE_COMMANDS=ON"
|
||||
#cmake_d="$cmake_d -D CMAKE_VERBOSE_MAKEFILE=ON"
|
||||
|
||||
|
@ -565,6 +565,9 @@ function(SETUP_BLENDER_SORTED_LIBS)
|
||||
set(BLENDER_SORTED_LIBS
|
||||
bf_windowmanager
|
||||
|
||||
# needed twice because of text undo
|
||||
bf_editor_util
|
||||
|
||||
bf_editor_space_api
|
||||
bf_editor_space_action
|
||||
bf_editor_space_buttons
|
||||
@ -689,6 +692,7 @@ function(SETUP_BLENDER_SORTED_LIBS)
|
||||
extern_sdlew
|
||||
|
||||
bf_intern_glew_mx
|
||||
bf_intern_clog
|
||||
)
|
||||
|
||||
if(NOT WITH_SYSTEM_GLOG)
|
||||
|
@ -10,6 +10,10 @@
|
||||
* \ingroup intern
|
||||
*/
|
||||
|
||||
/** \defgroup clog C-Logging (CLOG)
|
||||
* \ingroup intern
|
||||
*/
|
||||
|
||||
/** \defgroup ctr container
|
||||
* \ingroup intern
|
||||
*/
|
||||
|
@ -1,8 +1,11 @@
|
||||
"""
|
||||
Basic Video Playback
|
||||
++++++++++++++++++++
|
||||
Example of how to replace a texture in game with a video. It needs to run
|
||||
everyframe.
|
||||
Example of how to replace a texture in game with a video.
|
||||
It needs to run everyframe.
|
||||
To avoid any confusion with the location of the file,
|
||||
we will use ``GameLogic.expandPath()`` to build an absolute file name,
|
||||
assuming the video file is in the same directory as the blend-file.
|
||||
"""
|
||||
import bge
|
||||
from bge import texture
|
||||
@ -26,8 +29,18 @@ if not hasattr(logic, 'video'):
|
||||
logic.video.source = texture.VideoFFmpeg(movie)
|
||||
logic.video.source.scale = True
|
||||
|
||||
# Note that we can change the ``Texture`` source at any time.
|
||||
# Suppose we want to switch between two movies during the game:
|
||||
logic.mySources[0] = texture.VideoFFmpeg('movie1.avi')
|
||||
logic.mySources[1] = texture.VideoFFmpeg('movie2.avi')
|
||||
|
||||
#And then assign (and reassign) the source during the game
|
||||
logic.video.source = logic.mySources[movieSel]
|
||||
|
||||
# quick off the movie, but it wont play in the background
|
||||
logic.video.source.play()
|
||||
|
||||
# you need to call this function every frame to ensure update of the texture.
|
||||
|
||||
# Video playback is not a background process: it happens only when we refresh the texture.
|
||||
# So you need to call this function every frame to ensure update of the texture.
|
||||
logic.video.refresh(True)
|
||||
|
@ -6,18 +6,15 @@ Video Texture (bge.texture)
|
||||
Introduction
|
||||
************
|
||||
|
||||
The bge.texture module allows you to manipulate textures during the game.
|
||||
|
||||
Several sources for texture are possible: video files, image files, video capture, memory buffer,
|
||||
camera render or a mix of that.
|
||||
|
||||
The video and image files can be loaded from the internet using an URL instead of a file name.
|
||||
|
||||
In addition, you can apply filters on the images before sending them to the GPU, allowing video effect:
|
||||
blue screen, color band, gray, normal map.
|
||||
|
||||
bge.texture uses FFmpeg to load images and videos.
|
||||
All the formats and codecs that FFmpeg supports are supported by this module, including but not limited to:
|
||||
The ``bge.texture`` module allows you to manipulate textures during the game.
|
||||
Several sources for texture are possible: video files, image files, video capture,
|
||||
memory buffer, camera render or a mix of that.
|
||||
The video and image files can be loaded from the Internet using a URL instead of a file name.
|
||||
In addition, you can apply filters on the images before sending them to the GPU,
|
||||
allowing video effect: blue screen, color band, gray, normal map.
|
||||
``bge.texture`` uses FFmpeg to load images and videos.
|
||||
All the formats and codecs that FFmpeg supports are supported by ``bge.texture``,
|
||||
including but not limited to:
|
||||
|
||||
* AVI
|
||||
* Ogg
|
||||
@ -28,16 +25,45 @@ All the formats and codecs that FFmpeg supports are supported by this module, in
|
||||
* videoForWindows capture card (this includes many webcams)
|
||||
* JPG
|
||||
|
||||
The principle is simple: first you identify a texture on an existing object using
|
||||
the :class:`~bge.texture.materialID` function, then you create a new texture with dynamic content
|
||||
|
||||
How it works
|
||||
------------
|
||||
|
||||
The principle is simple: first you identify a texture on an existing object using the
|
||||
:class:`~bge.texture.materialID` function, then you create a new texture with dynamic content
|
||||
and swap the two textures in the GPU.
|
||||
|
||||
The GE is not aware of the substitution and continues to display the object as always,
|
||||
The game engine is not aware of the substitution and continues to display the object as always,
|
||||
except that you are now in control of the texture.
|
||||
|
||||
When the texture object is deleted, the new texture is deleted and the old texture restored.
|
||||
|
||||
.. module:: bge.texture
|
||||
|
||||
Game Preparation
|
||||
----------------
|
||||
|
||||
Before you can use the :mod:`bge.texture` module,
|
||||
you must have objects with textures applied appropriately.
|
||||
|
||||
Imagine you want to have a television showing live broadcast programs in the game.
|
||||
You will create a television object and UV-apply a different texture at the place of the screen,
|
||||
for example ``tv.png``. What this texture looks like is not important;
|
||||
probably you want to make it dark gray to simulate power-off state.
|
||||
When the television must be turned on, you create a dynamic texture from a video capture card
|
||||
and use it instead of ``tv.png``: the TV screen will come to life.
|
||||
|
||||
You have two ways to define textures that ``bge.texture`` can grab:
|
||||
|
||||
- Simple UV texture.
|
||||
- Blender material with image texture channel.
|
||||
|
||||
Because ``bge.texture`` works at texture level, it is compatible with all
|
||||
the Blender Game Engine's fancy texturing features: GLSL, multi-texture, custom shaders, etc.
|
||||
|
||||
|
||||
********
|
||||
Examples
|
||||
********
|
||||
|
||||
.. include:: __/examples/bge.texture.py
|
||||
:start-line: 1
|
||||
@ -53,7 +79,6 @@ When the texture object is deleted, the new texture is deleted and the old textu
|
||||
.. literalinclude:: __/examples/bge.texture.1.py
|
||||
:lines: 8-
|
||||
|
||||
|
||||
.. include:: __/examples/bge.texture.2.py
|
||||
:start-line: 1
|
||||
:end-line: 6
|
||||
@ -62,13 +87,15 @@ When the texture object is deleted, the new texture is deleted and the old textu
|
||||
:lines: 8-
|
||||
|
||||
|
||||
.. module:: bge.texture
|
||||
|
||||
*************
|
||||
Video classes
|
||||
*************
|
||||
|
||||
.. class:: VideoFFmpeg(file, capture=-1, rate=25.0, width=0, height=0)
|
||||
|
||||
FFmpeg video source.
|
||||
FFmpeg video source, used for video files, video captures, or video streams.
|
||||
|
||||
:arg file: Path to the video to load; if capture >= 0 on Windows, this parameter will not be used.
|
||||
:type file: str
|
||||
@ -90,19 +117,20 @@ Video classes
|
||||
|
||||
.. attribute:: range
|
||||
|
||||
Replay range.
|
||||
The start and stop time of the video playback, expressed in seconds from beginning.
|
||||
By default the entire video.
|
||||
|
||||
:type: sequence of two floats
|
||||
|
||||
.. attribute:: repeat
|
||||
|
||||
Repeat count, -1 for infinite repeat.
|
||||
Number of times to replay the video, -1 for infinite repeat.
|
||||
|
||||
:type: int
|
||||
|
||||
.. attribute:: framerate
|
||||
|
||||
Frame rate.
|
||||
Relative frame rate, <1.0 for slow, >1.0 for fast.
|
||||
|
||||
:type: float
|
||||
|
||||
@ -126,21 +154,26 @@ Video classes
|
||||
|
||||
.. attribute:: scale
|
||||
|
||||
Fast scale of image (near neighbour).
|
||||
Set to True to activate fast nearest neighbor scaling algorithm.
|
||||
Texture width and height must be a power of 2.
|
||||
If the video picture size is not a power of 2, rescaling is required.
|
||||
By default ``bge.texture`` uses the precise but slow ``gluScaleImage()`` function.
|
||||
Best is to rescale the video offline so that no scaling is necessary at runtime!
|
||||
|
||||
:type: bool
|
||||
|
||||
.. attribute:: flip
|
||||
|
||||
Flip image vertically.
|
||||
If True the imaged will be flipped vertically.
|
||||
FFmpeg always delivers the image upside down, so this attribute is set to True by default.
|
||||
|
||||
:type: bool
|
||||
|
||||
.. attribute:: filter
|
||||
|
||||
Pixel filter.
|
||||
An additional filter that is applied on the video before sending it to the GPU.
|
||||
|
||||
:type: one of...
|
||||
:type: one of:
|
||||
|
||||
* :class:`FilterBGR24`
|
||||
* :class:`FilterBlueScreen`
|
||||
@ -207,7 +240,7 @@ Image classes
|
||||
|
||||
.. class:: ImageFFmpeg(file)
|
||||
|
||||
FFmpeg image source.
|
||||
FFmpeg image source, used for image files and web based images.
|
||||
|
||||
:arg file: Path to the image to load.
|
||||
:type file: str
|
||||
@ -286,7 +319,8 @@ Image classes
|
||||
|
||||
.. class:: ImageBuff(width, height, color=0, scale=False)
|
||||
|
||||
Image source from image buffer.
|
||||
Image from application memory.
|
||||
For computer generated images, drawing applications.
|
||||
|
||||
:arg width: Width of the image.
|
||||
:type width: int
|
||||
@ -477,7 +511,7 @@ Image classes
|
||||
|
||||
.. class:: ImageMix
|
||||
|
||||
Image mixer.
|
||||
Image mixer used to mix multiple image sources together.
|
||||
|
||||
.. attribute:: filter
|
||||
|
||||
@ -592,7 +626,7 @@ Image classes
|
||||
|
||||
.. class:: ImageRender(scene, camera)
|
||||
|
||||
Image source from render.
|
||||
Image source from a render of a non active camera.
|
||||
The render is done on a custom framebuffer object if fbo is specified,
|
||||
otherwise on the default framebuffer.
|
||||
|
||||
@ -723,7 +757,8 @@ Image classes
|
||||
|
||||
.. class:: ImageViewport
|
||||
|
||||
Image source from viewport.
|
||||
Image source from viewport rendered by the active camera.
|
||||
To render from a non active camera see :class:`ImageRender`.
|
||||
|
||||
.. attribute:: alpha
|
||||
|
||||
@ -774,11 +809,10 @@ Image classes
|
||||
|
||||
Refresh video - copy the viewport to an external buffer (optional) then invalidate its current content.
|
||||
|
||||
:arg buffer: An optional object that implements the buffer protocol.
|
||||
If specified, the image is copied to the buffer, which must be big enough or an exception is thrown.
|
||||
The transfer to the buffer is optimal if no processing of the image is needed.
|
||||
This is the case if ``flip=False, alpha=True, scale=False, whole=True, depth=False, zbuff=False``
|
||||
and no filter is set.
|
||||
:arg buffer: An optional object that implements the buffer protocol. If specified,
|
||||
the image is copied to the buffer, which must be big enough or an exception is thrown.
|
||||
The transfer to the buffer is optimal if no processing of the image is needed. This is the case if
|
||||
``flip=False, alpha=True, scale=False, whole=True, depth=False, zbuff=False`` and no filter is set.
|
||||
:type buffer: any buffer type
|
||||
:arg format: An optional image format specifier for the image that will be copied to the buffer.
|
||||
Only valid values are "RGBA" or "BGRA"
|
||||
@ -838,18 +872,16 @@ Image classes
|
||||
:arg capture: Card number from which the input video must be captured.
|
||||
:type capture: int
|
||||
|
||||
The format argument must be written as ``<displayMode>/<pixelFormat>[/3D][:<cacheSize>]`` where ``<displayMode>``
|
||||
describes the frame size and rate and <pixelFormat> the encoding of the pixels.
|
||||
The format argument must be written as ``<displayMode>/<pixelFormat>[/3D][:<cacheSize>]``
|
||||
where ``<displayMode>`` describes the frame size and rate and <pixelFormat> the encoding of the pixels.
|
||||
The optional ``/3D`` suffix is to be used if the video stream is stereo with a left and right eye feed.
|
||||
The optional ``:<cacheSize>`` suffix determines the number of the video frames kept in cache, by default 8.
|
||||
Some DeckLink cards won't work below a certain cache size.
|
||||
The default value 8 should be sufficient for all cards.
|
||||
Some DeckLink cards won't work below a certain cache size. The default value 8 should be sufficient for all cards.
|
||||
You may try to reduce the cache size to reduce the memory footprint. For example the The 4K Extreme is known
|
||||
to work with 3 frames only, the Extreme 2 needs 4 frames and the Intensity Shuttle needs 6 frames, etc.
|
||||
Reducing the cache size may be useful when Decklink is used in conjunction with GPUDirect:
|
||||
all frames must be locked in memory in that case and that puts a lot of pressure on memory.
|
||||
If you reduce the cache size too much,
|
||||
you'll get no error but no video feed either.
|
||||
If you reduce the cache size too much, you'll get no error but no video feed either.
|
||||
|
||||
The valid ``<displayMode>`` values are copied from the ``BMDDisplayMode`` enum in the DeckLink API
|
||||
without the 'bmdMode' prefix. In case a mode that is not in this list is added in a later version
|
||||
@ -1006,15 +1038,20 @@ Texture classes
|
||||
|
||||
.. class:: Texture(gameObj, materialID=0, textureID=0, textureObj=None)
|
||||
|
||||
Texture object.
|
||||
Class that creates the ``Texture`` object that loads the dynamic texture on the GPU.
|
||||
|
||||
:arg gameObj: Game object to be created a video texture on.
|
||||
:type gameObj: :class:`~bge.types.KX_GameObject`
|
||||
:arg materialID: Material ID. (optional)
|
||||
:arg materialID: Material ID default, 0 is the first material. (optional)
|
||||
:type materialID: int
|
||||
:arg textureID: Texture ID. (optional)
|
||||
:arg textureID: Texture index in case of multi-texture channel, 0 = first channel by default.
|
||||
In case of UV texture, this parameter should always be 0. (optional)
|
||||
:type textureID: int
|
||||
:arg textureObj: Texture object with shared bindId. (optional)
|
||||
:arg textureObj: Reference to another ``Texture`` object with shared bindId
|
||||
which he user might want to reuse the texture.
|
||||
If this argument is used, you should not create any source on this texture
|
||||
and there is no need to refresh it either: the other ``Texture`` object will
|
||||
provide the texture for both materials/textures.(optional)
|
||||
:type textureObj: :class:`Texture`
|
||||
|
||||
.. attribute:: bindId
|
||||
@ -1094,7 +1131,7 @@ Texture classes
|
||||
|
||||
.. attribute:: source
|
||||
|
||||
This attribute must be set to one of the image source. If the image size does not fit exactly
|
||||
This attribute must be set to one of the image sources. If the image size does not fit exactly
|
||||
the frame size, the extend attribute determines what to do.
|
||||
|
||||
For best performance, the source image should match exactly the size of the output frame.
|
||||
@ -1368,7 +1405,7 @@ Functions
|
||||
|
||||
Returns a :class:`~bgl.Buffer` corresponding to the current image stored in a texture source object.
|
||||
|
||||
:arg image: Image source object of type ...
|
||||
:arg image: Image source object of type:
|
||||
|
||||
* :class:`VideoFFmpeg`
|
||||
* :class:`ImageFFmpeg`
|
||||
@ -1429,9 +1466,10 @@ Functions
|
||||
|
||||
.. function:: setLogFile(filename)
|
||||
|
||||
Sets the name of a text file in which runtime error messages will be written, in addition to the printing
|
||||
of the messages on the Python console. Only the runtime errors specific to the VideoTexture module
|
||||
are written in that file, ordinary runtime time errors are not written.
|
||||
Sets the name of a text file in which runtime error messages will be written,
|
||||
in addition to the printing of the messages on the Python console.
|
||||
Only the runtime errors specific to the VideoTexture module are written in that file,
|
||||
ordinary runtime time errors are not written.
|
||||
|
||||
:arg filename: Name of the error log file.
|
||||
:type filename: str
|
||||
@ -1517,4 +1555,3 @@ See Wikipedia's `Blend Modes <https://en.wikipedia.org/wiki/Blend_modes>`_ for r
|
||||
.. data:: IMB_BLEND_COPY_RGB
|
||||
|
||||
.. data:: IMB_BLEND_COPY_ALPHA
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
# ***** END GPL LICENSE BLOCK *****
|
||||
|
||||
# add_subdirectory(atomic) # header only
|
||||
add_subdirectory(clog)
|
||||
add_subdirectory(string)
|
||||
add_subdirectory(ghost)
|
||||
add_subdirectory(guardedalloc)
|
||||
|
211
intern/clog/CLG_log.h
Normal file
211
intern/clog/CLG_log.h
Normal file
@ -0,0 +1,211 @@
|
||||
/*
|
||||
* ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
#ifndef __CLOG_H__
|
||||
#define __CLOG_H__
|
||||
|
||||
/** \file clog/CLG_log.h
|
||||
* \ingroup clog
|
||||
*
|
||||
* C Logging Library (clog)
|
||||
* ========================
|
||||
*
|
||||
* Usage
|
||||
* -----
|
||||
*
|
||||
* - `CLG_LOGREF_DECLARE_GLOBAL` macro to declare #CLG_LogRef pointers.
|
||||
* - `CLOG_` prefixed macros for logging.
|
||||
*
|
||||
* Identifiers
|
||||
* -----------
|
||||
*
|
||||
* #CLG_LogRef holds an identifier which defines the category of the logger.
|
||||
*
|
||||
* You can define and use identifiers as needed, logging will lazily initialize them.
|
||||
*
|
||||
* By convention lower case dot separated identifiers are used, eg:
|
||||
* `module.sub_module`, this allows filtering by `module.*`,
|
||||
* see #CLG_type_filter_include, #CLG_type_filter_exclude
|
||||
*
|
||||
* There is currently no functionality to remove a category once it's created.
|
||||
*
|
||||
* Severity
|
||||
* --------
|
||||
*
|
||||
* - `INFO`: Simply log events, uses verbosity levels to control how much information to show.
|
||||
* - `WARN`: General warnings (which aren't necessary to show to users).
|
||||
* - `ERROR`: An error we can recover from, should not happen.
|
||||
* - `FATAL`: Similar to assert. This logs the message, then a stack trace and abort.
|
||||
*
|
||||
*
|
||||
* Verbosity Level
|
||||
* ---------------
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* - 0: Always show (used for warnings, errors).
|
||||
* Should never get in the way or become annoying.
|
||||
*
|
||||
* - 1: Top level module actions (eg: load a file, create a new window .. etc).
|
||||
*
|
||||
* - 2: Actions within a module (steps which compose an action, but don't flood output).
|
||||
* Running a tool, full data recalculation.
|
||||
*
|
||||
* - 3: Detailed actions which may be of interest when debugging internal logic of a module
|
||||
* These *may* flood the log with details.
|
||||
*
|
||||
* - 4+: May be used for more details than 3, should be avoided but not prevented.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#ifdef __GNUC__
|
||||
# define _CLOG_ATTR_NONNULL(args ...) __attribute__((nonnull(args)))
|
||||
#else
|
||||
# define _CLOG_ATTR_NONNULL(...)
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
# define _CLOG_ATTR_PRINTF_FORMAT(format_param, dots_param) __attribute__((format(printf, format_param, dots_param)))
|
||||
#else
|
||||
# define _CLOG_ATTR_PRINTF_FORMAT(format_param, dots_param)
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__func__)
|
||||
# define __func__MSVC
|
||||
# define __func__ __FUNCTION__
|
||||
#endif
|
||||
|
||||
#define STRINGIFY_ARG(x) "" #x
|
||||
#define STRINGIFY_APPEND(a, b) "" a #b
|
||||
#define STRINGIFY(x) STRINGIFY_APPEND("", x)
|
||||
|
||||
struct CLogContext;
|
||||
|
||||
/* Don't typedef enums. */
|
||||
enum CLG_LogFlag {
|
||||
CLG_FLAG_USE = (1 << 0),
|
||||
};
|
||||
|
||||
enum CLG_Severity {
|
||||
CLG_SEVERITY_INFO = 0,
|
||||
CLG_SEVERITY_WARN,
|
||||
CLG_SEVERITY_ERROR,
|
||||
CLG_SEVERITY_FATAL,
|
||||
};
|
||||
#define CLG_SEVERITY_LEN (CLG_SEVERITY_FATAL + 1)
|
||||
|
||||
/* Each logger ID has one of these. */
|
||||
typedef struct CLG_LogType {
|
||||
struct CLG_LogType *next;
|
||||
char identifier[64];
|
||||
/** FILE output. */
|
||||
struct CLogContext *ctx;
|
||||
/** Control behavior. */
|
||||
int level;
|
||||
enum CLG_LogFlag flag;
|
||||
} CLG_LogType;
|
||||
|
||||
typedef struct CLG_LogRef {
|
||||
const char *identifier;
|
||||
CLG_LogType *type;
|
||||
} CLG_LogRef;
|
||||
|
||||
void CLG_log_str(
|
||||
CLG_LogType *lg, enum CLG_Severity severity, const char *file_line, const char *fn,
|
||||
const char *message)
|
||||
_CLOG_ATTR_NONNULL(1, 3, 4, 5);
|
||||
void CLG_logf(
|
||||
CLG_LogType *lg, enum CLG_Severity severity, const char *file_line, const char *fn,
|
||||
const char *format, ...)
|
||||
_CLOG_ATTR_NONNULL(1, 3, 4, 5) _CLOG_ATTR_PRINTF_FORMAT(5, 6);
|
||||
|
||||
/* Main initializer and distructor (per session, not logger). */
|
||||
void CLG_init(void);
|
||||
void CLG_exit(void);
|
||||
|
||||
void CLG_output_set(void *file_handle);
|
||||
void CLG_output_use_basename_set(int value);
|
||||
void CLG_fatal_fn_set(void (*fatal_fn)(void *file_handle));
|
||||
|
||||
void CLG_type_filter_include(const char *type_filter, int type_filter_len);
|
||||
void CLG_type_filter_exclude(const char *type_filter, int type_filter_len);
|
||||
|
||||
void CLG_logref_init(CLG_LogRef *clg_ref);
|
||||
|
||||
/** Declare outside function, declare as extern in header. */
|
||||
#define CLG_LOGREF_DECLARE_GLOBAL(var, id) \
|
||||
static CLG_LogRef _static_ ## var = {id}; \
|
||||
CLG_LogRef *var = &_static_ ## var
|
||||
|
||||
/** Initialize struct once. */
|
||||
#define CLOG_ENSURE(clg_ref) \
|
||||
((clg_ref)->type ? (clg_ref)->type : (CLG_logref_init(clg_ref), (clg_ref)->type))
|
||||
|
||||
#define CLOG_AT_SEVERITY(clg_ref, severity, verbose_level, ...) { \
|
||||
CLG_LogType *_lg_ty = CLOG_ENSURE(clg_ref); \
|
||||
if (((_lg_ty->flag & CLG_FLAG_USE) && (_lg_ty->level >= verbose_level)) || (severity >= CLG_SEVERITY_WARN)) { \
|
||||
CLG_logf(_lg_ty, severity, __FILE__ ":" STRINGIFY(__LINE__), __func__, __VA_ARGS__); \
|
||||
} \
|
||||
} ((void)0)
|
||||
|
||||
#define CLOG_STR_AT_SEVERITY(clg_ref, severity, verbose_level, str) { \
|
||||
CLG_LogType *_lg_ty = CLOG_ENSURE(clg_ref); \
|
||||
if (((_lg_ty->flag & CLG_FLAG_USE) && (_lg_ty->level >= verbose_level)) || (severity >= CLG_SEVERITY_WARN)) { \
|
||||
CLG_log_str(lg, severity, __FILE__ ":" STRINGIFY(__LINE__), __func__, str); \
|
||||
} \
|
||||
} ((void)0)
|
||||
|
||||
#define CLOG_STR_AT_SEVERITY_N(clg_ref, severity, verbose_level, str) { \
|
||||
CLG_LogType *_lg_ty = CLOG_ENSURE(clg_ref); \
|
||||
if (((_lg_ty->flag & CLG_FLAG_USE) && (_lg_ty->level >= verbose_level)) || (severity >= CLG_SEVERITY_WARN)) { \
|
||||
const char *_str = str; \
|
||||
CLG_log_str(_lg_ty, severity, __FILE__ ":" STRINGIFY(__LINE__), __func__, _str); \
|
||||
MEM_freeN((void *)_str); \
|
||||
} \
|
||||
} ((void)0)
|
||||
|
||||
#define CLOG_INFO(clg_ref, level, ...) CLOG_AT_SEVERITY(clg_ref, CLG_SEVERITY_INFO, level, __VA_ARGS__)
|
||||
#define CLOG_WARN(clg_ref, ...) CLOG_AT_SEVERITY(clg_ref, CLG_SEVERITY_WARN, 0, __VA_ARGS__)
|
||||
#define CLOG_ERROR(clg_ref, ...) CLOG_AT_SEVERITY(clg_ref, CLG_SEVERITY_ERROR, 0, __VA_ARGS__)
|
||||
#define CLOG_FATAL(clg_ref, ...) CLOG_AT_SEVERITY(clg_ref, CLG_SEVERITY_FATAL, 0, __VA_ARGS__)
|
||||
|
||||
#define CLOG_STR_INFO(clg_ref, level, ...) CLOG_STR_AT_SEVERITY(clg_ref, CLG_SEVERITY_INFO, level, __VA_ARGS__)
|
||||
#define CLOG_STR_WARN(clg_ref, ...) CLOG_STR_AT_SEVERITY(clg_ref, CLG_SEVERITY_WARN, 0, __VA_ARGS__)
|
||||
#define CLOG_STR_ERROR(clg_ref, ...) CLOG_STR_AT_SEVERITY(clg_ref, CLG_SEVERITY_ERROR, 0, __VA_ARGS__)
|
||||
#define CLOG_STR_FATAL(clg_ref, ...) CLOG_STR_AT_SEVERITY(clg_ref, CLG_SEVERITY_FATAL, 0, __VA_ARGS__)
|
||||
|
||||
/* Allocated string which is immediately freed. */
|
||||
#define CLOG_STR_INFO_N(clg_ref, level, ...) CLOG_STR_AT_SEVERITY_N(clg_ref, CLG_SEVERITY_INFO, level, __VA_ARGS__)
|
||||
#define CLOG_STR_WARN_N(clg_ref, ...) CLOG_STR_AT_SEVERITY_N(clg_ref, CLG_SEVERITY_WARN, 0, __VA_ARGS__)
|
||||
#define CLOG_STR_ERROR_N(clg_ref, ...) CLOG_STR_AT_SEVERITY_N(clg_ref, CLG_SEVERITY_ERROR, 0, __VA_ARGS__)
|
||||
#define CLOG_STR_FATAL_N(clg_ref, ...) CLOG_STR_AT_SEVERITY_N(clg_ref, CLG_SEVERITY_FATAL, 0, __VA_ARGS__)
|
||||
|
||||
#ifdef __func__MSVC
|
||||
# undef __func__MSVC
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __CLOG_H__ */
|
34
intern/clog/CMakeLists.txt
Normal file
34
intern/clog/CMakeLists.txt
Normal file
@ -0,0 +1,34 @@
|
||||
# ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ***** END GPL LICENSE BLOCK *****
|
||||
|
||||
set(INC
|
||||
.
|
||||
../guardedalloc
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
|
||||
)
|
||||
|
||||
set(SRC
|
||||
clog.c
|
||||
|
||||
CLG_log.h
|
||||
)
|
||||
|
||||
blender_add_lib(bf_intern_clog "${SRC}" "${INC}" "${INC_SYS}")
|
583
intern/clog/clog.c
Normal file
583
intern/clog/clog.c
Normal file
@ -0,0 +1,583 @@
|
||||
/*
|
||||
* ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file clog/clog.c
|
||||
* \ingroup clog
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* For 'isatty' to check for color. */
|
||||
#if defined(__unix__)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
/* Only other dependency (could use regular malloc too). */
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
/* own include. */
|
||||
#include "CLG_log.h"
|
||||
|
||||
/* Local utility defines */
|
||||
#define STREQ(a, b) (strcmp(a, b) == 0)
|
||||
#define STREQLEN(a, b, n) (strncmp(a, b, n) == 0)
|
||||
|
||||
#ifdef _WIN32
|
||||
# define PATHSEP_CHAR '\\'
|
||||
#else
|
||||
# define PATHSEP_CHAR '/'
|
||||
#endif
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Internal Types
|
||||
* \{ */
|
||||
|
||||
typedef struct CLG_IDFilter {
|
||||
struct CLG_IDFilter *next;
|
||||
/** Over alloc. */
|
||||
char match[0];
|
||||
} CLG_IDFilter;
|
||||
|
||||
typedef struct CLogContext {
|
||||
/** Single linked list of types. */
|
||||
CLG_LogType *types;
|
||||
/* exclude, include filters. */
|
||||
CLG_IDFilter *filters[2];
|
||||
bool use_color;
|
||||
bool use_basename;
|
||||
|
||||
/** Borrowed, not owned. */
|
||||
FILE *output;
|
||||
|
||||
/** For new types. */
|
||||
struct {
|
||||
int level;
|
||||
} default_type;
|
||||
|
||||
struct {
|
||||
void (*fatal_fn)(void *file_handle);
|
||||
} callbacks;
|
||||
} CLogContext;
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Mini Buffer Functionality
|
||||
*
|
||||
* Use so we can do a single call to write.
|
||||
* \{ */
|
||||
|
||||
#define CLOG_BUF_LEN_INIT 512
|
||||
|
||||
typedef struct CLogStringBuf {
|
||||
char *data;
|
||||
uint len;
|
||||
uint len_alloc;
|
||||
bool is_alloc;
|
||||
} CLogStringBuf;
|
||||
|
||||
static void clg_str_init(CLogStringBuf *cstr, char *buf_stack, uint buf_stack_len)
|
||||
{
|
||||
cstr->data = buf_stack;
|
||||
cstr->len_alloc = buf_stack_len;
|
||||
cstr->len = 0;
|
||||
cstr->is_alloc = false;
|
||||
}
|
||||
|
||||
static void clg_str_free(CLogStringBuf *cstr)
|
||||
{
|
||||
if (cstr->is_alloc) {
|
||||
MEM_freeN(cstr->data);
|
||||
}
|
||||
}
|
||||
|
||||
static void clg_str_reserve(CLogStringBuf *cstr, const uint len)
|
||||
{
|
||||
if (len > cstr->len_alloc) {
|
||||
cstr->len_alloc *= 2;
|
||||
if (len > cstr->len_alloc) {
|
||||
cstr->len_alloc = len;
|
||||
}
|
||||
|
||||
if (cstr->is_alloc) {
|
||||
cstr->data = MEM_reallocN(cstr->data, cstr->len_alloc);
|
||||
}
|
||||
else {
|
||||
/* Copy the static buffer. */
|
||||
char *data = MEM_mallocN(cstr->len_alloc, __func__);
|
||||
memcpy(data, cstr->data, cstr->len);
|
||||
cstr->data = data;
|
||||
cstr->is_alloc = true;
|
||||
}
|
||||
cstr->len_alloc = len;
|
||||
}
|
||||
}
|
||||
|
||||
static void clg_str_append_with_len(CLogStringBuf *cstr, const char *str, const uint len)
|
||||
{
|
||||
uint len_next = cstr->len + len;
|
||||
clg_str_reserve(cstr, len_next);
|
||||
char *str_dst = cstr->data + cstr->len;
|
||||
memcpy(str_dst, str, len);
|
||||
#if 0 /* no need. */
|
||||
str_dst[len] = '\0';
|
||||
#endif
|
||||
cstr->len = len_next;
|
||||
}
|
||||
|
||||
static void clg_str_append(CLogStringBuf *cstr, const char *str)
|
||||
{
|
||||
clg_str_append_with_len(cstr, str, strlen(str));
|
||||
}
|
||||
|
||||
static void clg_str_vappendf(CLogStringBuf *cstr, const char *fmt, va_list args)
|
||||
{
|
||||
/* Use limit because windows may use '-1' for a formatting error. */
|
||||
const uint len_max = 65535;
|
||||
uint len_avail = (cstr->len_alloc - cstr->len);
|
||||
if (len_avail == 0) {
|
||||
len_avail = CLOG_BUF_LEN_INIT;
|
||||
clg_str_reserve(cstr, len_avail);
|
||||
}
|
||||
while (true) {
|
||||
va_list args_cpy;
|
||||
va_copy(args_cpy, args);
|
||||
int retval = vsnprintf(cstr->data + cstr->len, len_avail, fmt, args_cpy);
|
||||
va_end(args_cpy);
|
||||
if (retval != -1) {
|
||||
cstr->len += retval;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
len_avail *= 2;
|
||||
if (len_avail >= len_max) {
|
||||
break;
|
||||
}
|
||||
clg_str_reserve(cstr, len_avail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Internal Utilities
|
||||
* \{ */
|
||||
|
||||
enum eCLogColor {
|
||||
COLOR_DEFAULT,
|
||||
COLOR_RED,
|
||||
COLOR_GREEN,
|
||||
COLOR_YELLOW,
|
||||
|
||||
COLOR_RESET,
|
||||
};
|
||||
#define COLOR_LEN (COLOR_RESET + 1)
|
||||
|
||||
static const char *clg_color_table[COLOR_LEN] = {NULL};
|
||||
|
||||
static void clg_color_table_init(bool use_color)
|
||||
{
|
||||
for (int i = 0; i < COLOR_LEN; i++) {
|
||||
clg_color_table[i] = "";
|
||||
}
|
||||
if (use_color) {
|
||||
#ifdef _WIN32
|
||||
/* TODO */
|
||||
#else
|
||||
clg_color_table[COLOR_DEFAULT] = "\033[1;37m";
|
||||
clg_color_table[COLOR_RED] = "\033[1;31m";
|
||||
clg_color_table[COLOR_GREEN] = "\033[1;32m";
|
||||
clg_color_table[COLOR_YELLOW] = "\033[1;33m";
|
||||
clg_color_table[COLOR_RESET] = "\033[0m";
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static const char *clg_severity_str[CLG_SEVERITY_LEN] = {
|
||||
[CLG_SEVERITY_INFO] = "INFO",
|
||||
[CLG_SEVERITY_WARN] = "WARN",
|
||||
[CLG_SEVERITY_ERROR] = "ERROR",
|
||||
[CLG_SEVERITY_FATAL] = "FATAL",
|
||||
};
|
||||
|
||||
static const char *clg_severity_as_text(enum CLG_Severity severity)
|
||||
{
|
||||
bool ok = (unsigned int)severity < CLG_SEVERITY_LEN;
|
||||
assert(ok);
|
||||
if (ok) {
|
||||
return clg_severity_str[severity];
|
||||
}
|
||||
else {
|
||||
return "INVALID_SEVERITY";
|
||||
}
|
||||
}
|
||||
|
||||
static enum eCLogColor clg_severity_to_color(enum CLG_Severity severity)
|
||||
{
|
||||
assert((unsigned int)severity < CLG_SEVERITY_LEN);
|
||||
enum eCLogColor color = COLOR_DEFAULT;
|
||||
switch (severity) {
|
||||
case CLG_SEVERITY_INFO:
|
||||
color = COLOR_DEFAULT;
|
||||
break;
|
||||
case CLG_SEVERITY_WARN:
|
||||
color = COLOR_YELLOW;
|
||||
break;
|
||||
case CLG_SEVERITY_ERROR:
|
||||
case CLG_SEVERITY_FATAL:
|
||||
color = COLOR_RED;
|
||||
break;
|
||||
default:
|
||||
/* should never get here. */
|
||||
assert(false);
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Context Type Access
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Filter the indentifier based on very basic globbing.
|
||||
* - `foo` exact match of `foo`.
|
||||
* - `foo.bar` exact match for `foo.bar`
|
||||
* - `foo.*` match for `foo` & `foo.bar` & `foo.bar.baz`
|
||||
* - `*` matches everything.
|
||||
*/
|
||||
static bool clg_ctx_filter_check(CLogContext *ctx, const char *identifier)
|
||||
{
|
||||
const int identifier_len = strlen(identifier);
|
||||
for (uint i = 0; i < 2; i++) {
|
||||
const CLG_IDFilter *flt = ctx->filters[i];
|
||||
while (flt != NULL) {
|
||||
const int len = strlen(flt->match);
|
||||
if (STREQ(flt->match, "*") ||
|
||||
((len == identifier_len) && (STREQ(identifier, flt->match))))
|
||||
{
|
||||
return (bool)i;
|
||||
}
|
||||
if ((len >= 2) && (STREQLEN(".*", &flt->match[len - 2], 2))) {
|
||||
if (((identifier_len == len - 2) && STREQLEN(identifier, flt->match, len - 2)) ||
|
||||
((identifier_len >= len - 1) && STREQLEN(identifier, flt->match, len - 1)))
|
||||
{
|
||||
return (bool)i;
|
||||
}
|
||||
}
|
||||
flt = flt->next;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* \note This should never be called per logging call.
|
||||
* Searching is only to get an initial handle.
|
||||
*/
|
||||
static CLG_LogType *clg_ctx_type_find_by_name(CLogContext *ctx, const char *identifier)
|
||||
{
|
||||
for (CLG_LogType *ty = ctx->types; ty; ty = ty->next) {
|
||||
if (STREQ(identifier, ty->identifier)) {
|
||||
return ty;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static CLG_LogType *clg_ctx_type_register(CLogContext *ctx, const char *identifier)
|
||||
{
|
||||
assert(clg_ctx_type_find_by_name(ctx, identifier) == NULL);
|
||||
CLG_LogType *ty = MEM_callocN(sizeof(*ty), __func__);
|
||||
ty->next = ctx->types;
|
||||
ctx->types = ty;
|
||||
strncpy(ty->identifier, identifier, sizeof(ty->identifier) - 1);
|
||||
ty->ctx = ctx;
|
||||
ty->level = ctx->default_type.level;
|
||||
|
||||
if (clg_ctx_filter_check(ctx, ty->identifier)) {
|
||||
ty->flag |= CLG_FLAG_USE;
|
||||
}
|
||||
return ty;
|
||||
}
|
||||
|
||||
static void clg_ctx_fatal_action(CLogContext *ctx, FILE *file_handle)
|
||||
{
|
||||
if (ctx->callbacks.fatal_fn != NULL) {
|
||||
ctx->callbacks.fatal_fn(file_handle);
|
||||
}
|
||||
fflush(file_handle);
|
||||
abort();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Logging API
|
||||
* \{ */
|
||||
|
||||
static void write_severity(CLogStringBuf *cstr, enum CLG_Severity severity, bool use_color)
|
||||
{
|
||||
assert((unsigned int)severity < CLG_SEVERITY_LEN);
|
||||
if (use_color) {
|
||||
enum eCLogColor color = clg_severity_to_color(severity);
|
||||
clg_str_append(cstr, clg_color_table[color]);
|
||||
clg_str_append(cstr, clg_severity_as_text(severity));
|
||||
clg_str_append(cstr, clg_color_table[COLOR_RESET]);
|
||||
}
|
||||
else {
|
||||
clg_str_append(cstr, clg_severity_as_text(severity));
|
||||
}
|
||||
}
|
||||
|
||||
static void write_type(CLogStringBuf *cstr, CLG_LogType *lg)
|
||||
{
|
||||
clg_str_append(cstr, " (");
|
||||
clg_str_append(cstr, lg->identifier);
|
||||
clg_str_append(cstr, "): ");
|
||||
}
|
||||
|
||||
static void write_file_line_fn(CLogStringBuf *cstr, const char *file_line, const char *fn, const bool use_basename)
|
||||
{
|
||||
uint file_line_len = strlen(file_line);
|
||||
if (use_basename) {
|
||||
uint file_line_offset = file_line_len;
|
||||
while (file_line_offset-- > 0) {
|
||||
if (file_line[file_line_offset] == PATHSEP_CHAR) {
|
||||
file_line_offset++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
file_line += file_line_offset;
|
||||
file_line_len -= file_line_offset;
|
||||
}
|
||||
clg_str_append_with_len(cstr, file_line, file_line_len);
|
||||
|
||||
|
||||
clg_str_append(cstr, " ");
|
||||
clg_str_append(cstr, fn);
|
||||
clg_str_append(cstr, ": ");
|
||||
}
|
||||
|
||||
void CLG_log_str(
|
||||
CLG_LogType *lg, enum CLG_Severity severity, const char *file_line, const char *fn,
|
||||
const char *message)
|
||||
{
|
||||
CLogStringBuf cstr;
|
||||
char cstr_stack_buf[CLOG_BUF_LEN_INIT];
|
||||
clg_str_init(&cstr, cstr_stack_buf, sizeof(cstr_stack_buf));
|
||||
|
||||
write_severity(&cstr, severity, lg->ctx->use_color);
|
||||
write_type(&cstr, lg);
|
||||
|
||||
{
|
||||
write_file_line_fn(&cstr, file_line, fn, lg->ctx->use_basename);
|
||||
clg_str_append(&cstr, message);
|
||||
}
|
||||
clg_str_append(&cstr, "\n");
|
||||
|
||||
/* could be optional */
|
||||
fwrite(cstr.data, cstr.len, 1, lg->ctx->output);
|
||||
fflush(lg->ctx->output);
|
||||
|
||||
clg_str_free(&cstr);
|
||||
|
||||
if (severity == CLG_SEVERITY_FATAL) {
|
||||
clg_ctx_fatal_action(lg->ctx, lg->ctx->output);
|
||||
}
|
||||
}
|
||||
|
||||
void CLG_logf(
|
||||
CLG_LogType *lg, enum CLG_Severity severity, const char *file_line, const char *fn,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
CLogStringBuf cstr;
|
||||
char cstr_stack_buf[CLOG_BUF_LEN_INIT];
|
||||
clg_str_init(&cstr, cstr_stack_buf, sizeof(cstr_stack_buf));
|
||||
|
||||
write_severity(&cstr, severity, lg->ctx->use_color);
|
||||
write_type(&cstr, lg);
|
||||
|
||||
{
|
||||
write_file_line_fn(&cstr, file_line, fn, lg->ctx->use_basename);
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
clg_str_vappendf(&cstr, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
clg_str_append(&cstr, "\n");
|
||||
|
||||
/* could be optional */
|
||||
fwrite(cstr.data, cstr.len, 1, lg->ctx->output);
|
||||
fflush(lg->ctx->output);
|
||||
|
||||
clg_str_free(&cstr);
|
||||
|
||||
if (severity == CLG_SEVERITY_FATAL) {
|
||||
clg_ctx_fatal_action(lg->ctx, lg->ctx->output);
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Logging Context API
|
||||
* \{ */
|
||||
|
||||
static void CLG_ctx_output_set(CLogContext *ctx, void *file_handle)
|
||||
{
|
||||
ctx->output = file_handle;
|
||||
#if defined(__unix__)
|
||||
ctx->use_color = isatty(fileno(file_handle));
|
||||
#endif
|
||||
}
|
||||
|
||||
static void CLG_ctx_output_use_basename_set(CLogContext *ctx, int value)
|
||||
{
|
||||
ctx->use_basename = (bool)value;
|
||||
}
|
||||
|
||||
/** Action on fatal severity. */
|
||||
static void CLG_ctx_fatal_fn_set(CLogContext *ctx, void (*fatal_fn)(void *file_handle))
|
||||
{
|
||||
ctx->callbacks.fatal_fn = fatal_fn;
|
||||
}
|
||||
|
||||
static void clg_ctx_type_filter_append(CLG_IDFilter **flt_list, const char *type_match, int type_match_len)
|
||||
{
|
||||
if (type_match_len == 0) {
|
||||
return;
|
||||
}
|
||||
CLG_IDFilter *flt = MEM_callocN(sizeof(*flt) + (type_match_len + 1), __func__);
|
||||
flt->next = *flt_list;
|
||||
*flt_list = flt;
|
||||
memcpy(flt->match, type_match, type_match_len);
|
||||
/* no need to null terminate since we calloc'd */
|
||||
}
|
||||
|
||||
static void CLG_ctx_type_filter_exclude(CLogContext *ctx, const char *type_match, int type_match_len)
|
||||
{
|
||||
clg_ctx_type_filter_append(&ctx->filters[0], type_match, type_match_len);
|
||||
}
|
||||
|
||||
static void CLG_ctx_type_filter_include(CLogContext *ctx, const char *type_match, int type_match_len)
|
||||
{
|
||||
clg_ctx_type_filter_append(&ctx->filters[1], type_match, type_match_len);
|
||||
}
|
||||
|
||||
static CLogContext *CLG_ctx_init(void)
|
||||
{
|
||||
CLogContext *ctx = MEM_callocN(sizeof(*ctx), __func__);
|
||||
ctx->use_color = true;
|
||||
ctx->default_type.level = 1;
|
||||
CLG_ctx_output_set(ctx, stdout);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
static void CLG_ctx_free(CLogContext *ctx)
|
||||
{
|
||||
while (ctx->types != NULL) {
|
||||
CLG_LogType *item = ctx->types;
|
||||
ctx->types = item->next;
|
||||
MEM_freeN(item);
|
||||
}
|
||||
|
||||
for (uint i = 0; i < 2; i++) {
|
||||
while (ctx->filters[i] != NULL) {
|
||||
CLG_IDFilter *item = ctx->filters[i];
|
||||
ctx->filters[i] = item->next;
|
||||
MEM_freeN(item);
|
||||
}
|
||||
}
|
||||
MEM_freeN(ctx);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Public Logging API
|
||||
*
|
||||
* Currently uses global context.
|
||||
* \{ */
|
||||
|
||||
/* We could support multiple at once, for now this seems not needed. */
|
||||
struct CLogContext *g_ctx = NULL;
|
||||
|
||||
void CLG_init(void)
|
||||
{
|
||||
g_ctx = CLG_ctx_init();
|
||||
|
||||
clg_color_table_init(g_ctx->use_color);
|
||||
}
|
||||
|
||||
void CLG_exit(void)
|
||||
{
|
||||
CLG_ctx_free(g_ctx);
|
||||
}
|
||||
|
||||
void CLG_output_set(void *file_handle)
|
||||
{
|
||||
CLG_ctx_output_set(g_ctx, file_handle);
|
||||
}
|
||||
|
||||
void CLG_output_use_basename_set(int value)
|
||||
{
|
||||
CLG_ctx_output_use_basename_set(g_ctx, value);
|
||||
}
|
||||
|
||||
|
||||
void CLG_fatal_fn_set(void (*fatal_fn)(void *file_handle))
|
||||
{
|
||||
CLG_ctx_fatal_fn_set(g_ctx, fatal_fn);
|
||||
}
|
||||
|
||||
void CLG_type_filter_exclude(const char *type_match, int type_match_len)
|
||||
{
|
||||
CLG_ctx_type_filter_exclude(g_ctx, type_match, type_match_len);
|
||||
}
|
||||
|
||||
void CLG_type_filter_include(const char *type_match, int type_match_len)
|
||||
{
|
||||
CLG_ctx_type_filter_include(g_ctx, type_match, type_match_len);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Logging Reference API
|
||||
* Use to avoid lookups each time.
|
||||
* \{ */
|
||||
|
||||
void CLG_logref_init(CLG_LogRef *clg_ref)
|
||||
{
|
||||
assert(clg_ref->type == NULL);
|
||||
CLG_LogType *clg_ty = clg_ctx_type_find_by_name(g_ctx, clg_ref->identifier);
|
||||
clg_ref->type = clg_ty ? clg_ty : clg_ctx_type_register(g_ctx, clg_ref->identifier);
|
||||
}
|
||||
|
||||
/** \} */
|
@ -810,7 +810,7 @@ int BLF_height_max(int fontid)
|
||||
FontBLF *font = blf_get(fontid);
|
||||
|
||||
if (font && font->glyph_cache) {
|
||||
return font->glyph_cache->max_glyph_height;
|
||||
return font->glyph_cache->glyph_height_max;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -821,7 +821,7 @@ float BLF_width_max(int fontid)
|
||||
FontBLF *font = blf_get(fontid);
|
||||
|
||||
if (font && font->glyph_cache) {
|
||||
return font->glyph_cache->max_glyph_width;
|
||||
return font->glyph_cache->glyph_width_max;
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
|
@ -924,7 +924,7 @@ static void blf_font_wrap_apply(
|
||||
wrap.start = wrap.last[0];
|
||||
i = wrap.last[1];
|
||||
pen_x = 0;
|
||||
pen_y -= font->glyph_cache->max_glyph_height;
|
||||
pen_y -= font->glyph_cache->glyph_height_max;
|
||||
g_prev = NULL;
|
||||
lines += 1;
|
||||
continue;
|
||||
@ -1138,7 +1138,7 @@ static void blf_font_fill(FontBLF *font)
|
||||
#if BLF_BLUR_ENABLE
|
||||
font->blur = 0;
|
||||
#endif
|
||||
font->max_tex_size = -1;
|
||||
font->tex_size_max = -1;
|
||||
|
||||
font->buf_info.fbuf = NULL;
|
||||
font->buf_info.cbuf = NULL;
|
||||
|
@ -148,34 +148,34 @@ GlyphCacheBLF *blf_glyph_cache_new(FontBLF *font)
|
||||
memset(gc->bucket, 0, sizeof(gc->bucket));
|
||||
|
||||
gc->textures = (GLuint *)MEM_mallocN(sizeof(GLuint) * 256, __func__);
|
||||
gc->ntex = 256;
|
||||
gc->cur_tex = BLF_CURTEX_UNSET;
|
||||
gc->x_offs = 0;
|
||||
gc->y_offs = 0;
|
||||
gc->textures_len = 256;
|
||||
gc->texture_current = BLF_TEXTURE_UNSET;
|
||||
gc->offset_x = 0;
|
||||
gc->offset_y = 0;
|
||||
gc->pad = 3;
|
||||
|
||||
gc->num_glyphs = (int)font->face->num_glyphs;
|
||||
gc->rem_glyphs = (int)font->face->num_glyphs;
|
||||
gc->glyphs_len_max = (int)font->face->num_glyphs;
|
||||
gc->glyphs_len_free = (int)font->face->num_glyphs;
|
||||
gc->ascender = ((float)font->face->size->metrics.ascender) / 64.0f;
|
||||
gc->descender = ((float)font->face->size->metrics.descender) / 64.0f;
|
||||
|
||||
if (FT_IS_SCALABLE(font->face)) {
|
||||
gc->max_glyph_width = (int)((float)(font->face->bbox.xMax - font->face->bbox.xMin) *
|
||||
gc->glyph_width_max = (int)((float)(font->face->bbox.xMax - font->face->bbox.xMin) *
|
||||
(((float)font->face->size->metrics.x_ppem) /
|
||||
((float)font->face->units_per_EM)));
|
||||
|
||||
gc->max_glyph_height = (int)((float)(font->face->bbox.yMax - font->face->bbox.yMin) *
|
||||
gc->glyph_height_max = (int)((float)(font->face->bbox.yMax - font->face->bbox.yMin) *
|
||||
(((float)font->face->size->metrics.y_ppem) /
|
||||
((float)font->face->units_per_EM)));
|
||||
}
|
||||
else {
|
||||
gc->max_glyph_width = (int)(((float)font->face->size->metrics.max_advance) / 64.0f);
|
||||
gc->max_glyph_height = (int)(((float)font->face->size->metrics.height) / 64.0f);
|
||||
gc->glyph_width_max = (int)(((float)font->face->size->metrics.max_advance) / 64.0f);
|
||||
gc->glyph_height_max = (int)(((float)font->face->size->metrics.height) / 64.0f);
|
||||
}
|
||||
|
||||
/* can happen with size 1 fonts */
|
||||
CLAMP_MIN(gc->max_glyph_width, 1);
|
||||
CLAMP_MIN(gc->max_glyph_height, 1);
|
||||
CLAMP_MIN(gc->glyph_width_max, 1);
|
||||
CLAMP_MIN(gc->glyph_height_max, 1);
|
||||
|
||||
gc->p2_width = 0;
|
||||
gc->p2_height = 0;
|
||||
@ -205,9 +205,10 @@ void blf_glyph_cache_free(GlyphCacheBLF *gc)
|
||||
}
|
||||
}
|
||||
|
||||
if (gc->cur_tex != BLF_CURTEX_UNSET)
|
||||
glDeleteTextures((int)gc->cur_tex + 1, gc->textures);
|
||||
MEM_freeN((void *)gc->textures);
|
||||
if (gc->texture_current != BLF_TEXTURE_UNSET) {
|
||||
glDeleteTextures((int)gc->texture_current + 1, gc->textures);
|
||||
}
|
||||
MEM_freeN(gc->textures);
|
||||
MEM_freeN(gc);
|
||||
}
|
||||
|
||||
@ -216,25 +217,27 @@ static void blf_glyph_cache_texture(FontBLF *font, GlyphCacheBLF *gc)
|
||||
int i;
|
||||
|
||||
/* move the index. */
|
||||
gc->cur_tex++;
|
||||
gc->texture_current++;
|
||||
|
||||
if (UNLIKELY(gc->cur_tex >= gc->ntex)) {
|
||||
gc->ntex *= 2;
|
||||
gc->textures = (GLuint *)MEM_reallocN((void *)gc->textures, sizeof(GLuint) * gc->ntex);
|
||||
if (UNLIKELY(gc->texture_current >= gc->textures_len)) {
|
||||
gc->textures_len *= 2;
|
||||
gc->textures = MEM_reallocN((void *)gc->textures, sizeof(GLuint) * gc->textures_len);
|
||||
}
|
||||
|
||||
gc->p2_width = (int)blf_next_p2((unsigned int)((gc->rem_glyphs * gc->max_glyph_width) + (gc->pad * 2)));
|
||||
if (gc->p2_width > font->max_tex_size)
|
||||
gc->p2_width = font->max_tex_size;
|
||||
gc->p2_width = (int)blf_next_p2((unsigned int)((gc->glyphs_len_free * gc->glyph_width_max) + (gc->pad * 2)));
|
||||
if (gc->p2_width > font->tex_size_max) {
|
||||
gc->p2_width = font->tex_size_max;
|
||||
}
|
||||
|
||||
i = (int)((gc->p2_width - (gc->pad * 2)) / gc->max_glyph_width);
|
||||
gc->p2_height = (int)blf_next_p2((unsigned int)(((gc->num_glyphs / i) + 1) * gc->max_glyph_height));
|
||||
i = (int)((gc->p2_width - (gc->pad * 2)) / gc->glyph_width_max);
|
||||
gc->p2_height = (int)blf_next_p2((unsigned int)(((gc->glyphs_len_max / i) + 1) * gc->glyph_height_max));
|
||||
|
||||
if (gc->p2_height > font->max_tex_size)
|
||||
gc->p2_height = font->max_tex_size;
|
||||
if (gc->p2_height > font->tex_size_max) {
|
||||
gc->p2_height = font->tex_size_max;
|
||||
}
|
||||
|
||||
glGenTextures(1, &gc->textures[gc->cur_tex]);
|
||||
glBindTexture(GL_TEXTURE_2D, (font->tex_bind_state = gc->textures[gc->cur_tex]));
|
||||
glGenTextures(1, &gc->textures[gc->texture_current]);
|
||||
glBindTexture(GL_TEXTURE_2D, (font->tex_bind_state = gc->textures[gc->texture_current]));
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
@ -323,8 +326,8 @@ GlyphBLF *blf_glyph_add(FontBLF *font, unsigned int index, unsigned int c)
|
||||
g = (GlyphBLF *)MEM_callocN(sizeof(GlyphBLF), "blf_glyph_add");
|
||||
g->c = c;
|
||||
g->idx = (FT_UInt)index;
|
||||
g->xoff = -1;
|
||||
g->yoff = -1;
|
||||
g->offset_x = -1;
|
||||
g->offset_y = -1;
|
||||
bitmap = slot->bitmap;
|
||||
g->width = (int)bitmap.width;
|
||||
g->height = (int)bitmap.rows;
|
||||
@ -457,38 +460,38 @@ void blf_glyph_render(FontBLF *font, GlyphBLF *g, float x, float y)
|
||||
if (g->build_tex == 0) {
|
||||
GlyphCacheBLF *gc = font->glyph_cache;
|
||||
|
||||
if (font->max_tex_size == -1)
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, (GLint *)&font->max_tex_size);
|
||||
if (font->tex_size_max == -1)
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, (GLint *)&font->tex_size_max);
|
||||
|
||||
if (gc->cur_tex == BLF_CURTEX_UNSET) {
|
||||
if (gc->texture_current == BLF_TEXTURE_UNSET) {
|
||||
blf_glyph_cache_texture(font, gc);
|
||||
gc->x_offs = gc->pad;
|
||||
gc->y_offs = 0;
|
||||
gc->offset_x = gc->pad;
|
||||
gc->offset_y = 0;
|
||||
}
|
||||
|
||||
if (gc->x_offs > (gc->p2_width - gc->max_glyph_width)) {
|
||||
gc->x_offs = gc->pad;
|
||||
gc->y_offs += gc->max_glyph_height;
|
||||
if (gc->offset_x > (gc->p2_width - gc->glyph_width_max)) {
|
||||
gc->offset_x = gc->pad;
|
||||
gc->offset_y += gc->glyph_height_max;
|
||||
|
||||
if (gc->y_offs > (gc->p2_height - gc->max_glyph_height)) {
|
||||
gc->y_offs = 0;
|
||||
if (gc->offset_y > (gc->p2_height - gc->glyph_height_max)) {
|
||||
gc->offset_y = 0;
|
||||
blf_glyph_cache_texture(font, gc);
|
||||
}
|
||||
}
|
||||
|
||||
g->tex = gc->textures[gc->cur_tex];
|
||||
g->xoff = gc->x_offs;
|
||||
g->yoff = gc->y_offs;
|
||||
g->tex = gc->textures[gc->texture_current];
|
||||
g->offset_x = gc->offset_x;
|
||||
g->offset_y = gc->offset_y;
|
||||
|
||||
/* prevent glTexSubImage2D from failing if the character
|
||||
* asks for pixels out of bounds, this tends only to happen
|
||||
* with very small sizes (5px high or less) */
|
||||
if (UNLIKELY((g->xoff + g->width) > gc->p2_width)) {
|
||||
g->width -= (g->xoff + g->width) - gc->p2_width;
|
||||
if (UNLIKELY((g->offset_x + g->width) > gc->p2_width)) {
|
||||
g->width -= (g->offset_x + g->width) - gc->p2_width;
|
||||
BLI_assert(g->width > 0);
|
||||
}
|
||||
if (UNLIKELY((g->yoff + g->height) > gc->p2_height)) {
|
||||
g->height -= (g->yoff + g->height) - gc->p2_height;
|
||||
if (UNLIKELY((g->offset_y + g->height) > gc->p2_height)) {
|
||||
g->height -= (g->offset_y + g->height) - gc->p2_height;
|
||||
BLI_assert(g->height > 0);
|
||||
}
|
||||
|
||||
@ -503,21 +506,21 @@ void blf_glyph_render(FontBLF *font, GlyphBLF *g, float x, float y)
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, g->tex);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, g->xoff, g->yoff, g->width, g->height, GL_RED, GL_UNSIGNED_BYTE, g->bitmap);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, g->offset_x, g->offset_y, g->width, g->height, GL_RED, GL_UNSIGNED_BYTE, g->bitmap);
|
||||
|
||||
glPixelStorei(GL_UNPACK_LSB_FIRST, lsb_first);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, row_length);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
|
||||
|
||||
g->uv[0][0] = ((float)g->xoff) / ((float)gc->p2_width);
|
||||
g->uv[0][1] = ((float)g->yoff) / ((float)gc->p2_height);
|
||||
g->uv[1][0] = ((float)(g->xoff + g->width)) / ((float)gc->p2_width);
|
||||
g->uv[1][1] = ((float)(g->yoff + g->height)) / ((float)gc->p2_height);
|
||||
g->uv[0][0] = ((float)g->offset_x) / ((float)gc->p2_width);
|
||||
g->uv[0][1] = ((float)g->offset_y) / ((float)gc->p2_height);
|
||||
g->uv[1][0] = ((float)(g->offset_x + g->width)) / ((float)gc->p2_width);
|
||||
g->uv[1][1] = ((float)(g->offset_y + g->height)) / ((float)gc->p2_height);
|
||||
|
||||
/* update the x offset for the next glyph. */
|
||||
gc->x_offs += (int)BLI_rctf_size_x(&g->box) + gc->pad;
|
||||
gc->offset_x += (int)BLI_rctf_size_x(&g->box) + gc->pad;
|
||||
|
||||
gc->rem_glyphs--;
|
||||
gc->glyphs_len_free--;
|
||||
g->build_tex = 1;
|
||||
}
|
||||
|
||||
|
@ -80,33 +80,33 @@ typedef struct GlyphCacheBLF {
|
||||
unsigned int *textures;
|
||||
|
||||
/* size of the array. */
|
||||
unsigned int ntex;
|
||||
unsigned int textures_len;
|
||||
|
||||
/* and the last texture, aka. the current texture. */
|
||||
unsigned int cur_tex;
|
||||
unsigned int texture_current;
|
||||
|
||||
/* like bftgl, we draw every glyph in a big texture, so this is the
|
||||
* current position inside the texture.
|
||||
*/
|
||||
int x_offs;
|
||||
int y_offs;
|
||||
int offset_x;
|
||||
int offset_y;
|
||||
|
||||
/* and the space from one to other. */
|
||||
int pad;
|
||||
|
||||
/* and the bigger glyph in the font. */
|
||||
int max_glyph_width;
|
||||
int max_glyph_height;
|
||||
int glyph_width_max;
|
||||
int glyph_height_max;
|
||||
|
||||
/* next two integer power of two, to build the texture. */
|
||||
int p2_width;
|
||||
int p2_height;
|
||||
|
||||
/* number of glyphs in the font. */
|
||||
int num_glyphs;
|
||||
int glyphs_len_max;
|
||||
|
||||
/* number of glyphs that we load here. */
|
||||
int rem_glyphs;
|
||||
/* number of glyphs not yet loaded (decreases every glyph loaded). */
|
||||
int glyphs_len_free;
|
||||
|
||||
/* ascender and descender value. */
|
||||
float ascender;
|
||||
@ -135,8 +135,8 @@ typedef struct GlyphBLF {
|
||||
unsigned int tex;
|
||||
|
||||
/* position inside the texture where this glyph is store. */
|
||||
int xoff;
|
||||
int yoff;
|
||||
int offset_x;
|
||||
int offset_y;
|
||||
|
||||
/* Bitmap data, from freetype. Take care that this
|
||||
* can be NULL.
|
||||
@ -159,7 +159,7 @@ typedef struct GlyphBLF {
|
||||
float pos_y;
|
||||
|
||||
/* with value of zero mean that we need build the texture. */
|
||||
short build_tex;
|
||||
char build_tex;
|
||||
} GlyphBLF;
|
||||
|
||||
typedef struct FontBufInfoBLF {
|
||||
@ -240,7 +240,7 @@ typedef struct FontBLF {
|
||||
unsigned int size;
|
||||
|
||||
/* max texture size. */
|
||||
int max_tex_size;
|
||||
int tex_size_max;
|
||||
|
||||
/* cache current OpenGL texture to save calls into the API */
|
||||
unsigned int tex_bind_state;
|
||||
@ -284,6 +284,6 @@ typedef struct DirBLF {
|
||||
char *path;
|
||||
} DirBLF;
|
||||
|
||||
#define BLF_CURTEX_UNSET ((unsigned int)-1)
|
||||
#define BLF_TEXTURE_UNSET ((unsigned int)-1)
|
||||
|
||||
#endif /* __BLF_INTERNAL_TYPES_H__ */
|
||||
|
@ -31,22 +31,13 @@ extern "C" {
|
||||
struct bContext;
|
||||
struct Scene;
|
||||
struct Main;
|
||||
struct MemFileUndoData;
|
||||
|
||||
#define BKE_UNDO_STR_MAX 64
|
||||
|
||||
/* global undo */
|
||||
extern void BKE_undo_write(struct bContext *C, const char *name);
|
||||
extern void BKE_undo_step(struct bContext *C, int step);
|
||||
extern void BKE_undo_name(struct bContext *C, const char *name);
|
||||
extern bool BKE_undo_is_valid(const char *name);
|
||||
extern void BKE_undo_reset(void);
|
||||
extern void BKE_undo_number(struct bContext *C, int nr);
|
||||
extern const char *BKE_undo_get_name(int nr, bool *r_active);
|
||||
extern const char *BKE_undo_get_name_last(void);
|
||||
extern bool BKE_undo_save_file(const char *filename);
|
||||
extern struct Main *BKE_undo_get_main(struct Scene **r_scene);
|
||||
|
||||
extern void BKE_undo_callback_wm_kill_jobs_set(void (*callback)(struct bContext *C));
|
||||
struct MemFileUndoData *BKE_memfile_undo_encode(struct Main *bmain, struct MemFileUndoData *mfu_prev);
|
||||
bool BKE_memfile_undo_decode(struct MemFileUndoData *mfu, struct bContext *C);
|
||||
void BKE_memfile_undo_free(struct MemFileUndoData *mfu);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -80,6 +80,13 @@ typedef struct Global {
|
||||
* however this is now only used for runtime options */
|
||||
int f;
|
||||
|
||||
struct {
|
||||
/* Logging vars (different loggers may use). */
|
||||
int level;
|
||||
/* FILE handle or use stderr (we own this so close when done). */
|
||||
void *file;
|
||||
} log;
|
||||
|
||||
/* debug flag, G_DEBUG, G_DEBUG_PYTHON & friends, set python or command line args */
|
||||
int debug;
|
||||
|
||||
|
@ -84,7 +84,9 @@ typedef struct Main {
|
||||
short minversionfile, minsubversionfile;
|
||||
uint64_t build_commit_timestamp; /* commit's timestamp from buildinfo */
|
||||
char build_hash[16]; /* hash from buildinfo */
|
||||
short recovered; /* indicate the main->name (file) is the recovered one */
|
||||
char recovered; /* indicate the main->name (file) is the recovered one */
|
||||
/** All current ID's exist in the last memfile undo step. */
|
||||
char is_memfile_undo_written;
|
||||
|
||||
BlendThumbnail *blen_thumb;
|
||||
|
||||
|
@ -230,7 +230,6 @@ typedef struct PTCacheEditPoint {
|
||||
} PTCacheEditPoint;
|
||||
|
||||
typedef struct PTCacheUndo {
|
||||
struct PTCacheUndo *next, *prev;
|
||||
struct PTCacheEditPoint *points;
|
||||
|
||||
/* particles stuff */
|
||||
@ -243,12 +242,11 @@ typedef struct PTCacheUndo {
|
||||
struct ListBase mem_cache;
|
||||
|
||||
int totpoint;
|
||||
char name[64];
|
||||
|
||||
size_t undo_size;
|
||||
} PTCacheUndo;
|
||||
|
||||
typedef struct PTCacheEdit {
|
||||
ListBase undo;
|
||||
struct PTCacheUndo *curundo;
|
||||
PTCacheEditPoint *points;
|
||||
|
||||
struct PTCacheID pid;
|
||||
|
187
source/blender/blenkernel/BKE_undo_system.h
Normal file
187
source/blender/blenkernel/BKE_undo_system.h
Normal file
@ -0,0 +1,187 @@
|
||||
/*
|
||||
* ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
#ifndef __BKE_UNDO_SYSTEM_H__
|
||||
#define __BKE_UNDO_SYSTEM_H__
|
||||
|
||||
/** \file BKE_undo_system.h
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
struct Main;
|
||||
struct UndoStep;
|
||||
struct bContext;
|
||||
|
||||
/* ID's */
|
||||
struct Mesh;
|
||||
struct Object;
|
||||
struct Scene;
|
||||
struct Text;
|
||||
|
||||
#include "DNA_ID.h"
|
||||
#include "DNA_listBase.h"
|
||||
|
||||
typedef struct UndoRefID { struct ID *ptr; char name[MAX_ID_NAME]; } UndoRefID;
|
||||
/* UndoRefID_Mesh & friends. */
|
||||
#define UNDO_REF_ID_TYPE(ptr_ty) \
|
||||
typedef struct UndoRefID_##ptr_ty { struct ptr_ty *ptr; char name[MAX_ID_NAME]; } UndoRefID_##ptr_ty
|
||||
UNDO_REF_ID_TYPE(Mesh);
|
||||
UNDO_REF_ID_TYPE(Object);
|
||||
UNDO_REF_ID_TYPE(Scene);
|
||||
UNDO_REF_ID_TYPE(Text);
|
||||
|
||||
typedef struct UndoStack {
|
||||
ListBase steps;
|
||||
struct UndoStep *step_active;
|
||||
|
||||
/**
|
||||
* Some undo systems require begin/end, see: #UndoType.step_encode_init
|
||||
*
|
||||
* \note This is not included in the 'steps' list.
|
||||
* That is done once end is called.
|
||||
*/
|
||||
struct UndoStep *step_init;
|
||||
} UndoStack;
|
||||
|
||||
|
||||
typedef struct UndoStep {
|
||||
struct UndoStep *next, *prev;
|
||||
char name[64];
|
||||
const struct UndoType *type;
|
||||
/** Size in bytes of all data in step (not including the step). */
|
||||
size_t data_size;
|
||||
/** Users should never see this step (only use for internal consistency). */
|
||||
bool skip;
|
||||
/* Over alloc 'type->struct_size'. */
|
||||
} UndoStep;
|
||||
|
||||
typedef enum eUndoTypeMode {
|
||||
/**
|
||||
* Each undo step stores a version of the state.
|
||||
* This means we can simply load in a previous state at any time.
|
||||
*/
|
||||
BKE_UNDOTYPE_MODE_STORE = 1,
|
||||
/**
|
||||
* Each undo step is a series of edits.
|
||||
* This means to change states we need to apply each edit.
|
||||
* It also means the 'step_decode' callback needs to detect the difference between undo and redo.
|
||||
* (Currently used for text edit and image & sculpt painting).
|
||||
*/
|
||||
BKE_UNDOTYPE_MODE_ACCUMULATE = 2,
|
||||
} eUndoTypeMode;
|
||||
|
||||
typedef void (*UndoTypeForEachIDRefFn)(void *user_data, struct UndoRefID *id_ref);
|
||||
|
||||
typedef struct UndoType {
|
||||
struct UndoType *next, *prev;
|
||||
/** Only for debugging. */
|
||||
const char *name;
|
||||
|
||||
/**
|
||||
* When NULL, we don't consider this undo type for context checks.
|
||||
* Operators must explicitly set the undo type and handle adding the undo step.
|
||||
* This is needed when tools operate on data which isn't the primary mode (eg, paint-curve in sculpt mode).
|
||||
*/
|
||||
bool (*poll)(struct bContext *C);
|
||||
|
||||
/**
|
||||
* None of these callbacks manage list add/removal.
|
||||
*
|
||||
* Note that 'step_encode_init' is optional,
|
||||
* some undo types need to perform operatons before undo push finishes.
|
||||
*/
|
||||
void (*step_encode_init)(struct bContext *C, UndoStep *us);
|
||||
|
||||
bool (*step_encode)(struct bContext *C, UndoStep *us);
|
||||
void (*step_decode)(struct bContext *C, UndoStep *us, int dir);
|
||||
|
||||
/**
|
||||
* \note When freeing all steps,
|
||||
* free from the last since #MemFileUndoType will merge with the next undo type in the list. */
|
||||
void (*step_free)(UndoStep *us);
|
||||
|
||||
void (*step_foreach_ID_ref)(UndoStep *us, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data);
|
||||
|
||||
eUndoTypeMode mode;
|
||||
bool use_context;
|
||||
|
||||
int step_size;
|
||||
} UndoType;
|
||||
|
||||
/* expose since we need to perform operations on spesific undo types (rarely). */
|
||||
extern const UndoType *BKE_UNDOSYS_TYPE_MEMFILE;
|
||||
extern const UndoType *BKE_UNDOSYS_TYPE_IMAGE;
|
||||
extern const UndoType *BKE_UNDOSYS_TYPE_SCULPT;
|
||||
extern const UndoType *BKE_UNDOSYS_TYPE_PARTICLE;
|
||||
extern const UndoType *BKE_UNDOSYS_TYPE_PAINTCURVE;
|
||||
|
||||
UndoStack *BKE_undosys_stack_create(void);
|
||||
void BKE_undosys_stack_destroy(UndoStack *ustack);
|
||||
void BKE_undosys_stack_clear(UndoStack *ustack);
|
||||
bool BKE_undosys_stack_has_undo(UndoStack *ustack, const char *name);
|
||||
void BKE_undosys_stack_init_from_main(UndoStack *ustack, struct Main *bmain);
|
||||
UndoStep *BKE_undosys_stack_active_with_type(UndoStack *ustack, const UndoType *ut);
|
||||
UndoStep *BKE_undosys_stack_init_or_active_with_type(UndoStack *ustack, const UndoType *ut);
|
||||
void BKE_undosys_stack_limit_steps_and_memory(UndoStack *ustack, int steps, size_t memory_limit);
|
||||
|
||||
/* Only some UndoType's require init. */
|
||||
void BKE_undosys_step_push_init_with_type(UndoStack *ustack, struct bContext *C, const char *name, const UndoType *ut);
|
||||
void BKE_undosys_step_push_init(UndoStack *ustack, struct bContext *C, const char *name);
|
||||
|
||||
bool BKE_undosys_step_push_with_type(UndoStack *ustack, struct bContext *C, const char *name, const UndoType *ut);
|
||||
bool BKE_undosys_step_push(UndoStack *ustack, struct bContext *C, const char *name);
|
||||
|
||||
UndoStep *BKE_undosys_step_find_by_name_with_type(UndoStack *ustack, const char *name, const UndoType *ut);
|
||||
UndoStep *BKE_undosys_step_find_by_name(UndoStack *ustack, const char *name);
|
||||
|
||||
bool BKE_undosys_step_undo_with_data_ex(UndoStack *ustack, struct bContext *C, UndoStep *us, bool use_skip);
|
||||
bool BKE_undosys_step_undo_with_data(UndoStack *ustack, struct bContext *C, UndoStep *us);
|
||||
bool BKE_undosys_step_undo(UndoStack *ustack, struct bContext *C);
|
||||
|
||||
bool BKE_undosys_step_redo_with_data_ex(UndoStack *ustack, struct bContext *C, UndoStep *us, bool use_skip);
|
||||
bool BKE_undosys_step_redo_with_data(UndoStack *ustack, struct bContext *C, UndoStep *us);
|
||||
bool BKE_undosys_step_redo(UndoStack *ustack, struct bContext *C);
|
||||
|
||||
bool BKE_undosys_step_load_data(UndoStack *ustack, struct bContext *C, UndoStep *us);
|
||||
|
||||
bool BKE_undosys_step_undo_compat_only(UndoStack *ustack, struct bContext *C, int step);
|
||||
void BKE_undosys_step_undo_from_index(UndoStack *ustack, struct bContext *C, int index);
|
||||
UndoStep *BKE_undosys_step_same_type_next(UndoStep *us);
|
||||
UndoStep *BKE_undosys_step_same_type_prev(UndoStep *us);
|
||||
|
||||
/* Type System */
|
||||
UndoType *BKE_undosys_type_append(void (*undosys_fn)(UndoType *));
|
||||
void BKE_undosys_type_free_all(void);
|
||||
|
||||
/* ID Accessor */
|
||||
#if 0 /* functionality is only used internally for now. */
|
||||
void BKE_undosys_foreach_ID_ref(UndoStack *ustack, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data);
|
||||
#endif
|
||||
|
||||
/* Use when the undo step stores many arbitrary pointers. */
|
||||
struct UndoIDPtrMap;
|
||||
struct UndoIDPtrMap *BKE_undosys_ID_map_create(void);
|
||||
void BKE_undosys_ID_map_destroy(struct UndoIDPtrMap *map);
|
||||
void BKE_undosys_ID_map_add(struct UndoIDPtrMap *map, ID *id);
|
||||
struct ID *BKE_undosys_ID_map_lookup(const struct UndoIDPtrMap *map, const struct ID *id_src);
|
||||
void BKE_undosys_ID_map_foreach_ID_ref(
|
||||
struct UndoIDPtrMap *map,
|
||||
UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data);
|
||||
|
||||
#endif /* __BKE_UNDO_SYSTEM_H__ */
|
@ -49,6 +49,7 @@ set(INC
|
||||
../../../intern/mikktspace
|
||||
../../../intern/smoke/extern
|
||||
../../../intern/atomic
|
||||
../../../intern/clog
|
||||
../../../intern/libmv
|
||||
../../../extern/curve_fit_nd
|
||||
)
|
||||
@ -194,6 +195,7 @@ set(SRC
|
||||
intern/tracking_solver.c
|
||||
intern/tracking_stabilize.c
|
||||
intern/tracking_util.c
|
||||
intern/undo_system.c
|
||||
intern/unit.c
|
||||
intern/workspace.c
|
||||
intern/world.c
|
||||
@ -309,6 +311,7 @@ set(SRC
|
||||
BKE_text.h
|
||||
BKE_texture.h
|
||||
BKE_tracking.h
|
||||
BKE_undo_system.h
|
||||
BKE_unit.h
|
||||
BKE_workspace.h
|
||||
BKE_world.h
|
||||
|
@ -63,7 +63,6 @@
|
||||
#include "BKE_DerivedMesh.h"
|
||||
#include "BKE_deform.h"
|
||||
#include "BKE_displist.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_idprop.h"
|
||||
#include "BKE_library.h"
|
||||
#include "BKE_library_query.h"
|
||||
|
@ -47,7 +47,6 @@
|
||||
|
||||
#include "BIK_api.h"
|
||||
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_main.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
|
@ -85,6 +85,10 @@ void BKE_blender_free(void)
|
||||
BKE_main_free(G.main);
|
||||
G.main = NULL;
|
||||
|
||||
if (G.log.file != NULL) {
|
||||
fclose(G.log.file);
|
||||
}
|
||||
|
||||
BKE_spacetypes_free(); /* after free main, it uses space callbacks */
|
||||
|
||||
IMB_exit();
|
||||
@ -134,6 +138,8 @@ void BKE_blender_globals_init(void)
|
||||
#else
|
||||
G.f &= ~G_SCRIPT_AUTOEXEC;
|
||||
#endif
|
||||
|
||||
G.log.level = 1;
|
||||
}
|
||||
|
||||
void BKE_blender_globals_clear(void)
|
||||
|
@ -42,27 +42,18 @@
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BLI_fileops.h"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "IMB_imbuf.h"
|
||||
#include "IMB_moviecache.h"
|
||||
|
||||
#include "BKE_blender_undo.h" /* own include */
|
||||
#include "BKE_blendfile.h"
|
||||
#include "BKE_appdir.h"
|
||||
#include "BKE_brush.h"
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_image.h"
|
||||
#include "BKE_main.h"
|
||||
#include "RE_pipeline.h"
|
||||
|
||||
#include "BLO_undofile.h"
|
||||
#include "BLO_readfile.h"
|
||||
#include "BLO_writefile.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
@ -74,46 +65,21 @@
|
||||
|
||||
#define UNDO_DISK 0
|
||||
|
||||
typedef struct UndoElem {
|
||||
struct UndoElem *next, *prev;
|
||||
/* Only for 'UNDO_DISK' */
|
||||
char filename[FILE_MAX];
|
||||
char name[BKE_UNDO_STR_MAX];
|
||||
MemFile memfile;
|
||||
size_t undo_size;
|
||||
} UndoElem;
|
||||
|
||||
static ListBase undobase = {NULL, NULL};
|
||||
static UndoElem *curundo = NULL;
|
||||
|
||||
/**
|
||||
* Avoid bad-level call to #WM_jobs_kill_all_except()
|
||||
*/
|
||||
static void (*undo_wm_job_kill_callback)(struct bContext *C) = NULL;
|
||||
|
||||
void BKE_undo_callback_wm_kill_jobs_set(void (*callback)(struct bContext *C))
|
||||
{
|
||||
undo_wm_job_kill_callback = callback;
|
||||
}
|
||||
|
||||
static int read_undosave(bContext *C, UndoElem *uel)
|
||||
bool BKE_memfile_undo_decode(MemFileUndoData *mfu, bContext *C)
|
||||
{
|
||||
char mainstr[sizeof(G.main->name)];
|
||||
int success = 0, fileflags;
|
||||
|
||||
/* This is needed so undoing/redoing doesn't crash with threaded previews going */
|
||||
undo_wm_job_kill_callback(C);
|
||||
|
||||
BLI_strncpy(mainstr, G.main->name, sizeof(mainstr)); /* temporal store */
|
||||
|
||||
fileflags = G.fileflags;
|
||||
G.fileflags |= G_FILE_NO_UI;
|
||||
|
||||
if (UNDO_DISK) {
|
||||
success = (BKE_blendfile_read(C, uel->filename, NULL, 0) != BKE_BLENDFILE_READ_FAIL);
|
||||
success = (BKE_blendfile_read(C, mfu->filename, NULL, 0) != BKE_BLENDFILE_READ_FAIL);
|
||||
}
|
||||
else {
|
||||
success = BKE_blendfile_read_from_memfile(C, &uel->memfile, NULL, 0);
|
||||
success = BKE_blendfile_read_from_memfile(C, &mfu->memfile, NULL, 0);
|
||||
}
|
||||
|
||||
/* restore */
|
||||
@ -128,51 +94,9 @@ static int read_undosave(bContext *C, UndoElem *uel)
|
||||
return success;
|
||||
}
|
||||
|
||||
/* name can be a dynamic string */
|
||||
void BKE_undo_write(bContext *C, const char *name)
|
||||
MemFileUndoData *BKE_memfile_undo_encode(Main *bmain, MemFileUndoData *mfu_prev)
|
||||
{
|
||||
int nr /*, success */ /* UNUSED */;
|
||||
UndoElem *uel;
|
||||
|
||||
if ((U.uiflag & USER_GLOBALUNDO) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (U.undosteps == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* remove all undos after (also when curundo == NULL) */
|
||||
while (undobase.last != curundo) {
|
||||
uel = undobase.last;
|
||||
BLI_remlink(&undobase, uel);
|
||||
BLO_memfile_free(&uel->memfile);
|
||||
MEM_freeN(uel);
|
||||
}
|
||||
|
||||
/* make new */
|
||||
curundo = uel = MEM_callocN(sizeof(UndoElem), "undo file");
|
||||
BLI_strncpy(uel->name, name, sizeof(uel->name));
|
||||
BLI_addtail(&undobase, uel);
|
||||
|
||||
/* and limit amount to the maximum */
|
||||
nr = 0;
|
||||
uel = undobase.last;
|
||||
while (uel) {
|
||||
nr++;
|
||||
if (nr == U.undosteps) break;
|
||||
uel = uel->prev;
|
||||
}
|
||||
if (uel) {
|
||||
while (undobase.first != uel) {
|
||||
UndoElem *first = undobase.first;
|
||||
BLI_remlink(&undobase, first);
|
||||
/* the merge is because of compression */
|
||||
BLO_memfile_merge(&first->memfile, &first->next->memfile);
|
||||
MEM_freeN(first);
|
||||
}
|
||||
}
|
||||
|
||||
MemFileUndoData *mfu = MEM_callocN(sizeof(MemFileUndoData), __func__);
|
||||
|
||||
/* disk save version */
|
||||
if (UNDO_DISK) {
|
||||
@ -188,222 +112,25 @@ void BKE_undo_write(bContext *C, const char *name)
|
||||
BLI_snprintf(numstr, sizeof(numstr), "%d.blend", counter);
|
||||
BLI_make_file_string("/", filename, BKE_tempdir_session(), numstr);
|
||||
|
||||
/* success = */ /* UNUSED */ BLO_write_file(CTX_data_main(C), filename, fileflags, NULL, NULL);
|
||||
/* success = */ /* UNUSED */ BLO_write_file(bmain, filename, fileflags, NULL, NULL);
|
||||
|
||||
BLI_strncpy(curundo->filename, filename, sizeof(curundo->filename));
|
||||
BLI_strncpy(mfu->filename, filename, sizeof(mfu->filename));
|
||||
}
|
||||
else {
|
||||
MemFile *prevfile = NULL;
|
||||
|
||||
if (curundo->prev) prevfile = &(curundo->prev->memfile);
|
||||
|
||||
/* success = */ /* UNUSED */ BLO_write_file_mem(CTX_data_main(C), prevfile, &curundo->memfile, G.fileflags);
|
||||
curundo->undo_size = curundo->memfile.size;
|
||||
MemFile *prevfile = (mfu_prev) ? &(mfu_prev->memfile) : NULL;
|
||||
/* success = */ /* UNUSED */ BLO_write_file_mem(bmain, prevfile, &mfu->memfile, G.fileflags);
|
||||
mfu->undo_size = mfu->memfile.size;
|
||||
}
|
||||
|
||||
if (U.undomemory != 0) {
|
||||
size_t maxmem, totmem;
|
||||
/* limit to maximum memory (afterwards, we can't know in advance) */
|
||||
totmem = 0;
|
||||
maxmem = ((size_t)U.undomemory) * 1024 * 1024;
|
||||
bmain->is_memfile_undo_written = true;
|
||||
|
||||
/* keep at least two (original + other) */
|
||||
uel = undobase.last;
|
||||
while (uel && uel->prev) {
|
||||
totmem += uel->undo_size;
|
||||
if (totmem > maxmem) break;
|
||||
uel = uel->prev;
|
||||
}
|
||||
|
||||
if (uel) {
|
||||
if (uel->prev && uel->prev->prev)
|
||||
uel = uel->prev;
|
||||
|
||||
while (undobase.first != uel) {
|
||||
UndoElem *first = undobase.first;
|
||||
BLI_remlink(&undobase, first);
|
||||
/* the merge is because of compression */
|
||||
BLO_memfile_merge(&first->memfile, &first->next->memfile);
|
||||
MEM_freeN(first);
|
||||
}
|
||||
}
|
||||
}
|
||||
return mfu;
|
||||
}
|
||||
|
||||
/* 1 = an undo, -1 is a redo. we have to make sure 'curundo' remains at current situation */
|
||||
void BKE_undo_step(bContext *C, int step)
|
||||
void BKE_memfile_undo_free(MemFileUndoData *mfu)
|
||||
{
|
||||
|
||||
if (step == 0) {
|
||||
read_undosave(C, curundo);
|
||||
}
|
||||
else if (step == 1) {
|
||||
/* curundo should never be NULL, after restart or load file it should call undo_save */
|
||||
if (curundo == NULL || curundo->prev == NULL) {
|
||||
// XXX error("No undo available");
|
||||
}
|
||||
else {
|
||||
if (G.debug & G_DEBUG) printf("undo %s\n", curundo->name);
|
||||
curundo = curundo->prev;
|
||||
read_undosave(C, curundo);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* curundo has to remain current situation! */
|
||||
|
||||
if (curundo == NULL || curundo->next == NULL) {
|
||||
// XXX error("No redo available");
|
||||
}
|
||||
else {
|
||||
read_undosave(C, curundo->next);
|
||||
curundo = curundo->next;
|
||||
if (G.debug & G_DEBUG) printf("redo %s\n", curundo->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_undo_reset(void)
|
||||
{
|
||||
UndoElem *uel;
|
||||
|
||||
uel = undobase.first;
|
||||
while (uel) {
|
||||
BLO_memfile_free(&uel->memfile);
|
||||
uel = uel->next;
|
||||
}
|
||||
|
||||
BLI_freelistN(&undobase);
|
||||
curundo = NULL;
|
||||
}
|
||||
|
||||
/* based on index nr it does a restore */
|
||||
void BKE_undo_number(bContext *C, int nr)
|
||||
{
|
||||
curundo = BLI_findlink(&undobase, nr);
|
||||
BKE_undo_step(C, 0);
|
||||
}
|
||||
|
||||
/* go back to the last occurance of name in stack */
|
||||
void BKE_undo_name(bContext *C, const char *name)
|
||||
{
|
||||
UndoElem *uel = BLI_rfindstring(&undobase, name, offsetof(UndoElem, name));
|
||||
|
||||
if (uel && uel->prev) {
|
||||
curundo = uel->prev;
|
||||
BKE_undo_step(C, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* name optional */
|
||||
bool BKE_undo_is_valid(const char *name)
|
||||
{
|
||||
if (name) {
|
||||
UndoElem *uel = BLI_rfindstring(&undobase, name, offsetof(UndoElem, name));
|
||||
return uel && uel->prev;
|
||||
}
|
||||
|
||||
return undobase.last != undobase.first;
|
||||
}
|
||||
|
||||
/* get name of undo item, return null if no item with this index */
|
||||
/* if active pointer, set it to 1 if true */
|
||||
const char *BKE_undo_get_name(int nr, bool *r_active)
|
||||
{
|
||||
UndoElem *uel = BLI_findlink(&undobase, nr);
|
||||
|
||||
if (r_active) *r_active = false;
|
||||
|
||||
if (uel) {
|
||||
if (r_active && (uel == curundo)) {
|
||||
*r_active = true;
|
||||
}
|
||||
return uel->name;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* return the name of the last item */
|
||||
const char *BKE_undo_get_name_last(void)
|
||||
{
|
||||
UndoElem *uel = undobase.last;
|
||||
return (uel ? uel->name : NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves .blend using undo buffer.
|
||||
*
|
||||
* \return success.
|
||||
*/
|
||||
bool BKE_undo_save_file(const char *filename)
|
||||
{
|
||||
UndoElem *uel;
|
||||
MemFileChunk *chunk;
|
||||
int file, oflags;
|
||||
|
||||
if ((U.uiflag & USER_GLOBALUNDO) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uel = curundo;
|
||||
if (uel == NULL) {
|
||||
fprintf(stderr, "No undo buffer to save recovery file\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* note: This is currently used for autosave and 'quit.blend', where _not_ following symlinks is OK,
|
||||
* however if this is ever executed explicitly by the user, we may want to allow writing to symlinks.
|
||||
*/
|
||||
|
||||
oflags = O_BINARY | O_WRONLY | O_CREAT | O_TRUNC;
|
||||
#ifdef O_NOFOLLOW
|
||||
/* use O_NOFOLLOW to avoid writing to a symlink - use 'O_EXCL' (CVE-2008-1103) */
|
||||
oflags |= O_NOFOLLOW;
|
||||
#else
|
||||
/* TODO(sergey): How to deal with symlinks on windows? */
|
||||
# ifndef _MSC_VER
|
||||
# warning "Symbolic links will be followed on undo save, possibly causing CVE-2008-1103"
|
||||
# endif
|
||||
#endif
|
||||
file = BLI_open(filename, oflags, 0666);
|
||||
|
||||
if (file == -1) {
|
||||
fprintf(stderr, "Unable to save '%s': %s\n",
|
||||
filename, errno ? strerror(errno) : "Unknown error opening file");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (chunk = uel->memfile.chunks.first; chunk; chunk = chunk->next) {
|
||||
if (write(file, chunk->buf, chunk->size) != chunk->size) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
close(file);
|
||||
|
||||
if (chunk) {
|
||||
fprintf(stderr, "Unable to save '%s': %s\n",
|
||||
filename, errno ? strerror(errno) : "Unknown error writing file");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* sets curscene */
|
||||
Main *BKE_undo_get_main(Scene **r_scene)
|
||||
{
|
||||
Main *mainp = NULL;
|
||||
BlendFileData *bfd = BLO_read_from_memfile(G.main, G.main->name, &curundo->memfile, NULL, BLO_READ_SKIP_NONE);
|
||||
|
||||
if (bfd) {
|
||||
mainp = bfd->main;
|
||||
if (r_scene) {
|
||||
*r_scene = bfd->curscene;
|
||||
}
|
||||
|
||||
MEM_freeN(bfd);
|
||||
}
|
||||
|
||||
return mainp;
|
||||
BLO_memfile_free(&mfu->memfile);
|
||||
MEM_freeN(mfu);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -42,7 +42,6 @@
|
||||
|
||||
#include "BKE_animsys.h"
|
||||
#include "BKE_cachefile.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_library.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_modifier.h"
|
||||
|
@ -48,7 +48,6 @@
|
||||
#include "BKE_animsys.h"
|
||||
#include "BKE_camera.h"
|
||||
#include "BKE_object.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_layer.h"
|
||||
#include "BKE_library.h"
|
||||
#include "BKE_library_query.h"
|
||||
|
@ -54,7 +54,6 @@
|
||||
#include "BKE_customdata.h"
|
||||
#include "BKE_DerivedMesh.h"
|
||||
#include "BKE_fluidsim.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_modifier.h"
|
||||
#include "BKE_mesh.h"
|
||||
|
||||
|
@ -47,7 +47,6 @@
|
||||
#include "BKE_animsys.h"
|
||||
#include "BKE_colortools.h"
|
||||
#include "BKE_icons.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_lamp.h"
|
||||
#include "BKE_library.h"
|
||||
#include "BKE_library_query.h"
|
||||
|
@ -54,7 +54,6 @@
|
||||
#include "BKE_cdderivedmesh.h"
|
||||
#include "BKE_curve.h"
|
||||
#include "BKE_displist.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_key.h"
|
||||
#include "BKE_lattice.h"
|
||||
#include "BKE_library.h"
|
||||
|
@ -840,6 +840,7 @@ void BKE_libblock_management_main_add(Main *bmain, void *idv)
|
||||
new_id(lb, id, NULL);
|
||||
/* alphabetic insertion: is in new_id */
|
||||
id->tag &= ~(LIB_TAG_NO_MAIN | LIB_TAG_NO_USER_REFCOUNT);
|
||||
bmain->is_memfile_undo_written = false;
|
||||
BKE_main_unlock(bmain);
|
||||
}
|
||||
|
||||
@ -859,6 +860,7 @@ void BKE_libblock_management_main_remove(Main *bmain, void *idv)
|
||||
BKE_main_lock(bmain);
|
||||
BLI_remlink(lb, id);
|
||||
id->tag |= LIB_TAG_NO_MAIN;
|
||||
bmain->is_memfile_undo_written = false;
|
||||
BKE_main_unlock(bmain);
|
||||
}
|
||||
|
||||
@ -1229,6 +1231,7 @@ void *BKE_libblock_alloc(Main *bmain, short type, const char *name, const int fl
|
||||
BKE_main_lock(bmain);
|
||||
BLI_addtail(lb, id);
|
||||
new_id(lb, id, name);
|
||||
bmain->is_memfile_undo_written = false;
|
||||
/* alphabetic insertion: is in new_id */
|
||||
BKE_main_unlock(bmain);
|
||||
|
||||
|
@ -47,7 +47,6 @@
|
||||
#include "BKE_colorband.h"
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_freestyle.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_library.h"
|
||||
#include "BKE_linestyle.h"
|
||||
#include "BKE_node.h"
|
||||
|
@ -51,7 +51,7 @@
|
||||
|
||||
#include "BKE_animsys.h"
|
||||
#include "BKE_curve.h"
|
||||
#include "BKE_global.h"
|
||||
|
||||
#include "BKE_library.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_mask.h"
|
||||
|
@ -42,7 +42,6 @@
|
||||
#include "DNA_mask_types.h"
|
||||
|
||||
#include "BKE_curve.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_mask.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
|
@ -76,7 +76,6 @@
|
||||
#include "BKE_effect.h"
|
||||
#include "BKE_library_query.h"
|
||||
#include "BKE_particle.h"
|
||||
#include "BKE_global.h"
|
||||
|
||||
#include "BKE_collection.h"
|
||||
#include "BKE_DerivedMesh.h"
|
||||
|
@ -72,7 +72,6 @@
|
||||
#include "BKE_customdata.h"
|
||||
#include "BKE_deform.h"
|
||||
#include "BKE_DerivedMesh.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_effect.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_modifier.h"
|
||||
|
@ -32,7 +32,6 @@
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_animsys.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_library.h"
|
||||
#include "BKE_library_query.h"
|
||||
#include "BKE_library_remap.h"
|
||||
|
@ -55,7 +55,6 @@
|
||||
|
||||
#include "IMB_imbuf.h"
|
||||
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_main.h"
|
||||
|
||||
#include "BKE_colorband.h"
|
||||
|
795
source/blender/blenkernel/intern/undo_system.c
Normal file
795
source/blender/blenkernel/intern/undo_system.c
Normal file
@ -0,0 +1,795 @@
|
||||
/*
|
||||
* ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/editors/util/undo_system.c
|
||||
* \ingroup edutil
|
||||
*
|
||||
* Used by ED_undo.h, internal implementation.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_sys_types.h"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_sort_utils.h"
|
||||
|
||||
#include "DNA_listBase.h"
|
||||
#include "DNA_windowmanager_types.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_undo_system.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#define undo_stack _wm_undo_stack_disallow /* pass in as a variable always. */
|
||||
|
||||
/** Odd requirement of Blender that we always keep a memfile undo in the stack. */
|
||||
#define WITH_GLOBAL_UNDO_KEEP_ONE
|
||||
|
||||
/** Make sure all ID's created at the point we add an undo step that uses ID's. */
|
||||
#define WITH_GLOBAL_UNDO_ENSURE_UPDATED
|
||||
|
||||
/** We only need this locally. */
|
||||
static CLG_LogRef LOG = {"bke.undosys"};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Internal Nested Undo Checks
|
||||
*
|
||||
* Make sure we're not running undo operations from 'step_encode', 'step_decode' callbacks.
|
||||
* bugs caused by this situation aren't _that_ hard to spot but aren't always so obvious.
|
||||
* Best we have a check which shows the problem immediately.
|
||||
*
|
||||
* \{ */
|
||||
#define WITH_NESTED_UNDO_CHECK
|
||||
|
||||
#ifdef WITH_NESTED_UNDO_CHECK
|
||||
static bool g_undo_callback_running = false;
|
||||
# define UNDO_NESTED_ASSERT(state) BLI_assert(g_undo_callback_running == state)
|
||||
# define UNDO_NESTED_CHECK_BEGIN { \
|
||||
UNDO_NESTED_ASSERT(false); \
|
||||
g_undo_callback_running = true; \
|
||||
} ((void)0)
|
||||
# define UNDO_NESTED_CHECK_END { \
|
||||
UNDO_NESTED_ASSERT(true); \
|
||||
g_undo_callback_running = false; \
|
||||
} ((void)0)
|
||||
#else
|
||||
# define UNDO_NESTED_ASSERT(state) ((void)0)
|
||||
# define UNDO_NESTED_CHECK_BEGIN ((void)0)
|
||||
# define UNDO_NESTED_CHECK_END ((void)0)
|
||||
#endif
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Public Undo Types
|
||||
*
|
||||
* Unfortunately we need this for a handful of places.
|
||||
*/
|
||||
const UndoType *BKE_UNDOSYS_TYPE_MEMFILE = NULL;
|
||||
const UndoType *BKE_UNDOSYS_TYPE_IMAGE = NULL;
|
||||
const UndoType *BKE_UNDOSYS_TYPE_SCULPT = NULL;
|
||||
const UndoType *BKE_UNDOSYS_TYPE_PARTICLE = NULL;
|
||||
const UndoType *BKE_UNDOSYS_TYPE_PAINTCURVE = NULL;
|
||||
/** \} */
|
||||
|
||||
/* UndoType */
|
||||
|
||||
static ListBase g_undo_types = {NULL, NULL};
|
||||
|
||||
static const UndoType *BKE_undosys_type_from_context(bContext *C)
|
||||
{
|
||||
for (const UndoType *ut = g_undo_types.first; ut; ut = ut->next) {
|
||||
/* No poll means we don't check context. */
|
||||
if (ut->poll && ut->poll(C)) {
|
||||
return ut;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Internal Callback Wrappers
|
||||
*
|
||||
* #UndoRefID is simply a way to avoid inlining name copy and lookups,
|
||||
* since it's easy to forget a single case when done inline (crashing in some cases).
|
||||
*
|
||||
* \{ */
|
||||
|
||||
static void undosys_id_ref_store(void *UNUSED(user_data), UndoRefID *id_ref)
|
||||
{
|
||||
BLI_assert(id_ref->name[0] == '\0');
|
||||
if (id_ref->ptr) {
|
||||
BLI_strncpy(id_ref->name, id_ref->ptr->name, sizeof(id_ref->name));
|
||||
/* Not needed, just prevents stale data access. */
|
||||
id_ref->ptr = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void undosys_id_ref_resolve(void *user_data, UndoRefID *id_ref)
|
||||
{
|
||||
/* Note: we could optimize this, for now it's not too bad since it only runs when we access undo! */
|
||||
Main *bmain = user_data;
|
||||
ListBase *lb = which_libbase(bmain, GS(id_ref->name));
|
||||
for (ID *id = lb->first; id; id = id->next) {
|
||||
if (STREQ(id_ref->name, id->name) && (id->lib == NULL)) {
|
||||
id_ref->ptr = id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool undosys_step_encode(bContext *C, UndoStep *us)
|
||||
{
|
||||
CLOG_INFO(&LOG, 2, "%p '%s', type='%s'", us, us->name, us->type->name);
|
||||
UNDO_NESTED_CHECK_BEGIN;
|
||||
bool ok = us->type->step_encode(C, us);
|
||||
UNDO_NESTED_CHECK_END;
|
||||
if (ok) {
|
||||
if (us->type->step_foreach_ID_ref != NULL) {
|
||||
/* Don't use from context yet because sometimes context is fake and not all members are filled in. */
|
||||
Main *bmain = G.main;
|
||||
us->type->step_foreach_ID_ref(us, undosys_id_ref_store, bmain);
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
static void undosys_step_decode(bContext *C, UndoStep *us, int dir)
|
||||
{
|
||||
CLOG_INFO(&LOG, 2, "%p '%s', type='%s'", us, us->name, us->type->name);
|
||||
if (us->type->step_foreach_ID_ref) {
|
||||
/* Don't use from context yet because sometimes context is fake and not all members are filled in. */
|
||||
Main *bmain = G.main;
|
||||
us->type->step_foreach_ID_ref(us, undosys_id_ref_resolve, bmain);
|
||||
}
|
||||
|
||||
UNDO_NESTED_CHECK_BEGIN;
|
||||
us->type->step_decode(C, us, dir);
|
||||
UNDO_NESTED_CHECK_END;
|
||||
}
|
||||
|
||||
static void undosys_step_free_and_unlink(UndoStack *ustack, UndoStep *us)
|
||||
{
|
||||
CLOG_INFO(&LOG, 2, "%p '%s', type='%s'", us, us->name, us->type->name);
|
||||
UNDO_NESTED_CHECK_BEGIN;
|
||||
us->type->step_free(us);
|
||||
UNDO_NESTED_CHECK_END;
|
||||
|
||||
BLI_remlink(&ustack->steps, us);
|
||||
MEM_freeN(us);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Undo Stack
|
||||
* \{ */
|
||||
|
||||
#ifndef NDEBUG
|
||||
static void undosys_stack_validate(UndoStack *ustack, bool expect_non_empty)
|
||||
{
|
||||
if (ustack->step_active != NULL) {
|
||||
BLI_assert(!BLI_listbase_is_empty(&ustack->steps));
|
||||
BLI_assert(BLI_findindex(&ustack->steps, ustack->step_active) != -1);
|
||||
}
|
||||
if (expect_non_empty) {
|
||||
BLI_assert(!BLI_listbase_is_empty(&ustack->steps));
|
||||
}
|
||||
}
|
||||
#else
|
||||
static void undosys_stack_validate(UndoStack *ustack, bool expect_non_empty)
|
||||
{
|
||||
UNUSED_VARS(ustack, expect_non_empty);
|
||||
}
|
||||
#endif
|
||||
|
||||
UndoStack *BKE_undosys_stack_create(void)
|
||||
{
|
||||
UndoStack *ustack = MEM_callocN(sizeof(UndoStack), __func__);
|
||||
return ustack;
|
||||
}
|
||||
|
||||
void BKE_undosys_stack_destroy(UndoStack *ustack)
|
||||
{
|
||||
BKE_undosys_stack_clear(ustack);
|
||||
MEM_freeN(ustack);
|
||||
}
|
||||
|
||||
void BKE_undosys_stack_clear(UndoStack *ustack)
|
||||
{
|
||||
UNDO_NESTED_ASSERT(false);
|
||||
CLOG_INFO(&LOG, 1, "steps=%d", BLI_listbase_count(&ustack->steps));
|
||||
for (UndoStep *us = ustack->steps.last, *us_prev; us; us = us_prev) {
|
||||
us_prev = us->prev;
|
||||
undosys_step_free_and_unlink(ustack, us);
|
||||
}
|
||||
BLI_listbase_clear(&ustack->steps);
|
||||
ustack->step_active = NULL;
|
||||
}
|
||||
|
||||
static bool undosys_stack_push_main(UndoStack *ustack, const char *name, struct Main *bmain)
|
||||
{
|
||||
UNDO_NESTED_ASSERT(false);
|
||||
CLOG_INFO(&LOG, 1, "'%s'", name);
|
||||
bContext *C_temp = CTX_create();
|
||||
CTX_data_main_set(C_temp, bmain);
|
||||
bool ok = BKE_undosys_step_push_with_type(ustack, C_temp, name, BKE_UNDOSYS_TYPE_MEMFILE);
|
||||
CTX_free(C_temp);
|
||||
return ok;
|
||||
}
|
||||
|
||||
void BKE_undosys_stack_init_from_main(UndoStack *ustack, struct Main *bmain)
|
||||
{
|
||||
UNDO_NESTED_ASSERT(false);
|
||||
undosys_stack_push_main(ustack, "original", bmain);
|
||||
}
|
||||
|
||||
/* name optional */
|
||||
bool BKE_undosys_stack_has_undo(UndoStack *ustack, const char *name)
|
||||
{
|
||||
if (name) {
|
||||
UndoStep *us = BLI_rfindstring(&ustack->steps, name, offsetof(UndoStep, name));
|
||||
return us && us->prev;
|
||||
}
|
||||
|
||||
return !BLI_listbase_is_empty(&ustack->steps);
|
||||
}
|
||||
|
||||
UndoStep *BKE_undosys_stack_active_with_type(UndoStack *ustack, const UndoType *ut)
|
||||
{
|
||||
UndoStep *us = ustack->step_active;
|
||||
while (us && (us->type != ut)) {
|
||||
us = us->prev;
|
||||
}
|
||||
return us;
|
||||
}
|
||||
|
||||
UndoStep *BKE_undosys_stack_init_or_active_with_type(UndoStack *ustack, const UndoType *ut)
|
||||
{
|
||||
UNDO_NESTED_ASSERT(false);
|
||||
CLOG_INFO(&LOG, 1, "type='%s'", ut->name);
|
||||
if (ustack->step_init && (ustack->step_init->type == ut)) {
|
||||
return ustack->step_init;
|
||||
}
|
||||
return BKE_undosys_stack_active_with_type(ustack, ut);
|
||||
}
|
||||
|
||||
/**
|
||||
* \param steps: Limit the number of undo steps.
|
||||
* \param memory_limit: Limit the amount of memory used by the undo stack.
|
||||
*/
|
||||
void BKE_undosys_stack_limit_steps_and_memory(UndoStack *ustack, int steps, size_t memory_limit)
|
||||
{
|
||||
UNDO_NESTED_ASSERT(false);
|
||||
if (!(steps || memory_limit)) {
|
||||
return;
|
||||
}
|
||||
|
||||
CLOG_INFO(&LOG, 1, "steps=%d, memory_limit=%zu", steps, memory_limit);
|
||||
UndoStep *us;
|
||||
#ifdef WITH_GLOBAL_UNDO_KEEP_ONE
|
||||
UndoStep *us_exclude = NULL;
|
||||
#endif
|
||||
/* keep at least two (original + other) */
|
||||
size_t data_size_all = 0;
|
||||
size_t us_count = 0;
|
||||
for (us = ustack->steps.last; us && us->prev; us = us->prev) {
|
||||
if (memory_limit) {
|
||||
data_size_all += us->data_size;
|
||||
if (data_size_all > memory_limit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (steps) {
|
||||
if (us_count == steps) {
|
||||
break;
|
||||
}
|
||||
if (us->skip == false) {
|
||||
us_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (us) {
|
||||
if (us->prev && us->prev->prev) {
|
||||
us = us->prev;
|
||||
}
|
||||
|
||||
#ifdef WITH_GLOBAL_UNDO_KEEP_ONE
|
||||
/* Hack, we need to keep at least one BKE_UNDOSYS_TYPE_MEMFILE. */
|
||||
if (us->type != BKE_UNDOSYS_TYPE_MEMFILE) {
|
||||
us_exclude = us->prev;
|
||||
while (us_exclude && us->type != BKE_UNDOSYS_TYPE_MEMFILE) {
|
||||
us_exclude = us_exclude->prev;
|
||||
}
|
||||
if (us_exclude) {
|
||||
BLI_remlink(&ustack->steps, us_exclude);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/* Free from first to last, free functions may update de-duplication info (see #MemFileUndoStep). */
|
||||
while (ustack->steps.first != us) {
|
||||
UndoStep *us_first = ustack->steps.first;
|
||||
BLI_assert(us_first != ustack->step_active);
|
||||
undosys_step_free_and_unlink(ustack, us_first);
|
||||
}
|
||||
|
||||
#ifdef WITH_GLOBAL_UNDO_KEEP_ONE
|
||||
if (us_exclude) {
|
||||
BLI_addhead(&ustack->steps, us_exclude);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
void BKE_undosys_step_push_init_with_type(UndoStack *ustack, bContext *C, const char *name, const UndoType *ut)
|
||||
{
|
||||
UNDO_NESTED_ASSERT(false);
|
||||
/* We could detect and clean this up (but it should never happen!). */
|
||||
BLI_assert(ustack->step_init == NULL);
|
||||
if (ut->step_encode_init) {
|
||||
undosys_stack_validate(ustack, false);
|
||||
UndoStep *us = MEM_callocN(ut->step_size, __func__);
|
||||
CLOG_INFO(&LOG, 1, "%p, '%s', type='%s'", us, name, us->type->name);
|
||||
if (name != NULL) {
|
||||
BLI_strncpy(us->name, name, sizeof(us->name));
|
||||
}
|
||||
us->type = ut;
|
||||
ustack->step_init = us;
|
||||
ut->step_encode_init(C, us);
|
||||
undosys_stack_validate(ustack, true);
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_undosys_step_push_init(UndoStack *ustack, bContext *C, const char *name)
|
||||
{
|
||||
UNDO_NESTED_ASSERT(false);
|
||||
/* We could detect and clean this up (but it should never happen!). */
|
||||
BLI_assert(ustack->step_init == NULL);
|
||||
const UndoType *ut = BKE_undosys_type_from_context(C);
|
||||
if (ut == NULL) {
|
||||
return;
|
||||
}
|
||||
return BKE_undosys_step_push_init_with_type(ustack, C, name, ut);
|
||||
}
|
||||
|
||||
bool BKE_undosys_step_push_with_type(UndoStack *ustack, bContext *C, const char *name, const UndoType *ut)
|
||||
{
|
||||
UNDO_NESTED_ASSERT(false);
|
||||
undosys_stack_validate(ustack, false);
|
||||
bool is_not_empty = ustack->step_active != NULL;
|
||||
|
||||
/* Remove all undos after (also when 'ustack->step_active == NULL'). */
|
||||
while (ustack->steps.last != ustack->step_active) {
|
||||
UndoStep *us_iter = ustack->steps.last;
|
||||
undosys_step_free_and_unlink(ustack, us_iter);
|
||||
undosys_stack_validate(ustack, is_not_empty);
|
||||
}
|
||||
|
||||
if (ustack->step_active) {
|
||||
BLI_assert(BLI_findindex(&ustack->steps, ustack->step_active) != -1);
|
||||
}
|
||||
|
||||
#ifdef WITH_GLOBAL_UNDO_ENSURE_UPDATED
|
||||
if (ut->step_foreach_ID_ref != NULL) {
|
||||
Main *bmain = G.main;
|
||||
if (bmain->is_memfile_undo_written == false) {
|
||||
const char *name_internal = "MemFile Internal";
|
||||
if (undosys_stack_push_main(ustack, name_internal, bmain)) {
|
||||
UndoStep *us = ustack->steps.last;
|
||||
BLI_assert(STREQ(us->name, name_internal));
|
||||
us->skip = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
UndoStep *us = ustack->step_init ? ustack->step_init : MEM_callocN(ut->step_size, __func__);
|
||||
ustack->step_init = NULL;
|
||||
if (us->name[0] == '\0') {
|
||||
BLI_strncpy(us->name, name, sizeof(us->name));
|
||||
}
|
||||
us->type = ut;
|
||||
/* initialized, not added yet. */
|
||||
|
||||
if (undosys_step_encode(C, us)) {
|
||||
ustack->step_active = us;
|
||||
BLI_addtail(&ustack->steps, us);
|
||||
undosys_stack_validate(ustack, true);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
MEM_freeN(us);
|
||||
undosys_stack_validate(ustack, true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool BKE_undosys_step_push(UndoStack *ustack, bContext *C, const char *name)
|
||||
{
|
||||
UNDO_NESTED_ASSERT(false);
|
||||
const UndoType *ut = ustack->step_init ? ustack->step_init->type : BKE_undosys_type_from_context(C);
|
||||
if (ut == NULL) {
|
||||
return false;
|
||||
}
|
||||
return BKE_undosys_step_push_with_type(ustack, C, name, ut);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Useful when we want to diff against previous undo data but can't be sure the types match.
|
||||
*/
|
||||
UndoStep *BKE_undosys_step_same_type_next(UndoStep *us)
|
||||
{
|
||||
if (us) {
|
||||
const UndoType *ut = us->type;
|
||||
while ((us = us->next)) {
|
||||
if (us->type == ut) {
|
||||
return us;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return us;
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful when we want to diff against previous undo data but can't be sure the types match.
|
||||
*/
|
||||
UndoStep *BKE_undosys_step_same_type_prev(UndoStep *us)
|
||||
{
|
||||
if (us) {
|
||||
const UndoType *ut = us->type;
|
||||
while ((us = us->prev)) {
|
||||
if (us->type == ut) {
|
||||
return us;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return us;
|
||||
}
|
||||
|
||||
UndoStep *BKE_undosys_step_find_by_name_with_type(UndoStack *ustack, const char *name, const UndoType *ut)
|
||||
{
|
||||
for (UndoStep *us = ustack->steps.last; us; us = us->prev) {
|
||||
if (us->type == ut) {
|
||||
if (STREQ(name, us->name)) {
|
||||
return us;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
UndoStep *BKE_undosys_step_find_by_name(UndoStack *ustack, const char *name)
|
||||
{
|
||||
return BLI_rfindstring(&ustack->steps, name, offsetof(UndoStep, name));
|
||||
}
|
||||
|
||||
bool BKE_undosys_step_undo_with_data_ex(
|
||||
UndoStack *ustack, bContext *C, UndoStep *us,
|
||||
bool use_skip)
|
||||
{
|
||||
UNDO_NESTED_ASSERT(false);
|
||||
if (us) {
|
||||
undosys_stack_validate(ustack, true);
|
||||
}
|
||||
UndoStep *us_prev = us ? us->prev : NULL;
|
||||
if (us && us->type->mode == BKE_UNDOTYPE_MODE_STORE) {
|
||||
/* The current state is a copy, we need to load the previous state. */
|
||||
us = us_prev;
|
||||
}
|
||||
|
||||
if (us != NULL) {
|
||||
CLOG_INFO(&LOG, 1, "%p, '%s', type='%s'", us, us->name, us->type->name);
|
||||
undosys_step_decode(C, us, -1);
|
||||
ustack->step_active = us_prev;
|
||||
undosys_stack_validate(ustack, true);
|
||||
if (use_skip) {
|
||||
if (ustack->step_active && ustack->step_active->skip) {
|
||||
CLOG_INFO(&LOG, 2, "undo continue with skip %p '%s', type='%s'", us, us->name, us->type->name);
|
||||
BKE_undosys_step_undo_with_data(ustack, C, ustack->step_active);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool BKE_undosys_step_undo_with_data(UndoStack *ustack, bContext *C, UndoStep *us)
|
||||
{
|
||||
return BKE_undosys_step_undo_with_data_ex(ustack, C, us, true);
|
||||
}
|
||||
|
||||
bool BKE_undosys_step_undo(UndoStack *ustack, bContext *C)
|
||||
{
|
||||
return BKE_undosys_step_undo_with_data(ustack, C, ustack->step_active);
|
||||
}
|
||||
|
||||
void BKE_undosys_step_undo_from_index(UndoStack *ustack, bContext *C, int index)
|
||||
{
|
||||
UndoStep *us = BLI_findlink(&ustack->steps, index);
|
||||
BLI_assert(us->skip == false);
|
||||
BKE_undosys_step_load_data(ustack, C, us);
|
||||
}
|
||||
|
||||
bool BKE_undosys_step_redo_with_data_ex(
|
||||
UndoStack *ustack, bContext *C, UndoStep *us,
|
||||
bool use_skip)
|
||||
{
|
||||
UNDO_NESTED_ASSERT(false);
|
||||
UndoStep *us_next = us ? us->next : NULL;
|
||||
/* Unlike undo accumulate, we always use the next. */
|
||||
us = us_next;
|
||||
|
||||
if (us != NULL) {
|
||||
CLOG_INFO(&LOG, 1, "%p, '%s', type='%s'", us, us->name, us->type->name);
|
||||
undosys_step_decode(C, us, 1);
|
||||
ustack->step_active = us_next;
|
||||
if (use_skip) {
|
||||
if (ustack->step_active && ustack->step_active->skip) {
|
||||
CLOG_INFO(&LOG, 2, "redo continue with skip %p '%s', type='%s'", us, us->name, us->type->name);
|
||||
BKE_undosys_step_redo_with_data(ustack, C, ustack->step_active);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool BKE_undosys_step_redo_with_data(UndoStack *ustack, bContext *C, UndoStep *us)
|
||||
{
|
||||
return BKE_undosys_step_redo_with_data_ex(ustack, C, us, true);
|
||||
}
|
||||
|
||||
bool BKE_undosys_step_redo(UndoStack *ustack, bContext *C)
|
||||
{
|
||||
return BKE_undosys_step_redo_with_data(ustack, C, ustack->step_active);
|
||||
}
|
||||
|
||||
bool BKE_undosys_step_load_data(UndoStack *ustack, bContext *C, UndoStep *us)
|
||||
{
|
||||
UNDO_NESTED_ASSERT(false);
|
||||
const int index_active = BLI_findindex(&ustack->steps, ustack->step_active);
|
||||
const int index_target = BLI_findindex(&ustack->steps, us);
|
||||
BLI_assert(!ELEM(-1, index_active, index_target));
|
||||
bool ok = true;
|
||||
|
||||
if (index_target < index_active) {
|
||||
uint i = index_active - index_target;
|
||||
while (i-- && ok) {
|
||||
ok = BKE_undosys_step_undo_with_data_ex(ustack, C, ustack->step_active, false);
|
||||
}
|
||||
}
|
||||
else if (index_target > index_active) {
|
||||
uint i = index_target - index_active;
|
||||
while (i-- && ok) {
|
||||
ok = BKE_undosys_step_redo_with_data_ex(ustack, C, ustack->step_active, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
BLI_assert(ustack->step_active == us);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool BKE_undosys_step_undo_compat_only(UndoStack *ustack, bContext *C, int step)
|
||||
{
|
||||
if (step == 0) {
|
||||
return BKE_undosys_step_undo_with_data(ustack, C, ustack->step_active);
|
||||
}
|
||||
else if (step == 1) {
|
||||
return BKE_undosys_step_undo(ustack, C);
|
||||
}
|
||||
else {
|
||||
return BKE_undosys_step_redo(ustack, C);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Similar to #WM_operatortype_append
|
||||
*/
|
||||
UndoType *BKE_undosys_type_append(void (*undosys_fn)(UndoType *))
|
||||
{
|
||||
UndoType *ut;
|
||||
|
||||
ut = MEM_callocN(sizeof(UndoType), __func__);
|
||||
|
||||
undosys_fn(ut);
|
||||
|
||||
BLI_assert(ut->mode != 0);
|
||||
|
||||
BLI_addtail(&g_undo_types, ut);
|
||||
|
||||
return ut;
|
||||
}
|
||||
|
||||
void BKE_undosys_type_free_all(void)
|
||||
{
|
||||
UndoType *ut;
|
||||
while ((ut = BLI_pophead(&g_undo_types))) {
|
||||
MEM_freeN(ut);
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name ID Reference Utilities
|
||||
*
|
||||
* Unfortunately we need this for a handful of places.
|
||||
*/
|
||||
|
||||
static void UNUSED_FUNCTION(BKE_undosys_foreach_ID_ref(
|
||||
UndoStack *ustack, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data))
|
||||
{
|
||||
for (UndoStep *us = ustack->steps.first; us; us = us->next) {
|
||||
const UndoType *ut = us->type;
|
||||
if (ut->step_foreach_ID_ref != NULL) {
|
||||
ut->step_foreach_ID_ref(us, foreach_ID_ref_fn, user_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct UndoIDPtrMapItem {
|
||||
/** Never changes (matches undo data). Use as sort key for binary search. */
|
||||
const void *ptr;
|
||||
/** Write the new pointers here. */
|
||||
uint index;
|
||||
} UndoIDPtrMapItem;
|
||||
|
||||
typedef struct UndoIDPtrMap {
|
||||
UndoRefID *refs;
|
||||
/**
|
||||
* Pointer map, update 'dst' members before use.
|
||||
* This is always sorted (adds some overhead when adding, in practice it's acceptable since).
|
||||
*/
|
||||
UndoIDPtrMapItem *pmap;
|
||||
|
||||
/** Length for both 'refs' & 'pmap' */
|
||||
uint len;
|
||||
uint len_alloc;
|
||||
} UndoIDPtrMap;
|
||||
|
||||
#ifdef DEBUG
|
||||
# define PMAP_DEFAULT_ALLOC 1
|
||||
#else
|
||||
# define PMAP_DEFAULT_ALLOC 32
|
||||
#endif
|
||||
|
||||
void BKE_undosys_ID_map_foreach_ID_ref(
|
||||
UndoIDPtrMap *map,
|
||||
UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
|
||||
{
|
||||
for (uint i = 0; i < map->len; i++) {
|
||||
foreach_ID_ref_fn(user_data, &map->refs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true when found, otherwise index is set to the index we should insert.
|
||||
*/
|
||||
static bool undosys_ID_map_lookup_index(const UndoIDPtrMap *map, const void *key, uint *r_index)
|
||||
{
|
||||
const UndoIDPtrMapItem *pmap = map->pmap;
|
||||
const uint len = map->len;
|
||||
if (len == 0) {
|
||||
if (*r_index) {
|
||||
*r_index = 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
int min = 0, max = len - 1;
|
||||
while (min <= max) {
|
||||
const uint mid = (min + max) / 2;
|
||||
if (pmap[mid].ptr < key) {
|
||||
min = mid + 1;
|
||||
}
|
||||
else if (pmap[mid].ptr == key) {
|
||||
if (r_index) {
|
||||
*r_index = mid;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (pmap[mid].ptr > key) {
|
||||
max = mid - 1;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* A set of ID's use for efficient decoding, so we can map pointers back to the newly loaded data
|
||||
* without performing full look ups each time.
|
||||
*
|
||||
* This can be used as an old_pointer -> new_pointer lookup.
|
||||
*/
|
||||
UndoIDPtrMap *BKE_undosys_ID_map_create(void)
|
||||
{
|
||||
UndoIDPtrMap *map = MEM_mallocN(sizeof(*map), __func__);
|
||||
map->len_alloc = PMAP_DEFAULT_ALLOC;
|
||||
map->refs = MEM_mallocN(sizeof(*map->refs) * map->len_alloc, __func__);
|
||||
map->pmap = MEM_mallocN(sizeof(*map->pmap) * map->len_alloc, __func__);
|
||||
map->len = 0;
|
||||
return map;
|
||||
}
|
||||
void BKE_undosys_ID_map_destroy(UndoIDPtrMap *idpmap)
|
||||
{
|
||||
MEM_SAFE_FREE(idpmap->refs);
|
||||
MEM_SAFE_FREE(idpmap->pmap);
|
||||
MEM_freeN(idpmap);
|
||||
}
|
||||
|
||||
void BKE_undosys_ID_map_add(UndoIDPtrMap *map, ID *id)
|
||||
{
|
||||
uint index;
|
||||
if (id->lib != NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (undosys_ID_map_lookup_index(map, id, &index)) {
|
||||
return; /* exists. */
|
||||
}
|
||||
|
||||
const uint len_src = map->len;
|
||||
const uint len_dst = map->len + 1;
|
||||
if (len_dst > map->len_alloc) {
|
||||
map->len_alloc *= 2;
|
||||
BLI_assert(map->len_alloc >= len_dst);
|
||||
map->pmap = MEM_reallocN(map->pmap, sizeof(*map->pmap) * map->len_alloc);
|
||||
map->refs = MEM_reallocN(map->refs, sizeof(*map->refs) * map->len_alloc);
|
||||
}
|
||||
|
||||
#if 0 /* Will be done automatically in callback. */
|
||||
BLI_strncpy(map->refs[len_src].name, id->name, sizeof(id->name));
|
||||
#else
|
||||
map->refs[len_src].name[0] = '\0';
|
||||
#endif
|
||||
map->refs[len_src].ptr = id;
|
||||
|
||||
map->pmap[len_src].ptr = id;
|
||||
map->pmap[len_src].index = len_src;
|
||||
map->len = len_dst;
|
||||
|
||||
qsort(map->pmap, map->len, sizeof(*map->pmap), BLI_sortutil_cmp_ptr);
|
||||
}
|
||||
|
||||
ID *BKE_undosys_ID_map_lookup(const UndoIDPtrMap *map, const ID *id_src)
|
||||
{
|
||||
/* We should only ever lookup indices which exist! */
|
||||
uint index;
|
||||
if (!undosys_ID_map_lookup_index(map, id_src, &index)) {
|
||||
BLI_assert(0);
|
||||
}
|
||||
ID *id_dst = map->refs[index].ptr;
|
||||
BLI_assert(id_dst != NULL);
|
||||
BLI_assert(STREQ(id_dst->name, map->refs[index].name));
|
||||
return id_dst;
|
||||
}
|
||||
|
||||
/** \} */
|
@ -33,6 +33,7 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "DNA_world_types.h"
|
||||
|
@ -32,7 +32,7 @@
|
||||
* \note keep \a sort_value first,
|
||||
* so cmp functions can be reused.
|
||||
*/
|
||||
struct SortPointerByFloat {
|
||||
struct SortPtrByFloat {
|
||||
float sort_value;
|
||||
void *data;
|
||||
};
|
||||
@ -42,7 +42,7 @@ struct SortIntByFloat {
|
||||
int data;
|
||||
};
|
||||
|
||||
struct SortPointerByInt {
|
||||
struct SortPtrByInt {
|
||||
int sort_value;
|
||||
void *data;
|
||||
};
|
||||
@ -58,4 +58,7 @@ int BLI_sortutil_cmp_float_reverse(const void *a_, const void *b_);
|
||||
int BLI_sortutil_cmp_int(const void *a_, const void *b_);
|
||||
int BLI_sortutil_cmp_int_reverse(const void *a_, const void *b_);
|
||||
|
||||
int BLI_sortutil_cmp_ptr(const void *a_, const void *b_);
|
||||
int BLI_sortutil_cmp_ptr_reverse(const void *a_, const void *b_);
|
||||
|
||||
#endif /* __BLI_SORT_UTILS_H__ */
|
||||
|
@ -37,6 +37,10 @@ struct SortAnyByInt {
|
||||
int sort_value;
|
||||
};
|
||||
|
||||
struct SortAnyByPtr {
|
||||
const void *sort_value;
|
||||
};
|
||||
|
||||
int BLI_sortutil_cmp_float(const void *a_, const void *b_)
|
||||
{
|
||||
const struct SortAnyByFloat *a = a_;
|
||||
@ -72,3 +76,21 @@ int BLI_sortutil_cmp_int_reverse(const void *a_, const void *b_)
|
||||
else if (a->sort_value > b->sort_value) return -1;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
int BLI_sortutil_cmp_ptr(const void *a_, const void *b_)
|
||||
{
|
||||
const struct SortAnyByPtr *a = a_;
|
||||
const struct SortAnyByPtr *b = b_;
|
||||
if (a->sort_value > b->sort_value) return 1;
|
||||
else if (a->sort_value < b->sort_value) return -1;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
int BLI_sortutil_cmp_ptr_reverse(const void *a_, const void *b_)
|
||||
{
|
||||
const struct SortAnyByPtr *a = a_;
|
||||
const struct SortAnyByPtr *b = b_;
|
||||
if (a->sort_value < b->sort_value) return 1;
|
||||
else if (a->sort_value > b->sort_value) return -1;
|
||||
else return 0;
|
||||
}
|
||||
|
@ -160,6 +160,8 @@ void BLO_update_defaults_startup_blend(struct Main *mainvar);
|
||||
|
||||
struct BlendThumbnail *BLO_thumbnail_from_file(const char *filepath);
|
||||
|
||||
struct Main *BLO_main_from_memfile(struct MemFile *memfile, struct Main *bmain, struct Scene **r_scene);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -33,6 +33,8 @@
|
||||
* \ingroup blenloader
|
||||
*/
|
||||
|
||||
struct Scene;
|
||||
|
||||
typedef struct {
|
||||
void *next, *prev;
|
||||
const char *buf;
|
||||
@ -47,6 +49,12 @@ typedef struct MemFile {
|
||||
size_t size;
|
||||
} MemFile;
|
||||
|
||||
typedef struct MemFileUndoData {
|
||||
char filename[1024]; /* FILE_MAX */
|
||||
MemFile memfile;
|
||||
size_t undo_size;
|
||||
} MemFileUndoData;
|
||||
|
||||
/* actually only used writefile.c */
|
||||
extern void memfile_chunk_add(MemFile *compare, MemFile *current, const char *buf, unsigned int size);
|
||||
|
||||
@ -54,5 +62,9 @@ extern void memfile_chunk_add(MemFile *compare, MemFile *current, const char *bu
|
||||
extern void BLO_memfile_free(MemFile *memfile);
|
||||
extern void BLO_memfile_merge(MemFile *first, MemFile *second);
|
||||
|
||||
#endif
|
||||
/* utilities */
|
||||
extern struct Main *BLO_memfile_main_get(struct MemFile *memfile, struct Main *bmain, struct Scene **r_scene);
|
||||
extern bool BLO_memfile_write_file(struct MemFile *memfile, const char *filename);
|
||||
|
||||
#endif /* __BLO_UNDOFILE_H__ */
|
||||
|
||||
|
@ -6274,7 +6274,6 @@ static void direct_link_scene(FileData *fd, Scene *sce, Main *bmain)
|
||||
sce->toolsettings->imapaint.paintcursor = NULL;
|
||||
sce->toolsettings->particle.paintcursor = NULL;
|
||||
sce->toolsettings->particle.scene = NULL;
|
||||
sce->toolsettings->particle.view_layer = NULL;
|
||||
sce->toolsettings->particle.object = NULL;
|
||||
sce->toolsettings->gp_sculpt.paintcursor = NULL;
|
||||
|
||||
@ -6561,6 +6560,7 @@ static void direct_link_windowmanager(FileData *fd, wmWindowManager *wm)
|
||||
wm->defaultconf = NULL;
|
||||
wm->addonconf = NULL;
|
||||
wm->userconf = NULL;
|
||||
wm->undo_stack = NULL;
|
||||
|
||||
wm->message_bus = NULL;
|
||||
|
||||
|
@ -34,6 +34,15 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* open/close */
|
||||
#ifndef _WIN32
|
||||
# include <unistd.h>
|
||||
#else
|
||||
# include <io.h>
|
||||
#endif
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
@ -42,6 +51,9 @@
|
||||
#include "BLI_blenlib.h"
|
||||
|
||||
#include "BLO_undofile.h"
|
||||
#include "BLO_readfile.h"
|
||||
|
||||
#include "BKE_main.h"
|
||||
|
||||
/* keep last */
|
||||
#include "BLI_strict_flags.h"
|
||||
@ -124,3 +136,69 @@ void memfile_chunk_add(MemFile *compare, MemFile *current, const char *buf, unsi
|
||||
current->size += size;
|
||||
}
|
||||
}
|
||||
|
||||
struct Main *BLO_memfile_main_get(struct MemFile *memfile, struct Main *oldmain, struct Scene **r_scene)
|
||||
{
|
||||
struct Main *bmain_undo = NULL;
|
||||
BlendFileData *bfd = BLO_read_from_memfile(oldmain, oldmain->name, memfile, NULL, BLO_READ_SKIP_NONE);
|
||||
|
||||
if (bfd) {
|
||||
bmain_undo = bfd->main;
|
||||
if (r_scene) {
|
||||
*r_scene = bfd->curscene;
|
||||
}
|
||||
|
||||
MEM_freeN(bfd);
|
||||
}
|
||||
|
||||
return bmain_undo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Saves .blend using undo buffer.
|
||||
*
|
||||
* \return success.
|
||||
*/
|
||||
bool BLO_memfile_write_file(struct MemFile *memfile, const char *filename)
|
||||
{
|
||||
MemFileChunk *chunk;
|
||||
int file, oflags;
|
||||
|
||||
/* note: This is currently used for autosave and 'quit.blend', where _not_ following symlinks is OK,
|
||||
* however if this is ever executed explicitly by the user, we may want to allow writing to symlinks.
|
||||
*/
|
||||
|
||||
oflags = O_BINARY | O_WRONLY | O_CREAT | O_TRUNC;
|
||||
#ifdef O_NOFOLLOW
|
||||
/* use O_NOFOLLOW to avoid writing to a symlink - use 'O_EXCL' (CVE-2008-1103) */
|
||||
oflags |= O_NOFOLLOW;
|
||||
#else
|
||||
/* TODO(sergey): How to deal with symlinks on windows? */
|
||||
# ifndef _MSC_VER
|
||||
# warning "Symbolic links will be followed on undo save, possibly causing CVE-2008-1103"
|
||||
# endif
|
||||
#endif
|
||||
file = BLI_open(filename, oflags, 0666);
|
||||
|
||||
if (file == -1) {
|
||||
fprintf(stderr, "Unable to save '%s': %s\n",
|
||||
filename, errno ? strerror(errno) : "Unknown error opening file");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (chunk = memfile->chunks.first; chunk; chunk = chunk->next) {
|
||||
if ((size_t)write(file, chunk->buf, chunk->size) != chunk->size) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
close(file);
|
||||
|
||||
if (chunk) {
|
||||
fprintf(stderr, "Unable to save '%s': %s\n",
|
||||
filename, errno ? strerror(errno) : "Unknown error writing file");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -270,7 +270,7 @@ void bmo_join_triangles_exec(BMesh *bm, BMOperator *op)
|
||||
BMFace *f;
|
||||
BMEdge *e;
|
||||
/* data: edge-to-join, sort_value: error weight */
|
||||
struct SortPointerByFloat *jedges;
|
||||
struct SortPtrByFloat *jedges;
|
||||
unsigned i, totedge;
|
||||
uint totedge_tag = 0;
|
||||
|
||||
|
@ -33,21 +33,32 @@
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_array_utils.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_undo_system.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
|
||||
#include "ED_armature.h"
|
||||
#include "ED_object.h"
|
||||
#include "ED_util.h"
|
||||
|
||||
#include "WM_types.h"
|
||||
#include "WM_api.h"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Undo Conversion
|
||||
* \{ */
|
||||
|
||||
typedef struct UndoArmature {
|
||||
EditBone *act_edbone;
|
||||
ListBase lb;
|
||||
size_t undo_size;
|
||||
} UndoArmature;
|
||||
|
||||
static void undoBones_to_editBones(void *uarmv, void *armv, void *UNUSED(data))
|
||||
static void undoarm_to_editarm(UndoArmature *uarm, bArmature *arm)
|
||||
{
|
||||
UndoArmature *uarm = uarmv;
|
||||
bArmature *arm = armv;
|
||||
EditBone *ebone;
|
||||
|
||||
ED_armature_ebone_listbase_free(arm->edbo);
|
||||
@ -65,48 +76,117 @@ static void undoBones_to_editBones(void *uarmv, void *armv, void *UNUSED(data))
|
||||
ED_armature_ebone_listbase_temp_clear(arm->edbo);
|
||||
}
|
||||
|
||||
static void *editBones_to_undoBones(void *armv, void *UNUSED(obdata))
|
||||
static void *undoarm_from_editarm(UndoArmature *uarm, bArmature *arm)
|
||||
{
|
||||
bArmature *arm = armv;
|
||||
UndoArmature *uarm;
|
||||
EditBone *ebone;
|
||||
BLI_assert(BLI_array_is_zeroed(uarm, 1));
|
||||
|
||||
uarm = MEM_callocN(sizeof(UndoArmature), "listbase undo");
|
||||
/* TODO: include size of ID-properties. */
|
||||
uarm->undo_size = 0;
|
||||
|
||||
ED_armature_ebone_listbase_copy(&uarm->lb, arm->edbo);
|
||||
|
||||
/* active bone */
|
||||
if (arm->act_edbone) {
|
||||
ebone = arm->act_edbone;
|
||||
EditBone *ebone = arm->act_edbone;
|
||||
uarm->act_edbone = ebone->temp.ebone;
|
||||
}
|
||||
|
||||
ED_armature_ebone_listbase_temp_clear(&uarm->lb);
|
||||
|
||||
for (EditBone *ebone = uarm->lb.first; ebone; ebone = ebone->next) {
|
||||
uarm->undo_size += sizeof(EditBone);
|
||||
}
|
||||
|
||||
return uarm;
|
||||
}
|
||||
|
||||
static void free_undoBones(void *uarmv)
|
||||
static void undoarm_free_data(UndoArmature *uarm)
|
||||
{
|
||||
UndoArmature *uarm = uarmv;
|
||||
|
||||
ED_armature_ebone_listbase_free(&uarm->lb);
|
||||
|
||||
MEM_freeN(uarm);
|
||||
}
|
||||
|
||||
static void *get_armature_edit(bContext *C)
|
||||
static Object *editarm_object_from_context(bContext *C)
|
||||
{
|
||||
Object *obedit = CTX_data_edit_object(C);
|
||||
if (obedit && obedit->type == OB_ARMATURE) {
|
||||
return obedit->data;
|
||||
bArmature *arm = obedit->data;
|
||||
if (arm->edbo != NULL) {
|
||||
return obedit;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* and this is all the undo system needs to know */
|
||||
void undo_push_armature(bContext *C, const char *name)
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Implements ED Undo System
|
||||
* \{ */
|
||||
|
||||
typedef struct ArmatureUndoStep {
|
||||
UndoStep step;
|
||||
/* note: will split out into list for multi-object-editmode. */
|
||||
UndoRefID_Object obedit_ref;
|
||||
UndoArmature data;
|
||||
} ArmatureUndoStep;
|
||||
|
||||
static bool armature_undosys_poll(bContext *C)
|
||||
{
|
||||
// XXX solve getdata()
|
||||
undo_editmode_push(C, name, get_armature_edit, free_undoBones, undoBones_to_editBones, editBones_to_undoBones, NULL);
|
||||
return editarm_object_from_context(C) != NULL;
|
||||
}
|
||||
|
||||
static bool armature_undosys_step_encode(struct bContext *C, UndoStep *us_p)
|
||||
{
|
||||
ArmatureUndoStep *us = (ArmatureUndoStep *)us_p;
|
||||
us->obedit_ref.ptr = editarm_object_from_context(C);
|
||||
bArmature *arm = us->obedit_ref.ptr->data;
|
||||
undoarm_from_editarm(&us->data, arm);
|
||||
us->step.data_size = us->data.undo_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void armature_undosys_step_decode(struct bContext *C, UndoStep *us_p, int UNUSED(dir))
|
||||
{
|
||||
/* TODO(campbell): undo_system: use low-level API to set mode. */
|
||||
ED_object_mode_set(C, OB_MODE_EDIT);
|
||||
BLI_assert(armature_undosys_poll(C));
|
||||
|
||||
ArmatureUndoStep *us = (ArmatureUndoStep *)us_p;
|
||||
Object *obedit = us->obedit_ref.ptr;
|
||||
bArmature *arm = obedit->data;
|
||||
undoarm_to_editarm(&us->data, arm);
|
||||
DEG_id_tag_update(&obedit->id, OB_RECALC_DATA);
|
||||
WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
|
||||
}
|
||||
|
||||
static void armature_undosys_step_free(UndoStep *us_p)
|
||||
{
|
||||
ArmatureUndoStep *us = (ArmatureUndoStep *)us_p;
|
||||
undoarm_free_data(&us->data);
|
||||
}
|
||||
|
||||
static void armature_undosys_foreach_ID_ref(
|
||||
UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
|
||||
{
|
||||
ArmatureUndoStep *us = (ArmatureUndoStep *)us_p;
|
||||
foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->obedit_ref));
|
||||
}
|
||||
|
||||
/* Export for ED_undo_sys. */
|
||||
void ED_armature_undosys_type(UndoType *ut)
|
||||
{
|
||||
ut->name = "Edit Armature";
|
||||
ut->poll = armature_undosys_poll;
|
||||
ut->step_encode = armature_undosys_step_encode;
|
||||
ut->step_decode = armature_undosys_step_decode;
|
||||
ut->step_free = armature_undosys_step_free;
|
||||
|
||||
ut->step_foreach_ID_ref = armature_undosys_foreach_ID_ref;
|
||||
|
||||
ut->mode = BKE_UNDOTYPE_MODE_STORE;
|
||||
ut->use_context = true;
|
||||
|
||||
ut->step_size = sizeof(ArmatureUndoStep);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -1232,7 +1232,10 @@ void ED_curve_editnurb_make(Object *obedit)
|
||||
|
||||
if (actkey) {
|
||||
// XXX strcpy(G.editModeTitleExtra, "(Key) ");
|
||||
/* TODO(campbell): undo_system: investigate why this was needed. */
|
||||
#if 0
|
||||
undo_editmode_clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (editnurb) {
|
||||
|
@ -30,18 +30,30 @@
|
||||
|
||||
#include "BLI_blenlib.h"
|
||||
#include "BLI_ghash.h"
|
||||
#include "BLI_array_utils.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_curve.h"
|
||||
#include "BKE_fcurve.h"
|
||||
#include "BKE_library.h"
|
||||
#include "BKE_animsys.h"
|
||||
#include "BKE_undo_system.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
|
||||
#include "ED_object.h"
|
||||
#include "ED_util.h"
|
||||
#include "ED_curve.h"
|
||||
|
||||
#include "WM_types.h"
|
||||
#include "WM_api.h"
|
||||
|
||||
#include "curve_intern.h"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Undo Conversion
|
||||
* \{ */
|
||||
|
||||
typedef struct {
|
||||
ListBase nubase;
|
||||
int actvert;
|
||||
@ -49,13 +61,12 @@ typedef struct {
|
||||
ListBase fcurves, drivers;
|
||||
int actnu;
|
||||
int flag;
|
||||
size_t undo_size;
|
||||
} UndoCurve;
|
||||
|
||||
static void undoCurve_to_editCurve(void *ucu, void *UNUSED(edata), void *cu_v)
|
||||
static void undocurve_to_editcurve(UndoCurve *ucu, Curve *cu)
|
||||
{
|
||||
Curve *cu = cu_v;
|
||||
UndoCurve *undoCurve = ucu;
|
||||
ListBase *undobase = &undoCurve->nubase;
|
||||
ListBase *undobase = &ucu->nubase;
|
||||
ListBase *editbase = BKE_curve_editNurbs_get(cu);
|
||||
Nurb *nu, *newnu;
|
||||
EditNurb *editnurb = cu->editnurb;
|
||||
@ -63,19 +74,19 @@ static void undoCurve_to_editCurve(void *ucu, void *UNUSED(edata), void *cu_v)
|
||||
|
||||
BKE_nurbList_free(editbase);
|
||||
|
||||
if (undoCurve->undoIndex) {
|
||||
if (ucu->undoIndex) {
|
||||
BKE_curve_editNurb_keyIndex_free(&editnurb->keyindex);
|
||||
editnurb->keyindex = ED_curve_keyindex_hash_duplicate(undoCurve->undoIndex);
|
||||
editnurb->keyindex = ED_curve_keyindex_hash_duplicate(ucu->undoIndex);
|
||||
}
|
||||
|
||||
if (ad) {
|
||||
if (ad->action) {
|
||||
free_fcurves(&ad->action->curves);
|
||||
copy_fcurves(&ad->action->curves, &undoCurve->fcurves);
|
||||
copy_fcurves(&ad->action->curves, &ucu->fcurves);
|
||||
}
|
||||
|
||||
free_fcurves(&ad->drivers);
|
||||
copy_fcurves(&ad->drivers, &undoCurve->drivers);
|
||||
copy_fcurves(&ad->drivers, &ucu->drivers);
|
||||
}
|
||||
|
||||
/* copy */
|
||||
@ -89,75 +100,149 @@ static void undoCurve_to_editCurve(void *ucu, void *UNUSED(edata), void *cu_v)
|
||||
BLI_addtail(editbase, newnu);
|
||||
}
|
||||
|
||||
cu->actvert = undoCurve->actvert;
|
||||
cu->actnu = undoCurve->actnu;
|
||||
cu->flag = undoCurve->flag;
|
||||
cu->actvert = ucu->actvert;
|
||||
cu->actnu = ucu->actnu;
|
||||
cu->flag = ucu->flag;
|
||||
ED_curve_updateAnimPaths(cu);
|
||||
}
|
||||
|
||||
static void *editCurve_to_undoCurve(void *UNUSED(edata), void *cu_v)
|
||||
static void undocurve_from_editcurve(UndoCurve *ucu, Curve *cu)
|
||||
{
|
||||
Curve *cu = cu_v;
|
||||
BLI_assert(BLI_array_is_zeroed(ucu, 1));
|
||||
ListBase *nubase = BKE_curve_editNurbs_get(cu);
|
||||
UndoCurve *undoCurve;
|
||||
EditNurb *editnurb = cu->editnurb, tmpEditnurb;
|
||||
Nurb *nu, *newnu;
|
||||
AnimData *ad = BKE_animdata_from_id(&cu->id);
|
||||
|
||||
undoCurve = MEM_callocN(sizeof(UndoCurve), "undoCurve");
|
||||
/* TODO: include size of fcurve & undoIndex */
|
||||
// ucu->undo_size = 0;
|
||||
|
||||
if (editnurb->keyindex) {
|
||||
undoCurve->undoIndex = ED_curve_keyindex_hash_duplicate(editnurb->keyindex);
|
||||
tmpEditnurb.keyindex = undoCurve->undoIndex;
|
||||
ucu->undoIndex = ED_curve_keyindex_hash_duplicate(editnurb->keyindex);
|
||||
tmpEditnurb.keyindex = ucu->undoIndex;
|
||||
}
|
||||
|
||||
if (ad) {
|
||||
if (ad->action)
|
||||
copy_fcurves(&undoCurve->fcurves, &ad->action->curves);
|
||||
copy_fcurves(&ucu->fcurves, &ad->action->curves);
|
||||
|
||||
copy_fcurves(&undoCurve->drivers, &ad->drivers);
|
||||
copy_fcurves(&ucu->drivers, &ad->drivers);
|
||||
}
|
||||
|
||||
/* copy */
|
||||
for (nu = nubase->first; nu; nu = nu->next) {
|
||||
newnu = BKE_nurb_duplicate(nu);
|
||||
|
||||
if (undoCurve->undoIndex) {
|
||||
if (ucu->undoIndex) {
|
||||
ED_curve_keyindex_update_nurb(&tmpEditnurb, nu, newnu);
|
||||
}
|
||||
|
||||
BLI_addtail(&undoCurve->nubase, newnu);
|
||||
BLI_addtail(&ucu->nubase, newnu);
|
||||
|
||||
ucu->undo_size += (
|
||||
(nu->bezt ? (sizeof(BezTriple) * nu->pntsu) : 0) +
|
||||
(nu->bp ? (sizeof(BPoint) * (nu->pntsu * nu->pntsv)) : 0) +
|
||||
(nu->knotsu ? (sizeof(float) * KNOTSU(nu)) : 0) +
|
||||
(nu->knotsv ? (sizeof(float) * KNOTSV(nu)) : 0) +
|
||||
sizeof(Nurb));
|
||||
}
|
||||
|
||||
undoCurve->actvert = cu->actvert;
|
||||
undoCurve->actnu = cu->actnu;
|
||||
undoCurve->flag = cu->flag;
|
||||
|
||||
return undoCurve;
|
||||
ucu->actvert = cu->actvert;
|
||||
ucu->actnu = cu->actnu;
|
||||
ucu->flag = cu->flag;
|
||||
}
|
||||
|
||||
static void free_undoCurve(void *ucv)
|
||||
static void undocurve_free_data(UndoCurve *uc)
|
||||
{
|
||||
UndoCurve *undoCurve = ucv;
|
||||
BKE_nurbList_free(&uc->nubase);
|
||||
|
||||
BKE_nurbList_free(&undoCurve->nubase);
|
||||
BKE_curve_editNurb_keyIndex_free(&uc->undoIndex);
|
||||
|
||||
BKE_curve_editNurb_keyIndex_free(&undoCurve->undoIndex);
|
||||
|
||||
free_fcurves(&undoCurve->fcurves);
|
||||
free_fcurves(&undoCurve->drivers);
|
||||
|
||||
MEM_freeN(undoCurve);
|
||||
free_fcurves(&uc->fcurves);
|
||||
free_fcurves(&uc->drivers);
|
||||
}
|
||||
|
||||
static void *get_data(bContext *C)
|
||||
static Object *editcurve_object_from_context(bContext *C)
|
||||
{
|
||||
Object *obedit = CTX_data_edit_object(C);
|
||||
return obedit;
|
||||
if (obedit && ELEM(obedit->type, OB_CURVE, OB_SURF)) {
|
||||
Curve *cu = obedit->data;
|
||||
if (BKE_curve_editNurbs_get(cu) != NULL) {
|
||||
return obedit;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* and this is all the undo system needs to know */
|
||||
void undo_push_curve(bContext *C, const char *name)
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Implements ED Undo System
|
||||
* \{ */
|
||||
|
||||
typedef struct CurveUndoStep {
|
||||
UndoStep step;
|
||||
/* note: will split out into list for multi-object-editmode. */
|
||||
UndoRefID_Object obedit_ref;
|
||||
UndoCurve data;
|
||||
} CurveUndoStep;
|
||||
|
||||
static bool curve_undosys_poll(bContext *C)
|
||||
{
|
||||
undo_editmode_push(C, name, get_data, free_undoCurve, undoCurve_to_editCurve, editCurve_to_undoCurve, NULL);
|
||||
Object *obedit = editcurve_object_from_context(C);
|
||||
return (obedit != NULL);
|
||||
}
|
||||
|
||||
static bool curve_undosys_step_encode(struct bContext *C, UndoStep *us_p)
|
||||
{
|
||||
CurveUndoStep *us = (CurveUndoStep *)us_p;
|
||||
us->obedit_ref.ptr = editcurve_object_from_context(C);
|
||||
undocurve_from_editcurve(&us->data, us->obedit_ref.ptr->data);
|
||||
us->step.data_size = us->data.undo_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void curve_undosys_step_decode(struct bContext *C, UndoStep *us_p, int UNUSED(dir))
|
||||
{
|
||||
/* TODO(campbell): undo_system: use low-level API to set mode. */
|
||||
ED_object_mode_set(C, OB_MODE_EDIT);
|
||||
BLI_assert(curve_undosys_poll(C));
|
||||
|
||||
CurveUndoStep *us = (CurveUndoStep *)us_p;
|
||||
Object *obedit = us->obedit_ref.ptr;
|
||||
undocurve_to_editcurve(&us->data, obedit->data);
|
||||
DEG_id_tag_update(&obedit->id, OB_RECALC_DATA);
|
||||
WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
|
||||
}
|
||||
|
||||
static void curve_undosys_step_free(UndoStep *us_p)
|
||||
{
|
||||
CurveUndoStep *us = (CurveUndoStep *)us_p;
|
||||
undocurve_free_data(&us->data);
|
||||
}
|
||||
|
||||
static void curve_undosys_foreach_ID_ref(
|
||||
UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
|
||||
{
|
||||
CurveUndoStep *us = (CurveUndoStep *)us_p;
|
||||
foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->obedit_ref));
|
||||
}
|
||||
|
||||
/* Export for ED_undo_sys. */
|
||||
void ED_curve_undosys_type(UndoType *ut)
|
||||
{
|
||||
ut->name = "Edit Curve";
|
||||
ut->poll = curve_undosys_poll;
|
||||
ut->step_encode = curve_undosys_step_encode;
|
||||
ut->step_decode = curve_undosys_step_decode;
|
||||
ut->step_free = curve_undosys_step_free;
|
||||
|
||||
ut->step_foreach_ID_ref = curve_undosys_foreach_ID_ref;
|
||||
|
||||
ut->mode = BKE_UNDOTYPE_MODE_STORE;
|
||||
ut->use_context = true;
|
||||
|
||||
ut->step_size = sizeof(CurveUndoStep);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -29,6 +29,8 @@
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_array_utils.h"
|
||||
|
||||
|
||||
#include "DNA_curve_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
@ -36,10 +38,17 @@
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_font.h"
|
||||
#include "BKE_undo_system.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
|
||||
#include "ED_object.h"
|
||||
#include "ED_curve.h"
|
||||
#include "ED_util.h"
|
||||
|
||||
#include "WM_types.h"
|
||||
#include "WM_api.h"
|
||||
|
||||
#define USE_ARRAY_STORE
|
||||
|
||||
#ifdef USE_ARRAY_STORE
|
||||
@ -50,6 +59,10 @@
|
||||
# define ARRAY_CHUNK_SIZE 32
|
||||
#endif
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Undo Conversion
|
||||
* \{ */
|
||||
|
||||
typedef struct UndoFont {
|
||||
wchar_t *textbuf;
|
||||
struct CharInfo *textbufinfo;
|
||||
@ -62,6 +75,8 @@ typedef struct UndoFont {
|
||||
BArrayState *textbufinfo;
|
||||
} store;
|
||||
#endif
|
||||
|
||||
size_t undo_size;
|
||||
} UndoFont;
|
||||
|
||||
|
||||
@ -202,23 +217,20 @@ static void uf_arraystore_free(UndoFont *uf)
|
||||
|
||||
BLI_array_store_at_size_clear(&uf_arraystore.bs_stride);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
#endif /* USE_ARRAY_STORE */
|
||||
|
||||
static void undoFont_to_editFont(void *uf_v, void *ecu, void *UNUSED(obdata))
|
||||
static void undofont_to_editfont(UndoFont *uf, Curve *cu)
|
||||
{
|
||||
Curve *cu = (Curve *)ecu;
|
||||
EditFont *ef = cu->editfont;
|
||||
const UndoFont *uf = uf_v;
|
||||
|
||||
size_t final_size;
|
||||
|
||||
#ifdef USE_ARRAY_STORE
|
||||
uf_arraystore_expand(uf_v);
|
||||
uf_arraystore_expand(uf);
|
||||
#endif
|
||||
|
||||
final_size = sizeof(wchar_t) * (uf->len + 1);
|
||||
@ -233,16 +245,17 @@ static void undoFont_to_editFont(void *uf_v, void *ecu, void *UNUSED(obdata))
|
||||
ef->selstart = ef->selend = 0;
|
||||
|
||||
#ifdef USE_ARRAY_STORE
|
||||
uf_arraystore_expand_clear(uf_v);
|
||||
uf_arraystore_expand_clear(uf);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void *editFont_to_undoFont(void *ecu, void *UNUSED(obdata))
|
||||
static void *undofont_from_editfont(UndoFont *uf, Curve *cu)
|
||||
{
|
||||
Curve *cu = (Curve *)ecu;
|
||||
BLI_assert(BLI_array_is_zeroed(uf, 1));
|
||||
|
||||
EditFont *ef = cu->editfont;
|
||||
|
||||
UndoFont *uf = MEM_callocN(sizeof(*uf), __func__);
|
||||
size_t mem_used_prev = MEM_get_memory_in_use();
|
||||
|
||||
size_t final_size;
|
||||
|
||||
@ -269,13 +282,15 @@ static void *editFont_to_undoFont(void *ecu, void *UNUSED(obdata))
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t mem_used_curr = MEM_get_memory_in_use();
|
||||
|
||||
uf->undo_size = mem_used_prev < mem_used_curr ? mem_used_curr - mem_used_prev : sizeof(UndoFont);
|
||||
|
||||
return uf;
|
||||
}
|
||||
|
||||
static void free_undoFont(void *uf_v)
|
||||
static void undofont_free_data(UndoFont *uf)
|
||||
{
|
||||
UndoFont *uf = uf_v;
|
||||
|
||||
#ifdef USE_ARRAY_STORE
|
||||
{
|
||||
LinkData *link = BLI_findptr(&uf_arraystore.local_links, uf, offsetof(LinkData, data));
|
||||
@ -291,21 +306,91 @@ static void free_undoFont(void *uf_v)
|
||||
if (uf->textbufinfo) {
|
||||
MEM_freeN(uf->textbufinfo);
|
||||
}
|
||||
|
||||
MEM_freeN(uf);
|
||||
}
|
||||
|
||||
static void *get_undoFont(bContext *C)
|
||||
static Object *editfont_object_from_context(bContext *C)
|
||||
{
|
||||
Object *obedit = CTX_data_edit_object(C);
|
||||
if (obedit && obedit->type == OB_FONT) {
|
||||
return obedit->data;
|
||||
Curve *cu = obedit->data;
|
||||
EditFont *ef = cu->editfont;
|
||||
if (ef != NULL) {
|
||||
return obedit;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* and this is all the undo system needs to know */
|
||||
void undo_push_font(bContext *C, const char *name)
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Implements ED Undo System
|
||||
* \{ */
|
||||
|
||||
typedef struct FontUndoStep {
|
||||
UndoStep step;
|
||||
/* note: will split out into list for multi-object-editmode. */
|
||||
UndoRefID_Object obedit_ref;
|
||||
UndoFont data;
|
||||
} FontUndoStep;
|
||||
|
||||
static bool font_undosys_poll(bContext *C)
|
||||
{
|
||||
undo_editmode_push(C, name, get_undoFont, free_undoFont, undoFont_to_editFont, editFont_to_undoFont, NULL);
|
||||
return editfont_object_from_context(C) != NULL;
|
||||
}
|
||||
|
||||
static bool font_undosys_step_encode(struct bContext *C, UndoStep *us_p)
|
||||
{
|
||||
FontUndoStep *us = (FontUndoStep *)us_p;
|
||||
us->obedit_ref.ptr = editfont_object_from_context(C);
|
||||
Curve *cu = us->obedit_ref.ptr->data;
|
||||
undofont_from_editfont(&us->data, cu);
|
||||
us->step.data_size = us->data.undo_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void font_undosys_step_decode(struct bContext *C, UndoStep *us_p, int UNUSED(dir))
|
||||
{
|
||||
/* TODO(campbell): undo_system: use low-level API to set mode. */
|
||||
ED_object_mode_set(C, OB_MODE_EDIT);
|
||||
BLI_assert(font_undosys_poll(C));
|
||||
|
||||
FontUndoStep *us = (FontUndoStep *)us_p;
|
||||
Object *obedit = us->obedit_ref.ptr;
|
||||
Curve *cu = obedit->data;
|
||||
undofont_to_editfont(&us->data, cu);
|
||||
DEG_id_tag_update(&obedit->id, OB_RECALC_DATA);
|
||||
WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
|
||||
}
|
||||
|
||||
static void font_undosys_step_free(UndoStep *us_p)
|
||||
{
|
||||
FontUndoStep *us = (FontUndoStep *)us_p;
|
||||
undofont_free_data(&us->data);
|
||||
}
|
||||
|
||||
static void font_undosys_foreach_ID_ref(
|
||||
UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
|
||||
{
|
||||
FontUndoStep *us = (FontUndoStep *)us_p;
|
||||
foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->obedit_ref));
|
||||
}
|
||||
|
||||
/* Export for ED_undo_sys. */
|
||||
void ED_font_undosys_type(UndoType *ut)
|
||||
{
|
||||
ut->name = "Edit Font";
|
||||
ut->poll = font_undosys_poll;
|
||||
ut->step_encode = font_undosys_step_encode;
|
||||
ut->step_decode = font_undosys_step_decode;
|
||||
ut->step_free = font_undosys_step_free;
|
||||
|
||||
ut->step_foreach_ID_ref = font_undosys_foreach_ID_ref;
|
||||
|
||||
ut->mode = BKE_UNDOTYPE_MODE_STORE;
|
||||
ut->use_context = true;
|
||||
|
||||
ut->step_size = sizeof(FontUndoStep);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -54,7 +54,6 @@
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_gpencil.h"
|
||||
#include "BKE_library.h"
|
||||
#include "BKE_report.h"
|
||||
|
@ -53,7 +53,6 @@
|
||||
#include "DNA_gpencil_types.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_gpencil.h"
|
||||
#include "BKE_library.h"
|
||||
#include "BKE_object.h"
|
||||
|
@ -55,7 +55,6 @@
|
||||
#include "DNA_gpencil_types.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_gpencil.h"
|
||||
#include "BKE_library.h"
|
||||
#include "BKE_report.h"
|
||||
|
@ -52,6 +52,7 @@ struct ViewContext;
|
||||
struct wmKeyConfig;
|
||||
struct wmOperator;
|
||||
struct Main;
|
||||
struct UndoType;
|
||||
|
||||
typedef struct EditBone {
|
||||
struct EditBone *next, *prev;
|
||||
@ -149,7 +150,7 @@ bool ED_armature_select_pick(struct bContext *C, const int mval[2], bool extend,
|
||||
int join_armature_exec(struct bContext *C, struct wmOperator *op);
|
||||
struct Bone *get_indexed_bone(struct Object *ob, int index);
|
||||
float ED_rollBoneToVector(EditBone *bone, const float new_up_axis[3], const bool axis_only);
|
||||
EditBone *ED_armature_bone_find_name(const ListBase *edbo, const char *name);
|
||||
EditBone *ED_armature_bone_find_name(const struct ListBase *edbo, const char *name);
|
||||
EditBone *ED_armature_bone_get_mirrored(const struct ListBase *edbo, EditBone *ebo);
|
||||
void ED_armature_sync_selection(struct ListBase *edbo);
|
||||
void ED_armature_validate_active(struct bArmature *arm);
|
||||
@ -198,6 +199,9 @@ void ED_armature_ebone_select_set(EditBone *ebone, bool select);
|
||||
void ED_armature_ebone_selectflag_enable(EditBone *ebone, int flag);
|
||||
void ED_armature_ebone_selectflag_disable(EditBone *ebone, int flag);
|
||||
|
||||
/* editarmature_undo.c */
|
||||
void ED_armature_undosys_type(struct UndoType *ut);
|
||||
|
||||
/* armature_utils.c */
|
||||
void ED_armature_ebone_listbase_temp_clear(struct ListBase *lb);
|
||||
void ED_armature_ebone_listbase_free(struct ListBase *lb);
|
||||
|
@ -41,6 +41,7 @@ struct Curve;
|
||||
struct EditNurb;
|
||||
struct BezTriple;
|
||||
struct BPoint;
|
||||
struct UndoType;
|
||||
|
||||
/* curve_ops.c */
|
||||
void ED_operatortypes_curve(void);
|
||||
@ -48,7 +49,7 @@ void ED_operatormacros_curve(void);
|
||||
void ED_keymap_curve(struct wmKeyConfig *keyconf);
|
||||
|
||||
/* editcurve.c */
|
||||
ListBase *object_editcurve_get(struct Object *ob);
|
||||
struct ListBase *object_editcurve_get(struct Object *ob);
|
||||
|
||||
void ED_curve_editnurb_load(struct Object *obedit);
|
||||
void ED_curve_editnurb_make(struct Object *obedit);
|
||||
@ -72,7 +73,7 @@ void ED_curve_select_all(struct EditNurb *editnurb);
|
||||
void ED_curve_select_swap(struct EditNurb *editnurb, bool hide_handles);
|
||||
|
||||
/* editcurve_undo.c */
|
||||
void undo_push_curve(struct bContext *C, const char *name);
|
||||
void ED_curve_undosys_type(struct UndoType *ut);
|
||||
|
||||
/* editfont.c */
|
||||
void ED_curve_editfont_load(struct Object *obedit);
|
||||
@ -91,7 +92,8 @@ bool ED_curve_active_center(struct Curve *cu, float center[3]);
|
||||
bool ED_curve_editfont_select_pick(struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle);
|
||||
|
||||
/* editfont_undo.c */
|
||||
void undo_push_font(struct bContext *C, const char *name);
|
||||
void ED_font_undosys_type(struct UndoType *ut);
|
||||
|
||||
|
||||
#if 0
|
||||
/* debug only */
|
||||
|
@ -31,6 +31,8 @@
|
||||
#define __ED_LATTICE_H__
|
||||
|
||||
struct wmKeyConfig;
|
||||
struct UndoType;
|
||||
struct Object;
|
||||
|
||||
/* lattice_ops.c */
|
||||
void ED_operatortypes_lattice(void);
|
||||
@ -41,6 +43,6 @@ void ED_lattice_flags_set(struct Object *obedit, int flag);
|
||||
bool ED_lattice_select_pick(struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle);
|
||||
|
||||
/* editlattice_undo.c */
|
||||
void undo_push_lattice(struct bContext *C, const char *name);
|
||||
void ED_lattice_undosys_type(struct UndoType *ut);
|
||||
|
||||
#endif /* __ED_LATTICE_H__ */
|
||||
|
@ -34,6 +34,7 @@
|
||||
struct bContext;
|
||||
struct Object;
|
||||
struct wmKeyConfig;
|
||||
struct UndoType;
|
||||
|
||||
void ED_operatortypes_metaball(void);
|
||||
void ED_operatormacros_metaball(void);
|
||||
@ -47,6 +48,7 @@ void ED_mball_editmball_free(struct Object *obedit);
|
||||
void ED_mball_editmball_make(struct Object *obedit);
|
||||
void ED_mball_editmball_load(struct Object *obedit);
|
||||
|
||||
void undo_push_mball(struct bContext *C, const char *name);
|
||||
/* editmball_undo.c */
|
||||
void ED_mball_undosys_type(struct UndoType *ut);
|
||||
|
||||
#endif /* __ED_MBALL_H__ */
|
||||
|
@ -63,6 +63,7 @@ struct UvMapVert;
|
||||
struct ToolSettings;
|
||||
struct Object;
|
||||
struct rcti;
|
||||
struct UndoType;
|
||||
|
||||
/* editmesh_utils.c */
|
||||
void EDBM_verts_mirror_cache_begin_ex(struct BMEditMesh *em, const int axis,
|
||||
@ -99,8 +100,6 @@ void EDBM_selectmode_flush(struct BMEditMesh *em);
|
||||
void EDBM_deselect_flush(struct BMEditMesh *em);
|
||||
void EDBM_select_flush(struct BMEditMesh *em);
|
||||
|
||||
void undo_push_mesh(struct bContext *C, const char *name);
|
||||
|
||||
bool EDBM_vert_color_check(struct BMEditMesh *em);
|
||||
|
||||
void EDBM_mesh_hide(struct BMEditMesh *em, bool swap);
|
||||
@ -131,6 +130,9 @@ bool BMBVH_EdgeVisible(struct BMBVHTree *tree, struct BMEdge *e,
|
||||
const struct Depsgraph *depsgraph,
|
||||
struct ARegion *ar, struct View3D *v3d, struct Object *obedit);
|
||||
|
||||
/* editmesh_undo.c */
|
||||
void ED_mesh_undosys_type(struct UndoType *ut);
|
||||
|
||||
/* editmesh_select.c */
|
||||
void EDBM_select_mirrored(
|
||||
struct BMEditMesh *em, const int axis, const bool extend,
|
||||
|
@ -207,6 +207,7 @@ void ED_object_constraint_dependency_tag_update(struct Main *bmain, struct Objec
|
||||
bool ED_object_mode_compat_test(const struct Object *ob, eObjectMode mode);
|
||||
bool ED_object_mode_compat_set(struct bContext *C, struct WorkSpace *workspace, eObjectMode mode, struct ReportList *reports);
|
||||
void ED_object_mode_toggle(struct bContext *C, eObjectMode mode);
|
||||
void ED_object_mode_set(struct bContext *C, eObjectMode mode);
|
||||
|
||||
bool ED_object_mode_generic_enter(
|
||||
struct bContext *C,
|
||||
|
@ -28,31 +28,16 @@
|
||||
struct bContext;
|
||||
struct wmKeyConfig;
|
||||
struct wmOperator;
|
||||
struct ImBuf;
|
||||
struct Image;
|
||||
struct UndoStep;
|
||||
struct UndoType;
|
||||
|
||||
/* paint_ops.c */
|
||||
void ED_operatortypes_paint(void);
|
||||
void ED_operatormacros_paint(void);
|
||||
void ED_keymap_paint(struct wmKeyConfig *keyconf);
|
||||
|
||||
/* paint_undo.c */
|
||||
enum {
|
||||
UNDO_PAINT_IMAGE = 0,
|
||||
UNDO_PAINT_MESH = 1,
|
||||
};
|
||||
|
||||
typedef void (*UndoRestoreCb)(struct bContext *C, struct ListBase *lb);
|
||||
typedef void (*UndoFreeCb)(struct ListBase *lb);
|
||||
typedef bool (*UndoCleanupCb)(struct bContext *C, struct ListBase *lb);
|
||||
|
||||
int ED_undo_paint_step(struct bContext *C, int type, int step, const char *name);
|
||||
void ED_undo_paint_step_num(struct bContext *C, int type, int num);
|
||||
const char *ED_undo_paint_get_name(struct bContext *C, int type, int nr, bool *r_active);
|
||||
void ED_undo_paint_free(void);
|
||||
bool ED_undo_paint_is_valid(int type, const char *name);
|
||||
bool ED_undo_paint_empty(int type);
|
||||
void ED_undo_paint_push_begin(int type, const char *name, UndoRestoreCb restore, UndoFreeCb free, UndoCleanupCb cleanup);
|
||||
void ED_undo_paint_push_end(int type);
|
||||
|
||||
/* paint_image.c */
|
||||
void ED_imapaint_clear_partial_redraw(void);
|
||||
void ED_imapaint_dirty_region(struct Image *ima, struct ImBuf *ibuf, int x, int y, int w, int h, bool find_old);
|
||||
@ -61,6 +46,14 @@ void ED_imapaint_bucket_fill(struct bContext *C, float color[3], struct wmOperat
|
||||
/* paint_image_undo.c */
|
||||
void ED_image_undo_push_begin(const char *name);
|
||||
void ED_image_undo_push_end(void);
|
||||
void ED_image_undo_restore(void);
|
||||
void ED_image_undo_restore(struct UndoStep *us);
|
||||
|
||||
void ED_image_undosys_type(struct UndoType *ut);
|
||||
|
||||
/* paint_curve_undo.c */
|
||||
void ED_paintcurve_undo_push_begin(const char *name);
|
||||
void ED_paintcurve_undo_push_end(void);
|
||||
|
||||
void ED_paintcurve_undosys_type(struct UndoType *ut);
|
||||
|
||||
#endif /* __ED_PAINT_H__ */
|
||||
|
@ -39,13 +39,14 @@ struct rcti;
|
||||
struct PTCacheEdit;
|
||||
struct Scene;
|
||||
struct ViewLayer;
|
||||
struct UndoType;
|
||||
|
||||
/* particle edit mode */
|
||||
void PE_free_ptcache_edit(struct PTCacheEdit *edit);
|
||||
int PE_start_edit(struct PTCacheEdit *edit);
|
||||
|
||||
/* access */
|
||||
struct PTCacheEdit *PE_get_current(struct Scene *scene, struct ViewLayer *view_layer, struct Object *ob);
|
||||
struct PTCacheEdit *PE_get_current(struct Scene *scene, struct Object *ob);
|
||||
struct PTCacheEdit *PE_create_current(const struct EvaluationContext *eval_ctx, struct Scene *scene, struct Object *ob);
|
||||
void PE_current_changed(const struct EvaluationContext *eval_ctx, struct Scene *scene, struct Object *ob);
|
||||
int PE_minmax(struct Scene *scene, struct ViewLayer *view_layer, float min[3], float max[3]);
|
||||
@ -64,14 +65,8 @@ int PE_circle_select(struct bContext *C, int selecting, const int mval[2], float
|
||||
int PE_lasso_select(struct bContext *C, const int mcords[][2], const short moves, bool extend, bool select);
|
||||
void PE_deselect_all_visible(struct PTCacheEdit *edit);
|
||||
|
||||
/* undo */
|
||||
void PE_undo_push(struct Scene *scene, struct ViewLayer *view_layer, const char *str);
|
||||
void PE_undo_step(struct Scene *scene, struct ViewLayer *view_layer, int step);
|
||||
void PE_undo(struct Scene *scene, struct ViewLayer *view_layer);
|
||||
void PE_redo(struct Scene *scene, struct ViewLayer *view_layer);
|
||||
bool PE_undo_is_valid(struct Scene *scene, struct ViewLayer *view_layer);
|
||||
void PE_undo_number(struct Scene *scene, struct ViewLayer *view_layer, int nr);
|
||||
const char *PE_undo_get_name(struct Scene *scene, struct ViewLayer *view_layer, int nr, bool *r_active);
|
||||
/* particle_edit_undo.c */
|
||||
void ED_particle_undosys_type(struct UndoType *ut);
|
||||
|
||||
#endif /* __ED_PARTICLE_H__ */
|
||||
|
||||
|
@ -36,10 +36,16 @@ struct Object;
|
||||
struct RegionView3D;
|
||||
struct ViewContext;
|
||||
struct rcti;
|
||||
struct UndoStep;
|
||||
struct UndoType;
|
||||
struct ListBase;
|
||||
|
||||
/* sculpt.c */
|
||||
void ED_operatortypes_sculpt(void);
|
||||
void ED_sculpt_redraw_planes_get(float planes[4][4], struct ARegion *ar, struct Object *ob);
|
||||
int ED_sculpt_mask_box_select(struct bContext *C, struct ViewContext *vc, const struct rcti *rect, bool select, bool extend);
|
||||
|
||||
/* sculpt_undo.c */
|
||||
void ED_sculpt_undosys_type(struct UndoType *ut);
|
||||
|
||||
#endif /* __ED_SCULPT_H__ */
|
||||
|
@ -30,12 +30,13 @@
|
||||
#ifndef __ED_TEXT_H__
|
||||
#define __ED_TEXT_H__
|
||||
|
||||
struct bContext;
|
||||
struct SpaceText;
|
||||
struct ARegion;
|
||||
struct UndoType;
|
||||
|
||||
void ED_text_undo_step(struct bContext *C, int step);
|
||||
bool ED_text_region_location_from_cursor(struct SpaceText *st, struct ARegion *ar, const int cursor_co[2], int r_pixel_co[2]);
|
||||
|
||||
#endif /* __ED_TEXT_H__ */
|
||||
/* text_undo.c */
|
||||
void ED_text_undosys_type(struct UndoType *ut);
|
||||
|
||||
#endif /* __ED_TEXT_H__ */
|
||||
|
@ -35,6 +35,9 @@ struct bContext;
|
||||
struct SpaceLink;
|
||||
struct wmOperator;
|
||||
struct wmOperatorType;
|
||||
struct UndoStack;
|
||||
struct ScrArea;
|
||||
struct PackedFile;
|
||||
|
||||
/* ed_util.c */
|
||||
|
||||
@ -70,16 +73,12 @@ void ED_undo_operator_repeat_cb_evt(struct bContext *C, void *arg_op, int arg
|
||||
|
||||
bool ED_undo_is_valid(const struct bContext *C, const char *undoname);
|
||||
|
||||
/* undo_editmode.c */
|
||||
void undo_editmode_push(struct bContext *C, const char *name,
|
||||
void * (*getdata)(struct bContext *C),
|
||||
void (*freedata)(void *),
|
||||
void (*to_editmode)(void *, void *, void *),
|
||||
void *(*from_editmode)(void *, void *),
|
||||
int (*validate_undo)(void *, void *));
|
||||
/* undo_system_types.c */
|
||||
void ED_undosys_type_init(void);
|
||||
void ED_undosys_type_free(void);
|
||||
|
||||
|
||||
void undo_editmode_clear(void);
|
||||
/* memfile_undo.c */
|
||||
struct MemFile *ED_undosys_stack_memfile_get_active(struct UndoStack *ustack);
|
||||
|
||||
/* ************** XXX OLD CRUFT WARNING ************* */
|
||||
|
||||
|
@ -33,7 +33,6 @@
|
||||
|
||||
#include "BKE_cachefile.h"
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_library.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_report.h"
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_array_utils.h"
|
||||
|
||||
#include "DNA_curve_types.h"
|
||||
#include "DNA_lattice_types.h"
|
||||
@ -41,31 +42,40 @@
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_undo_system.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
|
||||
#include "ED_object.h"
|
||||
#include "ED_lattice.h"
|
||||
#include "ED_util.h"
|
||||
|
||||
#include "WM_types.h"
|
||||
#include "WM_api.h"
|
||||
|
||||
#include "lattice_intern.h"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Undo Conversion
|
||||
* \{ */
|
||||
|
||||
typedef struct UndoLattice {
|
||||
BPoint *def;
|
||||
int pntsu, pntsv, pntsw, actbp;
|
||||
size_t undo_size;
|
||||
} UndoLattice;
|
||||
|
||||
static void undoLatt_to_editLatt(void *data, void *edata, void *UNUSED(obdata))
|
||||
static void undolatt_to_editlatt(UndoLattice *ult, EditLatt *editlatt)
|
||||
{
|
||||
UndoLattice *ult = (UndoLattice *)data;
|
||||
EditLatt *editlatt = (EditLatt *)edata;
|
||||
int a = editlatt->latt->pntsu * editlatt->latt->pntsv * editlatt->latt->pntsw;
|
||||
int len = editlatt->latt->pntsu * editlatt->latt->pntsv * editlatt->latt->pntsw;
|
||||
|
||||
memcpy(editlatt->latt->def, ult->def, a * sizeof(BPoint));
|
||||
memcpy(editlatt->latt->def, ult->def, sizeof(BPoint) * len);
|
||||
editlatt->latt->actbp = ult->actbp;
|
||||
}
|
||||
|
||||
static void *editLatt_to_undoLatt(void *edata, void *UNUSED(obdata))
|
||||
static void *undolatt_from_editlatt(UndoLattice *ult, EditLatt *editlatt)
|
||||
{
|
||||
UndoLattice *ult = MEM_callocN(sizeof(UndoLattice), "UndoLattice");
|
||||
EditLatt *editlatt = (EditLatt *)edata;
|
||||
BLI_assert(BLI_array_is_zeroed(ult, 1));
|
||||
|
||||
ult->def = MEM_dupallocN(editlatt->latt->def);
|
||||
ult->pntsu = editlatt->latt->pntsu;
|
||||
@ -73,17 +83,19 @@ static void *editLatt_to_undoLatt(void *edata, void *UNUSED(obdata))
|
||||
ult->pntsw = editlatt->latt->pntsw;
|
||||
ult->actbp = editlatt->latt->actbp;
|
||||
|
||||
ult->undo_size += sizeof(*ult->def) * ult->pntsu * ult->pntsv * ult->pntsw;
|
||||
|
||||
return ult;
|
||||
}
|
||||
|
||||
static void free_undoLatt(void *data)
|
||||
static void undolatt_free_data(UndoLattice *ult)
|
||||
{
|
||||
UndoLattice *ult = (UndoLattice *)data;
|
||||
|
||||
if (ult->def) MEM_freeN(ult->def);
|
||||
MEM_freeN(ult);
|
||||
if (ult->def) {
|
||||
MEM_freeN(ult->def);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int validate_undoLatt(void *data, void *edata)
|
||||
{
|
||||
UndoLattice *ult = (UndoLattice *)data;
|
||||
@ -93,21 +105,92 @@ static int validate_undoLatt(void *data, void *edata)
|
||||
ult->pntsv == editlatt->latt->pntsv &&
|
||||
ult->pntsw == editlatt->latt->pntsw);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void *get_editlatt(bContext *C)
|
||||
static Object *editlatt_object_from_context(bContext *C)
|
||||
{
|
||||
Object *obedit = CTX_data_edit_object(C);
|
||||
|
||||
if (obedit && obedit->type == OB_LATTICE) {
|
||||
Lattice *lt = obedit->data;
|
||||
return lt->editlatt;
|
||||
if (lt->editlatt != NULL) {
|
||||
return obedit;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* and this is all the undo system needs to know */
|
||||
void undo_push_lattice(bContext *C, const char *name)
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Implements ED Undo System
|
||||
* \{ */
|
||||
|
||||
typedef struct LatticeUndoStep {
|
||||
UndoStep step;
|
||||
/* note: will split out into list for multi-object-editmode. */
|
||||
UndoRefID_Object obedit_ref;
|
||||
UndoLattice data;
|
||||
} LatticeUndoStep;
|
||||
|
||||
static bool lattice_undosys_poll(bContext *C)
|
||||
{
|
||||
undo_editmode_push(C, name, get_editlatt, free_undoLatt, undoLatt_to_editLatt, editLatt_to_undoLatt, validate_undoLatt);
|
||||
return editlatt_object_from_context(C) != NULL;
|
||||
}
|
||||
|
||||
static bool lattice_undosys_step_encode(struct bContext *C, UndoStep *us_p)
|
||||
{
|
||||
LatticeUndoStep *us = (LatticeUndoStep *)us_p;
|
||||
us->obedit_ref.ptr = editlatt_object_from_context(C);
|
||||
Lattice *lt = us->obedit_ref.ptr->data;
|
||||
undolatt_from_editlatt(&us->data, lt->editlatt);
|
||||
us->step.data_size = us->data.undo_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void lattice_undosys_step_decode(struct bContext *C, UndoStep *us_p, int UNUSED(dir))
|
||||
{
|
||||
/* TODO(campbell): undo_system: use low-level API to set mode. */
|
||||
ED_object_mode_set(C, OB_MODE_EDIT);
|
||||
BLI_assert(lattice_undosys_poll(C));
|
||||
|
||||
LatticeUndoStep *us = (LatticeUndoStep *)us_p;
|
||||
Object *obedit = us->obedit_ref.ptr;
|
||||
Lattice *lt = obedit->data;
|
||||
EditLatt *editlatt = lt->editlatt;
|
||||
undolatt_to_editlatt(&us->data, editlatt);
|
||||
DEG_id_tag_update(&obedit->id, OB_RECALC_DATA);
|
||||
WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
|
||||
}
|
||||
|
||||
static void lattice_undosys_step_free(UndoStep *us_p)
|
||||
{
|
||||
LatticeUndoStep *us = (LatticeUndoStep *)us_p;
|
||||
undolatt_free_data(&us->data);
|
||||
}
|
||||
|
||||
static void lattice_undosys_foreach_ID_ref(
|
||||
UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
|
||||
{
|
||||
LatticeUndoStep *us = (LatticeUndoStep *)us_p;
|
||||
foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->obedit_ref));
|
||||
}
|
||||
|
||||
/* Export for ED_undo_sys. */
|
||||
void ED_lattice_undosys_type(UndoType *ut)
|
||||
{
|
||||
ut->name = "Edit Lattice";
|
||||
ut->poll = lattice_undosys_poll;
|
||||
ut->step_encode = lattice_undosys_step_encode;
|
||||
ut->step_decode = lattice_undosys_step_decode;
|
||||
ut->step_free = lattice_undosys_step_free;
|
||||
|
||||
ut->step_foreach_ID_ref = lattice_undosys_foreach_ID_ref;
|
||||
|
||||
ut->mode = BKE_UNDOTYPE_MODE_STORE;
|
||||
ut->use_context = true;
|
||||
|
||||
ut->step_size = sizeof(LatticeUndoStep);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -36,7 +36,6 @@
|
||||
#include "BLI_listbase.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_object.h"
|
||||
#include "BKE_report.h"
|
||||
|
@ -3762,7 +3762,7 @@ static void edbm_fill_grid_prepare(BMesh *bm, int offset, int *r_span, bool span
|
||||
*
|
||||
* note: we may have already checked 'edbm_fill_grid_vert_tag_angle()' on each
|
||||
* vert, but advantage of de-duplicating is minimal. */
|
||||
struct SortPointerByFloat *ele_sort = MEM_mallocN(sizeof(*ele_sort) * verts_len, __func__);
|
||||
struct SortPtrByFloat *ele_sort = MEM_mallocN(sizeof(*ele_sort) * verts_len, __func__);
|
||||
LinkData *v_link;
|
||||
for (v_link = verts->first, i = 0; v_link; v_link = v_link->next, i++) {
|
||||
BMVert *v = v_link->data;
|
||||
|
@ -29,16 +29,25 @@
|
||||
#include "DNA_key_types.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_array_utils.h"
|
||||
#include "BLI_alloca.h"
|
||||
|
||||
#include "BKE_DerivedMesh.h"
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_key.h"
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_editmesh.h"
|
||||
#include "BKE_undo_system.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
|
||||
#include "ED_object.h"
|
||||
#include "ED_mesh.h"
|
||||
#include "ED_util.h"
|
||||
|
||||
#include "WM_types.h"
|
||||
#include "WM_api.h"
|
||||
|
||||
#define USE_ARRAY_STORE
|
||||
|
||||
#ifdef USE_ARRAY_STORE
|
||||
@ -60,6 +69,9 @@
|
||||
# include "BLI_task.h"
|
||||
#endif
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Undo Conversion
|
||||
* \{ */
|
||||
|
||||
#ifdef USE_ARRAY_STORE
|
||||
|
||||
@ -95,6 +107,8 @@ typedef struct UndoMesh {
|
||||
BArrayState *mselect;
|
||||
} store;
|
||||
#endif /* USE_ARRAY_STORE */
|
||||
|
||||
size_t undo_size;
|
||||
} UndoMesh;
|
||||
|
||||
|
||||
@ -474,23 +488,17 @@ static void um_arraystore_free(UndoMesh *um)
|
||||
|
||||
/* for callbacks */
|
||||
/* undo simply makes copies of a bmesh */
|
||||
static void *editbtMesh_to_undoMesh(void *emv, void *obdata)
|
||||
static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key)
|
||||
{
|
||||
|
||||
BLI_assert(BLI_array_is_zeroed(um, 1));
|
||||
#ifdef USE_ARRAY_STORE_THREAD
|
||||
/* changes this waits is low, but must have finished */
|
||||
if (um_arraystore.task_pool) {
|
||||
BLI_task_pool_work_and_wait(um_arraystore.task_pool);
|
||||
}
|
||||
#endif
|
||||
|
||||
BMEditMesh *em = emv;
|
||||
Mesh *obme = obdata;
|
||||
|
||||
UndoMesh *um = MEM_callocN(sizeof(UndoMesh), "undo Mesh");
|
||||
|
||||
/* make sure shape keys work */
|
||||
um->me.key = obme->key ? BKE_key_copy_nolib(obme->key) : NULL;
|
||||
um->me.key = key ? BKE_key_copy_nolib(key) : NULL;
|
||||
|
||||
/* BM_mesh_validate(em->bm); */ /* for troubleshooting */
|
||||
|
||||
@ -536,13 +544,12 @@ static void *editbtMesh_to_undoMesh(void *emv, void *obdata)
|
||||
return um;
|
||||
}
|
||||
|
||||
static void undoMesh_to_editbtMesh(void *um_v, void *em_v, void *obdata)
|
||||
static void undomesh_to_editmesh(UndoMesh *um, BMEditMesh *em, Mesh *obmesh)
|
||||
{
|
||||
BMEditMesh *em = em_v, *em_tmp;
|
||||
BMEditMesh *em_tmp;
|
||||
Object *ob = em->ob;
|
||||
UndoMesh *um = um_v;
|
||||
BMesh *bm;
|
||||
Key *key = ((Mesh *) obdata)->key;
|
||||
Key *key = obmesh->key;
|
||||
|
||||
#ifdef USE_ARRAY_STORE
|
||||
#ifdef USE_ARRAY_STORE_THREAD
|
||||
@ -615,9 +622,8 @@ static void undoMesh_to_editbtMesh(void *um_v, void *em_v, void *obdata)
|
||||
#endif
|
||||
}
|
||||
|
||||
static void free_undo(void *um_v)
|
||||
static void undomesh_free_data(UndoMesh *um)
|
||||
{
|
||||
UndoMesh *um = um_v;
|
||||
Mesh *me = &um->me;
|
||||
|
||||
#ifdef USE_ARRAY_STORE
|
||||
@ -644,28 +650,103 @@ static void free_undo(void *um_v)
|
||||
}
|
||||
|
||||
BKE_mesh_free(me);
|
||||
MEM_freeN(me);
|
||||
}
|
||||
|
||||
static void *getEditMesh(bContext *C)
|
||||
static Object *editmesh_object_from_context(bContext *C)
|
||||
{
|
||||
Object *obedit = CTX_data_edit_object(C);
|
||||
if (obedit && obedit->type == OB_MESH) {
|
||||
Mesh *me = obedit->data;
|
||||
return me->edit_btmesh;
|
||||
if (me->edit_btmesh != NULL) {
|
||||
return obedit;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* and this is all the undo system needs to know */
|
||||
void undo_push_mesh(bContext *C, const char *name)
|
||||
{
|
||||
/* em->ob gets out of date and crashes on mesh undo,
|
||||
* this is an easy way to ensure its OK
|
||||
* though we could investigate the matter further. */
|
||||
Object *obedit = CTX_data_edit_object(C);
|
||||
BMEditMesh *em = BKE_editmesh_from_object(obedit);
|
||||
em->ob = obedit;
|
||||
/** \} */
|
||||
|
||||
undo_editmode_push(C, name, getEditMesh, free_undo, undoMesh_to_editbtMesh, editbtMesh_to_undoMesh, NULL);
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Implements ED Undo System
|
||||
* \{ */
|
||||
|
||||
typedef struct MeshUndoStep {
|
||||
UndoStep step;
|
||||
/* Use for all ID lookups (can be NULL). */
|
||||
struct UndoIDPtrMap *id_map;
|
||||
|
||||
/* note: will split out into list for multi-object-editmode. */
|
||||
UndoRefID_Object obedit_ref;
|
||||
/* Needed for MTexPoly's image use. */
|
||||
UndoRefID_Object *image_array_ref;
|
||||
UndoMesh data;
|
||||
} MeshUndoStep;
|
||||
|
||||
static bool mesh_undosys_poll(bContext *C)
|
||||
{
|
||||
return editmesh_object_from_context(C) != NULL;
|
||||
}
|
||||
|
||||
static bool mesh_undosys_step_encode(struct bContext *C, UndoStep *us_p)
|
||||
{
|
||||
MeshUndoStep *us = (MeshUndoStep *)us_p;
|
||||
us->obedit_ref.ptr = editmesh_object_from_context(C);
|
||||
Mesh *me = us->obedit_ref.ptr->data;
|
||||
undomesh_from_editmesh(&us->data, me->edit_btmesh, me->key);
|
||||
us->step.data_size = us->data.undo_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void mesh_undosys_step_decode(struct bContext *C, UndoStep *us_p, int UNUSED(dir))
|
||||
{
|
||||
/* TODO(campbell): undo_system: use low-level API to set mode. */
|
||||
ED_object_mode_set(C, OB_MODE_EDIT);
|
||||
BLI_assert(mesh_undosys_poll(C));
|
||||
|
||||
MeshUndoStep *us = (MeshUndoStep *)us_p;
|
||||
Object *obedit = us->obedit_ref.ptr;
|
||||
Mesh *me = obedit->data;
|
||||
BMEditMesh *em = me->edit_btmesh;
|
||||
undomesh_to_editmesh(&us->data, em, obedit->data);
|
||||
DEG_id_tag_update(&obedit->id, OB_RECALC_DATA);
|
||||
WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
|
||||
}
|
||||
|
||||
static void mesh_undosys_step_free(UndoStep *us_p)
|
||||
{
|
||||
MeshUndoStep *us = (MeshUndoStep *)us_p;
|
||||
undomesh_free_data(&us->data);
|
||||
|
||||
if (us->id_map != NULL) {
|
||||
BKE_undosys_ID_map_destroy(us->id_map);
|
||||
}
|
||||
}
|
||||
|
||||
static void mesh_undosys_foreach_ID_ref(
|
||||
UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
|
||||
{
|
||||
MeshUndoStep *us = (MeshUndoStep *)us_p;
|
||||
foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->obedit_ref));
|
||||
if (us->id_map != NULL) {
|
||||
BKE_undosys_ID_map_foreach_ID_ref(us->id_map, foreach_ID_ref_fn, user_data);
|
||||
}
|
||||
}
|
||||
|
||||
/* Export for ED_undo_sys. */
|
||||
void ED_mesh_undosys_type(UndoType *ut)
|
||||
{
|
||||
ut->name = "Edit Mesh";
|
||||
ut->poll = mesh_undosys_poll;
|
||||
ut->step_encode = mesh_undosys_step_encode;
|
||||
ut->step_decode = mesh_undosys_step_decode;
|
||||
ut->step_free = mesh_undosys_step_free;
|
||||
|
||||
ut->step_foreach_ID_ref = mesh_undosys_foreach_ID_ref;
|
||||
|
||||
ut->mode = BKE_UNDOTYPE_MODE_STORE;
|
||||
ut->use_context = true;
|
||||
|
||||
ut->step_size = sizeof(MeshUndoStep);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -43,7 +43,6 @@
|
||||
|
||||
#include "BKE_DerivedMesh.h"
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_mesh_mapping.h"
|
||||
|
@ -29,19 +29,32 @@
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_array_utils.h"
|
||||
|
||||
#include "DNA_defs.h"
|
||||
#include "DNA_meta_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_undo_system.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
|
||||
#include "ED_object.h"
|
||||
#include "ED_mball.h"
|
||||
#include "ED_util.h"
|
||||
|
||||
#include "WM_types.h"
|
||||
#include "WM_api.h"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Undo Conversion
|
||||
* \{ */
|
||||
|
||||
typedef struct UndoMBall {
|
||||
ListBase editelems;
|
||||
int lastelem_index;
|
||||
size_t undo_size;
|
||||
} UndoMBall;
|
||||
|
||||
/* free all MetaElems from ListBase */
|
||||
@ -58,11 +71,8 @@ static void freeMetaElemlist(ListBase *lb)
|
||||
}
|
||||
}
|
||||
|
||||
static void undoMball_to_editMball(void *umb_v, void *mb_v, void *UNUSED(obdata))
|
||||
static void undomball_to_editmball(UndoMBall *umb, MetaBall *mb)
|
||||
{
|
||||
MetaBall *mb = mb_v;
|
||||
UndoMBall *umb = umb_v;
|
||||
|
||||
freeMetaElemlist(mb->editelems);
|
||||
mb->lastelem = NULL;
|
||||
|
||||
@ -75,16 +85,13 @@ static void undoMball_to_editMball(void *umb_v, void *mb_v, void *UNUSED(obdata)
|
||||
mb->lastelem = ml_edit;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void *editMball_to_undoMball(void *mb_v, void *UNUSED(obdata))
|
||||
static void *editmball_from_undomball(UndoMBall *umb, MetaBall *mb)
|
||||
{
|
||||
MetaBall *mb = mb_v;
|
||||
UndoMBall *umb;
|
||||
BLI_assert(BLI_array_is_zeroed(umb, 1));
|
||||
|
||||
/* allocate memory for undo ListBase */
|
||||
umb = MEM_callocN(sizeof(UndoMBall), __func__);
|
||||
umb->lastelem_index = -1;
|
||||
|
||||
/* copy contents of current ListBase to the undo ListBase */
|
||||
@ -95,37 +102,99 @@ static void *editMball_to_undoMball(void *mb_v, void *UNUSED(obdata))
|
||||
if (ml_edit == mb->lastelem) {
|
||||
umb->lastelem_index = index;
|
||||
}
|
||||
umb->undo_size += sizeof(MetaElem);
|
||||
}
|
||||
|
||||
return umb;
|
||||
}
|
||||
|
||||
/* free undo ListBase of MetaElems */
|
||||
static void free_undoMball(void *umb_v)
|
||||
static void undomball_free_data(UndoMBall *umb)
|
||||
{
|
||||
UndoMBall *umb = umb_v;
|
||||
|
||||
freeMetaElemlist(&umb->editelems);
|
||||
MEM_freeN(umb);
|
||||
}
|
||||
|
||||
static MetaBall *metaball_get_obdata(Object *ob)
|
||||
static Object *editmball_object_from_context(bContext *C)
|
||||
{
|
||||
if (ob && ob->type == OB_MBALL) {
|
||||
return ob->data;
|
||||
Object *obedit = CTX_data_edit_object(C);
|
||||
if (obedit && obedit->type == OB_MBALL) {
|
||||
MetaBall *mb = obedit->data;
|
||||
if (mb->editelems != NULL) {
|
||||
return obedit;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
static void *get_data(bContext *C)
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Implements ED Undo System
|
||||
* \{ */
|
||||
|
||||
typedef struct MBallUndoStep {
|
||||
UndoStep step;
|
||||
/* note: will split out into list for multi-object-editmode. */
|
||||
UndoRefID_Object obedit_ref;
|
||||
UndoMBall data;
|
||||
} MBallUndoStep;
|
||||
|
||||
static bool mball_undosys_poll(bContext *C)
|
||||
{
|
||||
Object *obedit = CTX_data_edit_object(C);
|
||||
return metaball_get_obdata(obedit);
|
||||
return editmball_object_from_context(C) != NULL;
|
||||
}
|
||||
|
||||
/* this is undo system for MetaBalls */
|
||||
void undo_push_mball(bContext *C, const char *name)
|
||||
static bool mball_undosys_step_encode(struct bContext *C, UndoStep *us_p)
|
||||
{
|
||||
undo_editmode_push(C, name, get_data, free_undoMball, undoMball_to_editMball, editMball_to_undoMball, NULL);
|
||||
MBallUndoStep *us = (MBallUndoStep *)us_p;
|
||||
us->obedit_ref.ptr = editmball_object_from_context(C);
|
||||
MetaBall *mb = us->obedit_ref.ptr->data;
|
||||
editmball_from_undomball(&us->data, mb);
|
||||
us->step.data_size = us->data.undo_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void mball_undosys_step_decode(struct bContext *C, UndoStep *us_p, int UNUSED(dir))
|
||||
{
|
||||
ED_object_mode_set(C, OB_MODE_EDIT);
|
||||
|
||||
MBallUndoStep *us = (MBallUndoStep *)us_p;
|
||||
Object *obedit = us->obedit_ref.ptr;
|
||||
MetaBall *mb = obedit->data;
|
||||
undomball_to_editmball(&us->data, mb);
|
||||
DEG_id_tag_update(&obedit->id, OB_RECALC_DATA);
|
||||
WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL);
|
||||
}
|
||||
|
||||
static void mball_undosys_step_free(UndoStep *us_p)
|
||||
{
|
||||
MBallUndoStep *us = (MBallUndoStep *)us_p;
|
||||
undomball_free_data(&us->data);
|
||||
}
|
||||
|
||||
static void mball_undosys_foreach_ID_ref(
|
||||
UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
|
||||
{
|
||||
MBallUndoStep *us = (MBallUndoStep *)us_p;
|
||||
foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->obedit_ref));
|
||||
}
|
||||
|
||||
/* Export for ED_undo_sys. */
|
||||
void ED_mball_undosys_type(UndoType *ut)
|
||||
{
|
||||
ut->name = "Edit MBall";
|
||||
ut->poll = mball_undosys_poll;
|
||||
ut->step_encode = mball_undosys_step_encode;
|
||||
ut->step_decode = mball_undosys_step_decode;
|
||||
ut->step_free = mball_undosys_step_free;
|
||||
|
||||
ut->step_foreach_ID_ref = mball_undosys_foreach_ID_ref;
|
||||
|
||||
ut->mode = BKE_UNDOTYPE_MODE_STORE;
|
||||
ut->use_context = true;
|
||||
|
||||
ut->step_size = sizeof(MBallUndoStep);
|
||||
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -152,6 +152,48 @@ void ED_object_mode_toggle(bContext *C, eObjectMode mode)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Wrapper for operator */
|
||||
void ED_object_mode_set(bContext *C, eObjectMode mode)
|
||||
{
|
||||
#if 0
|
||||
wmOperatorType *ot = WM_operatortype_find("OBJECT_OT_mode_set", false);
|
||||
PointerRNA ptr;
|
||||
|
||||
WM_operator_properties_create_ptr(&ptr, ot);
|
||||
RNA_enum_set(&ptr, "mode", mode);
|
||||
RNA_boolean_set(&ptr, "toggle", false);
|
||||
WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &ptr);
|
||||
WM_operator_properties_free(&ptr);
|
||||
#else
|
||||
const WorkSpace *workspace = CTX_wm_workspace(C);
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
if (ob == NULL) {
|
||||
return;
|
||||
}
|
||||
if (workspace->object_mode == mode) {
|
||||
/* pass */
|
||||
}
|
||||
else if (workspace->object_mode != OB_MODE_OBJECT) {
|
||||
if (ob && (workspace->object_mode & mode) == 0) {
|
||||
/* needed so we don't do undo pushes. */
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
wm->op_undo_depth++;
|
||||
ED_object_mode_toggle(C, mode);
|
||||
wm->op_undo_depth--;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* needed so we don't do undo pushes. */
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
wm->op_undo_depth++;
|
||||
ED_object_mode_toggle(C, workspace->object_mode);
|
||||
wm->op_undo_depth--;
|
||||
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
@ -101,21 +101,20 @@ int PE_poll(bContext *C)
|
||||
if (!scene || !view_layer || !ob || !(workspace->object_mode & OB_MODE_PARTICLE_EDIT)) {
|
||||
return 0;
|
||||
}
|
||||
return (PE_get_current(scene, view_layer, ob) != NULL);
|
||||
return (PE_get_current(scene, ob) != NULL);
|
||||
}
|
||||
|
||||
int PE_hair_poll(bContext *C)
|
||||
{
|
||||
const WorkSpace *workspace = CTX_wm_workspace(C);
|
||||
Scene *scene= CTX_data_scene(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
Object *ob= CTX_data_active_object(C);
|
||||
PTCacheEdit *edit;
|
||||
|
||||
if (!scene || !ob || !(workspace->object_mode & OB_MODE_PARTICLE_EDIT)) {
|
||||
return 0;
|
||||
}
|
||||
edit= PE_get_current(scene, view_layer, ob);
|
||||
edit= PE_get_current(scene, ob);
|
||||
|
||||
return (edit && edit->psys);
|
||||
}
|
||||
@ -136,8 +135,6 @@ void PE_free_ptcache_edit(PTCacheEdit *edit)
|
||||
|
||||
if (edit==0) return;
|
||||
|
||||
PTCacheUndo_clear(edit);
|
||||
|
||||
if (edit->points) {
|
||||
LOOP_POINTS {
|
||||
if (point->keys)
|
||||
@ -201,7 +198,7 @@ static float pe_brush_size_get(const Scene *UNUSED(scene), ParticleBrushData *br
|
||||
* note: this function runs on poll, therefor it can runs many times a second
|
||||
* keep it fast! */
|
||||
static PTCacheEdit *pe_get_current(
|
||||
const EvaluationContext *eval_ctx, Scene *scene, ViewLayer *view_layer, Object *ob, int create)
|
||||
const EvaluationContext *eval_ctx, Scene *scene, Object *ob, int create)
|
||||
{
|
||||
ParticleEditSettings *pset= PE_settings(scene);
|
||||
PTCacheEdit *edit = NULL;
|
||||
@ -212,7 +209,6 @@ static PTCacheEdit *pe_get_current(
|
||||
return NULL;
|
||||
|
||||
pset->scene = scene;
|
||||
pset->view_layer = view_layer;
|
||||
pset->object = ob;
|
||||
|
||||
BKE_ptcache_ids_from_object(&pidlist, ob, NULL, 0);
|
||||
@ -241,18 +237,18 @@ static PTCacheEdit *pe_get_current(
|
||||
if (psys->part && psys->part->type == PART_HAIR) {
|
||||
if (psys->flag & PSYS_HAIR_DYNAMICS && psys->pointcache->flag & PTCACHE_BAKED) {
|
||||
if (create && !psys->pointcache->edit)
|
||||
PE_create_particle_edit(eval_ctx, scene, view_layer, ob, pid->cache, NULL);
|
||||
PE_create_particle_edit(eval_ctx, scene, ob, pid->cache, NULL);
|
||||
edit = pid->cache->edit;
|
||||
}
|
||||
else {
|
||||
if (create && !psys->edit && psys->flag & PSYS_HAIR_DONE)
|
||||
PE_create_particle_edit(eval_ctx, scene, view_layer, ob, NULL, psys);
|
||||
PE_create_particle_edit(eval_ctx, scene, ob, NULL, psys);
|
||||
edit = psys->edit;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (create && pid->cache->flag & PTCACHE_BAKED && !pid->cache->edit)
|
||||
PE_create_particle_edit(eval_ctx, scene, view_layer, ob, pid->cache, psys);
|
||||
PE_create_particle_edit(eval_ctx, scene, ob, pid->cache, psys);
|
||||
edit = pid->cache->edit;
|
||||
}
|
||||
|
||||
@ -263,7 +259,7 @@ static PTCacheEdit *pe_get_current(
|
||||
if (create && pid->cache->flag & PTCACHE_BAKED && !pid->cache->edit) {
|
||||
pset->flag |= PE_FADE_TIME;
|
||||
// NICE TO HAVE but doesn't work: pset->brushtype = PE_BRUSH_COMB;
|
||||
PE_create_particle_edit(eval_ctx, scene, view_layer, ob, pid->cache, NULL);
|
||||
PE_create_particle_edit(eval_ctx, scene, ob, pid->cache, NULL);
|
||||
}
|
||||
edit = pid->cache->edit;
|
||||
break;
|
||||
@ -272,7 +268,7 @@ static PTCacheEdit *pe_get_current(
|
||||
if (create && pid->cache->flag & PTCACHE_BAKED && !pid->cache->edit) {
|
||||
pset->flag |= PE_FADE_TIME;
|
||||
// NICE TO HAVE but doesn't work: pset->brushtype = PE_BRUSH_COMB;
|
||||
PE_create_particle_edit(eval_ctx, scene, view_layer, ob, pid->cache, NULL);
|
||||
PE_create_particle_edit(eval_ctx, scene, ob, pid->cache, NULL);
|
||||
}
|
||||
edit = pid->cache->edit;
|
||||
break;
|
||||
@ -287,14 +283,14 @@ static PTCacheEdit *pe_get_current(
|
||||
return edit;
|
||||
}
|
||||
|
||||
PTCacheEdit *PE_get_current(Scene *scene, ViewLayer *view_layer, Object *ob)
|
||||
PTCacheEdit *PE_get_current(Scene *scene, Object *ob)
|
||||
{
|
||||
return pe_get_current(NULL, scene, view_layer, ob, 0);
|
||||
return pe_get_current(NULL, scene, ob, 0);
|
||||
}
|
||||
|
||||
PTCacheEdit *PE_create_current(const EvaluationContext *eval_ctx, Scene *scene, Object *ob)
|
||||
{
|
||||
return pe_get_current(eval_ctx, scene, eval_ctx->view_layer, ob, 1);
|
||||
return pe_get_current(eval_ctx, scene, ob, 1);
|
||||
}
|
||||
|
||||
void PE_current_changed(const EvaluationContext *eval_ctx, Scene *scene, Object *ob)
|
||||
@ -385,7 +381,7 @@ static void PE_set_data(bContext *C, PEData *data)
|
||||
data->view_layer = CTX_data_view_layer(C);
|
||||
data->ob = CTX_data_active_object(C);
|
||||
CTX_data_eval_ctx(C, &data->eval_ctx);
|
||||
data->edit = PE_get_current(data->scene, data->view_layer, data->ob);
|
||||
data->edit = PE_get_current(data->scene, data->ob);
|
||||
}
|
||||
|
||||
static void PE_set_view3d_data(bContext *C, PEData *data)
|
||||
@ -1146,7 +1142,7 @@ void recalc_emitter_field(Object *ob, ParticleSystem *psys)
|
||||
|
||||
static void PE_update_selection(const EvaluationContext *eval_ctx, Scene *scene, Object *ob, int useflag)
|
||||
{
|
||||
PTCacheEdit *edit = PE_get_current(scene, eval_ctx->view_layer, ob);
|
||||
PTCacheEdit *edit = PE_get_current(scene, ob);
|
||||
HairKey *hkey;
|
||||
POINT_P; KEY_K;
|
||||
|
||||
@ -1256,7 +1252,7 @@ void PE_update_object(const EvaluationContext *eval_ctx, Scene *scene, Object *o
|
||||
/* use this to do partial particle updates, not usable when adding or
|
||||
* removing, then a full redo is necessary and calling this may crash */
|
||||
ParticleEditSettings *pset= PE_settings(scene);
|
||||
PTCacheEdit *edit = PE_get_current(scene, eval_ctx->view_layer, ob);
|
||||
PTCacheEdit *edit = PE_get_current(scene, ob);
|
||||
POINT_P;
|
||||
|
||||
if (!edit)
|
||||
@ -1396,7 +1392,7 @@ static int pe_select_all_exec(bContext *C, wmOperator *op)
|
||||
EvaluationContext eval_ctx;
|
||||
CTX_data_eval_ctx(C, &eval_ctx);
|
||||
Object *ob= CTX_data_active_object(C);
|
||||
PTCacheEdit *edit= PE_get_current(scene, eval_ctx.view_layer, ob);
|
||||
PTCacheEdit *edit= PE_get_current(scene, ob);
|
||||
POINT_P; KEY_K;
|
||||
int action = RNA_enum_get(op->ptr, "action");
|
||||
|
||||
@ -1448,9 +1444,8 @@ int PE_mouse_particles(bContext *C, const int mval[2], bool extend, bool deselec
|
||||
{
|
||||
PEData data;
|
||||
Scene *scene= CTX_data_scene(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
Object *ob= CTX_data_active_object(C);
|
||||
PTCacheEdit *edit= PE_get_current(scene, view_layer, ob);
|
||||
PTCacheEdit *edit= PE_get_current(scene, ob);
|
||||
POINT_P; KEY_K;
|
||||
|
||||
if (!PE_start_edit(edit))
|
||||
@ -1642,7 +1637,7 @@ static int select_random_exec(bContext *C, wmOperator *op)
|
||||
data.select_action = SEL_SELECT;
|
||||
scene = CTX_data_scene(C);
|
||||
ob = CTX_data_active_object(C);
|
||||
edit = PE_get_current(scene, data.eval_ctx.view_layer, ob);
|
||||
edit = PE_get_current(scene, ob);
|
||||
|
||||
rng = BLI_rng_new_srandom(seed);
|
||||
|
||||
@ -1759,9 +1754,8 @@ void PE_deselect_all_visible(PTCacheEdit *edit)
|
||||
int PE_border_select(bContext *C, rcti *rect, bool select, bool extend)
|
||||
{
|
||||
Scene *scene= CTX_data_scene(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
Object *ob= CTX_data_active_object(C);
|
||||
PTCacheEdit *edit= PE_get_current(scene, view_layer, ob);
|
||||
PTCacheEdit *edit= PE_get_current(scene, ob);
|
||||
PEData data;
|
||||
|
||||
if (!PE_start_edit(edit))
|
||||
@ -1787,9 +1781,8 @@ int PE_border_select(bContext *C, rcti *rect, bool select, bool extend)
|
||||
int PE_circle_select(bContext *C, int selecting, const int mval[2], float rad)
|
||||
{
|
||||
Scene *scene= CTX_data_scene(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
Object *ob= CTX_data_active_object(C);
|
||||
PTCacheEdit *edit= PE_get_current(scene, view_layer, ob);
|
||||
PTCacheEdit *edit= PE_get_current(scene, ob);
|
||||
PEData data;
|
||||
|
||||
if (!PE_start_edit(edit))
|
||||
@ -1813,11 +1806,10 @@ int PE_circle_select(bContext *C, int selecting, const int mval[2], float rad)
|
||||
int PE_lasso_select(bContext *C, const int mcords[][2], const short moves, bool extend, bool select)
|
||||
{
|
||||
Scene *scene= CTX_data_scene(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
Object *ob= CTX_data_active_object(C);
|
||||
ARegion *ar= CTX_wm_region(C);
|
||||
ParticleEditSettings *pset= PE_settings(scene);
|
||||
PTCacheEdit *edit = PE_get_current(scene, view_layer, ob);
|
||||
PTCacheEdit *edit = PE_get_current(scene, ob);
|
||||
ParticleSystem *psys = edit->psys;
|
||||
ParticleSystemModifierData *psmd = psys_get_modifier(ob, psys);
|
||||
POINT_P; KEY_K;
|
||||
@ -1906,7 +1898,7 @@ static int hide_exec(bContext *C, wmOperator *op)
|
||||
EvaluationContext eval_ctx;
|
||||
CTX_data_eval_ctx(C, &eval_ctx);
|
||||
|
||||
PTCacheEdit *edit= PE_get_current(scene, eval_ctx.view_layer, ob);
|
||||
PTCacheEdit *edit= PE_get_current(scene, ob);
|
||||
POINT_P; KEY_K;
|
||||
|
||||
|
||||
@ -1961,7 +1953,7 @@ static int reveal_exec(bContext *C, wmOperator *op)
|
||||
Scene *scene= CTX_data_scene(C);
|
||||
EvaluationContext eval_ctx;
|
||||
CTX_data_eval_ctx(C, &eval_ctx);
|
||||
PTCacheEdit *edit= PE_get_current(scene, eval_ctx.view_layer, ob);
|
||||
PTCacheEdit *edit= PE_get_current(scene, ob);
|
||||
const bool select = RNA_boolean_get(op->ptr, "select");
|
||||
POINT_P; KEY_K;
|
||||
|
||||
@ -2228,9 +2220,9 @@ void PARTICLE_OT_rekey(wmOperatorType *ot)
|
||||
RNA_def_int(ot->srna, "keys_number", 2, 2, INT_MAX, "Number of Keys", "", 2, 100);
|
||||
}
|
||||
|
||||
static void rekey_particle_to_time(const bContext *C, Scene *scene, ViewLayer *view_layer, Object *ob, int pa_index, float path_time)
|
||||
static void rekey_particle_to_time(const bContext *C, Scene *scene, Object *ob, int pa_index, float path_time)
|
||||
{
|
||||
PTCacheEdit *edit= PE_get_current(scene, view_layer, ob);
|
||||
PTCacheEdit *edit= PE_get_current(scene, ob);
|
||||
ParticleSystem *psys;
|
||||
ParticleSimulationData sim = {0};
|
||||
ParticleData *pa;
|
||||
@ -2570,9 +2562,8 @@ void PARTICLE_OT_subdivide(wmOperatorType *ot)
|
||||
static int remove_doubles_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Scene *scene= CTX_data_scene(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
Object *ob= CTX_data_active_object(C);
|
||||
PTCacheEdit *edit= PE_get_current(scene, view_layer, ob);
|
||||
PTCacheEdit *edit= PE_get_current(scene, ob);
|
||||
ParticleSystem *psys = edit->psys;
|
||||
ParticleSystemModifierData *psmd;
|
||||
KDTree *tree;
|
||||
@ -2663,10 +2654,9 @@ void PARTICLE_OT_remove_doubles(wmOperatorType *ot)
|
||||
static int weight_set_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Scene *scene= CTX_data_scene(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
ParticleEditSettings *pset= PE_settings(scene);
|
||||
Object *ob= CTX_data_active_object(C);
|
||||
PTCacheEdit *edit= PE_get_current(scene, view_layer, ob);
|
||||
PTCacheEdit *edit= PE_get_current(scene, ob);
|
||||
ParticleSystem *psys = edit->psys;
|
||||
POINT_P;
|
||||
KEY_K;
|
||||
@ -2824,11 +2814,11 @@ void PARTICLE_OT_delete(wmOperatorType *ot)
|
||||
/*************************** mirror operator **************************/
|
||||
|
||||
static void PE_mirror_x(
|
||||
Scene *scene, ViewLayer *view_layer, Object *ob, int tagged)
|
||||
Scene *scene, Object *ob, int tagged)
|
||||
{
|
||||
Mesh *me= (Mesh *)(ob->data);
|
||||
ParticleSystemModifierData *psmd;
|
||||
PTCacheEdit *edit = PE_get_current(scene, view_layer, ob);
|
||||
PTCacheEdit *edit = PE_get_current(scene, ob);
|
||||
ParticleSystem *psys = edit->psys;
|
||||
ParticleData *pa, *newpa, *new_pars;
|
||||
PTCacheEditPoint *newpoint, *new_points;
|
||||
@ -2975,11 +2965,10 @@ static void PE_mirror_x(
|
||||
static int mirror_exec(bContext *C, wmOperator *UNUSED(op))
|
||||
{
|
||||
Scene *scene= CTX_data_scene(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
Object *ob= CTX_data_active_object(C);
|
||||
PTCacheEdit *edit= PE_get_current(scene, view_layer, ob);
|
||||
PTCacheEdit *edit= PE_get_current(scene, ob);
|
||||
|
||||
PE_mirror_x(scene, view_layer, ob, 0);
|
||||
PE_mirror_x(scene, ob, 0);
|
||||
|
||||
update_world_cos(ob, edit);
|
||||
WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_EDITED, ob);
|
||||
@ -3116,7 +3105,7 @@ static void brush_cut(PEData *data, int pa_index)
|
||||
edit->points[pa_index].flag |= PEP_TAG;
|
||||
}
|
||||
else {
|
||||
rekey_particle_to_time(data->context, data->scene, data->view_layer, ob, pa_index, cut_time);
|
||||
rekey_particle_to_time(data->context, data->scene, ob, pa_index, cut_time);
|
||||
edit->points[pa_index].flag |= PEP_EDIT_RECALC;
|
||||
}
|
||||
}
|
||||
@ -3771,7 +3760,7 @@ static int brush_edit_init(bContext *C, wmOperator *op)
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
Object *ob= CTX_data_active_object(C);
|
||||
ParticleEditSettings *pset= PE_settings(scene);
|
||||
PTCacheEdit *edit= PE_get_current(scene, view_layer, ob);
|
||||
PTCacheEdit *edit= PE_get_current(scene, ob);
|
||||
ARegion *ar= CTX_wm_region(C);
|
||||
BrushEdit *bedit;
|
||||
float min[3], max[3];
|
||||
@ -3805,7 +3794,6 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
|
||||
{
|
||||
BrushEdit *bedit= op->customdata;
|
||||
Scene *scene= bedit->scene;
|
||||
ViewLayer *view_layer = bedit->view_layer;
|
||||
Object *ob= bedit->ob;
|
||||
PTCacheEdit *edit= bedit->edit;
|
||||
ParticleEditSettings *pset= PE_settings(scene);
|
||||
@ -4000,7 +3988,7 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr)
|
||||
|
||||
if (ELEM(pset->brushtype, PE_BRUSH_ADD, PE_BRUSH_CUT) && (added || removed)) {
|
||||
if (pset->brushtype == PE_BRUSH_ADD && pe_x_mirror(ob))
|
||||
PE_mirror_x(scene, view_layer, ob, 1);
|
||||
PE_mirror_x(scene, ob, 1);
|
||||
|
||||
update_world_cos(ob, edit);
|
||||
psys_free_path_cache(NULL, edit);
|
||||
@ -4223,7 +4211,7 @@ static void shape_cut(PEData *data, int pa_index)
|
||||
edit->points[pa_index].flag |= PEP_TAG;
|
||||
}
|
||||
else {
|
||||
rekey_particle_to_time(data->context, data->scene, data->view_layer, ob, pa_index, cut_time);
|
||||
rekey_particle_to_time(data->context, data->scene, ob, pa_index, cut_time);
|
||||
edit->points[pa_index].flag |= PEP_EDIT_RECALC;
|
||||
}
|
||||
}
|
||||
@ -4232,10 +4220,9 @@ static void shape_cut(PEData *data, int pa_index)
|
||||
static int shape_cut_exec(bContext *C, wmOperator *UNUSED(op))
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
ParticleEditSettings *pset = PE_settings(scene);
|
||||
PTCacheEdit *edit = PE_get_current(scene, view_layer, ob);
|
||||
PTCacheEdit *edit = PE_get_current(scene, ob);
|
||||
Object *shapeob = pset->shape_object;
|
||||
int selected = count_selected_keys(scene, edit);
|
||||
int lock_root = pset->flag & PE_LOCK_FIRST;
|
||||
@ -4310,7 +4297,7 @@ void PARTICLE_OT_shape_cut(wmOperatorType *ot)
|
||||
int PE_minmax(Scene *scene, ViewLayer *view_layer, float min[3], float max[3])
|
||||
{
|
||||
Object *ob= OBACT(view_layer);
|
||||
PTCacheEdit *edit= PE_get_current(scene, view_layer, ob);
|
||||
PTCacheEdit *edit= PE_get_current(scene, ob);
|
||||
ParticleSystem *psys;
|
||||
ParticleSystemModifierData *psmd = NULL;
|
||||
POINT_P; KEY_K;
|
||||
@ -4348,7 +4335,7 @@ int PE_minmax(Scene *scene, ViewLayer *view_layer, float min[3], float max[3])
|
||||
|
||||
/* initialize needed data for bake edit */
|
||||
void PE_create_particle_edit(
|
||||
const EvaluationContext *eval_ctx, Scene *scene, ViewLayer *view_layer, Object *ob, PointCache *cache, ParticleSystem *psys)
|
||||
const EvaluationContext *eval_ctx, Scene *scene, Object *ob, PointCache *cache, ParticleSystem *psys)
|
||||
{
|
||||
PTCacheEdit *edit;
|
||||
ParticleSystemModifierData *psmd = (psys) ? psys_get_modifier(ob, psys) : NULL;
|
||||
@ -4450,9 +4437,6 @@ void PE_create_particle_edit(
|
||||
if (psys && !cache)
|
||||
recalc_emitter_field(ob, psys);
|
||||
PE_update_object(eval_ctx, scene, ob, 1);
|
||||
|
||||
PTCacheUndo_clear(edit);
|
||||
PE_undo_push(scene, view_layer, "Original");
|
||||
}
|
||||
}
|
||||
|
||||
@ -4666,7 +4650,7 @@ static int unify_length_exec(bContext *C, wmOperator *UNUSED(op))
|
||||
EvaluationContext eval_ctx;
|
||||
CTX_data_eval_ctx(C, &eval_ctx);
|
||||
|
||||
PTCacheEdit *edit = PE_get_current(scene, eval_ctx.view_layer, ob);
|
||||
PTCacheEdit *edit = PE_get_current(scene, ob);
|
||||
float average_length = calculate_average_length(edit);
|
||||
|
||||
if (average_length == 0.0f) {
|
||||
|
@ -38,6 +38,7 @@
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
#include "DNA_windowmanager_types.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_string.h"
|
||||
@ -46,39 +47,30 @@
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_particle.h"
|
||||
#include "BKE_pointcache.h"
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_undo_system.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
|
||||
#include "ED_object.h"
|
||||
#include "ED_particle.h"
|
||||
#include "ED_physics.h"
|
||||
|
||||
#include "particle_edit_utildefines.h"
|
||||
|
||||
#include "physics_intern.h"
|
||||
|
||||
static void free_PTCacheUndo(PTCacheUndo *undo)
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Undo Conversion
|
||||
* \{ */
|
||||
|
||||
static void undoptcache_from_editcache(PTCacheUndo *undo, PTCacheEdit *edit)
|
||||
{
|
||||
PTCacheEditPoint *point;
|
||||
int i;
|
||||
|
||||
for (i=0, point=undo->points; i<undo->totpoint; i++, point++) {
|
||||
if (undo->particles && (undo->particles + i)->hair)
|
||||
MEM_freeN((undo->particles + i)->hair);
|
||||
if (point->keys)
|
||||
MEM_freeN(point->keys);
|
||||
}
|
||||
if (undo->points)
|
||||
MEM_freeN(undo->points);
|
||||
|
||||
if (undo->particles)
|
||||
MEM_freeN(undo->particles);
|
||||
|
||||
BKE_ptcache_free_mem(&undo->mem_cache);
|
||||
}
|
||||
|
||||
static void make_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
|
||||
{
|
||||
PTCacheEditPoint *point;
|
||||
int i;
|
||||
size_t mem_used_prev = MEM_get_memory_in_use();
|
||||
|
||||
undo->totpoint= edit->totpoint;
|
||||
|
||||
@ -87,8 +79,9 @@ static void make_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
|
||||
|
||||
pa= undo->particles= MEM_dupallocN(edit->psys->particles);
|
||||
|
||||
for (i=0; i<edit->totpoint; i++, pa++)
|
||||
for (i=0; i<edit->totpoint; i++, pa++) {
|
||||
pa->hair= MEM_dupallocN(pa->hair);
|
||||
}
|
||||
|
||||
undo->psys_flag = edit->psys->flag;
|
||||
}
|
||||
@ -99,8 +92,9 @@ static void make_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
|
||||
pm = undo->mem_cache.first;
|
||||
|
||||
for (; pm; pm=pm->next) {
|
||||
for (i=0; i<BPHYS_TOT_DATA; i++)
|
||||
for (i=0; i<BPHYS_TOT_DATA; i++) {
|
||||
pm->data[i] = MEM_dupallocN(pm->data[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,9 +105,13 @@ static void make_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
|
||||
point->keys= MEM_dupallocN(point->keys);
|
||||
/* no need to update edit key->co & key->time pointers here */
|
||||
}
|
||||
|
||||
size_t mem_used_curr = MEM_get_memory_in_use();
|
||||
|
||||
undo->undo_size = mem_used_prev < mem_used_curr ? mem_used_curr - mem_used_prev : sizeof(PTCacheUndo);
|
||||
}
|
||||
|
||||
static void get_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
|
||||
static void undoptcache_to_editcache(PTCacheUndo *undo, PTCacheEdit *edit)
|
||||
{
|
||||
ParticleSystem *psys = edit->psys;
|
||||
ParticleData *pa;
|
||||
@ -121,16 +119,20 @@ static void get_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
|
||||
POINT_P; KEY_K;
|
||||
|
||||
LOOP_POINTS {
|
||||
if (psys && psys->particles[p].hair)
|
||||
if (psys && psys->particles[p].hair) {
|
||||
MEM_freeN(psys->particles[p].hair);
|
||||
}
|
||||
|
||||
if (point->keys)
|
||||
if (point->keys) {
|
||||
MEM_freeN(point->keys);
|
||||
}
|
||||
}
|
||||
if (psys && psys->particles)
|
||||
if (psys && psys->particles) {
|
||||
MEM_freeN(psys->particles);
|
||||
if (edit->points)
|
||||
}
|
||||
if (edit->points) {
|
||||
MEM_freeN(edit->points);
|
||||
}
|
||||
if (edit->mirror_cache) {
|
||||
MEM_freeN(edit->mirror_cache);
|
||||
edit->mirror_cache= NULL;
|
||||
@ -172,9 +174,9 @@ static void get_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
|
||||
pm = edit->pid.cache->mem_cache.first;
|
||||
|
||||
for (; pm; pm=pm->next) {
|
||||
for (i=0; i<BPHYS_TOT_DATA; i++)
|
||||
for (i = 0; i < BPHYS_TOT_DATA; i++) {
|
||||
pm->data[i] = MEM_dupallocN(pm->data[i]);
|
||||
|
||||
}
|
||||
BKE_ptcache_mem_pointers_init(pm);
|
||||
|
||||
LOOP_POINTS {
|
||||
@ -192,150 +194,110 @@ static void get_PTCacheUndo(PTCacheEdit *edit, PTCacheUndo *undo)
|
||||
}
|
||||
}
|
||||
|
||||
void PE_undo_push(Scene *scene, ViewLayer *view_layer, const char *str)
|
||||
static void undoptcache_free_data(PTCacheUndo *undo)
|
||||
{
|
||||
PTCacheEdit *edit= PE_get_current(scene, view_layer, OBACT(view_layer));
|
||||
PTCacheUndo *undo;
|
||||
int nr;
|
||||
PTCacheEditPoint *point;
|
||||
int i;
|
||||
|
||||
if (!edit) return;
|
||||
|
||||
/* remove all undos after (also when curundo==NULL) */
|
||||
while (edit->undo.last != edit->curundo) {
|
||||
undo= edit->undo.last;
|
||||
BLI_remlink(&edit->undo, undo);
|
||||
free_PTCacheUndo(undo);
|
||||
MEM_freeN(undo);
|
||||
}
|
||||
|
||||
/* make new */
|
||||
edit->curundo= undo= MEM_callocN(sizeof(PTCacheUndo), "particle undo file");
|
||||
BLI_strncpy(undo->name, str, sizeof(undo->name));
|
||||
BLI_addtail(&edit->undo, undo);
|
||||
|
||||
/* and limit amount to the maximum */
|
||||
nr= 0;
|
||||
undo= edit->undo.last;
|
||||
while (undo) {
|
||||
nr++;
|
||||
if (nr==U.undosteps) break;
|
||||
undo= undo->prev;
|
||||
}
|
||||
if (undo) {
|
||||
while (edit->undo.first != undo) {
|
||||
PTCacheUndo *first= edit->undo.first;
|
||||
BLI_remlink(&edit->undo, first);
|
||||
free_PTCacheUndo(first);
|
||||
MEM_freeN(first);
|
||||
for (i = 0, point=undo->points; i < undo->totpoint; i++, point++) {
|
||||
if (undo->particles && (undo->particles + i)->hair) {
|
||||
MEM_freeN((undo->particles + i)->hair);
|
||||
}
|
||||
if (point->keys) {
|
||||
MEM_freeN(point->keys);
|
||||
}
|
||||
}
|
||||
|
||||
/* copy */
|
||||
make_PTCacheUndo(edit, edit->curundo);
|
||||
if (undo->points) {
|
||||
MEM_freeN(undo->points);
|
||||
}
|
||||
if (undo->particles) {
|
||||
MEM_freeN(undo->particles);
|
||||
}
|
||||
BKE_ptcache_free_mem(&undo->mem_cache);
|
||||
}
|
||||
|
||||
void PE_undo_step(Scene *scene, ViewLayer *view_layer, int step)
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Implements ED Undo System
|
||||
* \{ */
|
||||
|
||||
typedef struct ParticleUndoStep {
|
||||
UndoStep step;
|
||||
UndoRefID_Scene scene_ref;
|
||||
UndoRefID_Object object_ref;
|
||||
PTCacheUndo data;
|
||||
} ParticleUndoStep;
|
||||
|
||||
static bool particle_undosys_poll(struct bContext *C)
|
||||
{
|
||||
PTCacheEdit *edit= PE_get_current(scene, view_layer, OBACT(view_layer));
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
Object *ob = OBACT(view_layer);
|
||||
PTCacheEdit *edit = PE_get_current(scene, ob);
|
||||
|
||||
if (!edit) return;
|
||||
return (edit != NULL);
|
||||
}
|
||||
|
||||
if (step==0) {
|
||||
get_PTCacheUndo(edit, edit->curundo);
|
||||
}
|
||||
else if (step==1) {
|
||||
static bool particle_undosys_step_encode(struct bContext *C, UndoStep *us_p)
|
||||
{
|
||||
ParticleUndoStep *us = (ParticleUndoStep *)us_p;
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
us->scene_ref.ptr = CTX_data_scene(C);
|
||||
us->object_ref.ptr = OBACT(view_layer);
|
||||
PTCacheEdit *edit = PE_get_current(us->scene_ref.ptr, us->object_ref.ptr);
|
||||
undoptcache_from_editcache(&us->data, edit);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (edit->curundo==NULL || edit->curundo->prev==NULL) {
|
||||
/* pass */
|
||||
}
|
||||
else {
|
||||
if (G.debug & G_DEBUG) printf("undo %s\n", edit->curundo->name);
|
||||
edit->curundo= edit->curundo->prev;
|
||||
get_PTCacheUndo(edit, edit->curundo);
|
||||
}
|
||||
static void particle_undosys_step_decode(struct bContext *C, UndoStep *us_p, int UNUSED(dir))
|
||||
{
|
||||
/* TODO(campbell): undo_system: use low-level API to set mode. */
|
||||
ED_object_mode_set(C, OB_MODE_PARTICLE_EDIT);
|
||||
BLI_assert(particle_undosys_poll(C));
|
||||
|
||||
ParticleUndoStep *us = (ParticleUndoStep *)us_p;
|
||||
Scene *scene = us->scene_ref.ptr;
|
||||
Object *ob = us->object_ref.ptr;
|
||||
PTCacheEdit *edit = PE_get_current(scene, ob);
|
||||
if (edit) {
|
||||
undoptcache_to_editcache(&us->data, edit);
|
||||
DEG_id_tag_update(&ob->id, OB_RECALC_DATA);
|
||||
}
|
||||
else {
|
||||
/* curundo has to remain current situation! */
|
||||
|
||||
if (edit->curundo==NULL || edit->curundo->next==NULL) {
|
||||
/* pass */
|
||||
}
|
||||
else {
|
||||
get_PTCacheUndo(edit, edit->curundo->next);
|
||||
edit->curundo= edit->curundo->next;
|
||||
if (G.debug & G_DEBUG) printf("redo %s\n", edit->curundo->name);
|
||||
}
|
||||
BLI_assert(0);
|
||||
}
|
||||
|
||||
DEG_id_tag_update(&OBACT(view_layer)->id, OB_RECALC_DATA);
|
||||
}
|
||||
|
||||
bool PE_undo_is_valid(Scene *scene, ViewLayer *view_layer)
|
||||
static void particle_undosys_step_free(UndoStep *us_p)
|
||||
{
|
||||
PTCacheEdit *edit= PE_get_current(scene, view_layer, OBACT(view_layer));
|
||||
|
||||
if (edit) {
|
||||
return (edit->undo.last != edit->undo.first);
|
||||
}
|
||||
return 0;
|
||||
ParticleUndoStep *us = (ParticleUndoStep *)us_p;
|
||||
undoptcache_free_data(&us->data);
|
||||
}
|
||||
|
||||
void PTCacheUndo_clear(PTCacheEdit *edit)
|
||||
static void particle_undosys_foreach_ID_ref(
|
||||
UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
|
||||
{
|
||||
PTCacheUndo *undo;
|
||||
|
||||
if (edit==NULL) return;
|
||||
|
||||
undo= edit->undo.first;
|
||||
while (undo) {
|
||||
free_PTCacheUndo(undo);
|
||||
undo= undo->next;
|
||||
}
|
||||
BLI_freelistN(&edit->undo);
|
||||
edit->curundo= NULL;
|
||||
ParticleUndoStep *us = (ParticleUndoStep *)us_p;
|
||||
foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->scene_ref));
|
||||
foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->object_ref));
|
||||
}
|
||||
|
||||
void PE_undo(Scene *scene, ViewLayer *view_layer)
|
||||
/* Export for ED_undo_sys. */
|
||||
void ED_particle_undosys_type(UndoType *ut)
|
||||
{
|
||||
PE_undo_step(scene, view_layer, 1);
|
||||
ut->name = "Edit Particle";
|
||||
ut->poll = particle_undosys_poll;
|
||||
ut->step_encode = particle_undosys_step_encode;
|
||||
ut->step_decode = particle_undosys_step_decode;
|
||||
ut->step_free = particle_undosys_step_free;
|
||||
|
||||
ut->step_foreach_ID_ref = particle_undosys_foreach_ID_ref;
|
||||
|
||||
ut->mode = BKE_UNDOTYPE_MODE_STORE;
|
||||
ut->use_context = true;
|
||||
|
||||
ut->step_size = sizeof(ParticleUndoStep);
|
||||
}
|
||||
|
||||
void PE_redo(Scene *scene, ViewLayer *view_layer)
|
||||
{
|
||||
PE_undo_step(scene, view_layer, -1);
|
||||
}
|
||||
|
||||
void PE_undo_number(Scene *scene, ViewLayer *view_layer, int nr)
|
||||
{
|
||||
PTCacheEdit *edit= PE_get_current(scene, view_layer, OBACT(view_layer));
|
||||
PTCacheUndo *undo;
|
||||
int a=0;
|
||||
|
||||
for (undo= edit->undo.first; undo; undo= undo->next, a++) {
|
||||
if (a==nr) break;
|
||||
}
|
||||
edit->curundo= undo;
|
||||
PE_undo_step(scene, view_layer, 0);
|
||||
}
|
||||
|
||||
|
||||
/* get name of undo item, return null if no item with this index */
|
||||
/* if active pointer, set it to 1 if true */
|
||||
const char *PE_undo_get_name(Scene *scene, ViewLayer *view_layer, int nr, bool *r_active)
|
||||
{
|
||||
PTCacheEdit *edit= PE_get_current(scene, view_layer, OBACT(view_layer));
|
||||
PTCacheUndo *undo;
|
||||
|
||||
if (r_active) *r_active = false;
|
||||
|
||||
if (edit) {
|
||||
undo= BLI_findlink(&edit->undo, nr);
|
||||
if (undo) {
|
||||
if (r_active && (undo == edit->curundo)) {
|
||||
*r_active = true;
|
||||
}
|
||||
return undo->name;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
/** \} */
|
||||
|
@ -932,9 +932,6 @@ static void copy_particle_edit(
|
||||
edit->emitter_field = NULL;
|
||||
edit->emitter_cosnos = NULL;
|
||||
|
||||
BLI_listbase_clear(&edit->undo);
|
||||
edit->curundo = NULL;
|
||||
|
||||
edit->points = MEM_dupallocN(edit_from->points);
|
||||
pa = psys->particles;
|
||||
LOOP_POINTS {
|
||||
@ -963,9 +960,6 @@ static void copy_particle_edit(
|
||||
recalc_lengths(edit);
|
||||
recalc_emitter_field(ob, psys);
|
||||
PE_update_object(eval_ctx, scene, ob, true);
|
||||
|
||||
PTCacheUndo_clear(edit);
|
||||
PE_undo_push(scene, view_layer, "Original");
|
||||
}
|
||||
|
||||
static void remove_particle_systems_from_object(Object *ob_to)
|
||||
|
@ -48,7 +48,6 @@
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_customdata.h"
|
||||
#include "BKE_fluidsim.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_modifier.h"
|
||||
#include "BKE_object.h"
|
||||
@ -69,6 +68,8 @@
|
||||
/* enable/disable overall compilation */
|
||||
#ifdef WITH_MOD_FLUID
|
||||
|
||||
#include "BKE_global.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
|
@ -70,9 +70,8 @@ void PARTICLE_OT_edited_clear(struct wmOperatorType *ot);
|
||||
|
||||
void PARTICLE_OT_unify_length(struct wmOperatorType *ot);
|
||||
|
||||
void PTCacheUndo_clear(struct PTCacheEdit *edit);
|
||||
void PE_create_particle_edit(
|
||||
const struct EvaluationContext *eval_ctx, struct Scene *scene, struct ViewLayer *view_layer,
|
||||
const struct EvaluationContext *eval_ctx, struct Scene *scene,
|
||||
struct Object *ob, struct PointCache *cache, struct ParticleSystem *psys);
|
||||
void recalc_lengths(struct PTCacheEdit *edit);
|
||||
void recalc_emitter_field(struct Object *ob, struct ParticleSystem *psys);
|
||||
|
@ -38,7 +38,6 @@
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_group.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_report.h"
|
||||
|
@ -43,7 +43,6 @@
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_group.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_report.h"
|
||||
|
@ -65,6 +65,7 @@
|
||||
#include "BKE_sequencer.h"
|
||||
#include "BKE_screen.h"
|
||||
#include "BKE_scene.h"
|
||||
#include "BKE_undo_system.h"
|
||||
#include "BKE_workspace.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
@ -92,6 +93,7 @@
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
|
||||
#include "BLO_undofile.h"
|
||||
|
||||
#include "render_intern.h"
|
||||
|
||||
@ -901,7 +903,8 @@ static int screen_render_invoke(bContext *C, wmOperator *op, const wmEvent *even
|
||||
/* get main */
|
||||
if (G.debug_value == 101) {
|
||||
/* thread-safety experiment, copy main from the undo buffer */
|
||||
mainp = BKE_undo_get_main(&scene);
|
||||
struct MemFile *memfile = ED_undosys_stack_memfile_get_active(CTX_wm_manager(C)->undo_stack);
|
||||
mainp = BLO_memfile_main_get(memfile, CTX_data_main(C), &scene);
|
||||
}
|
||||
else
|
||||
mainp = CTX_data_main(C);
|
||||
|
@ -52,7 +52,6 @@ set(SRC
|
||||
paint_mask.c
|
||||
paint_ops.c
|
||||
paint_stroke.c
|
||||
paint_undo.c
|
||||
paint_utils.c
|
||||
paint_vertex.c
|
||||
paint_vertex_color_ops.c
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "DEG_depsgraph.h"
|
||||
|
||||
#include "ED_view3d.h"
|
||||
#include "ED_paint.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
@ -207,7 +208,7 @@ static void paintcurve_point_add(bContext *C, wmOperator *op, const int loc[2])
|
||||
br->paint_curve = pc = BKE_paint_curve_add(bmain, "PaintCurve");
|
||||
}
|
||||
|
||||
ED_paintcurve_undo_push(C, op, pc);
|
||||
ED_paintcurve_undo_push_begin(op->type->name);
|
||||
|
||||
pcp = MEM_mallocN((pc->tot_points + 1) * sizeof(PaintCurvePoint), "PaintCurvePoint");
|
||||
add_index = pc->add_index;
|
||||
@ -245,6 +246,8 @@ static void paintcurve_point_add(bContext *C, wmOperator *op, const int loc[2])
|
||||
pcp[add_index].bez.h1 = HD_ALIGN;
|
||||
}
|
||||
|
||||
ED_paintcurve_undo_push_end();
|
||||
|
||||
WM_paint_cursor_tag_redraw(window, ar);
|
||||
}
|
||||
|
||||
@ -306,7 +309,7 @@ static int paintcurve_delete_point_exec(bContext *C, wmOperator *op)
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
ED_paintcurve_undo_push(C, op, pc);
|
||||
ED_paintcurve_undo_push_begin(op->type->name);
|
||||
|
||||
#define DELETE_TAG 2
|
||||
|
||||
@ -346,6 +349,8 @@ static int paintcurve_delete_point_exec(bContext *C, wmOperator *op)
|
||||
|
||||
#undef DELETE_TAG
|
||||
|
||||
ED_paintcurve_undo_push_end();
|
||||
|
||||
WM_paint_cursor_tag_redraw(window, ar);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
@ -383,7 +388,7 @@ static bool paintcurve_point_select(bContext *C, wmOperator *op, const int loc[2
|
||||
if (!pc)
|
||||
return false;
|
||||
|
||||
ED_paintcurve_undo_push(C, op, pc);
|
||||
ED_paintcurve_undo_push_begin(op->type->name);
|
||||
|
||||
if (toggle) {
|
||||
PaintCurvePoint *pcp;
|
||||
@ -448,10 +453,14 @@ static bool paintcurve_point_select(bContext *C, wmOperator *op, const int loc[2
|
||||
}
|
||||
}
|
||||
|
||||
if (!pcp)
|
||||
if (!pcp) {
|
||||
ED_paintcurve_undo_push_end();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ED_paintcurve_undo_push_end();
|
||||
|
||||
WM_paint_cursor_tag_redraw(window, ar);
|
||||
|
||||
return true;
|
||||
@ -566,9 +575,6 @@ static int paintcurve_slide_invoke(bContext *C, wmOperator *op, const wmEvent *e
|
||||
psd->align = align;
|
||||
op->customdata = psd;
|
||||
|
||||
if (do_select)
|
||||
ED_paintcurve_undo_push(C, op, pc);
|
||||
|
||||
/* first, clear all selection from points */
|
||||
for (i = 0; i < pc->tot_points; i++)
|
||||
pc->points[i].bez.f1 = pc->points[i].bez.f3 = pc->points[i].bez.f2 = 0;
|
||||
@ -591,6 +597,8 @@ static int paintcurve_slide_modal(bContext *C, wmOperator *op, const wmEvent *ev
|
||||
|
||||
if (event->type == psd->event && event->val == KM_RELEASE) {
|
||||
MEM_freeN(psd);
|
||||
ED_paintcurve_undo_push_begin(op->type->name);
|
||||
ED_paintcurve_undo_push_end();
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
|
@ -30,9 +30,13 @@
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_array_utils.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_paint.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_undo_system.h"
|
||||
|
||||
#include "ED_paint.h"
|
||||
|
||||
@ -41,89 +45,125 @@
|
||||
|
||||
#include "paint_intern.h"
|
||||
|
||||
typedef struct UndoCurve {
|
||||
struct UndoImageTile *next, *prev;
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Undo Conversion
|
||||
* \{ */
|
||||
|
||||
typedef struct UndoCurve {
|
||||
PaintCurvePoint *points; /* points of curve */
|
||||
int tot_points;
|
||||
int active_point;
|
||||
|
||||
char idname[MAX_ID_NAME]; /* name instead of pointer*/
|
||||
int add_index;
|
||||
} UndoCurve;
|
||||
|
||||
static void paintcurve_undo_restore(bContext *C, ListBase *lb)
|
||||
static void undocurve_from_paintcurve(UndoCurve *uc, const PaintCurve *pc)
|
||||
{
|
||||
BLI_assert(BLI_array_is_zeroed(uc, 1));
|
||||
uc->points = MEM_dupallocN(pc->points);
|
||||
uc->tot_points = pc->tot_points;
|
||||
uc->add_index = pc->add_index;
|
||||
}
|
||||
|
||||
static void undocurve_to_paintcurve(const UndoCurve *uc, PaintCurve *pc)
|
||||
{
|
||||
MEM_SAFE_FREE(pc->points);
|
||||
pc->points = MEM_dupallocN(uc->points);
|
||||
pc->tot_points = uc->tot_points;
|
||||
pc->add_index = uc->add_index;
|
||||
}
|
||||
|
||||
static void undocurve_free_data(UndoCurve *uc)
|
||||
{
|
||||
MEM_SAFE_FREE(uc->points);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Implements ED Undo System
|
||||
* \{ */
|
||||
|
||||
typedef struct PaintCurveUndoStep {
|
||||
UndoStep step;
|
||||
PaintCurve *pc;
|
||||
UndoCurve data;
|
||||
} PaintCurveUndoStep;
|
||||
|
||||
static bool paintcurve_undosys_poll(bContext *C)
|
||||
{
|
||||
Paint *p = BKE_paint_get_active_from_context(C);
|
||||
UndoCurve *uc;
|
||||
PaintCurve *pc = NULL;
|
||||
|
||||
if (p->brush) {
|
||||
pc = p->brush->paint_curve;
|
||||
}
|
||||
|
||||
if (!pc) {
|
||||
return;
|
||||
}
|
||||
|
||||
uc = (UndoCurve *)lb->first;
|
||||
|
||||
if (STREQLEN(uc->idname, pc->id.name, BLI_strnlen(uc->idname, sizeof(uc->idname)))) {
|
||||
SWAP(PaintCurvePoint *, pc->points, uc->points);
|
||||
SWAP(int, pc->tot_points, uc->tot_points);
|
||||
SWAP(int, pc->add_index, uc->active_point);
|
||||
}
|
||||
return (p->brush && p->brush->paint_curve);
|
||||
}
|
||||
|
||||
static void paintcurve_undo_delete(ListBase *lb)
|
||||
static void paintcurve_undosys_step_encode_init(struct bContext *C, UndoStep *us_p)
|
||||
{
|
||||
UndoCurve *uc;
|
||||
uc = (UndoCurve *)lb->first;
|
||||
|
||||
if (uc->points)
|
||||
MEM_freeN(uc->points);
|
||||
uc->points = NULL;
|
||||
/* XXX, use to set the undo type only. */
|
||||
UNUSED_VARS(C, us_p);
|
||||
}
|
||||
|
||||
/**
|
||||
* \note This is called before executing steps (not after).
|
||||
*/
|
||||
void ED_paintcurve_undo_push(bContext *C, wmOperator *op, PaintCurve *pc)
|
||||
static bool paintcurve_undosys_step_encode(struct bContext *C, UndoStep *us_p)
|
||||
{
|
||||
ePaintMode mode = BKE_paintmode_get_active_from_context(C);
|
||||
ListBase *lb = NULL;
|
||||
int undo_stack_id;
|
||||
UndoCurve *uc;
|
||||
|
||||
switch (mode) {
|
||||
case ePaintTexture2D:
|
||||
case ePaintTextureProjective:
|
||||
undo_stack_id = UNDO_PAINT_IMAGE;
|
||||
break;
|
||||
|
||||
case ePaintSculpt:
|
||||
undo_stack_id = UNDO_PAINT_MESH;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* do nothing, undo is handled by global */
|
||||
return;
|
||||
Paint *p = BKE_paint_get_active_from_context(C);
|
||||
PaintCurve *pc = p ? (p->brush ? p->brush->paint_curve : NULL) : NULL;
|
||||
if (pc == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PaintCurveUndoStep *us = (PaintCurveUndoStep *)us_p;
|
||||
BLI_assert(us->step.data_size == 0);
|
||||
|
||||
ED_undo_paint_push_begin(undo_stack_id, op->type->name,
|
||||
paintcurve_undo_restore, paintcurve_undo_delete, NULL);
|
||||
lb = undo_paint_push_get_list(undo_stack_id);
|
||||
us->pc = pc;
|
||||
undocurve_from_paintcurve(&us->data, pc);
|
||||
|
||||
uc = MEM_callocN(sizeof(*uc), "Undo_curve");
|
||||
|
||||
lb->first = uc;
|
||||
|
||||
BLI_strncpy(uc->idname, pc->id.name, sizeof(uc->idname));
|
||||
uc->tot_points = pc->tot_points;
|
||||
uc->active_point = pc->add_index;
|
||||
uc->points = MEM_dupallocN(pc->points);
|
||||
|
||||
undo_paint_push_count_alloc(undo_stack_id, sizeof(*uc) + sizeof(*pc->points) * pc->tot_points);
|
||||
|
||||
ED_undo_paint_push_end(undo_stack_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void paintcurve_undosys_step_decode(struct bContext *UNUSED(C), UndoStep *us_p, int UNUSED(dir))
|
||||
{
|
||||
PaintCurveUndoStep *us = (PaintCurveUndoStep *)us_p;
|
||||
undocurve_to_paintcurve(&us->data, us->pc);
|
||||
}
|
||||
|
||||
static void paintcurve_undosys_step_free(UndoStep *us_p)
|
||||
{
|
||||
PaintCurveUndoStep *us = (PaintCurveUndoStep *)us_p;
|
||||
undocurve_free_data(&us->data);
|
||||
}
|
||||
|
||||
/* Export for ED_undo_sys. */
|
||||
void ED_paintcurve_undosys_type(UndoType *ut)
|
||||
{
|
||||
ut->name = "Paint Curve";
|
||||
/* don't poll for now */
|
||||
ut->poll = paintcurve_undosys_poll;
|
||||
ut->step_encode_init = paintcurve_undosys_step_encode_init;
|
||||
ut->step_encode = paintcurve_undosys_step_encode;
|
||||
ut->step_decode = paintcurve_undosys_step_decode;
|
||||
ut->step_free = paintcurve_undosys_step_free;
|
||||
|
||||
ut->mode = BKE_UNDOTYPE_MODE_STORE;
|
||||
ut->use_context = false;
|
||||
|
||||
ut->step_size = sizeof(PaintCurveUndoStep);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Utilities
|
||||
* \{ */
|
||||
|
||||
void ED_paintcurve_undo_push_begin(const char *name)
|
||||
{
|
||||
bContext *C = NULL; /* special case, we never read from this. */
|
||||
wmWindowManager *wm = G.main->wm.first;
|
||||
BKE_undosys_step_push_init_with_type(wm->undo_stack, C, name, BKE_UNDOSYS_TYPE_PAINTCURVE);
|
||||
}
|
||||
|
||||
void ED_paintcurve_undo_push_end(void)
|
||||
{
|
||||
wmWindowManager *wm = G.main->wm.first; /* XXX, avoids adding extra arg. */
|
||||
BKE_undosys_step_push(wm->undo_stack, NULL, NULL);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -57,6 +57,8 @@
|
||||
#include "BKE_material.h"
|
||||
#include "BKE_node.h"
|
||||
#include "BKE_paint.h"
|
||||
#include "BKE_undo_system.h"
|
||||
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
|
||||
@ -146,9 +148,11 @@ void ED_imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int
|
||||
|
||||
imapaint_region_tiles(ibuf, x, y, w, h, &tilex, &tiley, &tilew, &tileh);
|
||||
|
||||
ListBase *undo_tiles = ED_image_undo_get_tiles();
|
||||
|
||||
for (ty = tiley; ty <= tileh; ty++)
|
||||
for (tx = tilex; tx <= tilew; tx++)
|
||||
image_undo_push_tile(ima, ibuf, &tmpibuf, tx, ty, NULL, NULL, false, find_old);
|
||||
image_undo_push_tile(undo_tiles, ima, ibuf, &tmpibuf, tx, ty, NULL, NULL, false, find_old);
|
||||
|
||||
ibuf->userflags |= IB_BITMAPDIRTY;
|
||||
|
||||
@ -511,7 +515,8 @@ static void paint_stroke_update_step(bContext *C, struct PaintStroke *stroke, Po
|
||||
BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * alphafac));
|
||||
|
||||
if ((brush->flag & BRUSH_DRAG_DOT) || (brush->flag & BRUSH_ANCHORED)) {
|
||||
ED_image_undo_restore();
|
||||
UndoStack *ustack = CTX_wm_manager(C)->undo_stack;
|
||||
ED_image_undo_restore(ustack->step_init);
|
||||
}
|
||||
|
||||
if (pop->mode == PAINT_MODE_3D_PROJECT) {
|
||||
@ -1208,14 +1213,17 @@ void PAINT_OT_brush_colors_flip(wmOperatorType *ot)
|
||||
|
||||
void ED_imapaint_bucket_fill(struct bContext *C, float color[3], wmOperator *op)
|
||||
{
|
||||
wmWindowManager *wm = CTX_wm_manager(C);
|
||||
SpaceImage *sima = CTX_wm_space_image(C);
|
||||
Image *ima = sima->image;
|
||||
|
||||
BKE_undosys_step_push_init_with_type(wm->undo_stack, C, op->type->name, BKE_UNDOSYS_TYPE_IMAGE);
|
||||
|
||||
ED_image_undo_push_begin(op->type->name);
|
||||
|
||||
paint_2d_bucket_fill(C, color, NULL, NULL, NULL);
|
||||
|
||||
ED_undo_paint_push_end(UNDO_PAINT_IMAGE);
|
||||
BKE_undosys_step_push(wm->undo_stack, C, op->type->name);
|
||||
|
||||
DEG_id_tag_update(&ima->id, 0);
|
||||
}
|
||||
|
@ -1036,6 +1036,8 @@ static void paint_2d_do_making_brush(ImagePaintState *s,
|
||||
ImBuf tmpbuf;
|
||||
IMB_initImBuf(&tmpbuf, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, 0);
|
||||
|
||||
ListBase *undo_tiles = ED_image_undo_get_tiles();
|
||||
|
||||
for (int ty = tiley; ty <= tileh; ty++) {
|
||||
for (int tx = tilex; tx <= tilew; tx++) {
|
||||
/* retrieve original pixels + mask from undo buffer */
|
||||
@ -1044,9 +1046,9 @@ static void paint_2d_do_making_brush(ImagePaintState *s,
|
||||
int origy = region->desty - ty * IMAPAINT_TILE_SIZE;
|
||||
|
||||
if (s->canvas->rect_float)
|
||||
tmpbuf.rect_float = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask, false);
|
||||
tmpbuf.rect_float = image_undo_find_tile(undo_tiles, s->image, s->canvas, tx, ty, &mask, false);
|
||||
else
|
||||
tmpbuf.rect = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask, false);
|
||||
tmpbuf.rect = image_undo_find_tile(undo_tiles, s->image, s->canvas, tx, ty, &mask, false);
|
||||
|
||||
IMB_rectblend(s->canvas, &tmpbuf, frombuf, mask,
|
||||
curveb, texmaskb, mask_max,
|
||||
|
@ -1501,15 +1501,16 @@ static int project_paint_undo_subtiles(const TileInfo *tinf, int tx, int ty)
|
||||
|
||||
|
||||
if (generate_tile) {
|
||||
ListBase *undo_tiles = ED_image_undo_get_tiles();
|
||||
volatile void *undorect;
|
||||
if (tinf->masked) {
|
||||
undorect = image_undo_push_tile(
|
||||
pjIma->ima, pjIma->ibuf, tinf->tmpibuf,
|
||||
undo_tiles, pjIma->ima, pjIma->ibuf, tinf->tmpibuf,
|
||||
tx, ty, &pjIma->maskRect[tile_index], &pjIma->valid[tile_index], true, false);
|
||||
}
|
||||
else {
|
||||
undorect = image_undo_push_tile(
|
||||
pjIma->ima, pjIma->ibuf, tinf->tmpibuf,
|
||||
undo_tiles, pjIma->ima, pjIma->ibuf, tinf->tmpibuf,
|
||||
tx, ty, NULL, &pjIma->valid[tile_index], true, false);
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,11 @@
|
||||
#include "BLI_threads.h"
|
||||
|
||||
#include "DNA_image_types.h"
|
||||
#include "DNA_windowmanager_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_space_types.h"
|
||||
#include "DNA_workspace_types.h"
|
||||
|
||||
#include "IMB_imbuf.h"
|
||||
#include "IMB_imbuf_types.h"
|
||||
@ -36,6 +41,8 @@
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_image.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_undo_system.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
|
||||
@ -45,10 +52,14 @@
|
||||
|
||||
#include "paint_intern.h"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Undo Conversion
|
||||
* \{ */
|
||||
|
||||
typedef struct UndoImageTile {
|
||||
struct UndoImageTile *next, *prev;
|
||||
|
||||
char idname[MAX_ID_NAME]; /* name instead of pointer*/
|
||||
char idname[MAX_ID_NAME]; /* name instead of pointer */
|
||||
char ibufname[IMB_FILENAME_SIZE];
|
||||
|
||||
union {
|
||||
@ -65,6 +76,8 @@ typedef struct UndoImageTile {
|
||||
short source, use_float;
|
||||
char gen_type;
|
||||
bool valid;
|
||||
|
||||
size_t undo_size;
|
||||
} UndoImageTile;
|
||||
|
||||
/* this is a static resource for non-globality,
|
||||
@ -130,13 +143,14 @@ static void undo_copy_tile(UndoImageTile *tile, ImBuf *tmpibuf, ImBuf *ibuf, Cop
|
||||
}
|
||||
}
|
||||
|
||||
void *image_undo_find_tile(Image *ima, ImBuf *ibuf, int x_tile, int y_tile, unsigned short **mask, bool validate)
|
||||
void *image_undo_find_tile(
|
||||
ListBase *undo_tiles,
|
||||
Image *ima, ImBuf *ibuf, int x_tile, int y_tile, unsigned short **mask, bool validate)
|
||||
{
|
||||
ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE);
|
||||
UndoImageTile *tile;
|
||||
short use_float = ibuf->rect_float ? 1 : 0;
|
||||
|
||||
for (tile = lb->first; tile; tile = tile->next) {
|
||||
for (tile = undo_tiles->first; tile; tile = tile->next) {
|
||||
if (tile->x == x_tile && tile->y == y_tile && ima->gen_type == tile->gen_type && ima->source == tile->source) {
|
||||
if (tile->use_float == use_float) {
|
||||
if (STREQ(tile->idname, ima->id.name) && STREQ(tile->ibufname, ibuf->name)) {
|
||||
@ -162,10 +176,10 @@ void *image_undo_find_tile(Image *ima, ImBuf *ibuf, int x_tile, int y_tile, unsi
|
||||
}
|
||||
|
||||
void *image_undo_push_tile(
|
||||
ListBase *undo_tiles,
|
||||
Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, int y_tile,
|
||||
unsigned short **mask, bool **valid, bool proj, bool find_prev)
|
||||
{
|
||||
ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE);
|
||||
UndoImageTile *tile;
|
||||
int allocsize;
|
||||
short use_float = ibuf->rect_float ? 1 : 0;
|
||||
@ -175,7 +189,7 @@ void *image_undo_push_tile(
|
||||
|
||||
/* in projective painting we keep accounting of tiles, so if we need one pushed, just push! */
|
||||
if (find_prev) {
|
||||
data = image_undo_find_tile(ima, ibuf, x_tile, y_tile, mask, true);
|
||||
data = image_undo_find_tile(undo_tiles, ima, ibuf, x_tile, y_tile, mask, true);
|
||||
if (data) {
|
||||
return data;
|
||||
}
|
||||
@ -215,8 +229,7 @@ void *image_undo_push_tile(
|
||||
if (proj) {
|
||||
BLI_spin_lock(&undolock);
|
||||
}
|
||||
undo_paint_push_count_alloc(UNDO_PAINT_IMAGE, allocsize);
|
||||
BLI_addtail(lb, tile);
|
||||
BLI_addtail(undo_tiles, tile);
|
||||
|
||||
if (proj) {
|
||||
BLI_spin_unlock(&undolock);
|
||||
@ -226,10 +239,10 @@ void *image_undo_push_tile(
|
||||
|
||||
void image_undo_remove_masks(void)
|
||||
{
|
||||
ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE);
|
||||
ListBase *undo_tiles = ED_image_undo_get_tiles();
|
||||
UndoImageTile *tile;
|
||||
|
||||
for (tile = lb->first; tile; tile = tile->next) {
|
||||
for (tile = undo_tiles->first; tile; tile = tile->next) {
|
||||
if (tile->mask) {
|
||||
MEM_freeN(tile->mask);
|
||||
tile->mask = NULL;
|
||||
@ -347,50 +360,146 @@ static void image_undo_free_list(ListBase *lb)
|
||||
|
||||
void ED_image_undo_push_begin(const char *name)
|
||||
{
|
||||
ED_undo_paint_push_begin(UNDO_PAINT_IMAGE, name, image_undo_restore_list, image_undo_free_list, NULL);
|
||||
bContext *C = NULL; /* special case, we never read from this. */
|
||||
wmWindowManager *wm = G.main->wm.first;
|
||||
BKE_undosys_step_push_init_with_type(wm->undo_stack, C, name, BKE_UNDOSYS_TYPE_IMAGE);
|
||||
}
|
||||
|
||||
void ED_image_undo_push_end(void)
|
||||
{
|
||||
ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE);
|
||||
UndoImageTile *tile;
|
||||
int deallocsize = 0;
|
||||
int allocsize = IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE * 4;
|
||||
|
||||
/* first dispose of invalid tiles (may happen due to drag dot for instance) */
|
||||
for (tile = lb->first; tile;) {
|
||||
if (!tile->valid) {
|
||||
UndoImageTile *tmp_tile = tile->next;
|
||||
deallocsize += allocsize * ((tile->use_float) ? sizeof(float) : sizeof(char));
|
||||
MEM_freeN(tile->rect.pt);
|
||||
BLI_freelinkN(lb, tile);
|
||||
tile = tmp_tile;
|
||||
}
|
||||
else {
|
||||
tile = tile->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* don't forget to remove the size of deallocated tiles */
|
||||
undo_paint_push_count_alloc(UNDO_PAINT_IMAGE, -deallocsize);
|
||||
|
||||
ED_undo_paint_push_end(UNDO_PAINT_IMAGE);
|
||||
wmWindowManager *wm = G.main->wm.first; /* XXX, avoids adding extra arg. */
|
||||
BKE_undosys_step_push(wm->undo_stack, NULL, NULL);
|
||||
}
|
||||
|
||||
static void image_undo_invalidate(void)
|
||||
{
|
||||
UndoImageTile *tile;
|
||||
ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE);
|
||||
ListBase *lb = ED_image_undo_get_tiles();
|
||||
|
||||
for (tile = lb->first; tile; tile = tile->next) {
|
||||
tile->valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* restore painting image to previous state. Used for anchored and drag-dot style brushes*/
|
||||
void ED_image_undo_restore(void)
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Implements ED Undo System
|
||||
* \{ */
|
||||
|
||||
typedef struct ImageUndoStep {
|
||||
UndoStep step;
|
||||
ListBase tiles;
|
||||
} ImageUndoStep;
|
||||
|
||||
static bool image_undosys_poll(bContext *C)
|
||||
{
|
||||
ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE);
|
||||
const WorkSpace *workspace = CTX_wm_workspace(C);
|
||||
Object *obact = CTX_data_active_object(C);
|
||||
|
||||
ScrArea *sa = CTX_wm_area(C);
|
||||
if (sa && (sa->spacetype == SPACE_IMAGE)) {
|
||||
SpaceImage *sima = (SpaceImage *)sa->spacedata.first;
|
||||
if ((obact && (workspace->object_mode & OB_MODE_TEXTURE_PAINT)) || (sima->mode == SI_MODE_PAINT)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (sa && (sa->spacetype == SPACE_VIEW3D)) {
|
||||
if (obact && (workspace->object_mode & OB_MODE_TEXTURE_PAINT)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void image_undosys_step_encode_init(struct bContext *UNUSED(C), UndoStep *us_p)
|
||||
{
|
||||
ImageUndoStep *us = (ImageUndoStep *)us_p;
|
||||
/* dummy, memory is cleared anyway. */
|
||||
BLI_listbase_clear(&us->tiles);
|
||||
}
|
||||
|
||||
static bool image_undosys_step_encode(struct bContext *UNUSED(C), UndoStep *us_p)
|
||||
{
|
||||
/* dummy, encoding is done along the way by adding tiles
|
||||
* to the current 'ImageUndoStep' added by encode_init. */
|
||||
ImageUndoStep *us = (ImageUndoStep *)us_p;
|
||||
|
||||
BLI_assert(us->step.data_size == 0);
|
||||
|
||||
int allocsize = IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE * 4;
|
||||
|
||||
/* first dispose of invalid tiles (may happen due to drag dot for instance) */
|
||||
for (UndoImageTile *tile = us->tiles.first; tile;) {
|
||||
if (!tile->valid) {
|
||||
UndoImageTile *tmp_tile = tile->next;
|
||||
MEM_freeN(tile->rect.pt);
|
||||
BLI_freelinkN(&us->tiles, tile);
|
||||
tile = tmp_tile;
|
||||
}
|
||||
else {
|
||||
us->step.data_size += allocsize * ((tile->use_float) ? sizeof(float) : sizeof(char));
|
||||
tile = tile->next;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void image_undosys_step_decode(struct bContext *C, UndoStep *us_p, int UNUSED(dir))
|
||||
{
|
||||
ImageUndoStep *us = (ImageUndoStep *)us_p;
|
||||
image_undo_restore_list(C, &us->tiles);
|
||||
}
|
||||
|
||||
static void image_undosys_step_free(UndoStep *us_p)
|
||||
{
|
||||
ImageUndoStep *us = (ImageUndoStep *)us_p;
|
||||
image_undo_free_list(&us->tiles);
|
||||
}
|
||||
|
||||
/* Export for ED_undo_sys. */
|
||||
void ED_image_undosys_type(UndoType *ut)
|
||||
{
|
||||
ut->name = "Image";
|
||||
ut->poll = image_undosys_poll;
|
||||
ut->step_encode_init = image_undosys_step_encode_init;
|
||||
ut->step_encode = image_undosys_step_encode;
|
||||
ut->step_decode = image_undosys_step_decode;
|
||||
ut->step_free = image_undosys_step_free;
|
||||
|
||||
ut->mode = BKE_UNDOTYPE_MODE_ACCUMULATE;
|
||||
ut->use_context = true;
|
||||
|
||||
ut->step_size = sizeof(ImageUndoStep);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Utilities
|
||||
* \{ */
|
||||
|
||||
ListBase *ED_image_undosys_step_get_tiles(UndoStep *us_p)
|
||||
{
|
||||
ImageUndoStep *us = (ImageUndoStep *)us_p;
|
||||
return &us->tiles;
|
||||
}
|
||||
|
||||
ListBase *ED_image_undo_get_tiles(void)
|
||||
{
|
||||
wmWindowManager *wm = G.main->wm.first; /* XXX, avoids adding extra arg. */
|
||||
UndoStep *us = BKE_undosys_stack_init_or_active_with_type(wm->undo_stack, BKE_UNDOSYS_TYPE_IMAGE);
|
||||
return ED_image_undosys_step_get_tiles(us);
|
||||
}
|
||||
|
||||
/* restore painting image to previous state. Used for anchored and drag-dot style brushes*/
|
||||
void ED_image_undo_restore(UndoStep *us)
|
||||
{
|
||||
ListBase *lb = ED_image_undosys_step_get_tiles(us);
|
||||
image_undo_restore_runtime(lb);
|
||||
image_undo_invalidate();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -55,6 +55,7 @@ struct wmOperator;
|
||||
struct wmOperatorType;
|
||||
struct wmWindowManager;
|
||||
struct DMCoNo;
|
||||
struct UndoStep;
|
||||
enum ePaintMode;
|
||||
|
||||
/* paint_stroke.c */
|
||||
@ -221,15 +222,20 @@ void PAINT_OT_add_simple_uvs(struct wmOperatorType *ot);
|
||||
|
||||
/* paint_image_undo.c */
|
||||
void *image_undo_find_tile(
|
||||
ListBase *undo_tiles,
|
||||
struct Image *ima, struct ImBuf *ibuf, int x_tile, int y_tile,
|
||||
unsigned short **mask, bool validate);
|
||||
void *image_undo_push_tile(
|
||||
ListBase *undo_tiles,
|
||||
struct Image *ima, struct ImBuf *ibuf, struct ImBuf **tmpibuf, int x_tile, int y_tile,
|
||||
unsigned short **, bool **valid, bool proj, bool find_prev);
|
||||
void image_undo_remove_masks(void);
|
||||
void image_undo_init_locks(void);
|
||||
void image_undo_end_locks(void);
|
||||
|
||||
struct ListBase *ED_image_undosys_step_get_tiles(struct UndoStep *us_p);
|
||||
struct ListBase *ED_image_undo_get_tiles(void);
|
||||
|
||||
/* sculpt_uv.c */
|
||||
int uv_sculpt_poll(struct bContext *C);
|
||||
int uv_sculpt_keymap_poll(struct bContext *C);
|
||||
@ -303,10 +309,6 @@ typedef enum {
|
||||
void set_brush_rc_props(struct PointerRNA *ptr, const char *paint, const char *prop, const char *secondary_prop,
|
||||
RCFlags flags);
|
||||
|
||||
/* paint_undo.c */
|
||||
struct ListBase *undo_paint_push_get_list(int type);
|
||||
void undo_paint_push_count_alloc(int type, int size);
|
||||
|
||||
/* paint_hide.c */
|
||||
|
||||
typedef enum {
|
||||
|
@ -1,410 +0,0 @@
|
||||
/*
|
||||
* ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/editors/sculpt_paint/paint_undo.c
|
||||
* \ingroup edsculpt
|
||||
* \brief Undo system for painting and sculpting.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "DNA_userdef_types.h"
|
||||
|
||||
#include "BKE_blender_undo.h"
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_global.h"
|
||||
|
||||
#include "ED_paint.h"
|
||||
|
||||
#include "paint_intern.h"
|
||||
|
||||
typedef struct UndoElem {
|
||||
struct UndoElem *next, *prev;
|
||||
char name[BKE_UNDO_STR_MAX];
|
||||
uintptr_t undosize;
|
||||
|
||||
ListBase elems;
|
||||
|
||||
UndoRestoreCb restore;
|
||||
UndoFreeCb free;
|
||||
UndoCleanupCb cleanup;
|
||||
} UndoElem;
|
||||
|
||||
typedef struct UndoStack {
|
||||
int type;
|
||||
ListBase elems;
|
||||
UndoElem *current;
|
||||
} UndoStack;
|
||||
|
||||
static UndoStack ImageUndoStack = {UNDO_PAINT_IMAGE, {NULL, NULL}, NULL};
|
||||
static UndoStack MeshUndoStack = {UNDO_PAINT_MESH, {NULL, NULL}, NULL};
|
||||
|
||||
/* Generic */
|
||||
|
||||
static void undo_restore(bContext *C, UndoStack *UNUSED(stack), UndoElem *uel)
|
||||
{
|
||||
if (uel && uel->restore)
|
||||
uel->restore(C, &uel->elems);
|
||||
}
|
||||
|
||||
static void undo_elem_free(UndoStack *UNUSED(stack), UndoElem *uel)
|
||||
{
|
||||
if (uel && uel->free) {
|
||||
uel->free(&uel->elems);
|
||||
BLI_freelistN(&uel->elems);
|
||||
}
|
||||
}
|
||||
|
||||
static void undo_stack_push_begin(UndoStack *stack, const char *name, UndoRestoreCb restore, UndoFreeCb free, UndoCleanupCb cleanup)
|
||||
{
|
||||
UndoElem *uel;
|
||||
int nr;
|
||||
|
||||
/* Undo push is split up in begin and end, the reason is that as painting
|
||||
* happens more tiles/nodes are added to the list, and at the very end we
|
||||
* know how much memory the undo used to remove old undo elements */
|
||||
|
||||
/* remove all undos after (also when stack->current==NULL) */
|
||||
while (stack->elems.last != stack->current) {
|
||||
uel = stack->elems.last;
|
||||
undo_elem_free(stack, uel);
|
||||
BLI_freelinkN(&stack->elems, uel);
|
||||
}
|
||||
|
||||
/* make new */
|
||||
stack->current = uel = MEM_callocN(sizeof(UndoElem), "undo file");
|
||||
uel->restore = restore;
|
||||
uel->free = free;
|
||||
uel->cleanup = cleanup;
|
||||
BLI_addtail(&stack->elems, uel);
|
||||
|
||||
/* name can be a dynamic string */
|
||||
BLI_strncpy(uel->name, name, sizeof(uel->name));
|
||||
|
||||
/* limit amount to the maximum amount*/
|
||||
nr = 0;
|
||||
uel = stack->elems.last;
|
||||
while (uel) {
|
||||
nr++;
|
||||
if (nr == U.undosteps) break;
|
||||
uel = uel->prev;
|
||||
}
|
||||
if (uel) {
|
||||
while (stack->elems.first != uel) {
|
||||
UndoElem *first = stack->elems.first;
|
||||
undo_elem_free(stack, first);
|
||||
BLI_freelinkN(&stack->elems, first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void undo_stack_push_end(UndoStack *stack)
|
||||
{
|
||||
UndoElem *uel;
|
||||
uintptr_t totmem, maxmem;
|
||||
int totundo = 0;
|
||||
|
||||
/* first limit to undo steps */
|
||||
uel = stack->elems.last;
|
||||
|
||||
while (uel) {
|
||||
totundo++;
|
||||
if (totundo > U.undosteps) break;
|
||||
uel = uel->prev;
|
||||
}
|
||||
|
||||
if (uel) {
|
||||
UndoElem *first;
|
||||
|
||||
/* in case the undo steps are zero, the current pointer will be invalid */
|
||||
if (uel == stack->current)
|
||||
stack->current = NULL;
|
||||
|
||||
do {
|
||||
first = stack->elems.first;
|
||||
undo_elem_free(stack, first);
|
||||
BLI_freelinkN(&stack->elems, first);
|
||||
} while (first != uel);
|
||||
}
|
||||
|
||||
if (U.undomemory != 0) {
|
||||
/* limit to maximum memory (afterwards, we can't know in advance) */
|
||||
totmem = 0;
|
||||
maxmem = ((uintptr_t)U.undomemory) * 1024 * 1024;
|
||||
|
||||
uel = stack->elems.last;
|
||||
while (uel) {
|
||||
totmem += uel->undosize;
|
||||
if (totmem > maxmem) break;
|
||||
uel = uel->prev;
|
||||
}
|
||||
|
||||
if (uel) {
|
||||
while (stack->elems.first != uel) {
|
||||
UndoElem *first = stack->elems.first;
|
||||
undo_elem_free(stack, first);
|
||||
BLI_freelinkN(&stack->elems, first);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void undo_stack_cleanup(UndoStack *stack, bContext *C)
|
||||
{
|
||||
UndoElem *uel = stack->elems.first;
|
||||
bool stack_reset = false;
|
||||
|
||||
while (uel) {
|
||||
if (uel->cleanup && uel->cleanup(C, &uel->elems)) {
|
||||
UndoElem *uel_tmp = uel->next;
|
||||
if (stack->current == uel) {
|
||||
stack->current = NULL;
|
||||
stack_reset = true;
|
||||
}
|
||||
undo_elem_free(stack, uel);
|
||||
BLI_freelinkN(&stack->elems, uel);
|
||||
uel = uel_tmp;
|
||||
}
|
||||
else
|
||||
uel = uel->next;
|
||||
}
|
||||
if (stack_reset) {
|
||||
stack->current = stack->elems.last;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int undo_stack_step(bContext *C, UndoStack *stack, int step, const char *name)
|
||||
{
|
||||
UndoElem *undo;
|
||||
|
||||
/* first cleanup any old undo steps that may belong to invalid data */
|
||||
undo_stack_cleanup(stack, C);
|
||||
|
||||
if (step == 1) {
|
||||
if (stack->current == NULL) {
|
||||
/* pass */
|
||||
}
|
||||
else {
|
||||
if (!name || STREQ(stack->current->name, name)) {
|
||||
if (G.debug & G_DEBUG_WM) {
|
||||
printf("%s: undo '%s'\n", __func__, stack->current->name);
|
||||
}
|
||||
undo_restore(C, stack, stack->current);
|
||||
stack->current = stack->current->prev;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (step == -1) {
|
||||
if ((stack->current != NULL && stack->current->next == NULL) || BLI_listbase_is_empty(&stack->elems)) {
|
||||
/* pass */
|
||||
}
|
||||
else {
|
||||
if (!name || STREQ(stack->current->name, name)) {
|
||||
undo = (stack->current && stack->current->next) ? stack->current->next : stack->elems.first;
|
||||
undo_restore(C, stack, undo);
|
||||
stack->current = undo;
|
||||
if (G.debug & G_DEBUG_WM) {
|
||||
printf("%s: redo %s\n", __func__, undo->name);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void undo_stack_free(UndoStack *stack)
|
||||
{
|
||||
UndoElem *uel;
|
||||
|
||||
for (uel = stack->elems.first; uel; uel = uel->next)
|
||||
undo_elem_free(stack, uel);
|
||||
|
||||
BLI_freelistN(&stack->elems);
|
||||
stack->current = NULL;
|
||||
}
|
||||
|
||||
/* Exported Functions */
|
||||
|
||||
void ED_undo_paint_push_begin(int type, const char *name, UndoRestoreCb restore, UndoFreeCb free, UndoCleanupCb cleanup)
|
||||
{
|
||||
if (type == UNDO_PAINT_IMAGE)
|
||||
undo_stack_push_begin(&ImageUndoStack, name, restore, free, cleanup);
|
||||
else if (type == UNDO_PAINT_MESH)
|
||||
undo_stack_push_begin(&MeshUndoStack, name, restore, free, cleanup);
|
||||
}
|
||||
|
||||
ListBase *undo_paint_push_get_list(int type)
|
||||
{
|
||||
if (type == UNDO_PAINT_IMAGE) {
|
||||
if (ImageUndoStack.current) {
|
||||
return &ImageUndoStack.current->elems;
|
||||
}
|
||||
}
|
||||
else if (type == UNDO_PAINT_MESH) {
|
||||
if (MeshUndoStack.current) {
|
||||
return &MeshUndoStack.current->elems;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void undo_paint_push_count_alloc(int type, int size)
|
||||
{
|
||||
if (type == UNDO_PAINT_IMAGE)
|
||||
ImageUndoStack.current->undosize += size;
|
||||
else if (type == UNDO_PAINT_MESH)
|
||||
MeshUndoStack.current->undosize += size;
|
||||
}
|
||||
|
||||
void ED_undo_paint_push_end(int type)
|
||||
{
|
||||
if (type == UNDO_PAINT_IMAGE)
|
||||
undo_stack_push_end(&ImageUndoStack);
|
||||
else if (type == UNDO_PAINT_MESH)
|
||||
undo_stack_push_end(&MeshUndoStack);
|
||||
}
|
||||
|
||||
int ED_undo_paint_step(bContext *C, int type, int step, const char *name)
|
||||
{
|
||||
if (type == UNDO_PAINT_IMAGE)
|
||||
return undo_stack_step(C, &ImageUndoStack, step, name);
|
||||
else if (type == UNDO_PAINT_MESH)
|
||||
return undo_stack_step(C, &MeshUndoStack, step, name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void undo_step_num(bContext *C, UndoStack *stack, int step)
|
||||
{
|
||||
UndoElem *uel;
|
||||
int a = 0;
|
||||
int curnum = BLI_findindex(&stack->elems, stack->current);
|
||||
|
||||
for (uel = stack->elems.first; uel; uel = uel->next, a++) {
|
||||
if (a == step) break;
|
||||
}
|
||||
|
||||
if (curnum > a) {
|
||||
while (a++ != curnum)
|
||||
undo_stack_step(C, stack, 1, NULL);
|
||||
}
|
||||
else if (curnum < a) {
|
||||
while (a-- != curnum)
|
||||
undo_stack_step(C, stack, -1, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void ED_undo_paint_step_num(bContext *C, int type, int step)
|
||||
{
|
||||
if (type == UNDO_PAINT_IMAGE)
|
||||
undo_step_num(C, &ImageUndoStack, step);
|
||||
else if (type == UNDO_PAINT_MESH)
|
||||
undo_step_num(C, &MeshUndoStack, step);
|
||||
}
|
||||
|
||||
static char *undo_stack_get_name(UndoStack *stack, int nr, bool *r_active)
|
||||
{
|
||||
UndoElem *uel;
|
||||
|
||||
if (r_active) *r_active = false;
|
||||
|
||||
uel = BLI_findlink(&stack->elems, nr);
|
||||
if (uel) {
|
||||
if (r_active && (uel == stack->current)) {
|
||||
*r_active = true;
|
||||
}
|
||||
return uel->name;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *ED_undo_paint_get_name(bContext *C, int type, int nr, bool *r_active)
|
||||
{
|
||||
|
||||
if (type == UNDO_PAINT_IMAGE) {
|
||||
undo_stack_cleanup(&ImageUndoStack, C);
|
||||
return undo_stack_get_name(&ImageUndoStack, nr, r_active);
|
||||
}
|
||||
else if (type == UNDO_PAINT_MESH) {
|
||||
undo_stack_cleanup(&MeshUndoStack, C);
|
||||
return undo_stack_get_name(&MeshUndoStack, nr, r_active);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool ED_undo_paint_empty(int type)
|
||||
{
|
||||
UndoStack *stack;
|
||||
|
||||
if (type == UNDO_PAINT_IMAGE)
|
||||
stack = &ImageUndoStack;
|
||||
else if (type == UNDO_PAINT_MESH)
|
||||
stack = &MeshUndoStack;
|
||||
else
|
||||
return true;
|
||||
|
||||
if (stack->current == NULL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ED_undo_paint_is_valid(int type, const char *name)
|
||||
{
|
||||
UndoStack *stack;
|
||||
|
||||
if (type == UNDO_PAINT_IMAGE)
|
||||
stack = &ImageUndoStack;
|
||||
else if (type == UNDO_PAINT_MESH)
|
||||
stack = &MeshUndoStack;
|
||||
else
|
||||
return 0;
|
||||
|
||||
if (stack->current == NULL) {
|
||||
/* pass */
|
||||
}
|
||||
else {
|
||||
if (name && STREQ(stack->current->name, name))
|
||||
return 1;
|
||||
else
|
||||
return stack->elems.first != stack->elems.last;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ED_undo_paint_free(void)
|
||||
{
|
||||
undo_stack_free(&ImageUndoStack);
|
||||
undo_stack_free(&MeshUndoStack);
|
||||
}
|
@ -125,6 +125,8 @@ typedef struct SculptUndoNode {
|
||||
|
||||
/* shape keys */
|
||||
char shapeName[sizeof(((KeyBlock *)0))->name];
|
||||
|
||||
size_t undo_size;
|
||||
} SculptUndoNode;
|
||||
|
||||
/* Factor of brush to have rake point following behind
|
||||
|
@ -48,6 +48,9 @@
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
#include "DNA_space_types.h"
|
||||
#include "DNA_workspace_types.h"
|
||||
|
||||
#include "BKE_ccg.h"
|
||||
#include "BKE_context.h"
|
||||
@ -57,6 +60,9 @@
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_subsurf.h"
|
||||
#include "DEG_depsgraph.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_undo_system.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
@ -64,12 +70,21 @@
|
||||
#include "GPU_buffers.h"
|
||||
|
||||
#include "ED_paint.h"
|
||||
#include "ED_object.h"
|
||||
#include "ED_sculpt.h"
|
||||
|
||||
#include "bmesh.h"
|
||||
#include "paint_intern.h"
|
||||
#include "sculpt_intern.h"
|
||||
|
||||
/************************** Undo *************************/
|
||||
|
||||
typedef struct UndoSculpt {
|
||||
ListBase nodes;
|
||||
|
||||
size_t undo_size;
|
||||
} UndoSculpt;
|
||||
|
||||
static UndoSculpt *sculpt_undo_get_nodes(void);
|
||||
|
||||
static void update_cb(PBVHNode *node, void *rebuild)
|
||||
{
|
||||
@ -457,7 +472,7 @@ static int sculpt_undo_bmesh_restore(bContext *C,
|
||||
return false;
|
||||
}
|
||||
|
||||
static void sculpt_undo_restore(bContext *C, ListBase *lb)
|
||||
static void sculpt_undo_restore_list(bContext *C, ListBase *lb)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
|
||||
@ -580,7 +595,7 @@ static void sculpt_undo_restore(bContext *C, ListBase *lb)
|
||||
}
|
||||
}
|
||||
|
||||
static void sculpt_undo_free(ListBase *lb)
|
||||
static void sculpt_undo_free_list(ListBase *lb)
|
||||
{
|
||||
SculptUndoNode *unode;
|
||||
int i;
|
||||
@ -623,6 +638,8 @@ static void sculpt_undo_free(ListBase *lb)
|
||||
}
|
||||
}
|
||||
|
||||
/* Most likely we don't need this. */
|
||||
#if 0
|
||||
static bool sculpt_undo_cleanup(bContext *C, ListBase *lb)
|
||||
{
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
@ -639,16 +656,17 @@ static bool sculpt_undo_cleanup(bContext *C, ListBase *lb)
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
SculptUndoNode *sculpt_undo_get_node(PBVHNode *node)
|
||||
{
|
||||
ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_MESH);
|
||||
UndoSculpt *usculpt = sculpt_undo_get_nodes();
|
||||
|
||||
if (!lb) {
|
||||
if (usculpt == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return BLI_findptr(lb, node, offsetof(SculptUndoNode, node));
|
||||
return BLI_findptr(&usculpt->nodes, node, offsetof(SculptUndoNode, node));
|
||||
}
|
||||
|
||||
static void sculpt_undo_alloc_and_store_hidden(PBVH *pbvh,
|
||||
@ -674,10 +692,11 @@ static void sculpt_undo_alloc_and_store_hidden(PBVH *pbvh,
|
||||
}
|
||||
}
|
||||
|
||||
static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node,
|
||||
SculptUndoType type)
|
||||
static SculptUndoNode *sculpt_undo_alloc_node(
|
||||
Object *ob, PBVHNode *node,
|
||||
SculptUndoType type)
|
||||
{
|
||||
ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_MESH);
|
||||
UndoSculpt *usculpt = sculpt_undo_get_nodes();
|
||||
SculptUndoNode *unode;
|
||||
SculptSession *ss = ob->sculpt;
|
||||
int totvert, allvert, totgrid, maxgrid, gridsize, *grids;
|
||||
@ -702,12 +721,10 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node,
|
||||
/* general TODO, fix count_alloc */
|
||||
switch (type) {
|
||||
case SCULPT_UNDO_COORDS:
|
||||
unode->co = MEM_mapallocN(sizeof(float) * 3 * allvert, "SculptUndoNode.co");
|
||||
unode->no = MEM_mapallocN(sizeof(short) * 3 * allvert, "SculptUndoNode.no");
|
||||
undo_paint_push_count_alloc(UNDO_PAINT_MESH,
|
||||
(sizeof(float) * 3 +
|
||||
sizeof(short) * 3 +
|
||||
sizeof(int)) * allvert);
|
||||
unode->co = MEM_mapallocN(sizeof(float[3]) * allvert, "SculptUndoNode.co");
|
||||
unode->no = MEM_mapallocN(sizeof(short[3]) * allvert, "SculptUndoNode.no");
|
||||
|
||||
usculpt->undo_size = (sizeof(float[3]) + sizeof(short[3]) + sizeof(int)) * allvert;
|
||||
break;
|
||||
case SCULPT_UNDO_HIDDEN:
|
||||
if (maxgrid)
|
||||
@ -718,7 +735,9 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node,
|
||||
break;
|
||||
case SCULPT_UNDO_MASK:
|
||||
unode->mask = MEM_mapallocN(sizeof(float) * allvert, "SculptUndoNode.mask");
|
||||
undo_paint_push_count_alloc(UNDO_PAINT_MESH, (sizeof(float) * sizeof(int)) * allvert);
|
||||
|
||||
usculpt->undo_size += (sizeof(float) * sizeof(int)) * allvert;
|
||||
|
||||
break;
|
||||
case SCULPT_UNDO_DYNTOPO_BEGIN:
|
||||
case SCULPT_UNDO_DYNTOPO_END:
|
||||
@ -727,7 +746,7 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node,
|
||||
break;
|
||||
}
|
||||
|
||||
BLI_addtail(lb, unode);
|
||||
BLI_addtail(&usculpt->nodes, unode);
|
||||
|
||||
if (maxgrid) {
|
||||
/* multires */
|
||||
@ -804,12 +823,13 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob,
|
||||
PBVHNode *node,
|
||||
SculptUndoType type)
|
||||
{
|
||||
ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_MESH);
|
||||
SculptUndoNode *unode = lb->first;
|
||||
UndoSculpt *usculpt = sculpt_undo_get_nodes();
|
||||
SculptSession *ss = ob->sculpt;
|
||||
PBVHVertexIter vd;
|
||||
|
||||
if (!lb->first) {
|
||||
SculptUndoNode *unode = usculpt->nodes.first;
|
||||
|
||||
if (unode == NULL) {
|
||||
unode = MEM_callocN(sizeof(*unode), __func__);
|
||||
|
||||
BLI_strncpy(unode->idname, ob->id.name, sizeof(unode->idname));
|
||||
@ -848,7 +868,7 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob,
|
||||
unode->bm_entry = BM_log_entry_add(ss->bm_log);
|
||||
}
|
||||
|
||||
BLI_addtail(lb, unode);
|
||||
BLI_addtail(&usculpt->nodes, unode);
|
||||
}
|
||||
|
||||
if (node) {
|
||||
@ -889,8 +909,9 @@ static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob,
|
||||
return unode;
|
||||
}
|
||||
|
||||
SculptUndoNode *sculpt_undo_push_node(Object *ob, PBVHNode *node,
|
||||
SculptUndoType type)
|
||||
SculptUndoNode *sculpt_undo_push_node(
|
||||
Object *ob, PBVHNode *node,
|
||||
SculptUndoType type)
|
||||
{
|
||||
SculptSession *ss = ob->sculpt;
|
||||
SculptUndoNode *unode;
|
||||
@ -960,17 +981,18 @@ SculptUndoNode *sculpt_undo_push_node(Object *ob, PBVHNode *node,
|
||||
|
||||
void sculpt_undo_push_begin(const char *name)
|
||||
{
|
||||
ED_undo_paint_push_begin(UNDO_PAINT_MESH, name,
|
||||
sculpt_undo_restore, sculpt_undo_free, sculpt_undo_cleanup);
|
||||
bContext *C = NULL; /* special case, we never read from this. */
|
||||
wmWindowManager *wm = G.main->wm.first;
|
||||
BKE_undosys_step_push_init_with_type(wm->undo_stack, C, name, BKE_UNDOSYS_TYPE_SCULPT);
|
||||
}
|
||||
|
||||
void sculpt_undo_push_end(void)
|
||||
{
|
||||
ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_MESH);
|
||||
UndoSculpt *usculpt = sculpt_undo_get_nodes();
|
||||
SculptUndoNode *unode;
|
||||
|
||||
/* we don't need normals in the undo stack */
|
||||
for (unode = lb->first; unode; unode = unode->next) {
|
||||
for (unode = usculpt->nodes.first; unode; unode = unode->next) {
|
||||
if (unode->no) {
|
||||
MEM_freeN(unode->no);
|
||||
unode->no = NULL;
|
||||
@ -980,7 +1002,98 @@ void sculpt_undo_push_end(void)
|
||||
BKE_pbvh_node_layer_disp_free(unode->node);
|
||||
}
|
||||
|
||||
ED_undo_paint_push_end(UNDO_PAINT_MESH);
|
||||
|
||||
WM_file_tag_modified();
|
||||
wmWindowManager *wm = G.main->wm.first; /* XXX, avoids adding extra arg. */
|
||||
BKE_undosys_step_push(wm->undo_stack, NULL, NULL);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Implements ED Undo System
|
||||
* \{ */
|
||||
|
||||
typedef struct SculptUndoStep {
|
||||
UndoStep step;
|
||||
/* note: will split out into list for multi-object-sculpt-mode. */
|
||||
UndoSculpt data;
|
||||
} SculptUndoStep;
|
||||
|
||||
static bool sculpt_undosys_poll(bContext *C)
|
||||
{
|
||||
ScrArea *sa = CTX_wm_area(C);
|
||||
if (sa && (sa->spacetype == SPACE_VIEW3D)) {
|
||||
const WorkSpace *workspace = CTX_wm_workspace(C);
|
||||
Object *obact = CTX_data_active_object(C);
|
||||
if (obact && (workspace->object_mode & OB_MODE_SCULPT)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void sculpt_undosys_step_encode_init(struct bContext *UNUSED(C), UndoStep *us_p)
|
||||
{
|
||||
SculptUndoStep *us = (SculptUndoStep *)us_p;
|
||||
/* dummy, memory is cleared anyway. */
|
||||
BLI_listbase_clear(&us->data.nodes);
|
||||
}
|
||||
|
||||
static bool sculpt_undosys_step_encode(struct bContext *UNUSED(C), UndoStep *us_p)
|
||||
{
|
||||
/* dummy, encoding is done along the way by adding tiles
|
||||
* to the current 'SculptUndoStep' added by encode_init. */
|
||||
SculptUndoStep *us = (SculptUndoStep *)us_p;
|
||||
us->step.data_size = us->data.undo_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void sculpt_undosys_step_decode(struct bContext *C, UndoStep *us_p, int UNUSED(dir))
|
||||
{
|
||||
/* TODO(campbell): undo_system: use low-level API to set mode. */
|
||||
ED_object_mode_set(C, OB_MODE_SCULPT);
|
||||
BLI_assert(sculpt_undosys_poll(C));
|
||||
|
||||
SculptUndoStep *us = (SculptUndoStep *)us_p;
|
||||
sculpt_undo_restore_list(C, &us->data.nodes);
|
||||
}
|
||||
|
||||
static void sculpt_undosys_step_free(UndoStep *us_p)
|
||||
{
|
||||
SculptUndoStep *us = (SculptUndoStep *)us_p;
|
||||
sculpt_undo_free_list(&us->data.nodes);
|
||||
}
|
||||
|
||||
/* Export for ED_undo_sys. */
|
||||
void ED_sculpt_undosys_type(UndoType *ut)
|
||||
{
|
||||
ut->name = "Sculpt";
|
||||
ut->poll = sculpt_undosys_poll;
|
||||
ut->step_encode_init = sculpt_undosys_step_encode_init;
|
||||
ut->step_encode = sculpt_undosys_step_encode;
|
||||
ut->step_decode = sculpt_undosys_step_decode;
|
||||
ut->step_free = sculpt_undosys_step_free;
|
||||
|
||||
ut->mode = BKE_UNDOTYPE_MODE_ACCUMULATE;
|
||||
ut->use_context = true;
|
||||
|
||||
ut->step_size = sizeof(SculptUndoStep);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Utilities
|
||||
* \{ */
|
||||
|
||||
static UndoSculpt *sculpt_undosys_step_get_nodes(UndoStep *us_p)
|
||||
{
|
||||
SculptUndoStep *us = (SculptUndoStep *)us_p;
|
||||
return &us->data;
|
||||
}
|
||||
|
||||
static UndoSculpt *sculpt_undo_get_nodes(void)
|
||||
{
|
||||
wmWindowManager *wm = G.main->wm.first; /* XXX, avoids adding extra arg. */
|
||||
UndoStep *us = BKE_undosys_stack_init_or_active_with_type(wm->undo_stack, BKE_UNDOSYS_TYPE_SCULPT);
|
||||
return sculpt_undosys_step_get_nodes(us);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@ -50,7 +50,6 @@
|
||||
#include "BKE_curve.h"
|
||||
#include "BKE_fcurve.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_screen.h"
|
||||
#include "BKE_unit.h"
|
||||
|
||||
|
@ -54,7 +54,6 @@
|
||||
#include "BKE_animsys.h"
|
||||
#include "BKE_action.h"
|
||||
#include "BKE_fcurve.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_library.h"
|
||||
#include "BKE_key.h"
|
||||
#include "BKE_main.h"
|
||||
|
@ -42,7 +42,6 @@
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_constraint.h"
|
||||
#include "BKE_tracking.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_layer.h"
|
||||
#include "BKE_object.h"
|
||||
#include "BKE_report.h"
|
||||
|
@ -2667,8 +2667,9 @@ static int image_invert_exec(bContext *C, wmOperator *op)
|
||||
if (ibuf->mipmap[0])
|
||||
ibuf->userflags |= IB_MIPMAP_INVALID;
|
||||
|
||||
if (support_undo)
|
||||
ED_undo_paint_push_end(UNDO_PAINT_IMAGE);
|
||||
if (support_undo) {
|
||||
ED_image_undo_push_end();
|
||||
}
|
||||
|
||||
/* force GPU reupload, all image is invalid */
|
||||
GPU_free_image(ima);
|
||||
|
@ -51,7 +51,6 @@
|
||||
|
||||
#include "BKE_animsys.h"
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_idcode.h"
|
||||
#include "BKE_layer.h"
|
||||
#include "BKE_library.h"
|
||||
|
@ -48,6 +48,7 @@ set(SRC
|
||||
text_format_py.c
|
||||
text_header.c
|
||||
text_ops.c
|
||||
text_undo.c
|
||||
|
||||
text_format.h
|
||||
text_intern.h
|
||||
|
@ -757,6 +757,9 @@ void TEXT_OT_paste(wmOperatorType *ot)
|
||||
ot->exec = text_paste_exec;
|
||||
ot->poll = text_edit_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_UNDO;
|
||||
|
||||
/* properties */
|
||||
RNA_def_boolean(ot->srna, "selection", 0, "Selection", "Paste text selected elsewhere rather than copied (X11 only)");
|
||||
}
|
||||
@ -789,6 +792,9 @@ void TEXT_OT_duplicate_line(wmOperatorType *ot)
|
||||
/* api callbacks */
|
||||
ot->exec = text_duplicate_line_exec;
|
||||
ot->poll = text_edit_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/******************* copy operator *********************/
|
||||
@ -860,6 +866,9 @@ void TEXT_OT_cut(wmOperatorType *ot)
|
||||
/* api callbacks */
|
||||
ot->exec = text_cut_exec;
|
||||
ot->poll = text_edit_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/******************* indent operator *********************/
|
||||
@ -895,6 +904,9 @@ void TEXT_OT_indent(wmOperatorType *ot)
|
||||
/* api callbacks */
|
||||
ot->exec = text_indent_exec;
|
||||
ot->poll = text_edit_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/******************* unindent operator *********************/
|
||||
@ -926,6 +938,9 @@ void TEXT_OT_unindent(wmOperatorType *ot)
|
||||
/* api callbacks */
|
||||
ot->exec = text_unindent_exec;
|
||||
ot->poll = text_edit_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/******************* line break operator *********************/
|
||||
@ -974,6 +989,9 @@ void TEXT_OT_line_break(wmOperatorType *ot)
|
||||
/* api callbacks */
|
||||
ot->exec = text_line_break_exec;
|
||||
ot->poll = text_edit_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/******************* comment operator *********************/
|
||||
@ -1007,6 +1025,9 @@ void TEXT_OT_comment(wmOperatorType *ot)
|
||||
/* api callbacks */
|
||||
ot->exec = text_comment_exec;
|
||||
ot->poll = text_edit_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/******************* uncomment operator *********************/
|
||||
@ -1041,6 +1062,9 @@ void TEXT_OT_uncomment(wmOperatorType *ot)
|
||||
/* api callbacks */
|
||||
ot->exec = text_uncomment_exec;
|
||||
ot->poll = text_edit_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/******************* convert whitespace operator *********************/
|
||||
@ -1174,6 +1198,9 @@ void TEXT_OT_convert_whitespace(wmOperatorType *ot)
|
||||
ot->exec = text_convert_whitespace_exec;
|
||||
ot->poll = text_edit_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_UNDO;
|
||||
|
||||
/* properties */
|
||||
RNA_def_enum(ot->srna, "type", whitespace_type_items, TO_SPACES, "Type", "Type of whitespace to convert to");
|
||||
}
|
||||
@ -1295,6 +1322,9 @@ void TEXT_OT_move_lines(wmOperatorType *ot)
|
||||
ot->exec = move_lines_exec;
|
||||
ot->poll = text_edit_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_UNDO;
|
||||
|
||||
/* properties */
|
||||
RNA_def_enum(ot->srna, "direction", direction_items, 1, "Direction", "");
|
||||
}
|
||||
@ -2919,6 +2949,9 @@ void TEXT_OT_insert(wmOperatorType *ot)
|
||||
ot->invoke = text_insert_invoke;
|
||||
ot->poll = text_edit_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_UNDO;
|
||||
|
||||
/* properties */
|
||||
prop = RNA_def_string(ot->srna, "text", NULL, 0, "Text", "Text to insert at the cursor position");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
@ -3024,6 +3057,9 @@ void TEXT_OT_replace(wmOperatorType *ot)
|
||||
/* api callbacks */
|
||||
ot->exec = text_replace_exec;
|
||||
ot->poll = text_space_edit_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/******************* find set selected *********************/
|
||||
@ -3081,6 +3117,9 @@ void TEXT_OT_replace_set_selected(wmOperatorType *ot)
|
||||
/* api callbacks */
|
||||
ot->exec = text_replace_set_selected_exec;
|
||||
ot->poll = text_space_edit_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/****************** resolve conflict operator ******************/
|
||||
@ -3201,26 +3240,3 @@ void TEXT_OT_to_3d_object(wmOperatorType *ot)
|
||||
/* properties */
|
||||
RNA_def_boolean(ot->srna, "split_lines", 0, "Split Lines", "Create one object per line in the text");
|
||||
}
|
||||
|
||||
|
||||
/************************ undo ******************************/
|
||||
|
||||
void ED_text_undo_step(bContext *C, int step)
|
||||
{
|
||||
Text *text = CTX_data_edit_text(C);
|
||||
|
||||
if (!text)
|
||||
return;
|
||||
|
||||
if (step == 1)
|
||||
txt_do_undo(text);
|
||||
else if (step == -1)
|
||||
txt_do_redo(text);
|
||||
|
||||
text_update_edited(text);
|
||||
|
||||
text_update_cursor_moved(C);
|
||||
text_drawcache_tag_update(CTX_wm_space_text(C), 1);
|
||||
WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
|
||||
}
|
||||
|
||||
|
169
source/blender/editors/space_text/text_undo.c
Normal file
169
source/blender/editors/space_text/text_undo.c
Normal file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
* ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
/** \file blender/editors/space_text/text_undo.c
|
||||
* \ingroup sptext
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "DNA_text_types.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_array_utils.h"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "PIL_time.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_library.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_report.h"
|
||||
#include "BKE_text.h"
|
||||
#include "BKE_undo_system.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
|
||||
#include "ED_text.h"
|
||||
#include "ED_curve.h"
|
||||
#include "ED_screen.h"
|
||||
|
||||
#include "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
|
||||
#include "text_intern.h"
|
||||
#include "text_format.h"
|
||||
|
||||
/* TODO(campbell): undo_system: move text undo out of text block. */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Implements ED Undo System
|
||||
* \{ */
|
||||
typedef struct TextUndoBuf {
|
||||
char *buf;
|
||||
int len;
|
||||
int pos;
|
||||
} TextUndoBuf;
|
||||
|
||||
typedef struct TextUndoStep {
|
||||
UndoStep step;
|
||||
UndoRefID_Text text_ref;
|
||||
TextUndoBuf data;
|
||||
} TextUndoStep;
|
||||
|
||||
static bool text_undosys_poll(bContext *C)
|
||||
{
|
||||
Text *text = CTX_data_edit_text(C);
|
||||
if (text == NULL) {
|
||||
return false;
|
||||
}
|
||||
if (ID_IS_LINKED(text)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool text_undosys_step_encode(struct bContext *C, UndoStep *us_p)
|
||||
{
|
||||
TextUndoStep *us = (TextUndoStep *)us_p;
|
||||
Text *text = CTX_data_edit_text(C);
|
||||
us->text_ref.ptr = text;
|
||||
|
||||
BLI_assert(BLI_array_is_zeroed(&us->data, 1));
|
||||
|
||||
us->data.buf = text->undo_buf;
|
||||
us->data.pos = text->undo_pos;
|
||||
us->data.len = text->undo_len;
|
||||
|
||||
text->undo_buf = NULL;
|
||||
text->undo_len = 0;
|
||||
text->undo_pos = -1;
|
||||
|
||||
us->step.data_size = text->undo_len;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void text_undosys_step_decode(struct bContext *C, UndoStep *us_p, int dir)
|
||||
{
|
||||
TextUndoStep *us = (TextUndoStep *)us_p;
|
||||
Text *text = us->text_ref.ptr;
|
||||
|
||||
/* TODO(campbell): undo_system: move undo out of Text data block. */
|
||||
text->undo_buf = us->data.buf;
|
||||
text->undo_len = us->data.len;
|
||||
if (dir < 0) {
|
||||
text->undo_pos = us->data.pos;
|
||||
txt_do_undo(text);
|
||||
}
|
||||
else {
|
||||
text->undo_pos = -1;
|
||||
txt_do_redo(text);
|
||||
}
|
||||
text->undo_buf = NULL;
|
||||
text->undo_len = 0;
|
||||
text->undo_pos = -1;
|
||||
|
||||
text_update_edited(text);
|
||||
text_update_cursor_moved(C);
|
||||
text_drawcache_tag_update(CTX_wm_space_text(C), 1);
|
||||
WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
|
||||
}
|
||||
|
||||
static void text_undosys_step_free(UndoStep *us_p)
|
||||
{
|
||||
TextUndoStep *us = (TextUndoStep *)us_p;
|
||||
MEM_SAFE_FREE(us->data.buf);
|
||||
}
|
||||
|
||||
static void text_undosys_foreach_ID_ref(
|
||||
UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data)
|
||||
{
|
||||
TextUndoStep *us = (TextUndoStep *)us_p;
|
||||
foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->text_ref));
|
||||
}
|
||||
|
||||
/* Export for ED_undo_sys. */
|
||||
|
||||
void ED_text_undosys_type(UndoType *ut)
|
||||
{
|
||||
ut->name = "Text";
|
||||
ut->poll = text_undosys_poll;
|
||||
ut->step_encode = text_undosys_step_encode;
|
||||
ut->step_decode = text_undosys_step_decode;
|
||||
ut->step_free = text_undosys_step_free;
|
||||
|
||||
ut->step_foreach_ID_ref = text_undosys_foreach_ID_ref;
|
||||
|
||||
ut->mode = BKE_UNDOTYPE_MODE_ACCUMULATE;
|
||||
ut->use_context = true;
|
||||
|
||||
ut->step_size = sizeof(TextUndoStep);
|
||||
}
|
||||
|
||||
/** \} */
|
@ -1893,7 +1893,7 @@ static void createTransParticleVerts(bContext *C, TransInfo *t)
|
||||
TransDataExtension *tx;
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
ParticleEditSettings *pset = PE_settings(t->scene);
|
||||
PTCacheEdit *edit = PE_get_current(t->scene, t->view_layer, ob);
|
||||
PTCacheEdit *edit = PE_get_current(t->scene, ob);
|
||||
ParticleSystem *psys = NULL;
|
||||
ParticleSystemModifierData *psmd = NULL;
|
||||
PTCacheEditPoint *point;
|
||||
@ -2010,7 +2010,7 @@ void flushTransParticles(TransInfo *t)
|
||||
Scene *scene = t->scene;
|
||||
ViewLayer *view_layer = t->view_layer;
|
||||
Object *ob = OBACT(view_layer);
|
||||
PTCacheEdit *edit = PE_get_current(scene, view_layer, ob);
|
||||
PTCacheEdit *edit = PE_get_current(scene, ob);
|
||||
ParticleSystem *psys = edit->psys;
|
||||
ParticleSystemModifierData *psmd = NULL;
|
||||
PTCacheEditPoint *point;
|
||||
@ -6550,7 +6550,7 @@ void special_aftertrans_update(bContext *C, TransInfo *t)
|
||||
else if ((t->view_layer->basact) &&
|
||||
(ob = t->view_layer->basact->object) &&
|
||||
(t->eval_ctx.object_mode & OB_MODE_PARTICLE_EDIT) &&
|
||||
PE_get_current(t->scene, t->view_layer, ob))
|
||||
PE_get_current(t->scene, ob))
|
||||
{
|
||||
/* do nothing */
|
||||
}
|
||||
@ -8307,7 +8307,7 @@ void createTransData(bContext *C, TransInfo *t)
|
||||
}
|
||||
}
|
||||
else if (ob && (t->eval_ctx.object_mode & OB_MODE_PARTICLE_EDIT) &&
|
||||
PE_start_edit(PE_get_current(scene, view_layer, ob)))
|
||||
PE_start_edit(PE_get_current(scene, ob)))
|
||||
{
|
||||
createTransParticleVerts(C, t);
|
||||
t->flag |= T_POINTS;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user