diff --git a/uv_magic_uv/__init__.py b/uv_magic_uv/__init__.py
index 20709e79250400212495338d007deccc0501eeb3..63591526db6e2001725105393dd30f47ef8c0a96 100644
--- a/uv_magic_uv/__init__.py
+++ b/uv_magic_uv/__init__.py
@@ -28,8 +28,8 @@ bl_info = {
     "name": "Magic UV",
     "author": "Nutti, Mifth, Jace Priester, kgeogeo, mem, imdjs"
               "Keith (Wahooney) Boshoff, McBuff, MaxRobinot, Alexander Milovsky",
-    "version": (5, 2, 0),
-    "blender": (2, 79, 0),
+    "version": (5, 3, 0),
+    "blender": (2, 80, 0),
     "location": "See Add-ons Preferences",
     "description": "UV Toolset. See Add-ons Preferences for details",
     "warning": "",
@@ -40,43 +40,80 @@ bl_info = {
     "category": "UV"
 }
 
+def check_version(major, minor, _):
+    """
+    Check blender version
+    """
+
+    if bpy.app.version[0] == major and bpy.app.version[1] == minor:
+        return 0
+    if bpy.app.version[0] > major:
+        return 1
+    if bpy.app.version[1] > minor:
+        return 1
+    return -1
+
+
 if "bpy" in locals():
     import importlib
-    importlib.reload(op)
-    importlib.reload(ui)
     importlib.reload(common)
-    importlib.reload(preferences)
-    importlib.reload(properites)
-    importlib.reload(addon_updater_ops)
-    importlib.reload(addon_updater)
+    importlib.reload(utils)
+    utils.bl_class_registry.BlClassRegistry.cleanup()
+    if check_version(2, 80, 0) >= 0:
+        importlib.reload(op)
+        importlib.reload(ui)
+        importlib.reload(properites)
+        importlib.reload(preferences)
+        importlib.reload(addon_updater_ops)
+        importlib.reload(addon_updater)
+    else:
+        importlib.reload(legacy)
 else:
-    from . import op
-    from . import ui
+    import bpy
     from . import common
-    from . import preferences
-    from . import properites
-    from . import addon_updater_ops
-    from . import addon_updater
+    from . import utils
+    if check_version(2, 80, 0) >= 0:
+        from . import op
+        from . import ui
+        from . import properites
+        from . import preferences
+        from . import addon_updater_ops
+        from . import addon_updater
+    else:
+        from . import legacy
+
 
 import bpy
 
 
 def register():
-    if not common.is_console_mode():
-        addon_updater_ops.register(bl_info)
-    properites.init_props(bpy.types.Scene)
-    bpy.utils.register_module(__name__)
-    if preferences.Preferences.enable_builtin_menu:
-        preferences.add_builtin_menu()
+    if common.check_version(2, 80, 0) >= 0:
+        utils.bl_class_registry.BlClassRegistry.register()
+        properites.init_props(bpy.types.Scene)
+        if preferences.Preferences.enable_builtin_menu:
+            preferences.add_builtin_menu()
+    else:
+        utils.bl_class_registry.BlClassRegistry.register()
+        legacy.properites.init_props(bpy.types.Scene)
+        if legacy.preferences.Preferences.enable_builtin_menu:
+            legacy.preferences.add_builtin_menu()
+        if not common.is_console_mode():
+            addon_updater_ops.register(bl_info)
 
 
 def unregister():
-    if preferences.Preferences.enable_builtin_menu:
-        preferences.remove_builtin_menu()
-    bpy.utils.unregister_module(__name__)
-    properites.clear_props(bpy.types.Scene)
-    if not common.is_console_mode():
-        addon_updater_ops.unregister()
+    if common.check_version(2, 80, 0) >= 0:
+        if preferences.Preferences.enable_builtin_menu:
+            preferences.remove_builtin_menu()
+        properites.clear_props(bpy.types.Scene)
+        utils.bl_class_registry.BlClassRegistry.unregister()
+    else:
+        if not common.is_console_mode():
+            addon_updater_ops.unregister()
+        if legacy.preferences.Preferences.enable_builtin_menu:
+            legacy.preferences.remove_builtin_menu()
+        legacy.properites.clear_props(bpy.types.Scene)
+        utils.bl_class_registry.BlClassRegistry.unregister()
 
 
 if __name__ == "__main__":
diff --git a/uv_magic_uv/common.py b/uv_magic_uv/common.py
index b0c4306e9497b0d8e8c4e79b54f51fb45ea9c5b6..bad88167743bedafa944cff34b2c94ffdfaa808c 100644
--- a/uv_magic_uv/common.py
+++ b/uv_magic_uv/common.py
@@ -35,10 +35,16 @@ import bmesh
 
 __all__ = [
     'is_console_mode',
+    'is_debug_mode',
+    'enable_debugg_mode',
+    'disable_debug_mode',
     'debug_print',
     'check_version',
     'redraw_all_areas',
     'get_space',
+    'mouse_on_region',
+    'mouse_on_area',
+    'mouse_on_regions',
     'create_bmesh',
     'create_new_uv_map',
     'get_island_info',
@@ -51,10 +57,12 @@ __all__ = [
     'measure_uv_area',
     'diff_point_to_segment',
     'get_loop_sequences',
+    'get_overlapped_uv_info',
+    'get_flipped_uv_info',
 ]
 
 
-__DEBUG_MODE = False
+__DEBUG_MODE = True
 
 
 def is_console_mode():
diff --git a/uv_magic_uv/impl/__init__.py b/uv_magic_uv/impl/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/uv_magic_uv/impl/copy_paste_uv_impl.py b/uv_magic_uv/impl/copy_paste_uv_impl.py
new file mode 100644
index 0000000000000000000000000000000000000000..ed44637b31b1b054010754a477e0f98bf5416662
--- /dev/null
+++ b/uv_magic_uv/impl/copy_paste_uv_impl.py
@@ -0,0 +1,271 @@
+# <pep8-80 compliant>
+
+# ##### 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 #####
+
+__author__ = "imdjs, Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+
+import bmesh
+
+from .. import common
+
+
+__all__ = [
+    'is_valid_context',
+    'get_copy_uv_layers',
+    'get_paste_uv_layers',
+    'get_src_face_info',
+    'get_dest_face_info',
+    'get_select_history_src_face_info',
+    'get_select_history_dest_face_info',
+    'paste_uv',
+]
+
+
+def is_valid_context(context):
+    obj = context.object
+
+    # only edit mode is allowed to execute
+    if obj is None:
+        return False
+    if obj.type != 'MESH':
+        return False
+    if context.object.mode != 'EDIT':
+        return False
+
+    # only 'VIEW_3D' space is allowed to execute
+    for space in context.area.spaces:
+        if space.type == 'VIEW_3D':
+            break
+    else:
+        return False
+
+    return True
+
+
+def get_copy_uv_layers(ops_obj, bm, uv_map):
+    uv_layers = []
+    if uv_map == "__default":
+        if not bm.loops.layers.uv:
+            ops_obj.report(
+                {'WARNING'}, "Object must have more than one UV map")
+            return None
+        uv_layers.append(bm.loops.layers.uv.verify())
+        ops_obj.report({'INFO'}, "Copy UV coordinate")
+    elif uv_map == "__all":
+        for uv in bm.loops.layers.uv.keys():
+            uv_layers.append(bm.loops.layers.uv[uv])
+        ops_obj.report({'INFO'}, "Copy UV coordinate (UV map: ALL)")
+    else:
+        uv_layers.append(bm.loops.layers.uv[uv_map])
+        ops_obj.report(
+            {'INFO'}, "Copy UV coordinate (UV map:{})".format(uv_map))
+
+    return uv_layers
+
+
+def get_paste_uv_layers(ops_obj, obj, bm, src_info, uv_map):
+    uv_layers = []
+    if uv_map == "__default":
+        if not bm.loops.layers.uv:
+            ops_obj.report(
+                {'WARNING'}, "Object must have more than one UV map")
+            return None
+        uv_layers.append(bm.loops.layers.uv.verify())
+        ops_obj.report({'INFO'}, "Paste UV coordinate")
+    elif uv_map == "__new":
+        new_uv_map = common.create_new_uv_map(obj)
+        if not new_uv_map:
+            ops_obj.report({'WARNING'},
+                           "Reached to the maximum number of UV map")
+            return None
+        uv_layers.append(bm.loops.layers.uv[new_uv_map.name])
+        ops_obj.report(
+            {'INFO'}, "Paste UV coordinate (UV map:{})".format(new_uv_map))
+    elif uv_map == "__all":
+        for src_layer in src_info.keys():
+            if src_layer not in bm.loops.layers.uv.keys():
+                new_uv_map = common.create_new_uv_map(obj, src_layer)
+                if not new_uv_map:
+                    ops_obj.report({'WARNING'},
+                                   "Reached to the maximum number of UV map")
+                    return None
+            uv_layers.append(bm.loops.layers.uv[src_layer])
+        ops_obj.report({'INFO'}, "Paste UV coordinate (UV map: ALL)")
+    else:
+        uv_layers.append(bm.loops.layers.uv[uv_map])
+        ops_obj.report(
+            {'INFO'}, "Paste UV coordinate (UV map:{})".format(uv_map))
+
+    return uv_layers
+
+
+def get_src_face_info(ops_obj, bm, uv_layers, only_select=False):
+    src_info = {}
+    for layer in uv_layers:
+        face_info = []
+        for face in bm.faces:
+            if not only_select or face.select:
+                info = {
+                    "index": face.index,
+                    "uvs": [l[layer].uv.copy() for l in face.loops],
+                    "pin_uvs": [l[layer].pin_uv for l in face.loops],
+                    "seams": [l.edge.seam for l in face.loops],
+                }
+                face_info.append(info)
+        if not face_info:
+            ops_obj.report({'WARNING'}, "No faces are selected")
+            return None
+        src_info[layer.name] = face_info
+
+    return src_info
+
+
+def get_dest_face_info(ops_obj, bm, uv_layers, src_info, strategy,
+                       only_select=False):
+    dest_info = {}
+    for layer in uv_layers:
+        face_info = []
+        for face in bm.faces:
+            if not only_select or face.select:
+                info = {
+                    "index": face.index,
+                    "uvs": [l[layer].uv.copy() for l in face.loops],
+                }
+                face_info.append(info)
+        if not face_info:
+            ops_obj.report({'WARNING'}, "No faces are selected")
+            return None
+        key = list(src_info.keys())[0]
+        src_face_count = len(src_info[key])
+        dest_face_count = len(face_info)
+        if strategy == 'N_N' and src_face_count != dest_face_count:
+            ops_obj.report(
+                {'WARNING'},
+                "Number of selected faces is different from copied" +
+                "(src:{}, dest:{})"
+                .format(src_face_count, dest_face_count))
+            return None
+        dest_info[layer.name] = face_info
+
+    return dest_info
+
+
+def get_select_history_src_face_info(ops_obj, bm, uv_layers):
+    src_info = {}
+    for layer in uv_layers:
+        face_info = []
+        for hist in bm.select_history:
+            if isinstance(hist, bmesh.types.BMFace) and hist.select:
+                info = {
+                    "index": hist.index,
+                    "uvs": [l[layer].uv.copy() for l in hist.loops],
+                    "pin_uvs": [l[layer].pin_uv for l in hist.loops],
+                    "seams": [l.edge.seam for l in hist.loops],
+                }
+                face_info.append(info)
+        if not face_info:
+            ops_obj.report({'WARNING'}, "No faces are selected")
+            return None
+        src_info[layer.name] = face_info
+
+    return src_info
+
+
+def get_select_history_dest_face_info(ops_obj, bm, uv_layers, src_info,
+                                      strategy):
+    dest_info = {}
+    for layer in uv_layers:
+        face_info = []
+        for hist in bm.select_history:
+            if isinstance(hist, bmesh.types.BMFace) and hist.select:
+                info = {
+                    "index": hist.index,
+                    "uvs": [l[layer].uv.copy() for l in hist.loops],
+                }
+                face_info.append(info)
+        if not face_info:
+            ops_obj.report({'WARNING'}, "No faces are selected")
+            return None
+        key = list(src_info.keys())[0]
+        src_face_count = len(src_info[key])
+        dest_face_count = len(face_info)
+        if strategy == 'N_N' and src_face_count != dest_face_count:
+            ops_obj.report(
+                {'WARNING'},
+                "Number of selected faces is different from copied" +
+                "(src:{}, dest:{})"
+                .format(src_face_count, dest_face_count))
+            return None
+        dest_info[layer.name] = face_info
+
+    return dest_info
+
+
+def paste_uv(ops_obj, bm, src_info, dest_info, uv_layers, strategy, flip,
+             rotate, copy_seams):
+    for slayer_name, dlayer in zip(src_info.keys(), uv_layers):
+        src_faces = src_info[slayer_name]
+        dest_faces = dest_info[dlayer.name]
+
+        for idx, dinfo in enumerate(dest_faces):
+            sinfo = None
+            if strategy == 'N_N':
+                sinfo = src_faces[idx]
+            elif strategy == 'N_M':
+                sinfo = src_faces[idx % len(src_faces)]
+
+            suv = sinfo["uvs"]
+            spuv = sinfo["pin_uvs"]
+            ss = sinfo["seams"]
+            if len(sinfo["uvs"]) != len(dinfo["uvs"]):
+                ops_obj.report({'WARNING'}, "Some faces are different size")
+                return -1
+
+            suvs_fr = [uv for uv in suv]
+            spuvs_fr = [pin_uv for pin_uv in spuv]
+            ss_fr = [s for s in ss]
+
+            # flip UVs
+            if flip is True:
+                suvs_fr.reverse()
+                spuvs_fr.reverse()
+                ss_fr.reverse()
+
+            # rotate UVs
+            for _ in range(rotate):
+                uv = suvs_fr.pop()
+                pin_uv = spuvs_fr.pop()
+                s = ss_fr.pop()
+                suvs_fr.insert(0, uv)
+                spuvs_fr.insert(0, pin_uv)
+                ss_fr.insert(0, s)
+
+            # paste UVs
+            for l, suv, spuv, ss in zip(bm.faces[dinfo["index"]].loops,
+                                        suvs_fr, spuvs_fr, ss_fr):
+                l[dlayer].uv = suv
+                l[dlayer].pin_uv = spuv
+                if copy_seams is True:
+                    l.edge.seam = ss
+
+    return 0
diff --git a/uv_magic_uv/impl/copy_paste_uv_uvedit_impl.py b/uv_magic_uv/impl/copy_paste_uv_uvedit_impl.py
new file mode 100644
index 0000000000000000000000000000000000000000..f14a70d63c9c81fedc297ee0d107c24b9d708548
--- /dev/null
+++ b/uv_magic_uv/impl/copy_paste_uv_uvedit_impl.py
@@ -0,0 +1,166 @@
+# <pep8-80 compliant>
+
+# ##### 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 #####
+
+__author__ = "imdjs, Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import math
+from math import atan2, sin, cos
+
+import bmesh
+from mathutils import Vector
+
+from .. import common
+
+
+__all__ = [
+    'is_valid_context',
+    'CopyUVImpl',
+    'PasteUVImpl',
+]
+
+
+def is_valid_context(context):
+    obj = context.object
+
+    # only edit mode is allowed to execute
+    if obj is None:
+        return False
+    if obj.type != 'MESH':
+        return False
+    if context.object.mode != 'EDIT':
+        return False
+
+    # 'IMAGE_EDITOR' and 'VIEW_3D' space is allowed to execute.
+    # If 'View_3D' space is not allowed, you can't find option in Tool-Shelf
+    # after the execution
+    for space in context.area.spaces:
+        if (space.type == 'IMAGE_EDITOR') or (space.type == 'VIEW_3D'):
+            break
+    else:
+        return False
+
+    return True
+
+
+class CopyUVImpl:
+    @classmethod
+    def poll(cls, context):
+        # we can not get area/space/region from console
+        if common.is_console_mode():
+            return True
+        return is_valid_context(context)
+
+    def execute(self, _, context):
+        props = context.scene.muv_props.copy_paste_uv_uvedit
+        obj = context.active_object
+        bm = bmesh.from_edit_mesh(obj.data)
+        uv_layer = bm.loops.layers.uv.verify()
+        if common.check_version(2, 73, 0) >= 0:
+            bm.faces.ensure_lookup_table()
+
+        props.src_uvs = []
+        for face in bm.faces:
+            if not face.select:
+                continue
+            skip = False
+            for l in face.loops:
+                if not l[uv_layer].select:
+                    skip = True
+                    break
+            if skip:
+                continue
+            props.src_uvs.append([l[uv_layer].uv.copy() for l in face.loops])
+
+        return {'FINISHED'}
+
+
+class PasteUVImpl:
+    @classmethod
+    def poll(cls, context):
+        # we can not get area/space/region from console
+        if common.is_console_mode():
+            return True
+        sc = context.scene
+        props = sc.muv_props.copy_paste_uv_uvedit
+        if not props.src_uvs:
+            return False
+        return is_valid_context(context)
+
+    def execute(self, _, context):
+        props = context.scene.muv_props.copy_paste_uv_uvedit
+        obj = context.active_object
+        bm = bmesh.from_edit_mesh(obj.data)
+        uv_layer = bm.loops.layers.uv.verify()
+        if common.check_version(2, 73, 0) >= 0:
+            bm.faces.ensure_lookup_table()
+
+        dest_uvs = []
+        dest_face_indices = []
+        for face in bm.faces:
+            if not face.select:
+                continue
+            skip = False
+            for l in face.loops:
+                if not l[uv_layer].select:
+                    skip = True
+                    break
+            if skip:
+                continue
+            dest_face_indices.append(face.index)
+            uvs = [l[uv_layer].uv.copy() for l in face.loops]
+            dest_uvs.append(uvs)
+
+        for suvs, duvs in zip(props.src_uvs, dest_uvs):
+            src_diff = suvs[1] - suvs[0]
+            dest_diff = duvs[1] - duvs[0]
+
+            src_base = suvs[0]
+            dest_base = duvs[0]
+
+            src_rad = atan2(src_diff.y, src_diff.x)
+            dest_rad = atan2(dest_diff.y, dest_diff.x)
+            if src_rad < dest_rad:
+                radian = dest_rad - src_rad
+            elif src_rad > dest_rad:
+                radian = math.pi * 2 - (src_rad - dest_rad)
+            else:       # src_rad == dest_rad
+                radian = 0.0
+
+            ratio = dest_diff.length / src_diff.length
+            break
+
+        for suvs, fidx in zip(props.src_uvs, dest_face_indices):
+            for l, suv in zip(bm.faces[fidx].loops, suvs):
+                base = suv - src_base
+                radian_ref = atan2(base.y, base.x)
+                radian_fin = (radian + radian_ref)
+                length = base.length
+                turn = Vector((length * cos(radian_fin),
+                               length * sin(radian_fin)))
+                target_uv = Vector((turn.x * ratio, turn.y * ratio)) + \
+                    dest_base
+                l[uv_layer].uv = target_uv
+
+        bmesh.update_edit_mesh(obj.data)
+
+        return {'FINISHED'}
diff --git a/uv_magic_uv/impl/flip_rotate_impl.py b/uv_magic_uv/impl/flip_rotate_impl.py
new file mode 100644
index 0000000000000000000000000000000000000000..f74bc256a6ac39c2958fc3ff50df3ae3299ce9ec
--- /dev/null
+++ b/uv_magic_uv/impl/flip_rotate_impl.py
@@ -0,0 +1,133 @@
+# <pep8-80 compliant>
+
+# ##### 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 #####
+
+__author__ = "imdjs, Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+
+__all__ = [
+    'is_valid_context',
+    'get_uv_layer',
+    'get_src_face_info',
+]
+
+
+def is_valid_context(context):
+    obj = context.object
+
+    # only edit mode is allowed to execute
+    if obj is None:
+        return False
+    if obj.type != 'MESH':
+        return False
+    if context.object.mode != 'EDIT':
+        return False
+
+    # only 'VIEW_3D' space is allowed to execute
+    for space in context.area.spaces:
+        if space.type == 'VIEW_3D':
+            break
+    else:
+        return False
+
+    return True
+
+
+def get_uv_layer(ops_obj, bm):
+    # get UV layer
+    if not bm.loops.layers.uv:
+        ops_obj.report({'WARNING'}, "Object must have more than one UV map")
+        return None
+    uv_layer = bm.loops.layers.uv.verify()
+
+    return uv_layer
+
+
+def get_src_face_info(ops_obj, bm, uv_layers, only_select=False):
+    src_info = {}
+    for layer in uv_layers:
+        face_info = []
+        for face in bm.faces:
+            if not only_select or face.select:
+                info = {
+                    "index": face.index,
+                    "uvs": [l[layer].uv.copy() for l in face.loops],
+                    "pin_uvs": [l[layer].pin_uv for l in face.loops],
+                    "seams": [l.edge.seam for l in face.loops],
+                }
+                face_info.append(info)
+        if not face_info:
+            ops_obj.report({'WARNING'}, "No faces are selected")
+            return None
+        src_info[layer.name] = face_info
+
+    return src_info
+
+
+def paste_uv(ops_obj, bm, src_info, dest_info, uv_layers, strategy, flip,
+             rotate, copy_seams):
+    for slayer_name, dlayer in zip(src_info.keys(), uv_layers):
+        src_faces = src_info[slayer_name]
+        dest_faces = dest_info[dlayer.name]
+
+        for idx, dinfo in enumerate(dest_faces):
+            sinfo = None
+            if strategy == 'N_N':
+                sinfo = src_faces[idx]
+            elif strategy == 'N_M':
+                sinfo = src_faces[idx % len(src_faces)]
+
+            suv = sinfo["uvs"]
+            spuv = sinfo["pin_uvs"]
+            ss = sinfo["seams"]
+            if len(sinfo["uvs"]) != len(dinfo["uvs"]):
+                ops_obj.report({'WARNING'}, "Some faces are different size")
+                return -1
+
+            suvs_fr = [uv for uv in suv]
+            spuvs_fr = [pin_uv for pin_uv in spuv]
+            ss_fr = [s for s in ss]
+
+            # flip UVs
+            if flip is True:
+                suvs_fr.reverse()
+                spuvs_fr.reverse()
+                ss_fr.reverse()
+
+            # rotate UVs
+            for _ in range(rotate):
+                uv = suvs_fr.pop()
+                pin_uv = spuvs_fr.pop()
+                s = ss_fr.pop()
+                suvs_fr.insert(0, uv)
+                spuvs_fr.insert(0, pin_uv)
+                ss_fr.insert(0, s)
+
+            # paste UVs
+            for l, suv, spuv, ss in zip(bm.faces[dinfo["index"]].loops,
+                                        suvs_fr, spuvs_fr, ss_fr):
+                l[dlayer].uv = suv
+                l[dlayer].pin_uv = spuv
+                if copy_seams is True:
+                    l.edge.seam = ss
+
+    return 0
diff --git a/uv_magic_uv/impl/mirror_uv_impl.py b/uv_magic_uv/impl/mirror_uv_impl.py
new file mode 100644
index 0000000000000000000000000000000000000000..e79fbc2ce2d7c97ab5c65532e96133e19536bee6
--- /dev/null
+++ b/uv_magic_uv/impl/mirror_uv_impl.py
@@ -0,0 +1,158 @@
+# <pep8-80 compliant>
+
+# ##### 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 #####
+
+__author__ = "Keith (Wahooney) Boshoff, Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bmesh
+from mathutils import Vector
+
+from .. import common
+
+
+__all__ = [
+    'is_valid_context',
+    'is_vector_similar',
+    'mirror_uvs',
+    'get_face_center',
+    'MirrorUVImpl',
+]
+
+
+def is_valid_context(context):
+    obj = context.object
+
+    # only edit mode is allowed to execute
+    if obj is None:
+        return False
+    if obj.type != 'MESH':
+        return False
+    if context.object.mode != 'EDIT':
+        return False
+
+    # only 'VIEW_3D' space is allowed to execute
+    for space in context.area.spaces:
+        if space.type == 'VIEW_3D':
+            break
+    else:
+        return False
+
+    return True
+
+
+def is_vector_similar(v1, v2, error):
+    """
+    Check if two vectors are similar, within an error threshold
+    """
+    within_err_x = abs(v2.x - v1.x) < error
+    within_err_y = abs(v2.y - v1.y) < error
+    within_err_z = abs(v2.z - v1.z) < error
+
+    return within_err_x and within_err_y and within_err_z
+
+
+def mirror_uvs(uv_layer, src, dst, axis, error):
+    """
+    Copy UV coordinates from one UV face to another
+    """
+    for sl in src.loops:
+        suv = sl[uv_layer].uv.copy()
+        svco = sl.vert.co.copy()
+        for dl in dst.loops:
+            dvco = dl.vert.co.copy()
+            if axis == 'X':
+                dvco.x = -dvco.x
+            elif axis == 'Y':
+                dvco.y = -dvco.y
+            elif axis == 'Z':
+                dvco.z = -dvco.z
+
+            if is_vector_similar(svco, dvco, error):
+                dl[uv_layer].uv = suv.copy()
+
+
+def get_face_center(face):
+    """
+    Get center coordinate of the face
+    """
+    center = Vector((0.0, 0.0, 0.0))
+    for v in face.verts:
+        center = center + v.co
+
+    return center / len(face.verts)
+
+
+class MirrorUVImpl:
+    @classmethod
+    def poll(cls, context):
+        # we can not get area/space/region from console
+        if common.is_console_mode():
+            return True
+        return is_valid_context(context)
+
+    def execute(self, ops_obj, context):
+        obj = context.active_object
+        bm = bmesh.from_edit_mesh(obj.data)
+
+        error = ops_obj.error
+        axis = ops_obj.axis
+
+        if common.check_version(2, 73, 0) >= 0:
+            bm.faces.ensure_lookup_table()
+        if not bm.loops.layers.uv:
+            ops_obj.report({'WARNING'},
+                           "Object must have more than one UV map")
+            return {'CANCELLED'}
+        uv_layer = bm.loops.layers.uv.verify()
+
+        faces = [f for f in bm.faces if f.select]
+        for f_dst in faces:
+            count = len(f_dst.verts)
+            for f_src in bm.faces:
+                # check if this is a candidate to do mirror UV
+                if f_src.index == f_dst.index:
+                    continue
+                if count != len(f_src.verts):
+                    continue
+
+                # test if the vertices x values are the same sign
+                dst = get_face_center(f_dst)
+                src = get_face_center(f_src)
+                if (dst.x > 0 and src.x > 0) or (dst.x < 0 and src.x < 0):
+                    continue
+
+                # invert source axis
+                if axis == 'X':
+                    src.x = -src.x
+                elif axis == 'Y':
+                    src.y = -src.z
+                elif axis == 'Z':
+                    src.z = -src.z
+
+                # do mirror UV
+                if is_vector_similar(dst, src, error):
+                    mirror_uvs(
+                        uv_layer, f_src, f_dst, ops_obj.axis, ops_obj.error)
+
+        bmesh.update_edit_mesh(obj.data)
+
+        return {'FINISHED'}
diff --git a/uv_magic_uv/impl/move_uv_impl.py b/uv_magic_uv/impl/move_uv_impl.py
new file mode 100644
index 0000000000000000000000000000000000000000..ce507fbab165c8ef75fc80e5a554a7344e62650c
--- /dev/null
+++ b/uv_magic_uv/impl/move_uv_impl.py
@@ -0,0 +1,166 @@
+# <pep8-80 compliant>
+
+# ##### 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 #####
+
+__author__ = "imdjs, Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bmesh
+from mathutils import Vector
+
+from .. import common
+
+
+__all__ = [
+    'is_valid_context',
+    'find_uv',
+    'MoveUVImpl',
+]
+
+
+def is_valid_context(context):
+    obj = context.object
+
+    # only edit mode is allowed to execute
+    if obj is None:
+        return False
+    if obj.type != 'MESH':
+        return False
+    if context.object.mode != 'EDIT':
+        return False
+
+    # only 'VIEW_3D' space is allowed to execute
+    for space in context.area.spaces:
+        if space.type == 'VIEW_3D':
+            break
+    else:
+        return False
+
+    return True
+
+
+def find_uv(context):
+    bm = bmesh.from_edit_mesh(context.object.data)
+    topology_dict = []
+    uvs = []
+    active_uv = bm.loops.layers.uv.active
+    for fidx, f in enumerate(bm.faces):
+        for vidx, v in enumerate(f.verts):
+            if v.select:
+                uvs.append(f.loops[vidx][active_uv].uv.copy())
+                topology_dict.append([fidx, vidx])
+
+    return topology_dict, uvs
+
+
+class MoveUVImpl():
+    __running = False
+
+    def __init__(self):
+        self.__topology_dict = []
+        self.__prev_mouse = Vector((0.0, 0.0))
+        self.__offset_uv = Vector((0.0, 0.0))
+        self.__prev_offset_uv = Vector((0.0, 0.0))
+        self.__first_time = True
+        self.__ini_uvs = []
+        self.__operating = False
+
+    @classmethod
+    def poll(cls, context):
+        # we can not get area/space/region from console
+        if common.is_console_mode():
+            return False
+        if cls.is_running(context):
+            return False
+        return is_valid_context(context)
+
+    @classmethod
+    def is_running(cls, _):
+        return cls.__running
+
+    def modal(self, _, context, event):
+        if self.__first_time is True:
+            self.__prev_mouse = Vector((
+                event.mouse_region_x, event.mouse_region_y))
+            self.__first_time = False
+            return {'RUNNING_MODAL'}
+
+        # move UV
+        div = 10000
+        self.__offset_uv += Vector((
+            (event.mouse_region_x - self.__prev_mouse.x) / div,
+            (event.mouse_region_y - self.__prev_mouse.y) / div))
+        ouv = self.__offset_uv
+        pouv = self.__prev_offset_uv
+        vec = Vector((ouv.x - ouv.y, ouv.x + ouv.y))
+        dv = vec - pouv
+        self.__prev_offset_uv = vec
+        self.__prev_mouse = Vector((
+            event.mouse_region_x, event.mouse_region_y))
+
+        # check if operation is started
+        if not self.__operating:
+            if event.type == 'LEFTMOUSE' and event.value == 'RELEASE':
+                self.__operating = True
+            return {'RUNNING_MODAL'}
+
+        # update UV
+        obj = context.object
+        bm = bmesh.from_edit_mesh(obj.data)
+        active_uv = bm.loops.layers.uv.active
+        for fidx, vidx in self.__topology_dict:
+            l = bm.faces[fidx].loops[vidx]
+            l[active_uv].uv = l[active_uv].uv + dv
+        bmesh.update_edit_mesh(obj.data)
+
+        # check mouse preference
+        if context.user_preferences.inputs.select_mouse == 'RIGHT':
+            confirm_btn = 'LEFTMOUSE'
+            cancel_btn = 'RIGHTMOUSE'
+        else:
+            confirm_btn = 'RIGHTMOUSE'
+            cancel_btn = 'LEFTMOUSE'
+
+        # cancelled
+        if event.type == cancel_btn and event.value == 'PRESS':
+            for (fidx, vidx), uv in zip(self.__topology_dict, self.__ini_uvs):
+                bm.faces[fidx].loops[vidx][active_uv].uv = uv
+            MoveUVImpl.__running = False
+            return {'FINISHED'}
+        # confirmed
+        if event.type == confirm_btn and event.value == 'PRESS':
+            MoveUVImpl.__running = False
+            return {'FINISHED'}
+
+        return {'RUNNING_MODAL'}
+
+    def execute(self, ops_obj, context):
+        MoveUVImpl.__running = True
+        self.__operating = False
+        self.__first_time = True
+
+        context.window_manager.modal_handler_add(ops_obj)
+        self.__topology_dict, self.__ini_uvs = find_uv(context)
+
+        if context.area:
+            context.area.tag_redraw()
+
+        return {'RUNNING_MODAL'}
diff --git a/uv_magic_uv/impl/transfer_uv_impl.py b/uv_magic_uv/impl/transfer_uv_impl.py
new file mode 100644
index 0000000000000000000000000000000000000000..adc973527f9d6dcd1a49c8a81150e8206917cc18
--- /dev/null
+++ b/uv_magic_uv/impl/transfer_uv_impl.py
@@ -0,0 +1,330 @@
+# <pep8-80 compliant>
+
+# ##### 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 #####
+
+__author__ = "imdjs, Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+from collections import OrderedDict
+
+import bpy
+import bmesh
+
+from .. import common
+
+
+__all__ = [
+    'is_valid_context',
+    'get_uv_layer',
+    'main_parse',
+    'parse_faces',
+    'get_new_shared_faces',
+    'get_other_verts_edges',
+    'get_selected_src_faces',
+    'paste_uv',
+]
+
+
+def is_valid_context(context):
+    obj = context.object
+
+    # only edit mode is allowed to execute
+    if obj is None:
+        return False
+    if obj.type != 'MESH':
+        return False
+    if context.object.mode != 'EDIT':
+        return False
+
+    # only 'VIEW_3D' space is allowed to execute
+    for space in context.area.spaces:
+        if space.type == 'VIEW_3D':
+            break
+    else:
+        return False
+
+    return True
+
+
+def get_uv_layer(ops_obj, bm):
+    # get UV layer
+    if not bm.loops.layers.uv:
+        ops_obj.report({'WARNING'}, "Object must have more than one UV map")
+        return None
+    uv_layer = bm.loops.layers.uv.verify()
+
+    return uv_layer
+
+
+def main_parse(ops_obj, uv_layer, sel_faces, active_face, active_face_nor):
+    all_sorted_faces = OrderedDict()  # This is the main stuff
+
+    used_verts = set()
+    used_edges = set()
+
+    faces_to_parse = []
+
+    # get shared edge of two faces
+    cross_edges = []
+    for edge in active_face.edges:
+        if edge in sel_faces[0].edges and edge in sel_faces[1].edges:
+            cross_edges.append(edge)
+
+    # parse two selected faces
+    if cross_edges and len(cross_edges) == 1:
+        shared_edge = cross_edges[0]
+        vert1 = None
+        vert2 = None
+
+        dot_n = active_face_nor.normalized()
+        edge_vec_1 = (shared_edge.verts[1].co - shared_edge.verts[0].co)
+        edge_vec_len = edge_vec_1.length
+        edge_vec_1 = edge_vec_1.normalized()
+
+        af_center = active_face.calc_center_median()
+        af_vec = shared_edge.verts[0].co + (edge_vec_1 * (edge_vec_len * 0.5))
+        af_vec = (af_vec - af_center).normalized()
+
+        if af_vec.cross(edge_vec_1).dot(dot_n) > 0:
+            vert1 = shared_edge.verts[0]
+            vert2 = shared_edge.verts[1]
+        else:
+            vert1 = shared_edge.verts[1]
+            vert2 = shared_edge.verts[0]
+
+        # get active face stuff and uvs
+        face_stuff = get_other_verts_edges(
+            active_face, vert1, vert2, shared_edge, uv_layer)
+        all_sorted_faces[active_face] = face_stuff
+        used_verts.update(active_face.verts)
+        used_edges.update(active_face.edges)
+
+        # get first selected face stuff and uvs as they share shared_edge
+        second_face = sel_faces[0]
+        if second_face is active_face:
+            second_face = sel_faces[1]
+        face_stuff = get_other_verts_edges(
+            second_face, vert1, vert2, shared_edge, uv_layer)
+        all_sorted_faces[second_face] = face_stuff
+        used_verts.update(second_face.verts)
+        used_edges.update(second_face.edges)
+
+        # first Grow
+        faces_to_parse.append(active_face)
+        faces_to_parse.append(second_face)
+
+    else:
+        ops_obj.report({'WARNING'}, "Two faces should share one edge")
+        return None
+
+    # parse all faces
+    while True:
+        new_parsed_faces = []
+        if not faces_to_parse:
+            break
+        for face in faces_to_parse:
+            face_stuff = all_sorted_faces.get(face)
+            new_faces = parse_faces(face, face_stuff, used_verts, used_edges,
+                                    all_sorted_faces, uv_layer)
+            if new_faces is None:
+                ops_obj.report({'WARNING'}, "More than 2 faces share edge")
+                return None
+
+            new_parsed_faces += new_faces
+        faces_to_parse = new_parsed_faces
+
+    return all_sorted_faces
+
+
+def parse_faces(check_face, face_stuff, used_verts, used_edges,
+                all_sorted_faces, uv_layer):
+    """recurse faces around the new_grow only"""
+
+    new_shared_faces = []
+    for sorted_edge in face_stuff[1]:
+        shared_faces = sorted_edge.link_faces
+        if shared_faces:
+            if len(shared_faces) > 2:
+                bpy.ops.mesh.select_all(action='DESELECT')
+                for face_sel in shared_faces:
+                    face_sel.select = True
+                shared_faces = []
+                return None
+
+            clear_shared_faces = get_new_shared_faces(
+                check_face, sorted_edge, shared_faces, all_sorted_faces.keys())
+            if clear_shared_faces:
+                shared_face = clear_shared_faces[0]
+                # get vertices of the edge
+                vert1 = sorted_edge.verts[0]
+                vert2 = sorted_edge.verts[1]
+
+                common.debug_print(face_stuff[0], vert1, vert2)
+                if face_stuff[0].index(vert1) > face_stuff[0].index(vert2):
+                    vert1 = sorted_edge.verts[1]
+                    vert2 = sorted_edge.verts[0]
+
+                common.debug_print(shared_face.verts, vert1, vert2)
+                new_face_stuff = get_other_verts_edges(
+                    shared_face, vert1, vert2, sorted_edge, uv_layer)
+                all_sorted_faces[shared_face] = new_face_stuff
+                used_verts.update(shared_face.verts)
+                used_edges.update(shared_face.edges)
+
+                if common.is_debug_mode():
+                    shared_face.select = True  # test which faces are parsed
+
+                new_shared_faces.append(shared_face)
+
+    return new_shared_faces
+
+
+def get_new_shared_faces(orig_face, shared_edge, check_faces, used_faces):
+    shared_faces = []
+
+    for face in check_faces:
+        is_shared_edge = shared_edge in face.edges
+        not_used = face not in used_faces
+        not_orig = face is not orig_face
+        not_hide = face.hide is False
+        if is_shared_edge and not_used and not_orig and not_hide:
+            shared_faces.append(face)
+
+    return shared_faces
+
+
+def get_other_verts_edges(face, vert1, vert2, first_edge, uv_layer):
+    face_edges = [first_edge]
+    face_verts = [vert1, vert2]
+    face_loops = []
+
+    other_edges = [edge for edge in face.edges if edge not in face_edges]
+
+    for _ in range(len(other_edges)):
+        found_edge = None
+        # get sorted verts and edges
+        for edge in other_edges:
+            if face_verts[-1] in edge.verts:
+                other_vert = edge.other_vert(face_verts[-1])
+
+                if other_vert not in face_verts:
+                    face_verts.append(other_vert)
+
+                found_edge = edge
+                if found_edge not in face_edges:
+                    face_edges.append(edge)
+                break
+
+        other_edges.remove(found_edge)
+
+    # get sorted uvs
+    for vert in face_verts:
+        for loop in face.loops:
+            if loop.vert is vert:
+                face_loops.append(loop[uv_layer])
+                break
+
+    return [face_verts, face_edges, face_loops]
+
+
+def get_selected_src_faces(ops_obj, bm, uv_layer):
+    topology_copied = []
+
+    # get selected faces
+    active_face = bm.faces.active
+    sel_faces = [face for face in bm.faces if face.select]
+    if len(sel_faces) != 2:
+        ops_obj.report({'WARNING'}, "Two faces must be selected")
+        return None
+    if not active_face or active_face not in sel_faces:
+        ops_obj.report({'WARNING'}, "Two faces must be active")
+        return None
+
+    # parse all faces according to selection
+    active_face_nor = active_face.normal.copy()
+    all_sorted_faces = main_parse(ops_obj, uv_layer, sel_faces, active_face,
+                                  active_face_nor)
+
+    if all_sorted_faces:
+        for face_data in all_sorted_faces.values():
+            edges = face_data[1]
+            uv_loops = face_data[2]
+            uvs = [l.uv.copy() for l in uv_loops]
+            pin_uvs = [l.pin_uv for l in uv_loops]
+            seams = [e.seam for e in edges]
+            topology_copied.append([uvs, pin_uvs, seams])
+    else:
+        return None
+
+    return topology_copied
+
+
+def paste_uv(ops_obj, bm, uv_layer, src_faces, invert_normals, copy_seams):
+    # get selection history
+    all_sel_faces = [e for e in bm.select_history
+                     if isinstance(e, bmesh.types.BMFace) and e.select]
+    if len(all_sel_faces) % 2 != 0:
+        ops_obj.report({'WARNING'}, "Two faces must be selected")
+        return -1
+
+    # parse selection history
+    for i, _ in enumerate(all_sel_faces):
+        if (i == 0) or (i % 2 == 0):
+            continue
+        sel_faces = [all_sel_faces[i - 1], all_sel_faces[i]]
+        active_face = all_sel_faces[i]
+
+        # parse all faces according to selection history
+        active_face_nor = active_face.normal.copy()
+        if invert_normals:
+            active_face_nor.negate()
+        all_sorted_faces = main_parse(ops_obj, uv_layer, sel_faces,
+                                      active_face, active_face_nor)
+
+        if all_sorted_faces:
+            # check amount of copied/pasted faces
+            if len(all_sorted_faces) != len(src_faces):
+                ops_obj.report({'WARNING'},
+                               "Mesh has different amount of faces")
+                return -1
+
+            for j, face_data in enumerate(all_sorted_faces.values()):
+                copied_data = src_faces[j]
+
+                # check amount of copied/pasted verts
+                if len(copied_data[0]) != len(face_data[2]):
+                    bpy.ops.mesh.select_all(action='DESELECT')
+                    # select problematic face
+                    list(all_sorted_faces.keys())[j].select = True
+                    ops_obj.report({'WARNING'},
+                                   "Face have different amount of vertices")
+                    return 0
+
+                for k, (edge, uvloop) in enumerate(zip(face_data[1],
+                                                       face_data[2])):
+                    uvloop.uv = copied_data[0][k]
+                    uvloop.pin_uv = copied_data[1][k]
+                    if copy_seams:
+                        edge.seam = copied_data[2][k]
+        else:
+            return -1
+
+    return 0
diff --git a/uv_magic_uv/impl/uvw_impl.py b/uv_magic_uv/impl/uvw_impl.py
new file mode 100644
index 0000000000000000000000000000000000000000..e815f54fcc95ef89959d2a66aaa5e40ade1f81fa
--- /dev/null
+++ b/uv_magic_uv/impl/uvw_impl.py
@@ -0,0 +1,154 @@
+# <pep8-80 compliant>
+
+# ##### 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 #####
+
+__author__ = "imdjs, Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+
+from math import sin, cos, pi
+
+from mathutils import Vector
+
+
+def is_valid_context(context):
+    obj = context.object
+
+    # only edit mode is allowed to execute
+    if obj is None:
+        return False
+    if obj.type != 'MESH':
+        return False
+    if context.object.mode != 'EDIT':
+        return False
+
+    # only 'VIEW_3D' space is allowed to execute
+    for space in context.area.spaces:
+        if space.type == 'VIEW_3D':
+            break
+    else:
+        return False
+
+    return True
+
+
+def get_uv_layer(ops_obj, bm, assign_uvmap):
+    # get UV layer
+    if not bm.loops.layers.uv:
+        if assign_uvmap:
+            bm.loops.layers.uv.new()
+        else:
+            ops_obj.report({'WARNING'},
+                           "Object must have more than one UV map")
+            return None
+    uv_layer = bm.loops.layers.uv.verify()
+
+    return uv_layer
+
+
+def apply_box_map(bm, uv_layer, size, offset, rotation, tex_aspect):
+    scale = 1.0 / size
+
+    sx = 1.0 * scale
+    sy = 1.0 * scale
+    sz = 1.0 * scale
+    ofx = offset[0]
+    ofy = offset[1]
+    ofz = offset[2]
+    rx = rotation[0] * pi / 180.0
+    ry = rotation[1] * pi / 180.0
+    rz = rotation[2] * pi / 180.0
+    aspect = tex_aspect
+
+    sel_faces = [f for f in bm.faces if f.select]
+
+    # update UV coordinate
+    for f in sel_faces:
+        n = f.normal
+        for l in f.loops:
+            co = l.vert.co
+            x = co.x * sx
+            y = co.y * sy
+            z = co.z * sz
+
+            # X-plane
+            if abs(n[0]) >= abs(n[1]) and abs(n[0]) >= abs(n[2]):
+                if n[0] >= 0.0:
+                    u = (y - ofy) * cos(rx) + (z - ofz) * sin(rx)
+                    v = -(y * aspect - ofy) * sin(rx) + \
+                        (z * aspect - ofz) * cos(rx)
+                else:
+                    u = -(y - ofy) * cos(rx) + (z - ofz) * sin(rx)
+                    v = (y * aspect - ofy) * sin(rx) + \
+                        (z * aspect - ofz) * cos(rx)
+            # Y-plane
+            elif abs(n[1]) >= abs(n[0]) and abs(n[1]) >= abs(n[2]):
+                if n[1] >= 0.0:
+                    u = -(x - ofx) * cos(ry) + (z - ofz) * sin(ry)
+                    v = (x * aspect - ofx) * sin(ry) + \
+                        (z * aspect - ofz) * cos(ry)
+                else:
+                    u = (x - ofx) * cos(ry) + (z - ofz) * sin(ry)
+                    v = -(x * aspect - ofx) * sin(ry) + \
+                        (z * aspect - ofz) * cos(ry)
+            # Z-plane
+            else:
+                if n[2] >= 0.0:
+                    u = (x - ofx) * cos(rz) + (y - ofy) * sin(rz)
+                    v = -(x * aspect - ofx) * sin(rz) + \
+                        (y * aspect - ofy) * cos(rz)
+                else:
+                    u = -(x - ofx) * cos(rz) - (y + ofy) * sin(rz)
+                    v = -(x * aspect + ofx) * sin(rz) + \
+                        (y * aspect - ofy) * cos(rz)
+
+            l[uv_layer].uv = Vector((u, v))
+
+
+def apply_planer_map(bm, uv_layer, size, offset, rotation, tex_aspect):
+    scale = 1.0 / size
+
+    sx = 1.0 * scale
+    sy = 1.0 * scale
+    ofx = offset[0]
+    ofy = offset[1]
+    rz = rotation * pi / 180.0
+    aspect = tex_aspect
+
+    sel_faces = [f for f in bm.faces if f.select]
+
+    # calculate average of normal
+    n_ave = Vector((0.0, 0.0, 0.0))
+    for f in sel_faces:
+        n_ave = n_ave + f.normal
+    q = n_ave.rotation_difference(Vector((0.0, 0.0, 1.0)))
+
+    # update UV coordinate
+    for f in sel_faces:
+        for l in f.loops:
+            co = q @ l.vert.co
+            x = co.x * sx
+            y = co.y * sy
+
+            u = x * cos(rz) - y * sin(rz) + ofx
+            v = -x * aspect * sin(rz) - y * aspect * cos(rz) + ofy
+
+            l[uv_layer].uv = Vector((u, v))
diff --git a/uv_magic_uv/legacy/__init__.py b/uv_magic_uv/legacy/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..794d02bc0c0c9f380584bc1f290d13395ed65e1b
--- /dev/null
+++ b/uv_magic_uv/legacy/__init__.py
@@ -0,0 +1,38 @@
+# <pep8-80 compliant>
+
+# ##### 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 #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+if "bpy" in locals():
+    import importlib
+    importlib.reload(op)
+    importlib.reload(ui)
+    importlib.reload(properites)
+    importlib.reload(preferences)
+else:
+    from . import op
+    from . import ui
+    from . import properites
+    from . import preferences
+
+import bpy
diff --git a/uv_magic_uv/legacy/op/__init__.py b/uv_magic_uv/legacy/op/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..9535b76d094b148741f5563ad34f356f810cad5a
--- /dev/null
+++ b/uv_magic_uv/legacy/op/__init__.py
@@ -0,0 +1,74 @@
+# <pep8-80 compliant>
+
+# ##### 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 #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+if "bpy" in locals():
+    import importlib
+    importlib.reload(align_uv)
+    importlib.reload(align_uv_cursor)
+    importlib.reload(copy_paste_uv)
+    importlib.reload(copy_paste_uv_object)
+    importlib.reload(copy_paste_uv_uvedit)
+    importlib.reload(flip_rotate_uv)
+    importlib.reload(mirror_uv)
+    importlib.reload(move_uv)
+    importlib.reload(pack_uv)
+    importlib.reload(preserve_uv_aspect)
+    importlib.reload(select_uv)
+    importlib.reload(smooth_uv)
+    importlib.reload(texture_lock)
+    importlib.reload(texture_projection)
+    importlib.reload(texture_wrap)
+    importlib.reload(transfer_uv)
+    importlib.reload(unwrap_constraint)
+    importlib.reload(uv_bounding_box)
+    importlib.reload(uv_inspection)
+    importlib.reload(uv_sculpt)
+    importlib.reload(uvw)
+    importlib.reload(world_scale_uv)
+else:
+    from . import align_uv
+    from . import align_uv_cursor
+    from . import copy_paste_uv
+    from . import copy_paste_uv_object
+    from . import copy_paste_uv_uvedit
+    from . import flip_rotate_uv
+    from . import mirror_uv
+    from . import move_uv
+    from . import pack_uv
+    from . import preserve_uv_aspect
+    from . import select_uv
+    from . import smooth_uv
+    from . import texture_lock
+    from . import texture_projection
+    from . import texture_wrap
+    from . import transfer_uv
+    from . import unwrap_constraint
+    from . import uv_bounding_box
+    from . import uv_inspection
+    from . import uv_sculpt
+    from . import uvw
+    from . import world_scale_uv
+
+import bpy
diff --git a/uv_magic_uv/op/align_uv.py b/uv_magic_uv/legacy/op/align_uv.py
similarity index 98%
rename from uv_magic_uv/op/align_uv.py
rename to uv_magic_uv/legacy/op/align_uv.py
index 90168a56a0126f2e21403fe9bbb4837201d2a767..9d0ff5f40b9764e389594801abeb6b69c457bd8f 100644
--- a/uv_magic_uv/op/align_uv.py
+++ b/uv_magic_uv/legacy/op/align_uv.py
@@ -31,14 +31,16 @@ import bmesh
 from mathutils import Vector
 from bpy.props import EnumProperty, BoolProperty, FloatProperty
 
-from .. import common
+from ... import common
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
 
 
 __all__ = [
     'Properties',
-    'OperatorCircle',
-    'OperatorStraighten',
-    'OperatorAxis',
+    'MUV_OT_AlignUV_Circle',
+    'MUV_OT_AlignUV_Straighten',
+    'MUV_OT_AlignUV_Axis',
 ]
 
 
@@ -65,59 +67,10 @@ def is_valid_context(context):
     return True
 
 
-# get sum vertex length of loop sequences
-def get_loop_vert_len(loops):
-    length = 0
-    for l1, l2 in zip(loops[:-1], loops[1:]):
-        diff = l2.vert.co - l1.vert.co
-        length = length + abs(diff.length)
-
-    return length
-
-
-# get sum uv length of loop sequences
-def get_loop_uv_len(loops, uv_layer):
-    length = 0
-    for l1, l2 in zip(loops[:-1], loops[1:]):
-        diff = l2[uv_layer].uv - l1[uv_layer].uv
-        length = length + abs(diff.length)
-
-    return length
-
-
-# get center/radius of circle by 3 vertices
-def get_circle(v):
-    alpha = atan2((v[0].y - v[1].y), (v[0].x - v[1].x)) + math.pi / 2
-    beta = atan2((v[1].y - v[2].y), (v[1].x - v[2].x)) + math.pi / 2
-    ex = (v[0].x + v[1].x) / 2.0
-    ey = (v[0].y + v[1].y) / 2.0
-    fx = (v[1].x + v[2].x) / 2.0
-    fy = (v[1].y + v[2].y) / 2.0
-    cx = (ey - fy - ex * tan(alpha) + fx * tan(beta)) / \
-         (tan(beta) - tan(alpha))
-    cy = ey - (ex - cx) * tan(alpha)
-    center = Vector((cx, cy))
-
-    r = v[0] - center
-    radian = r.length
-
-    return center, radian
-
-
-# get position on circle with same arc length
-def calc_v_on_circle(v, center, radius):
-    base = v[0]
-    theta = atan2(base.y - center.y, base.x - center.x)
-    new_v = []
-    for i in range(len(v)):
-        angle = theta + i * 2 * math.pi / len(v)
-        new_v.append(Vector((center.x + radius * sin(angle),
-                             center.y + radius * cos(angle))))
-
-    return new_v
-
-
+@PropertyClassRegistry(legacy=True)
 class Properties:
+    idname = "align_uv"
+
     @classmethod
     def init_props(cls, scene):
         scene.muv_align_uv_enabled = BoolProperty(
@@ -176,7 +129,60 @@ class Properties:
         del scene.muv_align_uv_location
 
 
-class OperatorCircle(bpy.types.Operator):
+# get sum vertex length of loop sequences
+def get_loop_vert_len(loops):
+    length = 0
+    for l1, l2 in zip(loops[:-1], loops[1:]):
+        diff = l2.vert.co - l1.vert.co
+        length = length + abs(diff.length)
+
+    return length
+
+
+# get sum uv length of loop sequences
+def get_loop_uv_len(loops, uv_layer):
+    length = 0
+    for l1, l2 in zip(loops[:-1], loops[1:]):
+        diff = l2[uv_layer].uv - l1[uv_layer].uv
+        length = length + abs(diff.length)
+
+    return length
+
+
+# get center/radius of circle by 3 vertices
+def get_circle(v):
+    alpha = atan2((v[0].y - v[1].y), (v[0].x - v[1].x)) + math.pi / 2
+    beta = atan2((v[1].y - v[2].y), (v[1].x - v[2].x)) + math.pi / 2
+    ex = (v[0].x + v[1].x) / 2.0
+    ey = (v[0].y + v[1].y) / 2.0
+    fx = (v[1].x + v[2].x) / 2.0
+    fy = (v[1].y + v[2].y) / 2.0
+    cx = (ey - fy - ex * tan(alpha) + fx * tan(beta)) / \
+         (tan(beta) - tan(alpha))
+    cy = ey - (ex - cx) * tan(alpha)
+    center = Vector((cx, cy))
+
+    r = v[0] - center
+    radian = r.length
+
+    return center, radian
+
+
+# get position on circle with same arc length
+def calc_v_on_circle(v, center, radius):
+    base = v[0]
+    theta = atan2(base.y - center.y, base.x - center.x)
+    new_v = []
+    for i in range(len(v)):
+        angle = theta + i * 2 * math.pi / len(v)
+        new_v.append(Vector((center.x + radius * sin(angle),
+                             center.y + radius * cos(angle))))
+
+    return new_v
+
+
+@BlClassRegistry(legacy=True)
+class MUV_OT_AlignUV_Circle(bpy.types.Operator):
 
     bl_idname = "uv.muv_align_uv_operator_circle"
     bl_label = "Align UV (Circle)"
@@ -437,7 +443,8 @@ def get_vdiff_uv(uv_layer, loop_seqs, vidx, hidx):
     return int((vidx + 1) / 2) * v_uv / (len(hseq) / 2)
 
 
-class OperatorStraighten(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_AlignUV_Straighten(bpy.types.Operator):
 
     bl_idname = "uv.muv_align_uv_operator_straighten"
     bl_label = "Align UV (Straighten)"
@@ -587,7 +594,8 @@ class OperatorStraighten(bpy.types.Operator):
         return {'FINISHED'}
 
 
-class OperatorAxis(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_AlignUV_Axis(bpy.types.Operator):
 
     bl_idname = "uv.muv_align_uv_operator_axis"
     bl_label = "Align UV (XY-Axis)"
diff --git a/uv_magic_uv/op/align_uv_cursor.py b/uv_magic_uv/legacy/op/align_uv_cursor.py
similarity index 96%
rename from uv_magic_uv/op/align_uv_cursor.py
rename to uv_magic_uv/legacy/op/align_uv_cursor.py
index d787bde941be21b16d4761e16a9b05e5b0626d80..ec3e703609ba9ab2180dcb15a2676ae97ebcd471 100644
--- a/uv_magic_uv/op/align_uv_cursor.py
+++ b/uv_magic_uv/legacy/op/align_uv_cursor.py
@@ -28,12 +28,14 @@ from mathutils import Vector
 from bpy.props import EnumProperty, BoolProperty, FloatVectorProperty
 import bmesh
 
-from .. import common
+from ... import common
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
 
 
 __all__ = [
     'Properties',
-    'Operator',
+    'MUV_OT_AlignUVCursor',
 ]
 
 
@@ -50,7 +52,10 @@ def is_valid_context(context):
     return True
 
 
+@PropertyClassRegistry(legacy=True)
 class Properties:
+    idname = "align_uv_cursor"
+
     @classmethod
     def init_props(cls, scene):
         def auvc_get_cursor_loc(self):
@@ -121,7 +126,8 @@ class Properties:
         del scene.muv_uv_cursor_location_enabled
 
 
-class Operator(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_AlignUVCursor(bpy.types.Operator):
 
     bl_idname = "uv.muv_align_uv_cursor_operator"
     bl_label = "Align UV Cursor"
diff --git a/uv_magic_uv/legacy/op/copy_paste_uv.py b/uv_magic_uv/legacy/op/copy_paste_uv.py
new file mode 100644
index 0000000000000000000000000000000000000000..a8aef017fb2e3c587be00cba29ea169b5775ddff
--- /dev/null
+++ b/uv_magic_uv/legacy/op/copy_paste_uv.py
@@ -0,0 +1,531 @@
+# <pep8-80 compliant>
+
+# ##### 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 #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>, Jace Priester"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+
+import bmesh
+import bpy.utils
+from bpy.props import (
+    StringProperty,
+    BoolProperty,
+    IntProperty,
+    EnumProperty,
+)
+
+from ...impl import copy_paste_uv_impl as impl
+from ... import common
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
+
+__all__ = [
+    'Properties',
+    'MUV_OT_CopyPasteUV_CopyUV',
+    'MUV_MT_CopyPasteUV_CopyUV',
+    'MUV_OT_CopyPasteUV_PasteUV',
+    'MUV_MT_CopyPasteUV_PasteUV',
+    'MUV_OT_CopyPasteUV_SelSeqCopyUV',
+    'MUV_MT_CopyPasteUV_SelSeqCopyUV',
+    'MUV_OT_CopyPasteUV_SelSeqPasteUV',
+    'MUV_MT_CopyPasteUV_SelSeqPasteUV',
+]
+
+
+@PropertyClassRegistry(legacy=True)
+class Properties:
+    idname = "copy_paste_uv"
+
+    @classmethod
+    def init_props(cls, scene):
+        class Props():
+            src_info = None
+
+        scene.muv_props.copy_paste_uv = Props()
+        scene.muv_props.copy_paste_uv_selseq = Props()
+
+        scene.muv_copy_paste_uv_enabled = BoolProperty(
+            name="Copy/Paste UV Enabled",
+            description="Copy/Paste UV is enabled",
+            default=False
+        )
+        scene.muv_copy_paste_uv_copy_seams = BoolProperty(
+            name="Seams",
+            description="Copy Seams",
+            default=True
+        )
+        scene.muv_copy_paste_uv_mode = EnumProperty(
+            items=[
+                ('DEFAULT', "Default", "Default Mode"),
+                ('SEL_SEQ', "Selection Sequence", "Selection Sequence Mode")
+            ],
+            name="Copy/Paste UV Mode",
+            description="Copy/Paste UV Mode",
+            default='DEFAULT'
+        )
+        scene.muv_copy_paste_uv_strategy = EnumProperty(
+            name="Strategy",
+            description="Paste Strategy",
+            items=[
+                ('N_N', 'N:N', 'Number of faces must be equal to source'),
+                ('N_M', 'N:M', 'Number of faces must not be equal to source')
+            ],
+            default='N_M'
+        )
+
+    @classmethod
+    def del_props(cls, scene):
+        del scene.muv_props.copy_paste_uv
+        del scene.muv_props.copy_paste_uv_selseq
+        del scene.muv_copy_paste_uv_enabled
+        del scene.muv_copy_paste_uv_copy_seams
+        del scene.muv_copy_paste_uv_mode
+        del scene.muv_copy_paste_uv_strategy
+
+
+@BlClassRegistry(legacy=True)
+class MUV_OT_CopyPasteUV_CopyUV(bpy.types.Operator):
+    """
+    Operation class: Copy UV coordinate
+    """
+
+    bl_idname = "uv.muv_copy_paste_uv_operator_copy_uv"
+    bl_label = "Copy UV"
+    bl_description = "Copy UV coordinate"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    uv_map = StringProperty(default="__default", options={'HIDDEN'})
+
+    @classmethod
+    def poll(cls, context):
+        # we can not get area/space/region from console
+        if common.is_console_mode():
+            return True
+        return impl.is_valid_context(context)
+
+    def execute(self, context):
+        props = context.scene.muv_props.copy_paste_uv
+        obj = context.active_object
+        bm = common.create_bmesh(obj)
+
+        # get UV layer
+        uv_layers = impl.get_copy_uv_layers(self, bm, self.uv_map)
+        if not uv_layers:
+            return {'CANCELLED'}
+
+        # get selected face
+        src_info = impl.get_src_face_info(self, bm, uv_layers, True)
+        if src_info is None:
+            return {'CANCELLED'}
+        props.src_info = src_info
+
+        face_count = len(props.src_info[list(props.src_info.keys())[0]])
+        self.report({'INFO'}, "{} face(s) are copied".format(face_count))
+
+        return {'FINISHED'}
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_CopyPasteUV_CopyUV(bpy.types.Menu):
+    """
+    Menu class: Copy UV coordinate
+    """
+
+    bl_idname = "uv.muv_copy_paste_uv_menu_copy_uv"
+    bl_label = "Copy UV (Menu)"
+    bl_description = "Menu of Copy UV coordinate"
+
+    @classmethod
+    def poll(cls, context):
+        return impl.is_valid_context(context)
+
+    def draw(self, context):
+        layout = self.layout
+        # create sub menu
+        obj = context.active_object
+        bm = common.create_bmesh(obj)
+        uv_maps = bm.loops.layers.uv.keys()
+
+        ops = layout.operator(MUV_OT_CopyPasteUV_CopyUV.bl_idname,
+                              text="[Default]")
+        ops.uv_map = "__default"
+
+        ops = layout.operator(MUV_OT_CopyPasteUV_CopyUV.bl_idname,
+                              text="[All]")
+        ops.uv_map = "__all"
+
+        for m in uv_maps:
+            ops = layout.operator(MUV_OT_CopyPasteUV_CopyUV.bl_idname, text=m)
+            ops.uv_map = m
+
+
+@BlClassRegistry(legacy=True)
+class MUV_OT_CopyPasteUV_PasteUV(bpy.types.Operator):
+    """
+    Operation class: Paste UV coordinate
+    """
+
+    bl_idname = "uv.muv_copy_paste_uv_operator_paste_uv"
+    bl_label = "Paste UV"
+    bl_description = "Paste UV coordinate"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    uv_map = StringProperty(default="__default", options={'HIDDEN'})
+    strategy = EnumProperty(
+        name="Strategy",
+        description="Paste Strategy",
+        items=[
+            ('N_N', 'N:N', 'Number of faces must be equal to source'),
+            ('N_M', 'N:M', 'Number of faces must not be equal to source')
+        ],
+        default="N_M"
+    )
+    flip_copied_uv = BoolProperty(
+        name="Flip Copied UV",
+        description="Flip Copied UV...",
+        default=False
+    )
+    rotate_copied_uv = IntProperty(
+        default=0,
+        name="Rotate Copied UV",
+        min=0,
+        max=30
+    )
+    copy_seams = BoolProperty(
+        name="Seams",
+        description="Copy Seams",
+        default=True
+    )
+
+    @classmethod
+    def poll(cls, context):
+        # we can not get area/space/region from console
+        if common.is_console_mode():
+            return True
+        sc = context.scene
+        props = sc.muv_props.copy_paste_uv
+        if not props.src_info:
+            return False
+        return impl.is_valid_context(context)
+
+    def execute(self, context):
+        props = context.scene.muv_props.copy_paste_uv
+        if not props.src_info:
+            self.report({'WARNING'}, "Need copy UV at first")
+            return {'CANCELLED'}
+        obj = context.active_object
+        bm = common.create_bmesh(obj)
+
+        # get UV layer
+        uv_layers = impl.get_paste_uv_layers(self, obj, bm, props.src_info,
+                                             self.uv_map)
+        if not uv_layers:
+            return {'CANCELLED'}
+
+        # get selected face
+        dest_info = impl.get_dest_face_info(self, bm, uv_layers,
+                                            props.src_info, self.strategy,
+                                            True)
+        if dest_info is None:
+            return {'CANCELLED'}
+
+        # paste
+        ret = impl.paste_uv(self, bm, props.src_info, dest_info, uv_layers,
+                            self.strategy, self.flip_copied_uv,
+                            self.rotate_copied_uv, self.copy_seams)
+        if ret:
+            return {'CANCELLED'}
+
+        face_count = len(props.src_info[list(dest_info.keys())[0]])
+        self.report({'INFO'}, "{} face(s) are pasted".format(face_count))
+
+        bmesh.update_edit_mesh(obj.data)
+        if self.copy_seams is True:
+            obj.data.show_edge_seams = True
+
+        return {'FINISHED'}
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_CopyPasteUV_PasteUV(bpy.types.Menu):
+    """
+    Menu class: Paste UV coordinate
+    """
+
+    bl_idname = "uv.muv_copy_paste_uv_menu_paste_uv"
+    bl_label = "Paste UV (Menu)"
+    bl_description = "Menu of Paste UV coordinate"
+
+    @classmethod
+    def poll(cls, context):
+        sc = context.scene
+        props = sc.muv_props.copy_paste_uv
+        if not props.src_info:
+            return False
+        return impl.is_valid_context(context)
+
+    def draw(self, context):
+        sc = context.scene
+        layout = self.layout
+        # create sub menu
+        obj = context.active_object
+        bm = common.create_bmesh(obj)
+        uv_maps = bm.loops.layers.uv.keys()
+
+        ops = layout.operator(MUV_OT_CopyPasteUV_PasteUV.bl_idname,
+                              text="[Default]")
+        ops.uv_map = "__default"
+        ops.copy_seams = sc.muv_copy_paste_uv_copy_seams
+        ops.strategy = sc.muv_copy_paste_uv_strategy
+
+        ops = layout.operator(MUV_OT_CopyPasteUV_PasteUV.bl_idname,
+                              text="[New]")
+        ops.uv_map = "__new"
+        ops.copy_seams = sc.muv_copy_paste_uv_copy_seams
+        ops.strategy = sc.muv_copy_paste_uv_strategy
+
+        ops = layout.operator(MUV_OT_CopyPasteUV_PasteUV.bl_idname,
+                              text="[All]")
+        ops.uv_map = "__all"
+        ops.copy_seams = sc.muv_copy_paste_uv_copy_seams
+        ops.strategy = sc.muv_copy_paste_uv_strategy
+
+        for m in uv_maps:
+            ops = layout.operator(MUV_OT_CopyPasteUV_PasteUV.bl_idname, text=m)
+            ops.uv_map = m
+            ops.copy_seams = sc.muv_copy_paste_uv_copy_seams
+            ops.strategy = sc.muv_copy_paste_uv_strategy
+
+
+@BlClassRegistry(legacy=True)
+class MUV_OT_CopyPasteUV_SelSeqCopyUV(bpy.types.Operator):
+    """
+    Operation class: Copy UV coordinate by selection sequence
+    """
+
+    bl_idname = "uv.muv_copy_paste_uv_operator_selseq_copy_uv"
+    bl_label = "Copy UV (Selection Sequence)"
+    bl_description = "Copy UV data by selection sequence"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    uv_map = StringProperty(default="__default", options={'HIDDEN'})
+
+    @classmethod
+    def poll(cls, context):
+        # we can not get area/space/region from console
+        if common.is_console_mode():
+            return True
+        return impl.is_valid_context(context)
+
+    def execute(self, context):
+        props = context.scene.muv_props.copy_paste_uv_selseq
+        obj = context.active_object
+        bm = common.create_bmesh(obj)
+
+        # get UV layer
+        uv_layers = impl.get_copy_uv_layers(self, bm, self.uv_map)
+        if not uv_layers:
+            return {'CANCELLED'}
+
+        # get selected face
+        src_info = impl.get_select_history_src_face_info(self, bm, uv_layers)
+        if src_info is None:
+            return {'CANCELLED'}
+        props.src_info = src_info
+
+        face_count = len(props.src_info[list(props.src_info.keys())[0]])
+        self.report({'INFO'}, "{} face(s) are selected".format(face_count))
+
+        return {'FINISHED'}
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_CopyPasteUV_SelSeqCopyUV(bpy.types.Menu):
+    """
+    Menu class: Copy UV coordinate by selection sequence
+    """
+
+    bl_idname = "uv.muv_copy_paste_uv_menu_selseq_copy_uv"
+    bl_label = "Copy UV (Selection Sequence) (Menu)"
+    bl_description = "Menu of Copy UV coordinate by selection sequence"
+
+    @classmethod
+    def poll(cls, context):
+        return impl.is_valid_context(context)
+
+    def draw(self, context):
+        layout = self.layout
+        obj = context.active_object
+        bm = common.create_bmesh(obj)
+        uv_maps = bm.loops.layers.uv.keys()
+
+        ops = layout.operator(MUV_OT_CopyPasteUV_SelSeqCopyUV.bl_idname,
+                              text="[Default]")
+        ops.uv_map = "__default"
+
+        ops = layout.operator(MUV_OT_CopyPasteUV_SelSeqCopyUV.bl_idname,
+                              text="[All]")
+        ops.uv_map = "__all"
+
+        for m in uv_maps:
+            ops = layout.operator(MUV_OT_CopyPasteUV_SelSeqCopyUV.bl_idname,
+                                  text=m)
+            ops.uv_map = m
+
+
+@BlClassRegistry(legacy=True)
+class MUV_OT_CopyPasteUV_SelSeqPasteUV(bpy.types.Operator):
+    """
+    Operation class: Paste UV coordinate by selection sequence
+    """
+
+    bl_idname = "uv.muv_copy_paste_uv_operator_selseq_paste_uv"
+    bl_label = "Paste UV (Selection Sequence)"
+    bl_description = "Paste UV coordinate by selection sequence"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    uv_map = StringProperty(default="__default", options={'HIDDEN'})
+    strategy = EnumProperty(
+        name="Strategy",
+        description="Paste Strategy",
+        items=[
+            ('N_N', 'N:N', 'Number of faces must be equal to source'),
+            ('N_M', 'N:M', 'Number of faces must not be equal to source')
+        ],
+        default="N_M"
+    )
+    flip_copied_uv = BoolProperty(
+        name="Flip Copied UV",
+        description="Flip Copied UV...",
+        default=False
+    )
+    rotate_copied_uv = IntProperty(
+        default=0,
+        name="Rotate Copied UV",
+        min=0,
+        max=30
+    )
+    copy_seams = BoolProperty(
+        name="Seams",
+        description="Copy Seams",
+        default=True
+    )
+
+    @classmethod
+    def poll(cls, context):
+        # we can not get area/space/region from console
+        if common.is_console_mode():
+            return True
+        sc = context.scene
+        props = sc.muv_props.copy_paste_uv_selseq
+        if not props.src_info:
+            return False
+        return impl.is_valid_context(context)
+
+    def execute(self, context):
+        props = context.scene.muv_props.copy_paste_uv_selseq
+        if not props.src_info:
+            self.report({'WARNING'}, "Need copy UV at first")
+            return {'CANCELLED'}
+        obj = context.active_object
+        bm = common.create_bmesh(obj)
+
+        # get UV layer
+        uv_layers = impl.get_paste_uv_layers(self, obj, bm, props.src_info,
+                                             self.uv_map)
+        if not uv_layers:
+            return {'CANCELLED'}
+
+        # get selected face
+        dest_info = impl.get_select_history_dest_face_info(self, bm, uv_layers,
+                                                           props.src_info,
+                                                           self.strategy)
+        if dest_info is None:
+            return {'CANCELLED'}
+
+        # paste
+        ret = impl.paste_uv(self, bm, props.src_info, dest_info, uv_layers,
+                            self.strategy, self.flip_copied_uv,
+                            self.rotate_copied_uv, self.copy_seams)
+        if ret:
+            return {'CANCELLED'}
+
+        face_count = len(props.src_info[list(dest_info.keys())[0]])
+        self.report({'INFO'}, "{} face(s) are pasted".format(face_count))
+
+        bmesh.update_edit_mesh(obj.data)
+        if self.copy_seams is True:
+            obj.data.show_edge_seams = True
+
+        return {'FINISHED'}
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_CopyPasteUV_SelSeqPasteUV(bpy.types.Menu):
+    """
+    Menu class: Paste UV coordinate by selection sequence
+    """
+
+    bl_idname = "uv.muv_copy_paste_uv_menu_selseq_paste_uv"
+    bl_label = "Paste UV (Selection Sequence) (Menu)"
+    bl_description = "Menu of Paste UV coordinate by selection sequence"
+
+    @classmethod
+    def poll(cls, context):
+        sc = context.scene
+        props = sc.muv_props.copy_paste_uv_selseq
+        if not props.src_uvs or not props.src_pin_uvs:
+            return False
+        return impl.is_valid_context(context)
+
+    def draw(self, context):
+        sc = context.scene
+        layout = self.layout
+        # create sub menu
+        obj = context.active_object
+        bm = common.create_bmesh(obj)
+        uv_maps = bm.loops.layers.uv.keys()
+
+        ops = layout.operator(MUV_OT_CopyPasteUV_SelSeqPasteUV.bl_idname,
+                              text="[Default]")
+        ops.uv_map = "__default"
+        ops.copy_seams = sc.muv_copy_paste_uv_copy_seams
+        ops.strategy = sc.muv_copy_paste_uv_strategy
+
+        ops = layout.operator(MUV_OT_CopyPasteUV_SelSeqPasteUV.bl_idname,
+                              text="[New]")
+        ops.uv_map = "__new"
+        ops.copy_seams = sc.muv_copy_paste_uv_copy_seams
+        ops.strategy = sc.muv_copy_paste_uv_strategy
+
+        ops = layout.operator(MUV_OT_CopyPasteUV_SelSeqPasteUV.bl_idname,
+                              text="[All]")
+        ops.uv_map = "__all"
+        ops.copy_seams = sc.muv_copy_paste_uv_copy_seams
+        ops.strategy = sc.muv_copy_paste_uv_strategy
+
+        for m in uv_maps:
+            ops = layout.operator(MUV_OT_CopyPasteUV_SelSeqPasteUV.bl_idname,
+                                  text=m)
+            ops.uv_map = m
+            ops.copy_seams = sc.muv_copy_paste_uv_copy_seams
+            ops.strategy = sc.muv_copy_paste_uv_strategy
diff --git a/uv_magic_uv/legacy/op/copy_paste_uv_object.py b/uv_magic_uv/legacy/op/copy_paste_uv_object.py
new file mode 100644
index 0000000000000000000000000000000000000000..e09b003b91a96f5ed5fdf5bdcf7499f7f79d8ee2
--- /dev/null
+++ b/uv_magic_uv/legacy/op/copy_paste_uv_object.py
@@ -0,0 +1,298 @@
+# <pep8-80 compliant>
+
+# ##### 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 #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bmesh
+import bpy
+from bpy.props import (
+    StringProperty,
+    BoolProperty,
+)
+
+from ...impl import copy_paste_uv_impl as impl
+from ... import common
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
+
+__all__ = [
+    'Properties',
+    'MUV_OT_CopyPasteUVObject_CopyUV',
+    'MUV_MT_CopyPasteUVObject_CopyUV',
+    'MUV_OT_CopyPasteUVObject_PasteUV',
+    'MUV_MT_CopyPasteUVObject_PasteUV',
+]
+
+
+def is_valid_context(context):
+    obj = context.object
+
+    # only object mode is allowed to execute
+    if obj is None:
+        return False
+    if obj.type != 'MESH':
+        return False
+    if context.object.mode != 'OBJECT':
+        return False
+
+    # only 'VIEW_3D' space is allowed to execute
+    for space in context.area.spaces:
+        if space.type == 'VIEW_3D':
+            break
+    else:
+        return False
+
+    return True
+
+
+@PropertyClassRegistry(legacy=True)
+class Properties:
+    idname = "copy_paste_uv_object"
+
+    @classmethod
+    def init_props(cls, scene):
+        class Props():
+            src_info = None
+
+        scene.muv_props.copy_paste_uv_object = Props()
+
+        scene.muv_copy_paste_uv_object_copy_seams = BoolProperty(
+            name="Seams",
+            description="Copy Seams",
+            default=True
+        )
+
+    @classmethod
+    def del_props(cls, scene):
+        del scene.muv_props.copy_paste_uv_object
+        del scene.muv_copy_paste_uv_object_copy_seams
+
+
+def memorize_view_3d_mode(fn):
+    def __memorize_view_3d_mode(self, context):
+        mode_orig = bpy.context.object.mode
+        result = fn(self, context)
+        bpy.ops.object.mode_set(mode=mode_orig)
+        return result
+    return __memorize_view_3d_mode
+
+
+@BlClassRegistry(legacy=True)
+class MUV_OT_CopyPasteUVObject_CopyUV(bpy.types.Operator):
+    """
+    Operation class: Copy UV coordinate among objects
+    """
+
+    bl_idname = "object.muv_copy_paste_uv_object_operator_copy_uv"
+    bl_label = "Copy UV (Among Objects)"
+    bl_description = "Copy UV coordinate (Among Objects)"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    uv_map = StringProperty(default="__default", options={'HIDDEN'})
+
+    @classmethod
+    def poll(cls, context):
+        # we can not get area/space/region from console
+        if common.is_console_mode():
+            return True
+        return is_valid_context(context)
+
+    @memorize_view_3d_mode
+    def execute(self, context):
+        props = context.scene.muv_props.copy_paste_uv_object
+        bpy.ops.object.mode_set(mode='EDIT')
+        obj = context.active_object
+        bm = common.create_bmesh(obj)
+
+        # get UV layer
+        uv_layers = impl.get_copy_uv_layers(self, bm, self.uv_map)
+        if not uv_layers:
+            return {'CANCELLED'}
+
+        # get selected face
+        src_info = impl.get_src_face_info(self, bm, uv_layers)
+        if src_info is None:
+            return {'CANCELLED'}
+        props.src_info = src_info
+
+        self.report({'INFO'},
+                    "{}'s UV coordinates are copied".format(obj.name))
+
+        return {'FINISHED'}
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_CopyPasteUVObject_CopyUV(bpy.types.Menu):
+    """
+    Menu class: Copy UV coordinate among objects
+    """
+
+    bl_idname = "object.muv_copy_paste_uv_object_menu_copy_uv"
+    bl_label = "Copy UV (Among Objects) (Menu)"
+    bl_description = "Menu of Copy UV coordinate (Among Objects)"
+
+    @classmethod
+    def poll(cls, context):
+        return is_valid_context(context)
+
+    def draw(self, _):
+        layout = self.layout
+        # create sub menu
+        uv_maps = bpy.context.active_object.data.uv_textures.keys()
+
+        ops = layout.operator(MUV_OT_CopyPasteUVObject_CopyUV.bl_idname,
+                              text="[Default]")
+        ops.uv_map = "__default"
+
+        ops = layout.operator(MUV_OT_CopyPasteUVObject_CopyUV.bl_idname,
+                              text="[All]")
+        ops.uv_map = "__all"
+
+        for m in uv_maps:
+            ops = layout.operator(MUV_OT_CopyPasteUVObject_CopyUV.bl_idname,
+                                  text=m)
+            ops.uv_map = m
+
+
+@BlClassRegistry(legacy=True)
+class MUV_OT_CopyPasteUVObject_PasteUV(bpy.types.Operator):
+    """
+    Operation class: Paste UV coordinate among objects
+    """
+
+    bl_idname = "object.muv_copy_paste_uv_object_operator_paste_uv"
+    bl_label = "Paste UV (Among Objects)"
+    bl_description = "Paste UV coordinate (Among Objects)"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    uv_map = StringProperty(default="__default", options={'HIDDEN'})
+    copy_seams = BoolProperty(
+        name="Seams",
+        description="Copy Seams",
+        default=True
+    )
+
+    @classmethod
+    def poll(cls, context):
+        # we can not get area/space/region from console
+        if common.is_console_mode():
+            return True
+        sc = context.scene
+        props = sc.muv_props.copy_paste_uv_object
+        if not props.src_info:
+            return False
+        return is_valid_context(context)
+
+    @memorize_view_3d_mode
+    def execute(self, context):
+        props = context.scene.muv_props.copy_paste_uv_object
+        if not props.src_info:
+            self.report({'WARNING'}, "Need copy UV at first")
+            return {'CANCELLED'}
+
+        for o in bpy.data.objects:
+            if not hasattr(o.data, "uv_textures") or not o.select:
+                continue
+
+            bpy.ops.object.mode_set(mode='OBJECT')
+            bpy.context.scene.objects.active = o
+            bpy.ops.object.mode_set(mode='EDIT')
+
+            obj = context.active_object
+            bm = common.create_bmesh(obj)
+
+            # get UV layer
+            uv_layers = impl.get_paste_uv_layers(self, obj, bm, props.src_info,
+                                                 self.uv_map)
+            if not uv_layers:
+                return {'CANCELLED'}
+
+            # get selected face
+            dest_info = impl.get_dest_face_info(self, bm, uv_layers,
+                                                props.src_info, 'N_N')
+            if dest_info is None:
+                return {'CANCELLED'}
+
+            # paste
+            ret = impl.paste_uv(self, bm, props.src_info, dest_info, uv_layers,
+                                'N_N', 0, 0, self.copy_seams)
+            if ret:
+                return {'CANCELLED'}
+
+            bmesh.update_edit_mesh(obj.data)
+            if self.copy_seams is True:
+                obj.data.show_edge_seams = True
+
+            self.report(
+                {'INFO'}, "{}'s UV coordinates are pasted".format(obj.name))
+
+        return {'FINISHED'}
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_CopyPasteUVObject_PasteUV(bpy.types.Menu):
+    """
+    Menu class: Paste UV coordinate among objects
+    """
+
+    bl_idname = "object.muv_copy_paste_uv_object_menu_paste_uv"
+    bl_label = "Paste UV (Among Objects) (Menu)"
+    bl_description = "Menu of Paste UV coordinate (Among Objects)"
+
+    @classmethod
+    def poll(cls, context):
+        sc = context.scene
+        props = sc.muv_props.copy_paste_uv_object
+        if not props.src_info:
+            return False
+        return is_valid_context(context)
+
+    def draw(self, context):
+        sc = context.scene
+        layout = self.layout
+        # create sub menu
+        uv_maps = []
+        for obj in bpy.data.objects:
+            if hasattr(obj.data, "uv_textures") and obj.select:
+                uv_maps.extend(obj.data.uv_textures.keys())
+
+        ops = layout.operator(MUV_OT_CopyPasteUVObject_PasteUV.bl_idname,
+                              text="[Default]")
+        ops.uv_map = "__default"
+        ops.copy_seams = sc.muv_copy_paste_uv_object_copy_seams
+
+        ops = layout.operator(MUV_OT_CopyPasteUVObject_PasteUV.bl_idname,
+                              text="[New]")
+        ops.uv_map = "__new"
+        ops.copy_seams = sc.muv_copy_paste_uv_object_copy_seams
+
+        ops = layout.operator(MUV_OT_CopyPasteUVObject_PasteUV.bl_idname,
+                              text="[All]")
+        ops.uv_map = "__all"
+        ops.copy_seams = sc.muv_copy_paste_uv_object_copy_seams
+
+        for m in uv_maps:
+            ops = layout.operator(MUV_OT_CopyPasteUVObject_PasteUV.bl_idname,
+                                  text=m)
+            ops.uv_map = m
+            ops.copy_seams = sc.muv_copy_paste_uv_object_copy_seams
diff --git a/uv_magic_uv/legacy/op/copy_paste_uv_uvedit.py b/uv_magic_uv/legacy/op/copy_paste_uv_uvedit.py
new file mode 100644
index 0000000000000000000000000000000000000000..bb72d42a1b83708e2608e2db810a4c89bca103c5
--- /dev/null
+++ b/uv_magic_uv/legacy/op/copy_paste_uv_uvedit.py
@@ -0,0 +1,97 @@
+# <pep8-80 compliant>
+
+# ##### 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 #####
+
+__author__ = "imdjs, Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bpy
+
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
+from ...impl import copy_paste_uv_uvedit_impl as impl
+
+
+__all__ = [
+    'Properties',
+    'MUV_OT_CopyPasteUVUVEdit_CopyUV',
+    'MUV_OT_CopyPasteUVUVEdit_PasteUV',
+]
+
+
+@PropertyClassRegistry(legacy=True)
+class Properties:
+    idname = "copy_paste_uv_uvedit"
+
+    @classmethod
+    def init_props(cls, scene):
+        class Props():
+            src_uvs = None
+
+        scene.muv_props.copy_paste_uv_uvedit = Props()
+
+    @classmethod
+    def del_props(cls, scene):
+        del scene.muv_props.copy_paste_uv_uvedit
+
+
+@BlClassRegistry(legacy=True)
+class MUV_OT_CopyPasteUVUVEdit_CopyUV(bpy.types.Operator):
+    """
+    Operation class: Copy UV coordinate on UV/Image Editor
+    """
+
+    bl_idname = "uv.muv_copy_paste_uv_uvedit_operator_copy_uv"
+    bl_label = "Copy UV (UV/Image Editor)"
+    bl_description = "Copy UV coordinate (only selected in UV/Image Editor)"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    def __init__(self):
+        self.__impl = impl.CopyUVImpl()
+
+    @classmethod
+    def poll(cls, context):
+        return impl.CopyUVImpl.poll(context)
+
+    def execute(self, context):
+        return self.__impl.execute(self, context)
+
+
+@BlClassRegistry(legacy=True)
+class MUV_OT_CopyPasteUVUVEdit_PasteUV(bpy.types.Operator):
+    """
+    Operation class: Paste UV coordinate on UV/Image Editor
+    """
+
+    bl_idname = "uv.muv_copy_paste_uv_uvedit_operator_paste_uv"
+    bl_label = "Paste UV (UV/Image Editor)"
+    bl_description = "Paste UV coordinate (only selected in UV/Image Editor)"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    def __init__(self):
+        self.__impl = impl.PasteUVImpl()
+
+    @classmethod
+    def poll(cls, context):
+        return impl.PasteUVImpl.poll(context)
+
+    def execute(self, context):
+        return self.__impl.execute(self, context)
diff --git a/uv_magic_uv/legacy/op/flip_rotate_uv.py b/uv_magic_uv/legacy/op/flip_rotate_uv.py
new file mode 100644
index 0000000000000000000000000000000000000000..d94e4808c269ce75460fa4686a93ce93099ff143
--- /dev/null
+++ b/uv_magic_uv/legacy/op/flip_rotate_uv.py
@@ -0,0 +1,132 @@
+# <pep8-80 compliant>
+
+# ##### 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 #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bpy
+import bmesh
+from bpy.props import (
+    BoolProperty,
+    IntProperty,
+)
+
+from ... import common
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
+from ...impl import flip_rotate_impl as impl
+
+__all__ = [
+    'Properties',
+    'MUV_OT_FlipRotate',
+]
+
+
+@PropertyClassRegistry(legacy=True)
+class Properties:
+    idname = "flip_rotate_uv"
+
+    @classmethod
+    def init_props(cls, scene):
+        scene.muv_flip_rotate_uv_enabled = BoolProperty(
+            name="Flip/Rotate UV Enabled",
+            description="Flip/Rotate UV is enabled",
+            default=False
+        )
+        scene.muv_flip_rotate_uv_seams = BoolProperty(
+            name="Seams",
+            description="Seams",
+            default=True
+        )
+
+    @classmethod
+    def del_props(cls, scene):
+        del scene.muv_flip_rotate_uv_enabled
+        del scene.muv_flip_rotate_uv_seams
+
+
+@BlClassRegistry(legacy=True)
+class MUV_OT_FlipRotate(bpy.types.Operator):
+    """
+    Operation class: Flip and Rotate UV coordinate
+    """
+
+    bl_idname = "uv.muv_flip_rotate_uv_operator"
+    bl_label = "Flip/Rotate UV"
+    bl_description = "Flip/Rotate UV coordinate"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    flip = BoolProperty(
+        name="Flip UV",
+        description="Flip UV...",
+        default=False
+    )
+    rotate = IntProperty(
+        default=0,
+        name="Rotate UV",
+        min=0,
+        max=30
+    )
+    seams = BoolProperty(
+        name="Seams",
+        description="Seams",
+        default=True
+    )
+
+    @classmethod
+    def poll(cls, context):
+        # we can not get area/space/region from console
+        if common.is_console_mode():
+            return True
+        return impl.is_valid_context(context)
+
+    def execute(self, context):
+        self.report({'INFO'}, "Flip/Rotate UV")
+        obj = context.active_object
+        bm = bmesh.from_edit_mesh(obj.data)
+        if common.check_version(2, 73, 0) >= 0:
+            bm.faces.ensure_lookup_table()
+
+        # get UV layer
+        uv_layer = impl.get_uv_layer(self, bm)
+        if not uv_layer:
+            return {'CANCELLED'}
+
+        # get selected face
+        src_info = impl.get_src_face_info(self, bm, [uv_layer], True)
+        if not src_info:
+            return {'CANCELLED'}
+
+        face_count = len(src_info[list(src_info.keys())[0]])
+        self.report({'INFO'}, "{} face(s) are selected".format(face_count))
+
+        # paste
+        ret = impl.paste_uv(self, bm, src_info, src_info, [uv_layer], 'N_N',
+                            self.flip, self.rotate, self.seams)
+        if ret:
+            return {'CANCELLED'}
+
+        bmesh.update_edit_mesh(obj.data)
+        if self.seams is True:
+            obj.data.show_edge_seams = True
+
+        return {'FINISHED'}
diff --git a/uv_magic_uv/legacy/op/mirror_uv.py b/uv_magic_uv/legacy/op/mirror_uv.py
new file mode 100644
index 0000000000000000000000000000000000000000..e869e5e8ab964f5ef9c52347643398f0315713a2
--- /dev/null
+++ b/uv_magic_uv/legacy/op/mirror_uv.py
@@ -0,0 +1,110 @@
+# <pep8-80 compliant>
+
+# ##### 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 #####
+
+__author__ = "Keith (Wahooney) Boshoff, Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bpy
+from bpy.props import (
+    EnumProperty,
+    FloatProperty,
+    BoolProperty,
+)
+
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
+from ...impl import mirror_uv_impl as impl
+
+
+__all__ = [
+    'Properties',
+    'MUV_OT_MirrorUV',
+]
+
+
+@PropertyClassRegistry(legacy=True)
+class Properties:
+    idname = "mirror_uv"
+
+    @classmethod
+    def init_props(cls, scene):
+        scene.muv_mirror_uv_enabled = BoolProperty(
+            name="Mirror UV Enabled",
+            description="Mirror UV is enabled",
+            default=False
+        )
+        scene.muv_mirror_uv_axis = EnumProperty(
+            items=[
+                ('X', "X", "Mirror Along X axis"),
+                ('Y', "Y", "Mirror Along Y axis"),
+                ('Z', "Z", "Mirror Along Z axis")
+            ],
+            name="Axis",
+            description="Mirror Axis",
+            default='X'
+        )
+
+    @classmethod
+    def del_props(cls, scene):
+        del scene.muv_mirror_uv_enabled
+        del scene.muv_mirror_uv_axis
+
+
+@BlClassRegistry(legacy=True)
+class MUV_OT_MirrorUV(bpy.types.Operator):
+    """
+    Operation class: Mirror UV
+    """
+
+    bl_idname = "uv.muv_mirror_uv_operator"
+    bl_label = "Mirror UV"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    axis = EnumProperty(
+        items=(
+            ('X', "X", "Mirror Along X axis"),
+            ('Y', "Y", "Mirror Along Y axis"),
+            ('Z', "Z", "Mirror Along Z axis")
+        ),
+        name="Axis",
+        description="Mirror Axis",
+        default='X'
+    )
+    error = FloatProperty(
+        name="Error",
+        description="Error threshold",
+        default=0.001,
+        min=0.0,
+        max=100.0,
+        soft_min=0.0,
+        soft_max=1.0
+    )
+
+    def __init__(self):
+        self.__impl = impl.MirrorUVImpl()
+
+    @classmethod
+    def poll(cls, context):
+        return impl.MirrorUVImpl.poll(context)
+
+    def execute(self, context):
+        return self.__impl.execute(self, context)
diff --git a/uv_magic_uv/legacy/op/move_uv.py b/uv_magic_uv/legacy/op/move_uv.py
new file mode 100644
index 0000000000000000000000000000000000000000..2988c2ce6cf699e551d05ef71f8cd35d0e67a544
--- /dev/null
+++ b/uv_magic_uv/legacy/op/move_uv.py
@@ -0,0 +1,82 @@
+# <pep8-80 compliant>
+
+# ##### 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 #####
+
+__author__ = "kgeogeo, mem, Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bpy
+from bpy.props import BoolProperty
+
+from ...impl import move_uv_impl as impl
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
+
+
+__all__ = [
+    'Properties',
+    'MUV_OT_MoveUV',
+]
+
+
+@PropertyClassRegistry(legacy=True)
+class Properties:
+    idname = "move_uv"
+
+    @classmethod
+    def init_props(cls, scene):
+        scene.muv_move_uv_enabled = BoolProperty(
+            name="Move UV Enabled",
+            description="Move UV is enabled",
+            default=False
+        )
+
+    @classmethod
+    def del_props(cls, scene):
+        del scene.muv_move_uv_enabled
+
+
+@BlClassRegistry(legacy=True)
+class MUV_OT_MoveUV(bpy.types.Operator):
+    """
+    Operator class: Move UV
+    """
+
+    bl_idname = "uv.muv_move_uv_operator"
+    bl_label = "Move UV"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    def __init__(self):
+        self.__impl = impl.MoveUVImpl()
+
+    @classmethod
+    def poll(cls, context):
+        return impl.MoveUVImpl.poll(context)
+
+    @classmethod
+    def is_running(cls, _):
+        return impl.MoveUVImpl.is_running(_)
+
+    def modal(self, context, event):
+        return self.__impl.modal(self, context, event)
+
+    def execute(self, context):
+        return self.__impl.execute(self, context)
diff --git a/uv_magic_uv/op/pack_uv.py b/uv_magic_uv/legacy/op/pack_uv.py
similarity index 96%
rename from uv_magic_uv/op/pack_uv.py
rename to uv_magic_uv/legacy/op/pack_uv.py
index 39340fda0a9aef34c2a40883a9f98529f751ceda..f8d5884324628e28b27b5152645c31c9b8df50a0 100644
--- a/uv_magic_uv/op/pack_uv.py
+++ b/uv_magic_uv/legacy/op/pack_uv.py
@@ -35,12 +35,14 @@ from bpy.props import (
 )
 from mathutils import Vector
 
-from .. import common
+from ... import common
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
 
 
 __all__ = [
     'Properties',
-    'Operator',
+    'MUV_OT_PackUV',
 ]
 
 
@@ -67,7 +69,10 @@ def is_valid_context(context):
     return True
 
 
+@PropertyClassRegistry(legacy=True)
 class Properties:
+    idname = "pack_uv"
+
     @classmethod
     def init_props(cls, scene):
         scene.muv_pack_uv_enabled = BoolProperty(
@@ -99,7 +104,8 @@ class Properties:
         del scene.muv_pack_uv_allowable_size_deviation
 
 
-class Operator(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_PackUV(bpy.types.Operator):
     """
     Operation class: Pack UV with same UV islands are integrated
     Island matching algorithm
diff --git a/uv_magic_uv/op/preserve_uv_aspect.py b/uv_magic_uv/legacy/op/preserve_uv_aspect.py
similarity index 96%
rename from uv_magic_uv/op/preserve_uv_aspect.py
rename to uv_magic_uv/legacy/op/preserve_uv_aspect.py
index cb11bd457e1a380bdb16348121c4ae1622aeec45..cf9349bcc4d412ec1e10e1d5d648fa93cf0ce916 100644
--- a/uv_magic_uv/op/preserve_uv_aspect.py
+++ b/uv_magic_uv/legacy/op/preserve_uv_aspect.py
@@ -28,12 +28,14 @@ import bmesh
 from bpy.props import StringProperty, EnumProperty, BoolProperty
 from mathutils import Vector
 
-from .. import common
+from ... import common
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
 
 
 __all__ = [
     'Properties',
-    'Operator',
+    'MUV_OT_PreserveUVAspect',
 ]
 
 
@@ -58,7 +60,10 @@ def is_valid_context(context):
     return True
 
 
+@PropertyClassRegistry(legacy=True)
 class Properties:
+    idname = "preserve_uv_aspect"
+
     @classmethod
     def init_props(cls, scene):
         def get_loaded_texture_name(_, __):
@@ -101,7 +106,8 @@ class Properties:
         del scene.muv_preserve_uv_aspect_origin
 
 
-class Operator(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_PreserveUVAspect(bpy.types.Operator):
     """
     Operation class: Preserve UV Aspect
     """
diff --git a/uv_magic_uv/op/select_uv.py b/uv_magic_uv/legacy/op/select_uv.py
similarity index 90%
rename from uv_magic_uv/op/select_uv.py
rename to uv_magic_uv/legacy/op/select_uv.py
index 3a7bcbc3bb7e5377deaebfb0640e4f9115935596..bdc182d5cc46b81bf064c194d15fc1aa320837df 100644
--- a/uv_magic_uv/op/select_uv.py
+++ b/uv_magic_uv/legacy/op/select_uv.py
@@ -27,13 +27,15 @@ import bpy
 import bmesh
 from bpy.props import BoolProperty
 
-from .. import common
+from ... import common
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
 
 
 __all__ = [
     'Properties',
-    'OperatorSelectFlipped',
-    'OperatorSelectOverlapped',
+    'MUV_OT_SelectUV_SelectFlipped',
+    'MUV_OT_SelectUV_SelectOverlapped',
 ]
 
 
@@ -60,7 +62,10 @@ def is_valid_context(context):
     return True
 
 
+@PropertyClassRegistry(legacy=True)
 class Properties:
+    idname = "select_uv"
+
     @classmethod
     def init_props(cls, scene):
         scene.muv_select_uv_enabled = BoolProperty(
@@ -74,7 +79,8 @@ class Properties:
         del scene.muv_select_uv_enabled
 
 
-class OperatorSelectOverlapped(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_SelectUV_SelectOverlapped(bpy.types.Operator):
     """
     Operation class: Select faces which have overlapped UVs
     """
@@ -118,7 +124,8 @@ class OperatorSelectOverlapped(bpy.types.Operator):
         return {'FINISHED'}
 
 
-class OperatorSelectFlipped(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_SelectUV_SelectFlipped(bpy.types.Operator):
     """
     Operation class: Select faces which have flipped UVs
     """
diff --git a/uv_magic_uv/op/smooth_uv.py b/uv_magic_uv/legacy/op/smooth_uv.py
similarity index 96%
rename from uv_magic_uv/op/smooth_uv.py
rename to uv_magic_uv/legacy/op/smooth_uv.py
index 31bef1556f6cfb99f5c44070e60e08f638cb2fa4..630625547eb31f2a75226a7fefac9b6fc077b3e9 100644
--- a/uv_magic_uv/op/smooth_uv.py
+++ b/uv_magic_uv/legacy/op/smooth_uv.py
@@ -27,12 +27,14 @@ import bpy
 import bmesh
 from bpy.props import BoolProperty, FloatProperty
 
-from .. import common
+from ... import common
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
 
 
 __all__ = [
     'Properties',
-    'Operator',
+    'MUV_OT_SmoothUV',
 ]
 
 
@@ -59,7 +61,10 @@ def is_valid_context(context):
     return True
 
 
+@PropertyClassRegistry(legacy=True)
 class Properties:
+    idname = "smooth_uv"
+
     @classmethod
     def init_props(cls, scene):
         scene.muv_smooth_uv_enabled = BoolProperty(
@@ -93,7 +98,8 @@ class Properties:
         del scene.muv_smooth_uv_select
 
 
-class Operator(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_SmoothUV(bpy.types.Operator):
 
     bl_idname = "uv.muv_smooth_uv_operator"
     bl_label = "Smooth"
diff --git a/uv_magic_uv/op/texture_lock.py b/uv_magic_uv/legacy/op/texture_lock.py
similarity index 93%
rename from uv_magic_uv/op/texture_lock.py
rename to uv_magic_uv/legacy/op/texture_lock.py
index 4be97c62bbb10b4e2da352e544cd6e1b498e38be..65873106e20ae4ab2bedccc6a676afc0aaac8faa 100644
--- a/uv_magic_uv/op/texture_lock.py
+++ b/uv_magic_uv/legacy/op/texture_lock.py
@@ -31,14 +31,16 @@ import bmesh
 from mathutils import Vector
 from bpy.props import BoolProperty
 
-from .. import common
+from ... import common
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
 
 
 __all__ = [
     'Properties',
-    'OperatorLock',
-    'OperatorUnlock',
-    'OperatorIntr',
+    'MUV_OT_TextureLock_Lock',
+    'MUV_OT_TextureLock_Unlock',
+    'MUV_OT_TextureLock_Intr',
 ]
 
 
@@ -213,7 +215,10 @@ def is_valid_context(context):
     return True
 
 
+@PropertyClassRegistry(legacy=True)
 class Properties:
+    idname = "texture_lock"
+
     @classmethod
     def init_props(cls, scene):
         class Props():
@@ -222,7 +227,7 @@ class Properties:
         scene.muv_props.texture_lock = Props()
 
         def get_func(_):
-            return OperatorIntr.is_running(bpy.context)
+            return MUV_OT_TextureLock_Intr.is_running(bpy.context)
 
         def set_func(_, __):
             pass
@@ -256,7 +261,8 @@ class Properties:
         del scene.muv_texture_lock_connect
 
 
-class OperatorLock(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_TextureLock_Lock(bpy.types.Operator):
     """
     Operation class: Lock Texture
     """
@@ -302,7 +308,8 @@ class OperatorLock(bpy.types.Operator):
         return {'FINISHED'}
 
 
-class OperatorUnlock(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_TextureLock_Unlock(bpy.types.Operator):
     """
     Operation class: Unlock Texture
     """
@@ -326,7 +333,11 @@ class OperatorUnlock(bpy.types.Operator):
         props = sc.muv_props.texture_lock
         if not props.verts_orig:
             return False
-        return OperatorLock.is_ready(context) and is_valid_context(context)
+        if not MUV_OT_TextureLock_Lock.is_ready(context):
+            return False
+        if not is_valid_context(context):
+            return False
+        return True
 
     def execute(self, context):
         sc = context.scene
@@ -383,7 +394,8 @@ class OperatorUnlock(bpy.types.Operator):
         return {'FINISHED'}
 
 
-class OperatorIntr(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_TextureLock_Intr(bpy.types.Operator):
     """
     Operation class: Texture Lock (Interactive mode)
     """
@@ -500,10 +512,10 @@ class OperatorIntr(bpy.types.Operator):
 
     def modal(self, context, event):
         if not is_valid_context(context):
-            OperatorIntr.handle_remove(context)
+            MUV_OT_TextureLock_Intr.handle_remove(context)
             return {'FINISHED'}
 
-        if not OperatorIntr.is_running(context):
+        if not MUV_OT_TextureLock_Intr.is_running(context):
             return {'FINISHED'}
 
         if context.area:
@@ -521,11 +533,11 @@ class OperatorIntr(bpy.types.Operator):
         if not is_valid_context(context):
             return {'CANCELLED'}
 
-        if not OperatorIntr.is_running(context):
-            OperatorIntr.handle_add(self, context)
+        if not MUV_OT_TextureLock_Intr.is_running(context):
+            MUV_OT_TextureLock_Intr.handle_add(self, context)
             return {'RUNNING_MODAL'}
         else:
-            OperatorIntr.handle_remove(context)
+            MUV_OT_TextureLock_Intr.handle_remove(context)
 
         if context.area:
             context.area.tag_redraw()
diff --git a/uv_magic_uv/op/texture_projection.py b/uv_magic_uv/legacy/op/texture_projection.py
similarity index 93%
rename from uv_magic_uv/op/texture_projection.py
rename to uv_magic_uv/legacy/op/texture_projection.py
index bdf0ad67f4d34ec4d511d91e33d14809eca93cb0..58f69c9d8733143eb5ad17d975b61565ddff68e4 100644
--- a/uv_magic_uv/op/texture_projection.py
+++ b/uv_magic_uv/legacy/op/texture_projection.py
@@ -36,13 +36,15 @@ from bpy.props import (
     FloatProperty,
 )
 
-from .. import common
+from ... import common
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
 
 
 __all__ = [
     'Properties',
-    'Operator',
-    'OperatorProject',
+    'MUV_OT_TextureProjection',
+    'MUV_OT_TextureProjection_Project',
 ]
 
 
@@ -143,11 +145,14 @@ def is_valid_context(context):
     return True
 
 
+@PropertyClassRegistry(legacy=True)
 class Properties:
+    idname = "texture_projection"
+
     @classmethod
     def init_props(cls, scene):
         def get_func(_):
-            return Operator.is_running(bpy.context)
+            return MUV_OT_TextureProjection.is_running(bpy.context)
 
         def set_func(_, __):
             pass
@@ -214,7 +219,8 @@ class Properties:
         del scene.muv_texture_projection_assign_uvmap
 
 
-class Operator(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_TextureProjection(bpy.types.Operator):
     """
     Operation class: Texture Projection
     Render texture
@@ -240,7 +246,7 @@ class Operator(bpy.types.Operator):
     @classmethod
     def handle_add(cls, obj, context):
         cls.__handle = bpy.types.SpaceView3D.draw_handler_add(
-            Operator.draw_texture,
+            MUV_OT_TextureProjection.draw_texture,
             (obj, context), 'WINDOW', 'POST_PIXEL')
 
     @classmethod
@@ -301,10 +307,10 @@ class Operator(bpy.types.Operator):
         bgl.glEnd()
 
     def invoke(self, context, _):
-        if not Operator.is_running(context):
-            Operator.handle_add(self, context)
+        if not MUV_OT_TextureProjection.is_running(context):
+            MUV_OT_TextureProjection.handle_add(self, context)
         else:
-            Operator.handle_remove()
+            MUV_OT_TextureProjection.handle_remove()
 
         if context.area:
             context.area.tag_redraw()
@@ -312,7 +318,8 @@ class Operator(bpy.types.Operator):
         return {'FINISHED'}
 
 
-class OperatorProject(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_TextureProjection_Project(bpy.types.Operator):
     """
     Operation class: Project texture
     """
@@ -327,7 +334,7 @@ class OperatorProject(bpy.types.Operator):
         # we can not get area/space/region from console
         if common.is_console_mode():
             return True
-        if not Operator.is_running(context):
+        if not MUV_OT_TextureProjection.is_running(context):
             return False
         return is_valid_context(context)
 
diff --git a/uv_magic_uv/op/texture_wrap.py b/uv_magic_uv/legacy/op/texture_wrap.py
similarity index 95%
rename from uv_magic_uv/op/texture_wrap.py
rename to uv_magic_uv/legacy/op/texture_wrap.py
index a7c58847654780e2adbc6698c3cf4c04ae15187e..cb4cc78c9f04ed48736997e506988dd43f763cd3 100644
--- a/uv_magic_uv/op/texture_wrap.py
+++ b/uv_magic_uv/legacy/op/texture_wrap.py
@@ -29,13 +29,15 @@ from bpy.props import (
     BoolProperty,
 )
 
-from .. import common
+from ... import common
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
 
 
 __all__ = [
     'Properties',
-    'OperatorRefer',
-    'OperatorSet',
+    'MUV_OT_TextureWrap_Refer',
+    'MUV_OT_TextureWrap_Set',
 ]
 
 
@@ -60,7 +62,10 @@ def is_valid_context(context):
     return True
 
 
+@PropertyClassRegistry(legacy=True)
 class Properties:
+    idname = "texture_wrap"
+
     @classmethod
     def init_props(cls, scene):
         class Props():
@@ -93,7 +98,8 @@ class Properties:
         del scene.muv_texture_wrap_selseq
 
 
-class OperatorRefer(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_TextureWrap_Refer(bpy.types.Operator):
     """
     Operation class: Refer UV
     """
@@ -132,7 +138,8 @@ class OperatorRefer(bpy.types.Operator):
         return {'FINISHED'}
 
 
-class OperatorSet(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_TextureWrap_Set(bpy.types.Operator):
     """
     Operation class: Set UV
     """
diff --git a/uv_magic_uv/legacy/op/transfer_uv.py b/uv_magic_uv/legacy/op/transfer_uv.py
new file mode 100644
index 0000000000000000000000000000000000000000..cd0e4dd9feffde6d3d95a6e8484bcdc38e24ce48
--- /dev/null
+++ b/uv_magic_uv/legacy/op/transfer_uv.py
@@ -0,0 +1,172 @@
+# <pep8-80 compliant>
+
+# ##### 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 #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>, Mifth, MaxRobinot"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bpy
+import bmesh
+from bpy.props import BoolProperty
+
+from ... import common
+from ...impl import transfer_uv_impl as impl
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
+
+
+__all__ = [
+    'Properties',
+    'MUV_OT_TransferUV_CopyUV',
+    'MUV_OT_TransferUV_PasteUV',
+]
+
+
+@PropertyClassRegistry(legacy=True)
+class Properties:
+    idname = "transfer_uv"
+
+    @classmethod
+    def init_props(cls, scene):
+        class Props():
+            topology_copied = None
+
+        scene.muv_props.transfer_uv = Props()
+
+        scene.muv_transfer_uv_enabled = BoolProperty(
+            name="Transfer UV Enabled",
+            description="Transfer UV is enabled",
+            default=False
+        )
+        scene.muv_transfer_uv_invert_normals = BoolProperty(
+            name="Invert Normals",
+            description="Invert Normals",
+            default=False
+        )
+        scene.muv_transfer_uv_copy_seams = BoolProperty(
+            name="Copy Seams",
+            description="Copy Seams",
+            default=True
+        )
+
+    @classmethod
+    def del_props(cls, scene):
+        del scene.muv_transfer_uv_enabled
+        del scene.muv_transfer_uv_invert_normals
+        del scene.muv_transfer_uv_copy_seams
+
+
+@BlClassRegistry(legacy=True)
+class MUV_OT_TransferUV_CopyUV(bpy.types.Operator):
+    """
+        Operation class: Transfer UV copy
+        Topological based copy
+    """
+
+    bl_idname = "uv.muv_transfer_uv_operator_copy_uv"
+    bl_label = "Transfer UV Copy UV"
+    bl_description = "Transfer UV Copy UV (Topological based copy)"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    @classmethod
+    def poll(cls, context):
+        # we can not get area/space/region from console
+        if common.is_console_mode():
+            return True
+        return impl.is_valid_context(context)
+
+    def execute(self, context):
+        props = context.scene.muv_props.transfer_uv
+        active_obj = context.scene.objects.active
+        bm = bmesh.from_edit_mesh(active_obj.data)
+        if common.check_version(2, 73, 0) >= 0:
+            bm.faces.ensure_lookup_table()
+
+        uv_layer = impl.get_uv_layer(self, bm)
+        if uv_layer is None:
+            return {'CANCELLED'}
+
+        faces = impl.get_selected_src_faces(self, bm, uv_layer)
+        if faces is None:
+            return {'CANCELLED'}
+        props.topology_copied = faces
+
+        bmesh.update_edit_mesh(active_obj.data)
+
+        return {'FINISHED'}
+
+
+@BlClassRegistry(legacy=True)
+class MUV_OT_TransferUV_PasteUV(bpy.types.Operator):
+    """
+        Operation class: Transfer UV paste
+        Topological based paste
+    """
+
+    bl_idname = "uv.muv_transfer_uv_operator_paste_uv"
+    bl_label = "Transfer UV Paste UV"
+    bl_description = "Transfer UV Paste UV (Topological based paste)"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    invert_normals = BoolProperty(
+        name="Invert Normals",
+        description="Invert Normals",
+        default=False
+    )
+    copy_seams = BoolProperty(
+        name="Copy Seams",
+        description="Copy Seams",
+        default=True
+    )
+
+    @classmethod
+    def poll(cls, context):
+        # we can not get area/space/region from console
+        if common.is_console_mode():
+            return True
+        sc = context.scene
+        props = sc.muv_props.transfer_uv
+        if not props.topology_copied:
+            return False
+        return impl.is_valid_context(context)
+
+    def execute(self, context):
+        props = context.scene.muv_props.transfer_uv
+        active_obj = context.scene.objects.active
+        bm = bmesh.from_edit_mesh(active_obj.data)
+        if common.check_version(2, 73, 0) >= 0:
+            bm.faces.ensure_lookup_table()
+
+        # get UV layer
+        uv_layer = impl.get_uv_layer(self, bm)
+        if uv_layer is None:
+            return {'CANCELLED'}
+
+        ret = impl.paste_uv(self, bm, uv_layer, props.topology_copied,
+                            self.invert_normals, self.copy_seams)
+        if ret:
+            return {'CANCELLED'}
+
+        bmesh.update_edit_mesh(active_obj.data)
+        if self.copy_seams:
+            active_obj.data.show_edge_seams = True
+
+        return {'FINISHED'}
diff --git a/uv_magic_uv/op/unwrap_constraint.py b/uv_magic_uv/legacy/op/unwrap_constraint.py
similarity index 94%
rename from uv_magic_uv/op/unwrap_constraint.py
rename to uv_magic_uv/legacy/op/unwrap_constraint.py
index b2368fc45e3b3c4a7d70587f09dd0487c43dc8e9..f06efce18b2228702d9c9550c72e580fb35d5f46 100644
--- a/uv_magic_uv/op/unwrap_constraint.py
+++ b/uv_magic_uv/legacy/op/unwrap_constraint.py
@@ -29,12 +29,14 @@ from bpy.props import (
     FloatProperty,
 )
 
-from .. import common
+from ... import common
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
 
 
 __all__ = [
     'Properties',
-    'Operator',
+    'MUV_OT_UnwrapConstraint',
 ]
 
 
@@ -59,7 +61,10 @@ def is_valid_context(context):
     return True
 
 
+@PropertyClassRegistry(legacy=True)
 class Properties:
+    idname = "unwrap_constraint"
+
     @classmethod
     def init_props(cls, scene):
         scene.muv_unwrap_constraint_enabled = BoolProperty(
@@ -85,7 +90,8 @@ class Properties:
         del scene.muv_unwrap_constraint_v_const
 
 
-class Operator(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_UnwrapConstraint(bpy.types.Operator):
     """
     Operation class: Unwrap with constrain UV coordinate
     """
diff --git a/uv_magic_uv/op/uv_bounding_box.py b/uv_magic_uv/legacy/op/uv_bounding_box.py
similarity index 97%
rename from uv_magic_uv/op/uv_bounding_box.py
rename to uv_magic_uv/legacy/op/uv_bounding_box.py
index 4aa8874bc32f4f5aae173e960e50b03f12cd5be4..47c27e41c752f8fef122d8cd727f9dfa4b8ee496 100644
--- a/uv_magic_uv/op/uv_bounding_box.py
+++ b/uv_magic_uv/legacy/op/uv_bounding_box.py
@@ -32,12 +32,14 @@ import mathutils
 import bmesh
 from bpy.props import BoolProperty, EnumProperty
 
-from .. import common
+from ... import common
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
 
 
 __all__ = [
     'Properties',
-    'Operator',
+    'MUV_OT_UVBoundingBox',
 ]
 
 
@@ -67,7 +69,10 @@ def is_valid_context(context):
     return True
 
 
+@PropertyClassRegistry(legacy=True)
 class Properties:
+    idname = "uv_bounding_box"
+
     @classmethod
     def init_props(cls, scene):
         class Props():
@@ -78,7 +83,7 @@ class Properties:
         scene.muv_props.uv_bounding_box = Props()
 
         def get_func(_):
-            return Operator.is_running(bpy.context)
+            return MUV_OT_UVBoundingBox.is_running(bpy.context)
 
         def set_func(_, __):
             pass
@@ -599,7 +604,8 @@ class StateManager():
         return self.__state
 
 
-class Operator(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_UVBoundingBox(bpy.types.Operator):
     """
     Operation class: UV Bounding Box
     """
@@ -677,7 +683,7 @@ class Operator(bpy.types.Operator):
         """
         props = context.scene.muv_props.uv_bounding_box
 
-        if not Operator.is_running(context):
+        if not MUV_OT_UVBoundingBox.is_running(context):
             return
 
         if not is_valid_context(context):
@@ -781,11 +787,11 @@ class Operator(bpy.types.Operator):
         props = context.scene.muv_props.uv_bounding_box
         common.redraw_all_areas()
 
-        if not Operator.is_running(context):
+        if not MUV_OT_UVBoundingBox.is_running(context):
             return {'FINISHED'}
 
         if not is_valid_context(context):
-            Operator.handle_remove(context)
+            MUV_OT_UVBoundingBox.handle_remove(context)
             return {'FINISHED'}
 
         region_types = [
@@ -812,15 +818,15 @@ class Operator(bpy.types.Operator):
     def invoke(self, context, _):
         props = context.scene.muv_props.uv_bounding_box
 
-        if Operator.is_running(context):
-            Operator.handle_remove(context)
+        if MUV_OT_UVBoundingBox.is_running(context):
+            MUV_OT_UVBoundingBox.handle_remove(context)
             return {'FINISHED'}
 
         props.uv_info_ini = self.__get_uv_info(context)
         if props.uv_info_ini is None:
             return {'CANCELLED'}
 
-        Operator.handle_add(self, context)
+        MUV_OT_UVBoundingBox.handle_add(self, context)
 
         props.ctrl_points_ini = self.__get_ctrl_point(props.uv_info_ini)
         trans_mat = self.__cmd_exec.execute()
diff --git a/uv_magic_uv/op/uv_inspection.py b/uv_magic_uv/legacy/op/uv_inspection.py
similarity index 89%
rename from uv_magic_uv/op/uv_inspection.py
rename to uv_magic_uv/legacy/op/uv_inspection.py
index 0c05e03d8eaeb54207fee8d303f34bce2ec365ea..57d424685ec3910b0fdb38d4750dd2d506946572 100644
--- a/uv_magic_uv/op/uv_inspection.py
+++ b/uv_magic_uv/legacy/op/uv_inspection.py
@@ -28,13 +28,15 @@ import bmesh
 import bgl
 from bpy.props import BoolProperty, EnumProperty
 
-from .. import common
+from ... import common
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
 
 
 __all__ = [
     'Properties',
-    'OperatorRender',
-    'OperatorUpdate',
+    'MUV_OT_UVInspection_Render',
+    'MUV_OT_UVInspection_Update',
 ]
 
 
@@ -61,7 +63,10 @@ def is_valid_context(context):
     return True
 
 
+@PropertyClassRegistry(legacy=True)
 class Properties:
+    idname = "uv_inspection"
+
     @classmethod
     def init_props(cls, scene):
         class Props():
@@ -71,7 +76,7 @@ class Properties:
         scene.muv_props.uv_inspection = Props()
 
         def get_func(_):
-            return OperatorRender.is_running(bpy.context)
+            return MUV_OT_UVInspection_Render.is_running(bpy.context)
 
         def set_func(_, __):
             pass
@@ -122,7 +127,8 @@ class Properties:
         del scene.muv_uv_inspection_show_mode
 
 
-class OperatorRender(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_UVInspection_Render(bpy.types.Operator):
     """
     Operation class: Render UV Inspection
     No operation (only rendering)
@@ -149,7 +155,8 @@ class OperatorRender(bpy.types.Operator):
     def handle_add(cls, obj, context):
         sie = bpy.types.SpaceImageEditor
         cls.__handle = sie.draw_handler_add(
-            OperatorRender.draw, (obj, context), 'WINDOW', 'POST_PIXEL')
+            MUV_OT_UVInspection_Render.draw, (obj, context),
+            'WINDOW', 'POST_PIXEL')
 
     @classmethod
     def handle_remove(cls):
@@ -164,7 +171,7 @@ class OperatorRender(bpy.types.Operator):
         props = sc.muv_props.uv_inspection
         prefs = context.user_preferences.addons["uv_magic_uv"].preferences
 
-        if not OperatorRender.is_running(context):
+        if not MUV_OT_UVInspection_Render.is_running(context):
             return
 
         # OpenGL configuration
@@ -213,11 +220,11 @@ class OperatorRender(bpy.types.Operator):
                     bgl.glEnd()
 
     def invoke(self, context, _):
-        if not OperatorRender.is_running(context):
+        if not MUV_OT_UVInspection_Render.is_running(context):
             update_uvinsp_info(context)
-            OperatorRender.handle_add(self, context)
+            MUV_OT_UVInspection_Render.handle_add(self, context)
         else:
-            OperatorRender.handle_remove()
+            MUV_OT_UVInspection_Render.handle_remove()
 
         if context.area:
             context.area.tag_redraw()
@@ -244,7 +251,8 @@ def update_uvinsp_info(context):
     props.flipped_info = common.get_flipped_uv_info(sel_faces, uv_layer)
 
 
-class OperatorUpdate(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_UVInspection_Update(bpy.types.Operator):
     """
     Operation class: Update
     """
@@ -259,7 +267,7 @@ class OperatorUpdate(bpy.types.Operator):
         # we can not get area/space/region from console
         if common.is_console_mode():
             return True
-        if not OperatorRender.is_running(context):
+        if not MUV_OT_UVInspection_Render.is_running(context):
             return False
         return is_valid_context(context)
 
diff --git a/uv_magic_uv/op/uv_sculpt.py b/uv_magic_uv/legacy/op/uv_sculpt.py
similarity index 96%
rename from uv_magic_uv/op/uv_sculpt.py
rename to uv_magic_uv/legacy/op/uv_sculpt.py
index 63c1adfe42d1eb7332a6e506654de160d9ad5ee7..3754a759c06dcfed49c1f5ad6644b139301d834b 100644
--- a/uv_magic_uv/op/uv_sculpt.py
+++ b/uv_magic_uv/legacy/op/uv_sculpt.py
@@ -39,12 +39,14 @@ from bpy.props import (
     FloatProperty,
 )
 
-from .. import common
+from ... import common
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
 
 
 __all__ = [
     'Properties',
-    'Operator',
+    'MUV_OT_UVSculpt',
 ]
 
 
@@ -69,11 +71,14 @@ def is_valid_context(context):
     return True
 
 
+@PropertyClassRegistry(legacy=True)
 class Properties:
+    idname = "uv_sculpt"
+
     @classmethod
     def init_props(cls, scene):
         def get_func(_):
-            return Operator.is_running(bpy.context)
+            return MUV_OT_UVSculpt.is_running(bpy.context)
 
         def set_func(_, __):
             pass
@@ -151,7 +156,8 @@ class Properties:
         del scene.muv_uv_sculpt_relax_method
 
 
-class Operator(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_UVSculpt(bpy.types.Operator):
     """
     Operation class: UV Sculpt in View3D
     """
@@ -427,8 +433,8 @@ class Operator(bpy.types.Operator):
         if context.area:
             context.area.tag_redraw()
 
-        if not Operator.is_running(context):
-            Operator.handle_remove(context)
+        if not MUV_OT_UVSculpt.is_running(context):
+            MUV_OT_UVSculpt.handle_remove(context)
 
             return {'FINISHED'}
 
@@ -469,9 +475,9 @@ class Operator(bpy.types.Operator):
         if context.area:
             context.area.tag_redraw()
 
-        if Operator.is_running(context):
-            Operator.handle_remove(context)
+        if MUV_OT_UVSculpt.is_running(context):
+            MUV_OT_UVSculpt.handle_remove(context)
         else:
-            Operator.handle_add(self, context)
+            MUV_OT_UVSculpt.handle_add(self, context)
 
         return {'RUNNING_MODAL'}
diff --git a/uv_magic_uv/legacy/op/uvw.py b/uv_magic_uv/legacy/op/uvw.py
new file mode 100644
index 0000000000000000000000000000000000000000..56777b18aa18f1a451a0bcbafb42a31ead56012d
--- /dev/null
+++ b/uv_magic_uv/legacy/op/uvw.py
@@ -0,0 +1,181 @@
+# <pep8-80 compliant>
+
+# ##### 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 #####
+
+__author__ = "Alexander Milovsky, Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bpy
+import bmesh
+from bpy.props import (
+    FloatProperty,
+    FloatVectorProperty,
+    BoolProperty
+)
+
+from ... import common
+from ...impl import uvw_impl as impl
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
+
+
+__all__ = [
+    'Properties',
+    'MUV_OT_UVW_BoxMap',
+    'MUV_OT_UVW_BestPlanerMap',
+]
+
+
+@PropertyClassRegistry(legacy=True)
+class Properties:
+    idname = "uvw"
+
+    @classmethod
+    def init_props(cls, scene):
+        scene.muv_uvw_enabled = BoolProperty(
+            name="UVW Enabled",
+            description="UVW is enabled",
+            default=False
+        )
+        scene.muv_uvw_assign_uvmap = BoolProperty(
+            name="Assign UVMap",
+            description="Assign UVMap when no UVmaps are available",
+            default=True
+        )
+
+    @classmethod
+    def del_props(cls, scene):
+        del scene.muv_uvw_enabled
+        del scene.muv_uvw_assign_uvmap
+
+
+@BlClassRegistry(legacy=True)
+class MUV_OT_UVW_BoxMap(bpy.types.Operator):
+    bl_idname = "uv.muv_uvw_operator_box_map"
+    bl_label = "Box Map"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    size = FloatProperty(
+        name="Size",
+        default=1.0,
+        precision=4
+    )
+    rotation = FloatVectorProperty(
+        name="XYZ Rotation",
+        size=3,
+        default=(0.0, 0.0, 0.0)
+    )
+    offset = FloatVectorProperty(
+        name="XYZ Offset",
+        size=3,
+        default=(0.0, 0.0, 0.0)
+    )
+    tex_aspect = FloatProperty(
+        name="Texture Aspect",
+        default=1.0,
+        precision=4
+    )
+    assign_uvmap = BoolProperty(
+        name="Assign UVMap",
+        description="Assign UVMap when no UVmaps are available",
+        default=True
+    )
+
+    @classmethod
+    def poll(cls, context):
+        # we can not get area/space/region from console
+        if common.is_console_mode():
+            return True
+        return impl.is_valid_context(context)
+
+    def execute(self, context):
+        obj = context.active_object
+        bm = bmesh.from_edit_mesh(obj.data)
+        if common.check_version(2, 73, 0) >= 0:
+            bm.faces.ensure_lookup_table()
+
+        # get UV layer
+        uv_layer = impl.get_uv_layer(self, bm, self.assign_uvmap)
+        if not uv_layer:
+            return {'CANCELLED'}
+
+        impl.apply_box_map(bm, uv_layer, self.size, self.offset,
+                           self.rotation, self.tex_aspect)
+        bmesh.update_edit_mesh(obj.data)
+
+        return {'FINISHED'}
+
+
+@BlClassRegistry(legacy=True)
+class MUV_OT_UVW_BestPlanerMap(bpy.types.Operator):
+    bl_idname = "uv.muv_uvw_operator_best_planer_map"
+    bl_label = "Best Planer Map"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    size = FloatProperty(
+        name="Size",
+        default=1.0,
+        precision=4
+    )
+    rotation = FloatProperty(
+        name="XY Rotation",
+        default=0.0
+    )
+    offset = FloatVectorProperty(
+        name="XY Offset",
+        size=2,
+        default=(0.0, 0.0)
+    )
+    tex_aspect = FloatProperty(
+        name="Texture Aspect",
+        default=1.0,
+        precision=4
+    )
+    assign_uvmap = BoolProperty(
+        name="Assign UVMap",
+        description="Assign UVMap when no UVmaps are available",
+        default=True
+    )
+
+    @classmethod
+    def poll(cls, context):
+        # we can not get area/space/region from console
+        if common.is_console_mode():
+            return True
+        return impl.is_valid_context(context)
+
+    def execute(self, context):
+        obj = context.active_object
+        bm = bmesh.from_edit_mesh(obj.data)
+        if common.check_version(2, 73, 0) >= 0:
+            bm.faces.ensure_lookup_table()
+
+        # get UV layer
+        uv_layer = impl.get_uv_layer(self, bm, self.assign_uvmap)
+        if not uv_layer:
+            return {'CANCELLED'}
+
+        impl.apply_planer_map(bm, uv_layer, self.size, self.offset,
+                              self.rotation, self.tex_aspect)
+
+        bmesh.update_edit_mesh(obj.data)
+
+        return {'FINISHED'}
diff --git a/uv_magic_uv/op/world_scale_uv.py b/uv_magic_uv/legacy/op/world_scale_uv.py
similarity index 96%
rename from uv_magic_uv/op/world_scale_uv.py
rename to uv_magic_uv/legacy/op/world_scale_uv.py
index e1a44954b982a5d036d857b19d0528f0037ea632..e56b6bfae3c4ca2c4f6cc8b31cce48103ceb556e 100644
--- a/uv_magic_uv/op/world_scale_uv.py
+++ b/uv_magic_uv/legacy/op/world_scale_uv.py
@@ -35,15 +35,17 @@ from bpy.props import (
     BoolProperty,
 )
 
-from .. import common
+from ... import common
+from ...utils.bl_class_registry import BlClassRegistry
+from ...utils.property_class_registry import PropertyClassRegistry
 
 
 __all__ = [
     'Properties',
-    'OperatorMeasure',
-    'OperatorApplyManual',
-    'OperatorApplyScalingDensity',
-    'OperatorApplyProportionalToMesh',
+    'MUV_OT_WorldScaleUV_Measure',
+    'MUV_OT_WorldScaleUV_ApplyManual',
+    'MUV_OT_WorldScaleUV_ApplyScalingDensity',
+    'MUV_OT_WorldScaleUV_ApplyProportionalToMesh',
 ]
 
 
@@ -83,7 +85,10 @@ def measure_wsuv_info(obj, tex_size=None):
     return uv_area, mesh_area, density
 
 
+@PropertyClassRegistry(legacy=True)
 class Properties:
+    idname = "world_scale_uv"
+
     @classmethod
     def init_props(cls, scene):
         scene.muv_world_scale_uv_enabled = BoolProperty(
@@ -172,7 +177,8 @@ class Properties:
         del scene.muv_world_scale_uv_origin
 
 
-class OperatorMeasure(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_WorldScaleUV_Measure(bpy.types.Operator):
     """
     Operation class: Measure face size
     """
@@ -310,7 +316,8 @@ def apply(obj, origin, factor):
     bmesh.update_edit_mesh(obj.data)
 
 
-class OperatorApplyManual(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_WorldScaleUV_ApplyManual(bpy.types.Operator):
     """
     Operation class: Apply scaled UV (Manual)
     """
@@ -407,7 +414,8 @@ class OperatorApplyManual(bpy.types.Operator):
         return self.__apply_manual(context)
 
 
-class OperatorApplyScalingDensity(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_WorldScaleUV_ApplyScalingDensity(bpy.types.Operator):
     """
     Operation class: Apply scaled UV (Scaling Density)
     """
@@ -529,7 +537,8 @@ class OperatorApplyScalingDensity(bpy.types.Operator):
         return self.__apply_scaling_density(context)
 
 
-class OperatorApplyProportionalToMesh(bpy.types.Operator):
+@BlClassRegistry(legacy=True)
+class MUV_OT_WorldScaleUV_ApplyProportionalToMesh(bpy.types.Operator):
     """
     Operation class: Apply scaled UV (Proportional to mesh)
     """
diff --git a/uv_magic_uv/legacy/preferences.py b/uv_magic_uv/legacy/preferences.py
new file mode 100644
index 0000000000000000000000000000000000000000..931cc1d49152e0eb2af3f06ba0591f1eb0f18f4a
--- /dev/null
+++ b/uv_magic_uv/legacy/preferences.py
@@ -0,0 +1,468 @@
+# <pep8-80 compliant>
+
+# ##### 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 #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bpy
+from bpy.props import (
+    FloatProperty,
+    FloatVectorProperty,
+    BoolProperty,
+    EnumProperty,
+    IntProperty,
+)
+from bpy.types import AddonPreferences
+
+from . import op
+from . import ui
+from .. import addon_updater_ops
+from ..utils.bl_class_registry import BlClassRegistry
+
+__all__ = [
+    'add_builtin_menu',
+    'remove_builtin_menu',
+    'Preferences'
+]
+
+
+def view3d_uvmap_menu_fn(self, context):
+    layout = self.layout
+    sc = context.scene
+
+    layout.separator()
+    layout.label(text="Copy/Paste UV", icon='IMAGE_COL')
+    # Copy/Paste UV
+    layout.menu(ui.VIEW3D_MT_uv_map.MUV_MT_CopyPasteUV.bl_idname,
+                text="Copy/Paste UV")
+    # Transfer UV
+    layout.menu(ui.VIEW3D_MT_uv_map.MUV_MT_TransferUV.bl_idname,
+                text="Transfer UV")
+
+    layout.separator()
+    layout.label("UV Manipulation", icon='IMAGE_COL')
+    # Flip/Rotate UV
+    ops = layout.operator(op.flip_rotate_uv.MUV_OT_FlipRotate.bl_idname,
+                          text="Flip/Rotate UV")
+    ops.seams = sc.muv_flip_rotate_uv_seams
+    # Mirror UV
+    ops = layout.operator(op.mirror_uv.MUV_OT_MirrorUV.bl_idname,
+                          text="Mirror UV")
+    ops.axis = sc.muv_mirror_uv_axis
+    # Move UV
+    layout.operator(op.move_uv.MUV_OT_MoveUV.bl_idname, text="Move UV")
+    # World Scale UV
+    layout.menu(ui.VIEW3D_MT_uv_map.MUV_MT_WorldScaleUV.bl_idname,
+                text="World Scale UV")
+    # Preserve UV
+    layout.menu(ui.VIEW3D_MT_uv_map.MUV_MT_PreserveUVAspect.bl_idname,
+                text="Preserve UV")
+    # Texture Lock
+    layout.menu(ui.VIEW3D_MT_uv_map.MUV_MT_TextureLock.bl_idname,
+                text="Texture Lock")
+    # Texture Wrap
+    layout.menu(ui.VIEW3D_MT_uv_map.MUV_MT_TextureWrap.bl_idname,
+                text="Texture Wrap")
+    # UV Sculpt
+    layout.prop(sc, "muv_uv_sculpt_enable", text="UV Sculpt")
+
+    layout.separator()
+    layout.label("UV Mapping", icon='IMAGE_COL')
+    # Unwrap Constraint
+    ops = layout.operator(
+        op.unwrap_constraint.MUV_OT_UnwrapConstraint.bl_idname,
+        text="Unwrap Constraint")
+    ops.u_const = sc.muv_unwrap_constraint_u_const
+    ops.v_const = sc.muv_unwrap_constraint_v_const
+    # Texture Projection
+    layout.menu(ui.VIEW3D_MT_uv_map.MUV_MT_TextureProjection.bl_idname,
+                text="Texture Projection")
+    # UVW
+    layout.menu(ui.VIEW3D_MT_uv_map.MUV_MT_UVW.bl_idname, text="UVW")
+
+
+def view3d_object_menu_fn(self, _):
+    layout = self.layout
+
+    layout.separator()
+    # Copy/Paste UV (Among Objecct)
+    layout.menu(ui.VIEW3D_MT_object.MUV_MT_CopyPasteUV_Object.bl_idname,
+                text="Copy/Paste UV")
+    layout.label("Copy/Paste UV", icon='IMAGE_COL')
+
+
+def image_uvs_menu_fn(self, context):
+    layout = self.layout
+    sc = context.scene
+
+    layout.separator()
+    # Align UV Cursor
+    layout.menu(ui.IMAGE_MT_uvs.MUV_MT_AlignUVCursor.bl_idname,
+                text="Align UV Cursor")
+    # UV Bounding Box
+    layout.prop(sc, "muv_uv_bounding_box_show", text="UV Bounding Box")
+    # UV Inspection
+    layout.menu(ui.IMAGE_MT_uvs.MUV_MT_UVInspection.bl_idname,
+                text="UV Inspection")
+    layout.label("Editor Enhancement", icon='IMAGE_COL')
+
+    layout.separator()
+    # Align UV
+    layout.menu(ui.IMAGE_MT_uvs.MUV_MT_AlignUV.bl_idname, text="Align UV")
+    # Smooth UV
+    ops = layout.operator(op.smooth_uv.MUV_OT_SmoothUV.bl_idname,
+                          text="Smooth")
+    ops.transmission = sc.muv_smooth_uv_transmission
+    ops.select = sc.muv_smooth_uv_select
+    ops.mesh_infl = sc.muv_smooth_uv_mesh_infl
+    # Select UV
+    layout.menu(ui.IMAGE_MT_uvs.MUV_MT_SelectUV.bl_idname, text="Select UV")
+    # Pack UV
+    ops = layout.operator(op.pack_uv.MUV_OT_PackUV.bl_idname, text="Pack UV")
+    ops.allowable_center_deviation = sc.muv_pack_uv_allowable_center_deviation
+    ops.allowable_size_deviation = sc.muv_pack_uv_allowable_size_deviation
+    layout.label("UV Manipulation", icon='IMAGE_COL')
+
+    layout.separator()
+    # Copy/Paste UV (on UV/Image Editor)
+    layout.menu(ui.IMAGE_MT_uvs.MUV_MT_CopyPasteUV_UVEdit.bl_idname,
+                text="Copy/Paste UV")
+    layout.label("Copy/Paste UV", icon='IMAGE_COL')
+
+
+def add_builtin_menu():
+    bpy.types.VIEW3D_MT_uv_map.append(view3d_uvmap_menu_fn)
+    bpy.types.VIEW3D_MT_object.append(view3d_object_menu_fn)
+    bpy.types.IMAGE_MT_uvs.append(image_uvs_menu_fn)
+
+
+def remove_builtin_menu():
+    bpy.types.IMAGE_MT_uvs.remove(image_uvs_menu_fn)
+    bpy.types.VIEW3D_MT_uv_map.remove(view3d_uvmap_menu_fn)
+    bpy.types.VIEW3D_MT_object.remove(view3d_object_menu_fn)
+
+
+@BlClassRegistry(legacy=True)
+class Preferences(AddonPreferences):
+    """Preferences class: Preferences for this add-on"""
+
+    bl_idname = "uv_magic_uv"
+
+    def update_enable_builtin_menu(self, _):
+        if self['enable_builtin_menu']:
+            add_builtin_menu()
+        else:
+            remove_builtin_menu()
+
+    # enable to add features to built-in menu
+    enable_builtin_menu = BoolProperty(
+        name="Built-in Menu",
+        description="Enable built-in menu",
+        default=True,
+        update=update_enable_builtin_menu
+    )
+
+    # for UV Sculpt
+    uv_sculpt_brush_color = FloatVectorProperty(
+        name="Color",
+        description="Color",
+        default=(1.0, 0.4, 0.4, 1.0),
+        min=0.0,
+        max=1.0,
+        size=4,
+        subtype='COLOR'
+    )
+
+    # for Overlapped UV
+    uv_inspection_overlapped_color = FloatVectorProperty(
+        name="Color",
+        description="Color",
+        default=(0.0, 0.0, 1.0, 0.3),
+        min=0.0,
+        max=1.0,
+        size=4,
+        subtype='COLOR'
+    )
+
+    # for Flipped UV
+    uv_inspection_flipped_color = FloatVectorProperty(
+        name="Color",
+        description="Color",
+        default=(1.0, 0.0, 0.0, 0.3),
+        min=0.0,
+        max=1.0,
+        size=4,
+        subtype='COLOR'
+    )
+
+    # for Texture Projection
+    texture_projection_canvas_padding = FloatVectorProperty(
+        name="Canvas Padding",
+        description="Canvas Padding",
+        size=2,
+        max=50.0,
+        min=0.0,
+        default=(20.0, 20.0))
+
+    # for UV Bounding Box
+    uv_bounding_box_cp_size = FloatProperty(
+        name="Size",
+        description="Control Point Size",
+        default=6.0,
+        min=3.0,
+        max=100.0)
+    uv_bounding_box_cp_react_size = FloatProperty(
+        name="React Size",
+        description="Size event fired",
+        default=10.0,
+        min=3.0,
+        max=100.0)
+
+    # for UI
+    category = EnumProperty(
+        name="Category",
+        description="Preferences Category",
+        items=[
+            ('INFO', "Information", "Information about this add-on"),
+            ('CONFIG', "Configuration", "Configuration about this add-on"),
+            ('UPDATE', "Update", "Update this add-on"),
+        ],
+        default='INFO'
+    )
+    info_desc_expanded = BoolProperty(
+        name="Description",
+        description="Description",
+        default=False
+    )
+    info_loc_expanded = BoolProperty(
+        name="Location",
+        description="Location",
+        default=False
+    )
+    conf_uv_sculpt_expanded = BoolProperty(
+        name="UV Sculpt",
+        description="UV Sculpt",
+        default=False
+    )
+    conf_uv_inspection_expanded = BoolProperty(
+        name="UV Inspection",
+        description="UV Inspection",
+        default=False
+    )
+    conf_texture_projection_expanded = BoolProperty(
+        name="Texture Projection",
+        description="Texture Projection",
+        default=False
+    )
+    conf_uv_bounding_box_expanded = BoolProperty(
+        name="UV Bounding Box",
+        description="UV Bounding Box",
+        default=False
+    )
+
+    # for add-on updater
+    auto_check_update = BoolProperty(
+        name="Auto-check for Update",
+        description="If enabled, auto-check for updates using an interval",
+        default=False
+    )
+    updater_intrval_months = IntProperty(
+        name='Months',
+        description="Number of months between checking for updates",
+        default=0,
+        min=0
+    )
+    updater_intrval_days = IntProperty(
+        name='Days',
+        description="Number of days between checking for updates",
+        default=7,
+        min=0
+    )
+    updater_intrval_hours = IntProperty(
+        name='Hours',
+        description="Number of hours between checking for updates",
+        default=0,
+        min=0,
+        max=23
+    )
+    updater_intrval_minutes = IntProperty(
+        name='Minutes',
+        description="Number of minutes between checking for updates",
+        default=0,
+        min=0,
+        max=59
+    )
+
+    def draw(self, context):
+        layout = self.layout
+
+        layout.row().prop(self, "category", expand=True)
+
+        if self.category == 'INFO':
+            layout.prop(
+                self, "info_desc_expanded", text="Description",
+                icon='DISCLOSURE_TRI_DOWN' if self.info_desc_expanded
+                else 'DISCLOSURE_TRI_RIGHT')
+            if self.info_desc_expanded:
+                column = layout.column(align=True)
+                column.label("Magic UV is composed of many UV editing" +
+                             " features.")
+                column.label("See tutorial page if you are new to this" +
+                             " add-on.")
+                column.label("https://github.com/nutti/Magic-UV/wiki/Tutorial")
+
+            layout.prop(
+                self, "info_loc_expanded", text="Location",
+                icon='DISCLOSURE_TRI_DOWN' if self.info_loc_expanded
+                else 'DISCLOSURE_TRI_RIGHT')
+            if self.info_loc_expanded:
+                row = layout.row(align=True)
+                sp = row.split(percentage=0.5)
+                sp.label("3D View > Tool shelf > Copy/Paste UV (Object mode)")
+                sp = sp.split(percentage=1.0)
+                col = sp.column(align=True)
+                col.label("Copy/Paste UV (Among objects)")
+
+                row = layout.row(align=True)
+                sp = row.split(percentage=0.5)
+                sp.label("3D View > Tool shelf > Copy/Paste UV (Edit mode)")
+                sp = sp.split(percentage=1.0)
+                col = sp.column(align=True)
+                col.label("Copy/Paste UV (Among faces in 3D View)")
+                col.label("Transfer UV")
+
+                row = layout.row(align=True)
+                sp = row.split(percentage=0.5)
+                sp.label("3D View > Tool shelf > UV Manipulation (Edit mode)")
+                sp = sp.split(percentage=1.0)
+                col = sp.column(align=True)
+                col.label("Flip/Rotate UV")
+                col.label("Mirror UV")
+                col.label("Move UV")
+                col.label("World Scale UV")
+                col.label("Preserve UV Aspect")
+                col.label("Texture Lock")
+                col.label("Texture Wrap")
+                col.label("UV Sculpt")
+
+                row = layout.row(align=True)
+                sp = row.split(percentage=0.5)
+                sp.label("3D View > Tool shelf > UV Manipulation (Edit mode)")
+                sp = sp.split(percentage=1.0)
+                col = sp.column(align=True)
+                col.label("Unwrap Constraint")
+                col.label("Texture Projection")
+                col.label("UVW")
+
+                row = layout.row(align=True)
+                sp = row.split(percentage=0.5)
+                sp.label("UV/Image Editor > Tool shelf > Copy/Paste UV")
+                sp = sp.split(percentage=1.0)
+                col = sp.column(align=True)
+                col.label("Copy/Paste UV (Among faces in UV/Image Editor)")
+
+                row = layout.row(align=True)
+                sp = row.split(percentage=0.5)
+                sp.label("UV/Image Editor > Tool shelf > UV Manipulation")
+                sp = sp.split(percentage=1.0)
+                col = sp.column(align=True)
+                col.label("Align UV")
+                col.label("Smooth UV")
+                col.label("Select UV")
+                col.label("Pack UV (Extension)")
+
+                row = layout.row(align=True)
+                sp = row.split(percentage=0.5)
+                sp.label("UV/Image Editor > Tool shelf > Editor Enhancement")
+                sp = sp.split(percentage=1.0)
+                col = sp.column(align=True)
+                col.label("Align UV Cursor")
+                col.label("UV Cursor Location")
+                col.label("UV Bounding Box")
+                col.label("UV Inspection")
+
+        elif self.category == 'CONFIG':
+            layout.prop(self, "enable_builtin_menu", text="Built-in Menu")
+
+            layout.separator()
+
+            layout.prop(
+                self, "conf_uv_sculpt_expanded", text="UV Sculpt",
+                icon='DISCLOSURE_TRI_DOWN' if self.conf_uv_sculpt_expanded
+                else 'DISCLOSURE_TRI_RIGHT')
+            if self.conf_uv_sculpt_expanded:
+                sp = layout.split(percentage=0.05)
+                col = sp.column()  # spacer
+                sp = sp.split(percentage=0.3)
+                col = sp.column()
+                col.label("Brush Color:")
+                col.prop(self, "uv_sculpt_brush_color", text="")
+                layout.separator()
+
+            layout.prop(
+                self, "conf_uv_inspection_expanded", text="UV Inspection",
+                icon='DISCLOSURE_TRI_DOWN' if self.conf_uv_inspection_expanded
+                else 'DISCLOSURE_TRI_RIGHT')
+            if self.conf_uv_inspection_expanded:
+                sp = layout.split(percentage=0.05)
+                col = sp.column()  # spacer
+                sp = sp.split(percentage=0.3)
+                col = sp.column()
+                col.label("Overlapped UV Color:")
+                col.prop(self, "uv_inspection_overlapped_color", text="")
+                sp = sp.split(percentage=0.45)
+                col = sp.column()
+                col.label("Flipped UV Color:")
+                col.prop(self, "uv_inspection_flipped_color", text="")
+                layout.separator()
+
+            layout.prop(
+                self, "conf_texture_projection_expanded",
+                text="Texture Projection",
+                icon='DISCLOSURE_TRI_DOWN'
+                if self.conf_texture_projection_expanded
+                else 'DISCLOSURE_TRI_RIGHT')
+            if self.conf_texture_projection_expanded:
+                sp = layout.split(percentage=0.05)
+                col = sp.column()       # spacer
+                sp = sp.split(percentage=0.3)
+                col = sp.column()
+                col.prop(self, "texture_projection_canvas_padding")
+                layout.separator()
+
+            layout.prop(
+                self, "conf_uv_bounding_box_expanded", text="UV Bounding Box",
+                icon='DISCLOSURE_TRI_DOWN'
+                if self.conf_uv_bounding_box_expanded
+                else 'DISCLOSURE_TRI_RIGHT')
+            if self.conf_uv_bounding_box_expanded:
+                sp = layout.split(percentage=0.05)
+                col = sp.column()       # spacer
+                sp = sp.split(percentage=0.3)
+                col = sp.column()
+                col.label("Control Point:")
+                col.prop(self, "uv_bounding_box_cp_size")
+                col.prop(self, "uv_bounding_box_cp_react_size")
+                layout.separator()
+
+        elif self.category == 'UPDATE':
+            addon_updater_ops.update_settings_ui(self, context)
diff --git a/uv_magic_uv/legacy/properites.py b/uv_magic_uv/legacy/properites.py
new file mode 100644
index 0000000000000000000000000000000000000000..b64a48f39010fdfa5357146a0bcbbaac08dea18e
--- /dev/null
+++ b/uv_magic_uv/legacy/properites.py
@@ -0,0 +1,61 @@
+# <pep8-80 compliant>
+
+# ##### 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 #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+
+from ..utils.property_class_registry import PropertyClassRegistry
+
+__all__ = [
+    'MUV_Properties',
+    'init_props',
+    'clear_props',
+]
+
+
+# Properties used in this add-on.
+# pylint: disable=W0612
+class MUV_Properties():
+    def __init__(self):
+        self.prefs = MUV_Prefs()
+
+
+class MUV_Prefs():
+    expanded = {
+        "info_desc": False,
+        "info_loc": False,
+        "conf_uvsculpt": False,
+        "conf_uvinsp": False,
+        "conf_texproj": False,
+        "conf_uvbb": False
+    }
+
+
+def init_props(scene):
+    scene.muv_props = MUV_Properties()
+    PropertyClassRegistry.init_props(scene)
+
+
+def clear_props(scene):
+    PropertyClassRegistry.del_props(scene)
+    del scene.muv_props
diff --git a/uv_magic_uv/legacy/ui/IMAGE_MT_uvs.py b/uv_magic_uv/legacy/ui/IMAGE_MT_uvs.py
new file mode 100644
index 0000000000000000000000000000000000000000..bf071bf5f07ea56d291e8fa1f6bc3640ea8d11dc
--- /dev/null
+++ b/uv_magic_uv/legacy/ui/IMAGE_MT_uvs.py
@@ -0,0 +1,197 @@
+# <pep8-80 compliant>
+
+# ##### 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 #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bpy
+
+from ..op import (
+    align_uv_cursor,
+    copy_paste_uv_uvedit,
+    align_uv,
+    select_uv,
+    uv_inspection
+)
+from ...utils.bl_class_registry import BlClassRegistry
+
+__all__ = [
+    'MUV_MT_CopyPasteUV_UVEdit',
+    'MUV_MT_AlignUV',
+    'MUV_MT_SelectUV',
+    'MUV_MT_AlignUVCursor',
+    'MUV_MT_UVInspection',
+]
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_CopyPasteUV_UVEdit(bpy.types.Menu):
+    """
+    Menu class: Master menu of Copy/Paste UV coordinate on UV/ImageEditor
+    """
+
+    bl_idname = "uv.muv_copy_paste_uv_uvedit_menu"
+    bl_label = "Copy/Paste UV"
+    bl_description = "Copy and Paste UV coordinate among object"
+
+    def draw(self, _):
+        layout = self.layout
+
+        layout.operator(
+            copy_paste_uv_uvedit.MUV_OT_CopyPasteUVUVEdit_CopyUV.bl_idname,
+            text="Copy")
+        layout.operator(
+            copy_paste_uv_uvedit.MUV_OT_CopyPasteUVUVEdit_PasteUV.bl_idname,
+            text="Paste")
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_AlignUV(bpy.types.Menu):
+    """
+    Menu class: Master menu of Align UV
+    """
+
+    bl_idname = "uv.muv_align_uv_menu"
+    bl_label = "Align UV"
+    bl_description = "Align UV"
+
+    def draw(self, context):
+        layout = self.layout
+        sc = context.scene
+
+        ops = layout.operator(align_uv.MUV_OT_AlignUV_Circle.bl_idname,
+                              text="Circle")
+        ops.transmission = sc.muv_align_uv_transmission
+        ops.select = sc.muv_align_uv_select
+
+        ops = layout.operator(align_uv.MUV_OT_AlignUV_Straighten.bl_idname,
+                              text="Straighten")
+        ops.transmission = sc.muv_align_uv_transmission
+        ops.select = sc.muv_align_uv_select
+        ops.vertical = sc.muv_align_uv_vertical
+        ops.horizontal = sc.muv_align_uv_horizontal
+
+        ops = layout.operator(align_uv.MUV_OT_AlignUV_Axis.bl_idname,
+                              text="XY-axis")
+        ops.transmission = sc.muv_align_uv_transmission
+        ops.select = sc.muv_align_uv_select
+        ops.vertical = sc.muv_align_uv_vertical
+        ops.horizontal = sc.muv_align_uv_horizontal
+        ops.location = sc.muv_align_uv_location
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_SelectUV(bpy.types.Menu):
+    """
+    Menu class: Master menu of Select UV
+    """
+
+    bl_idname = "uv.muv_select_uv_menu"
+    bl_label = "Select UV"
+    bl_description = "Select UV"
+
+    def draw(self, _):
+        layout = self.layout
+
+        layout.operator(select_uv.MUV_OT_SelectUV_SelectOverlapped.bl_idname,
+                        text="Overlapped")
+        layout.operator(select_uv.MUV_OT_SelectUV_SelectFlipped.bl_idname,
+                        text="Flipped")
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_AlignUVCursor(bpy.types.Menu):
+    """
+    Menu class: Master menu of Align UV Cursor
+    """
+
+    bl_idname = "uv.muv_align_uv_cursor_menu"
+    bl_label = "Align UV Cursor"
+    bl_description = "Align UV cursor"
+
+    def draw(self, context):
+        layout = self.layout
+        sc = context.scene
+
+        ops = layout.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
+                              text="Left Top")
+        ops.position = 'LEFT_TOP'
+        ops.base = sc.muv_align_uv_cursor_align_method
+
+        ops = layout.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
+                              text="Middle Top")
+        ops.position = 'MIDDLE_TOP'
+        ops.base = sc.muv_align_uv_cursor_align_method
+
+        ops = layout.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
+                              text="Right Top")
+        ops.position = 'RIGHT_TOP'
+        ops.base = sc.muv_align_uv_cursor_align_method
+
+        ops = layout.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
+                              text="Left Middle")
+        ops.position = 'LEFT_MIDDLE'
+        ops.base = sc.muv_align_uv_cursor_align_method
+
+        ops = layout.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
+                              text="Center")
+        ops.position = 'CENTER'
+        ops.base = sc.muv_align_uv_cursor_align_method
+
+        ops = layout.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
+                              text="Right Middle")
+        ops.position = 'RIGHT_MIDDLE'
+        ops.base = sc.muv_align_uv_cursor_align_method
+
+        ops = layout.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
+                              text="Left Bottom")
+        ops.position = 'LEFT_BOTTOM'
+        ops.base = sc.muv_align_uv_cursor_align_method
+
+        ops = layout.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
+                              text="Middle Bottom")
+        ops.position = 'MIDDLE_BOTTOM'
+        ops.base = sc.muv_align_uv_cursor_align_method
+
+        ops = layout.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
+                              text="Right Bottom")
+        ops.position = 'RIGHT_BOTTOM'
+        ops.base = sc.muv_align_uv_cursor_align_method
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_UVInspection(bpy.types.Menu):
+    """
+    Menu class: Master menu of UV Inspection
+    """
+
+    bl_idname = "uv.muv_uv_inspection_menu"
+    bl_label = "UV Inspection"
+    bl_description = "UV Inspection"
+
+    def draw(self, context):
+        layout = self.layout
+        sc = context.scene
+
+        layout.prop(sc, "muv_uv_inspection_show", text="UV Inspection")
+        layout.operator(uv_inspection.MUV_OT_UVInspection_Update.bl_idname,
+                        text="Update")
diff --git a/uv_magic_uv/legacy/ui/VIEW3D_MT_object.py b/uv_magic_uv/legacy/ui/VIEW3D_MT_object.py
new file mode 100644
index 0000000000000000000000000000000000000000..e1c751ae798dfc1793bae3dfc08b362dd62e2a17
--- /dev/null
+++ b/uv_magic_uv/legacy/ui/VIEW3D_MT_object.py
@@ -0,0 +1,54 @@
+# <pep8-80 compliant>
+
+# ##### 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 #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bpy
+
+from ..op import copy_paste_uv_object
+from ...utils.bl_class_registry import BlClassRegistry
+
+__all__ = [
+    'MUV_MT_CopyPasteUV_Object',
+]
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_CopyPasteUV_Object(bpy.types.Menu):
+    """
+    Menu class: Master menu of Copy/Paste UV coordinate among object
+    """
+
+    bl_idname = "uv.muv_copy_paste_uv_object_menu"
+    bl_label = "Copy/Paste UV"
+    bl_description = "Copy and Paste UV coordinate among object"
+
+    def draw(self, _):
+        layout = self.layout
+
+        layout.menu(
+            copy_paste_uv_object.MUV_MT_CopyPasteUVObject_CopyUV.bl_idname,
+            text="Copy")
+        layout.menu(
+            copy_paste_uv_object.MUV_MT_CopyPasteUVObject_PasteUV.bl_idname,
+            text="Paste")
diff --git a/uv_magic_uv/legacy/ui/VIEW3D_MT_uv_map.py b/uv_magic_uv/legacy/ui/VIEW3D_MT_uv_map.py
new file mode 100644
index 0000000000000000000000000000000000000000..d229322a034aa382920c3b0ab782bb1d1f891d20
--- /dev/null
+++ b/uv_magic_uv/legacy/ui/VIEW3D_MT_uv_map.py
@@ -0,0 +1,257 @@
+# <pep8-80 compliant>
+
+# ##### 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 #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bpy
+import bpy.utils
+
+from ..op import (
+    texture_lock,
+    copy_paste_uv,
+    preserve_uv_aspect,
+    texture_projection,
+    texture_wrap,
+    transfer_uv,
+    uvw,
+    world_scale_uv
+)
+from ..op.world_scale_uv import MUV_OT_WorldScaleUV_ApplyProportionalToMesh
+from ...utils.bl_class_registry import BlClassRegistry
+
+__all__ = [
+    'MUV_MT_CopyPasteUV',
+    'MUV_MT_TransferUV',
+    'MUV_MT_TextureLock',
+    'MUV_MT_WorldScaleUV',
+    'MUV_MT_TextureWrap',
+    'MUV_MT_UVW',
+    'MUV_MT_TextureProjection',
+    'MUV_MT_PreserveUVAspect',
+]
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_CopyPasteUV(bpy.types.Menu):
+    """
+    Menu class: Master menu of Copy/Paste UV coordinate
+    """
+
+    bl_idname = "uv.muv_copy_paste_uv_menu"
+    bl_label = "Copy/Paste UV"
+    bl_description = "Copy and Paste UV coordinate"
+
+    def draw(self, _):
+        layout = self.layout
+
+        layout.label(text="Default")
+        layout.menu(copy_paste_uv.MUV_MT_CopyPasteUV_CopyUV.bl_idname,
+                    text="Copy")
+        layout.menu(copy_paste_uv.MUV_MT_CopyPasteUV_PasteUV.bl_idname,
+                    text="Paste")
+
+        layout.separator()
+
+        layout.label(text="Selection Sequence")
+        layout.menu(copy_paste_uv.MUV_MT_CopyPasteUV_SelSeqCopyUV.bl_idname,
+                    text="Copy")
+        layout.menu(copy_paste_uv.MUV_MT_CopyPasteUV_SelSeqPasteUV.bl_idname,
+                    text="Paste")
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_TransferUV(bpy.types.Menu):
+    """
+    Menu class: Master menu of Transfer UV coordinate
+    """
+
+    bl_idname = "uv.muv_transfer_uv_menu"
+    bl_label = "Transfer UV"
+    bl_description = "Transfer UV coordinate"
+
+    def draw(self, context):
+        layout = self.layout
+        sc = context.scene
+
+        layout.operator(transfer_uv.MUV_OT_TransferUV_CopyUV.bl_idname,
+                        text="Copy")
+        ops = layout.operator(transfer_uv.MUV_OT_TransferUV_PasteUV.bl_idname,
+                              text="Paste")
+        ops.invert_normals = sc.muv_transfer_uv_invert_normals
+        ops.copy_seams = sc.muv_transfer_uv_copy_seams
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_TextureLock(bpy.types.Menu):
+    """
+    Menu class: Master menu of Texture Lock
+    """
+
+    bl_idname = "uv.muv_texture_lock_menu"
+    bl_label = "Texture Lock"
+    bl_description = "Lock texture when vertices of mesh (Preserve UV)"
+
+    def draw(self, context):
+        layout = self.layout
+        sc = context.scene
+
+        layout.label("Normal Mode")
+        layout.operator(
+            texture_lock.MUV_OT_TextureLock_Lock.bl_idname,
+            text="Lock"
+            if not texture_lock.MUV_OT_TextureLock_Lock.is_ready(context)
+            else "ReLock")
+        ops = layout.operator(texture_lock.MUV_OT_TextureLock_Unlock.bl_idname,
+                              text="Unlock")
+        ops.connect = sc.muv_texture_lock_connect
+
+        layout.separator()
+
+        layout.label("Interactive Mode")
+        layout.prop(sc, "muv_texture_lock_lock", text="Lock")
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_WorldScaleUV(bpy.types.Menu):
+    """
+    Menu class: Master menu of world scale UV
+    """
+
+    bl_idname = "uv.muv_world_scale_uv_menu"
+    bl_label = "World Scale UV"
+    bl_description = ""
+
+    def draw(self, context):
+        layout = self.layout
+        sc = context.scene
+
+        layout.operator(world_scale_uv.MUV_OT_WorldScaleUV_Measure.bl_idname,
+                        text="Measure")
+
+        layout.operator(
+            world_scale_uv.MUV_OT_WorldScaleUV_ApplyManual.bl_idname,
+            text="Apply (Manual)")
+
+        ops = layout.operator(
+            world_scale_uv.MUV_OT_WorldScaleUV_ApplyScalingDensity.bl_idname,
+            text="Apply (Same Desity)")
+        ops.src_density = sc.muv_world_scale_uv_src_density
+        ops.same_density = True
+
+        ops = layout.operator(
+            world_scale_uv.MUV_OT_WorldScaleUV_ApplyScalingDensity.bl_idname,
+            text="Apply (Scaling Desity)")
+        ops.src_density = sc.muv_world_scale_uv_src_density
+        ops.same_density = False
+        ops.tgt_scaling_factor = sc.muv_world_scale_uv_tgt_scaling_factor
+
+        ops = layout.operator(
+            MUV_OT_WorldScaleUV_ApplyProportionalToMesh.bl_idname,
+            text="Apply (Proportional to Mesh)")
+        ops.src_density = sc.muv_world_scale_uv_src_density
+        ops.src_uv_area = sc.muv_world_scale_uv_src_uv_area
+        ops.src_mesh_area = sc.muv_world_scale_uv_src_mesh_area
+        ops.origin = sc.muv_world_scale_uv_origin
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_TextureWrap(bpy.types.Menu):
+    """
+    Menu class: Master menu of Texture Wrap
+    """
+
+    bl_idname = "uv.muv_texture_wrap_menu"
+    bl_label = "Texture Wrap"
+    bl_description = ""
+
+    def draw(self, _):
+        layout = self.layout
+
+        layout.operator(texture_wrap.MUV_OT_TextureWrap_Refer.bl_idname,
+                        text="Refer")
+        layout.operator(texture_wrap.MUV_OT_TextureWrap_Set.bl_idname,
+                        text="Set")
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_UVW(bpy.types.Menu):
+    """
+    Menu class: Master menu of UVW
+    """
+
+    bl_idname = "uv.muv_uvw_menu"
+    bl_label = "UVW"
+    bl_description = ""
+
+    def draw(self, context):
+        layout = self.layout
+        sc = context.scene
+
+        ops = layout.operator(uvw.MUV_OT_UVW_BoxMap.bl_idname, text="Box")
+        ops.assign_uvmap = sc.muv_uvw_assign_uvmap
+
+        ops = layout.operator(uvw.MUV_OT_UVW_BestPlanerMap.bl_idname,
+                              text="Best Planner")
+        ops.assign_uvmap = sc.muv_uvw_assign_uvmap
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_TextureProjection(bpy.types.Menu):
+    """
+    Menu class: Master menu of Texture Projection
+    """
+
+    bl_idname = "uv.muv_texture_projection_menu"
+    bl_label = "Texture Projection"
+    bl_description = ""
+
+    def draw(self, context):
+        layout = self.layout
+        sc = context.scene
+
+        layout.prop(sc, "muv_texture_projection_enable",
+                    text="Texture Projection")
+        layout.operator(
+            texture_projection.MUV_OT_TextureProjection_Project.bl_idname,
+            text="Project")
+
+
+@BlClassRegistry(legacy=True)
+class MUV_MT_PreserveUVAspect(bpy.types.Menu):
+    """
+    Menu class: Master menu of Preserve UV Aspect
+    """
+
+    bl_idname = "uv.muv_preserve_uv_aspect_menu"
+    bl_label = "Preserve UV Aspect"
+    bl_description = ""
+
+    def draw(self, context):
+        layout = self.layout
+        sc = context.scene
+
+        for key in bpy.data.images.keys():
+            ops = layout.operator(
+                preserve_uv_aspect.MUV_OT_PreserveUVAspect.bl_idname, text=key)
+            ops.dest_img_name = key
+            ops.origin = sc.muv_preserve_uv_aspect_origin
diff --git a/uv_magic_uv/legacy/ui/__init__.py b/uv_magic_uv/legacy/ui/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..bf790a8e4203c5d0e6593d695283e020c3368d49
--- /dev/null
+++ b/uv_magic_uv/legacy/ui/__init__.py
@@ -0,0 +1,50 @@
+# <pep8-80 compliant>
+
+# ##### 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 #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+if "bpy" in locals():
+    import importlib
+    importlib.reload(view3d_copy_paste_uv_objectmode)
+    importlib.reload(view3d_copy_paste_uv_editmode)
+    importlib.reload(view3d_uv_manipulation)
+    importlib.reload(view3d_uv_mapping)
+    importlib.reload(uvedit_copy_paste_uv)
+    importlib.reload(uvedit_uv_manipulation)
+    importlib.reload(uvedit_editor_enhancement)
+    importlib.reload(VIEW3D_MT_uv_map)
+    importlib.reload(VIEW3D_MT_object)
+    importlib.reload(IMAGE_MT_uvs)
+else:
+    from . import view3d_copy_paste_uv_objectmode
+    from . import view3d_copy_paste_uv_editmode
+    from . import view3d_uv_manipulation
+    from . import view3d_uv_mapping
+    from . import uvedit_copy_paste_uv
+    from . import uvedit_uv_manipulation
+    from . import uvedit_editor_enhancement
+    from . import VIEW3D_MT_uv_map
+    from . import VIEW3D_MT_object
+    from . import IMAGE_MT_uvs
+
+import bpy
\ No newline at end of file
diff --git a/uv_magic_uv/legacy/ui/uvedit_copy_paste_uv.py b/uv_magic_uv/legacy/ui/uvedit_copy_paste_uv.py
new file mode 100644
index 0000000000000000000000000000000000000000..9848f03b78951ca083828a90d32912459a8ecaeb
--- /dev/null
+++ b/uv_magic_uv/legacy/ui/uvedit_copy_paste_uv.py
@@ -0,0 +1,62 @@
+# <pep8-80 compliant>
+
+# ##### 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 #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bpy
+
+from ..op import copy_paste_uv_uvedit
+from ...utils.bl_class_registry import BlClassRegistry
+
+__all__ = [
+    'MUV_PT_UVEdit_CopyPasteUV',
+]
+
+
+@BlClassRegistry(legacy=True)
+class MUV_PT_UVEdit_CopyPasteUV(bpy.types.Panel):
+    """
+    Panel class: Copy/Paste UV on Property Panel on UV/ImageEditor
+    """
+
+    bl_space_type = 'IMAGE_EDITOR'
+    bl_region_type = 'TOOLS'
+    bl_label = "Copy/Paste UV"
+    bl_category = "Magic UV"
+    bl_context = 'mesh_edit'
+    bl_options = {'DEFAULT_CLOSED'}
+
+    def draw_header(self, _):
+        layout = self.layout
+        layout.label(text="", icon='IMAGE_COL')
+
+    def draw(self, _):
+        layout = self.layout
+
+        row = layout.row(align=True)
+        row.operator(
+            copy_paste_uv_uvedit.MUV_OT_CopyPasteUVUVEdit_CopyUV.bl_idname,
+            text="Copy")
+        row.operator(
+            copy_paste_uv_uvedit.MUV_OT_CopyPasteUVUVEdit_PasteUV.bl_idname,
+            text="Paste")
diff --git a/uv_magic_uv/ui/uvedit_editor_enhancement.py b/uv_magic_uv/legacy/ui/uvedit_editor_enhancement.py
similarity index 70%
rename from uv_magic_uv/ui/uvedit_editor_enhancement.py
rename to uv_magic_uv/legacy/ui/uvedit_editor_enhancement.py
index 9cde0cad6b753404907ef146472323e22ad45d88..3f750febfbefbc51fd5f062b8916ad4572ea0b72 100644
--- a/uv_magic_uv/ui/uvedit_editor_enhancement.py
+++ b/uv_magic_uv/legacy/ui/uvedit_editor_enhancement.py
@@ -25,17 +25,20 @@ __date__ = "17 Nov 2018"
 
 import bpy
 
-from ..op import align_uv_cursor
-from ..op import uv_bounding_box
-from ..op import uv_inspection
-
+from ..op import (
+    align_uv_cursor,
+    uv_bounding_box,
+    uv_inspection,
+)
+from ...utils.bl_class_registry import BlClassRegistry
 
 __all__ = [
-    'PanelEditorEnhancement',
+    'MUV_PT_UVEdit_EditorEnhancement',
 ]
 
 
-class PanelEditorEnhancement(bpy.types.Panel):
+@BlClassRegistry(legacy=True)
+class MUV_PT_UVEdit_EditorEnhancement(bpy.types.Panel):
     """
     Panel class: UV/Image Editor Enhancement
     """
@@ -63,43 +66,43 @@ class PanelEditorEnhancement(bpy.types.Panel):
             col = box.column(align=True)
 
             row = col.row(align=True)
-            ops = row.operator(align_uv_cursor.Operator.bl_idname,
+            ops = row.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
                                text="Left Top")
             ops.position = 'LEFT_TOP'
             ops.base = sc.muv_align_uv_cursor_align_method
-            ops = row.operator(align_uv_cursor.Operator.bl_idname,
+            ops = row.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
                                text="Middle Top")
             ops.position = 'MIDDLE_TOP'
             ops.base = sc.muv_align_uv_cursor_align_method
-            ops = row.operator(align_uv_cursor.Operator.bl_idname,
+            ops = row.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
                                text="Right Top")
             ops.position = 'RIGHT_TOP'
             ops.base = sc.muv_align_uv_cursor_align_method
 
             row = col.row(align=True)
-            ops = row.operator(align_uv_cursor.Operator.bl_idname,
+            ops = row.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
                                text="Left Middle")
             ops.position = 'LEFT_MIDDLE'
             ops.base = sc.muv_align_uv_cursor_align_method
-            ops = row.operator(align_uv_cursor.Operator.bl_idname,
+            ops = row.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
                                text="Center")
             ops.position = 'CENTER'
             ops.base = sc.muv_align_uv_cursor_align_method
-            ops = row.operator(align_uv_cursor.Operator.bl_idname,
+            ops = row.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
                                text="Right Middle")
             ops.position = 'RIGHT_MIDDLE'
             ops.base = sc.muv_align_uv_cursor_align_method
 
             row = col.row(align=True)
-            ops = row.operator(align_uv_cursor.Operator.bl_idname,
+            ops = row.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
                                text="Left Bottom")
             ops.position = 'LEFT_BOTTOM'
             ops.base = sc.muv_align_uv_cursor_align_method
-            ops = row.operator(align_uv_cursor.Operator.bl_idname,
+            ops = row.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
                                text="Middle Bottom")
             ops.position = 'MIDDLE_BOTTOM'
             ops.base = sc.muv_align_uv_cursor_align_method
-            ops = row.operator(align_uv_cursor.Operator.bl_idname,
+            ops = row.operator(align_uv_cursor.MUV_OT_AlignUVCursor.bl_idname,
                                text="Right Bottom")
             ops.position = 'RIGHT_BOTTOM'
             ops.base = sc.muv_align_uv_cursor_align_method
@@ -113,13 +116,14 @@ class PanelEditorEnhancement(bpy.types.Panel):
         box = layout.box()
         box.prop(sc, "muv_uv_bounding_box_enabled", text="UV Bounding Box")
         if sc.muv_uv_bounding_box_enabled:
-            box.prop(sc, "muv_uv_bounding_box_show",
-                     text="Hide"
-                     if uv_bounding_box.Operator.is_running(context)
-                     else "Show",
-                     icon='RESTRICT_VIEW_OFF'
-                     if uv_bounding_box.Operator.is_running(context)
-                     else 'RESTRICT_VIEW_ON')
+            box.prop(
+                sc, "muv_uv_bounding_box_show",
+                text="Hide"
+                if uv_bounding_box.MUV_OT_UVBoundingBox.is_running(context)
+                else "Show",
+                icon='RESTRICT_VIEW_OFF'
+                if uv_bounding_box.MUV_OT_UVBoundingBox.is_running(context)
+                else 'RESTRICT_VIEW_ON')
             box.prop(sc, "muv_uv_bounding_box_uniform_scaling",
                      text="Uniform Scaling")
             box.prop(sc, "muv_uv_bounding_box_boundary", text="Boundary")
@@ -128,14 +132,15 @@ class PanelEditorEnhancement(bpy.types.Panel):
         box.prop(sc, "muv_uv_inspection_enabled", text="UV Inspection")
         if sc.muv_uv_inspection_enabled:
             row = box.row()
-            row.prop(sc, "muv_uv_inspection_show",
-                     text="Hide"
-                     if uv_inspection.OperatorRender.is_running(context)
-                     else "Show",
-                     icon='RESTRICT_VIEW_OFF'
-                     if uv_inspection.OperatorRender.is_running(context)
-                     else 'RESTRICT_VIEW_ON')
-            row.operator(uv_inspection.OperatorUpdate.bl_idname,
+            row.prop(
+                sc, "muv_uv_inspection_show",
+                text="Hide"
+                if uv_inspection.MUV_OT_UVInspection_Render.is_running(context)
+                else "Show",
+                icon='RESTRICT_VIEW_OFF'
+                if uv_inspection.MUV_OT_UVInspection_Render.is_running(context)
+                else 'RESTRICT_VIEW_ON')
+            row.operator(uv_inspection.MUV_OT_UVInspection_Update.bl_idname,
                          text="Update")
             row = box.row()
             row.prop(sc, "muv_uv_inspection_show_overlapped")
diff --git a/uv_magic_uv/ui/uvedit_uv_manipulation.py b/uv_magic_uv/legacy/ui/uvedit_uv_manipulation.py
similarity index 84%
rename from uv_magic_uv/ui/uvedit_uv_manipulation.py
rename to uv_magic_uv/legacy/ui/uvedit_uv_manipulation.py
index 0f6a105e96586cd2e9f43e1c8888591b61d3a2f2..96cf17d324c17eeba2b45cf478559702a237ff90 100644
--- a/uv_magic_uv/ui/uvedit_uv_manipulation.py
+++ b/uv_magic_uv/legacy/ui/uvedit_uv_manipulation.py
@@ -25,18 +25,21 @@ __date__ = "17 Nov 2018"
 
 import bpy
 
-from ..op import align_uv
-from ..op import smooth_uv
-from ..op import pack_uv
-from ..op import select_uv
-
+from ..op import (
+    align_uv,
+    smooth_uv,
+    pack_uv,
+    select_uv,
+)
+from ...utils.bl_class_registry import BlClassRegistry
 
 __all__ = [
-    'MenuUVManipulation',
+    'MUV_PT_UVEdit_UVManipulation',
 ]
 
 
-class MenuUVManipulation(bpy.types.Panel):
+@BlClassRegistry(legacy=True)
+class MUV_PT_UVEdit_UVManipulation(bpy.types.Panel):
     """
     Panel class: UV Manipulation on Property Panel on UV/ImageEditor
     """
@@ -61,11 +64,11 @@ class MenuUVManipulation(bpy.types.Panel):
         if sc.muv_align_uv_enabled:
             col = box.column()
             row = col.row(align=True)
-            ops = row.operator(align_uv.OperatorCircle.bl_idname,
+            ops = row.operator(align_uv.MUV_OT_AlignUV_Circle.bl_idname,
                                text="Circle")
             ops.transmission = sc.muv_align_uv_transmission
             ops.select = sc.muv_align_uv_select
-            ops = row.operator(align_uv.OperatorStraighten.bl_idname,
+            ops = row.operator(align_uv.MUV_OT_AlignUV_Straighten.bl_idname,
                                text="Straighten")
             ops.transmission = sc.muv_align_uv_transmission
             ops.select = sc.muv_align_uv_select
@@ -73,7 +76,8 @@ class MenuUVManipulation(bpy.types.Panel):
             ops.horizontal = sc.muv_align_uv_horizontal
             ops.mesh_infl = sc.muv_align_uv_mesh_infl
             row = col.row()
-            ops = row.operator(align_uv.OperatorAxis.bl_idname, text="XY-axis")
+            ops = row.operator(align_uv.MUV_OT_AlignUV_Axis.bl_idname,
+                               text="XY-axis")
             ops.transmission = sc.muv_align_uv_transmission
             ops.select = sc.muv_align_uv_select
             ops.vertical = sc.muv_align_uv_vertical
@@ -94,7 +98,7 @@ class MenuUVManipulation(bpy.types.Panel):
         box = layout.box()
         box.prop(sc, "muv_smooth_uv_enabled", text="Smooth UV")
         if sc.muv_smooth_uv_enabled:
-            ops = box.operator(smooth_uv.Operator.bl_idname,
+            ops = box.operator(smooth_uv.MUV_OT_SmoothUV.bl_idname,
                                text="Smooth")
             ops.transmission = sc.muv_smooth_uv_transmission
             ops.select = sc.muv_smooth_uv_select
@@ -109,13 +113,13 @@ class MenuUVManipulation(bpy.types.Panel):
         box.prop(sc, "muv_select_uv_enabled", text="Select UV")
         if sc.muv_select_uv_enabled:
             row = box.row(align=True)
-            row.operator(select_uv.OperatorSelectOverlapped.bl_idname)
-            row.operator(select_uv.OperatorSelectFlipped.bl_idname)
+            row.operator(select_uv.MUV_OT_SelectUV_SelectOverlapped.bl_idname)
+            row.operator(select_uv.MUV_OT_SelectUV_SelectFlipped.bl_idname)
 
         box = layout.box()
         box.prop(sc, "muv_pack_uv_enabled", text="Pack UV (Extension)")
         if sc.muv_pack_uv_enabled:
-            ops = box.operator(pack_uv.Operator.bl_idname, text="Pack UV")
+            ops = box.operator(pack_uv.MUV_OT_PackUV.bl_idname, text="Pack UV")
             ops.allowable_center_deviation = \
                 sc.muv_pack_uv_allowable_center_deviation
             ops.allowable_size_deviation = \
diff --git a/uv_magic_uv/legacy/ui/view3d_copy_paste_uv_editmode.py b/uv_magic_uv/legacy/ui/view3d_copy_paste_uv_editmode.py
new file mode 100644
index 0000000000000000000000000000000000000000..ee2713b22d19cfd193dd1ddd430e679c41aa0c1a
--- /dev/null
+++ b/uv_magic_uv/legacy/ui/view3d_copy_paste_uv_editmode.py
@@ -0,0 +1,93 @@
+# <pep8-80 compliant>
+
+# ##### 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 #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bpy
+
+from ..op import (
+    transfer_uv,
+    copy_paste_uv,
+)
+from ...utils.bl_class_registry import BlClassRegistry
+
+__all__ = [
+    'MUV_PT_View3D_Edit_CopyPasteUV',
+]
+
+
+@BlClassRegistry(legacy=True)
+class MUV_PT_View3D_Edit_CopyPasteUV(bpy.types.Panel):
+    """
+    Panel class: Copy/Paste UV on Property Panel on View3D
+    """
+
+    bl_space_type = 'VIEW_3D'
+    bl_region_type = 'TOOLS'
+    bl_label = "Copy/Paste UV"
+    bl_category = "Magic UV"
+    bl_context = 'mesh_edit'
+    bl_options = {'DEFAULT_CLOSED'}
+
+    def draw_header(self, _):
+        layout = self.layout
+        layout.label(text="", icon='IMAGE_COL')
+
+    def draw(self, context):
+        sc = context.scene
+        layout = self.layout
+
+        box = layout.box()
+        box.prop(sc, "muv_copy_paste_uv_enabled", text="Copy/Paste UV")
+        if sc.muv_copy_paste_uv_enabled:
+            row = box.row(align=True)
+            if sc.muv_copy_paste_uv_mode == 'DEFAULT':
+                row.menu(copy_paste_uv.MUV_MT_CopyPasteUV_CopyUV.bl_idname,
+                         text="Copy")
+                row.menu(copy_paste_uv.MUV_MT_CopyPasteUV_PasteUV.bl_idname,
+                         text="Paste")
+            elif sc.muv_copy_paste_uv_mode == 'SEL_SEQ':
+                row.menu(
+                    copy_paste_uv.MUV_MT_CopyPasteUV_SelSeqCopyUV.bl_idname,
+                    text="Copy")
+                row.menu(
+                    copy_paste_uv.MUV_MT_CopyPasteUV_SelSeqPasteUV.bl_idname,
+                    text="Paste")
+            box.prop(sc, "muv_copy_paste_uv_mode", expand=True)
+            box.prop(sc, "muv_copy_paste_uv_copy_seams", text="Seams")
+            box.prop(sc, "muv_copy_paste_uv_strategy", text="Strategy")
+
+        box = layout.box()
+        box.prop(sc, "muv_transfer_uv_enabled", text="Transfer UV")
+        if sc.muv_transfer_uv_enabled:
+            row = box.row(align=True)
+            row.operator(transfer_uv.MUV_OT_TransferUV_CopyUV.bl_idname,
+                         text="Copy")
+            ops = row.operator(transfer_uv.MUV_OT_TransferUV_PasteUV.bl_idname,
+                               text="Paste")
+            ops.invert_normals = sc.muv_transfer_uv_invert_normals
+            ops.copy_seams = sc.muv_transfer_uv_copy_seams
+            row = box.row()
+            row.prop(sc, "muv_transfer_uv_invert_normals",
+                     text="Invert Normals")
+            row.prop(sc, "muv_transfer_uv_copy_seams", text="Seams")
diff --git a/uv_magic_uv/legacy/ui/view3d_copy_paste_uv_objectmode.py b/uv_magic_uv/legacy/ui/view3d_copy_paste_uv_objectmode.py
new file mode 100644
index 0000000000000000000000000000000000000000..58031b0f3158a2f78689741713bd55ac3d899bb7
--- /dev/null
+++ b/uv_magic_uv/legacy/ui/view3d_copy_paste_uv_objectmode.py
@@ -0,0 +1,65 @@
+# <pep8-80 compliant>
+
+# ##### 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 #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bpy
+
+from ..op import copy_paste_uv_object
+from ...utils.bl_class_registry import BlClassRegistry
+
+__all__ = [
+    'MUV_PT_View3D_Object_CopyPasteUV',
+]
+
+
+@BlClassRegistry(legacy=True)
+class MUV_PT_View3D_Object_CopyPasteUV(bpy.types.Panel):
+    """
+    Panel class: Copy/Paste UV on Property Panel on View3D
+    """
+
+    bl_space_type = 'VIEW_3D'
+    bl_region_type = 'TOOLS'
+    bl_label = "Copy/Paste UV"
+    bl_category = "Magic UV"
+    bl_context = 'objectmode'
+    bl_options = {'DEFAULT_CLOSED'}
+
+    def draw_header(self, _):
+        layout = self.layout
+        layout.label(text="", icon='IMAGE_COL')
+
+    def draw(self, context):
+        sc = context.scene
+        layout = self.layout
+
+        row = layout.row(align=True)
+        row.menu(
+            copy_paste_uv_object.MUV_MT_CopyPasteUVObject_CopyUV.bl_idname,
+            text="Copy")
+        row.menu(
+            copy_paste_uv_object.MUV_MT_CopyPasteUVObject_PasteUV.bl_idname,
+            text="Paste")
+        layout.prop(sc, "muv_copy_paste_uv_object_copy_seams",
+                    text="Seams")
diff --git a/uv_magic_uv/legacy/ui/view3d_uv_manipulation.py b/uv_magic_uv/legacy/ui/view3d_uv_manipulation.py
new file mode 100644
index 0000000000000000000000000000000000000000..7d828a38fb1bf5aef2356f0a955badb4b915c17e
--- /dev/null
+++ b/uv_magic_uv/legacy/ui/view3d_uv_manipulation.py
@@ -0,0 +1,289 @@
+# <pep8-80 compliant>
+
+# ##### 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 #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bpy
+
+from ..op import (
+    move_uv,
+    flip_rotate_uv,
+    mirror_uv,
+    preserve_uv_aspect,
+    texture_lock,
+    texture_wrap,
+    uv_sculpt,
+    world_scale_uv,
+)
+from ..op.world_scale_uv import (
+    MUV_OT_WorldScaleUV_ApplyProportionalToMesh,
+    MUV_OT_WorldScaleUV_ApplyScalingDensity
+)
+from ...utils.bl_class_registry import BlClassRegistry
+
+__all__ = [
+    'MUV_PT_View3D_UVManipulation',
+]
+
+
+@BlClassRegistry(legacy=True)
+class MUV_PT_View3D_UVManipulation(bpy.types.Panel):
+    """
+    Panel class: UV Manipulation on Property Panel on View3D
+    """
+
+    bl_space_type = 'VIEW_3D'
+    bl_region_type = 'TOOLS'
+    bl_label = "UV Manipulation"
+    bl_category = "Magic UV"
+    bl_context = 'mesh_edit'
+    bl_options = {'DEFAULT_CLOSED'}
+
+    def draw_header(self, _):
+        layout = self.layout
+        layout.label(text="", icon='IMAGE_COL')
+
+    def draw(self, context):
+        sc = context.scene
+        layout = self.layout
+
+        box = layout.box()
+        box.prop(sc, "muv_flip_rotate_uv_enabled", text="Flip/Rotate UV")
+        if sc.muv_flip_rotate_uv_enabled:
+            row = box.row()
+            ops = row.operator(flip_rotate_uv.MUV_OT_FlipRotate.bl_idname,
+                               text="Flip/Rotate")
+            ops.seams = sc.muv_flip_rotate_uv_seams
+            row.prop(sc, "muv_flip_rotate_uv_seams", text="Seams")
+
+        box = layout.box()
+        box.prop(sc, "muv_mirror_uv_enabled", text="Mirror UV")
+        if sc.muv_mirror_uv_enabled:
+            row = box.row()
+            ops = row.operator(mirror_uv.MUV_OT_MirrorUV.bl_idname,
+                               text="Mirror")
+            ops.axis = sc.muv_mirror_uv_axis
+            row.prop(sc, "muv_mirror_uv_axis", text="")
+
+        box = layout.box()
+        box.prop(sc, "muv_move_uv_enabled", text="Move UV")
+        if sc.muv_move_uv_enabled:
+            col = box.column()
+            if not move_uv.MUV_OT_MoveUV.is_running(context):
+                col.operator(move_uv.MUV_OT_MoveUV.bl_idname, icon='PLAY',
+                             text="Start")
+            else:
+                col.operator(move_uv.MUV_OT_MoveUV.bl_idname, icon='PAUSE',
+                             text="Stop")
+
+        box = layout.box()
+        box.prop(sc, "muv_world_scale_uv_enabled", text="World Scale UV")
+        if sc.muv_world_scale_uv_enabled:
+            box.prop(sc, "muv_world_scale_uv_mode", text="")
+
+            if sc.muv_world_scale_uv_mode == 'MANUAL':
+                sp = box.split(percentage=0.5)
+                col = sp.column()
+                col.prop(sc, "muv_world_scale_uv_tgt_texture_size",
+                         text="Texture Size")
+                sp = sp.split(percentage=1.0)
+                col = sp.column()
+                col.label("Density:")
+                col.prop(sc, "muv_world_scale_uv_tgt_density", text="")
+                box.prop(sc, "muv_world_scale_uv_origin", text="Origin")
+                ops = box.operator(
+                    world_scale_uv.MUV_OT_WorldScaleUV_ApplyManual.bl_idname,
+                    text="Apply")
+                ops.tgt_density = sc.muv_world_scale_uv_tgt_density
+                ops.tgt_texture_size = sc.muv_world_scale_uv_tgt_texture_size
+                ops.origin = sc.muv_world_scale_uv_origin
+                ops.show_dialog = False
+
+            elif sc.muv_world_scale_uv_mode == 'SAME_DENSITY':
+                sp = box.split(percentage=0.4)
+                col = sp.column(align=True)
+                col.label("Source:")
+                sp = sp.split(percentage=1.0)
+                col = sp.column(align=True)
+                col.operator(
+                    world_scale_uv.MUV_OT_WorldScaleUV_Measure.bl_idname,
+                    text="Measure")
+
+                sp = box.split(percentage=0.7)
+                col = sp.column(align=True)
+                col.prop(sc, "muv_world_scale_uv_src_density", text="Density")
+                col.enabled = False
+                sp = sp.split(percentage=1.0)
+                col = sp.column(align=True)
+                col.label("px2/cm2")
+
+                box.separator()
+                box.prop(sc, "muv_world_scale_uv_origin", text="Origin")
+                ops = box.operator(
+                    MUV_OT_WorldScaleUV_ApplyScalingDensity.bl_idname,
+                    text="Apply")
+                ops.src_density = sc.muv_world_scale_uv_src_density
+                ops.origin = sc.muv_world_scale_uv_origin
+                ops.same_density = True
+                ops.show_dialog = False
+
+            elif sc.muv_world_scale_uv_mode == 'SCALING_DENSITY':
+                sp = box.split(percentage=0.4)
+                col = sp.column(align=True)
+                col.label("Source:")
+                sp = sp.split(percentage=1.0)
+                col = sp.column(align=True)
+                col.operator(
+                    world_scale_uv.MUV_OT_WorldScaleUV_Measure.bl_idname,
+                    text="Measure")
+
+                sp = box.split(percentage=0.7)
+                col = sp.column(align=True)
+                col.prop(sc, "muv_world_scale_uv_src_density", text="Density")
+                col.enabled = False
+                sp = sp.split(percentage=1.0)
+                col = sp.column(align=True)
+                col.label("px2/cm2")
+
+                box.separator()
+                box.prop(sc, "muv_world_scale_uv_tgt_scaling_factor",
+                         text="Scaling Factor")
+                box.prop(sc, "muv_world_scale_uv_origin", text="Origin")
+                ops = box.operator(
+                    MUV_OT_WorldScaleUV_ApplyScalingDensity.bl_idname,
+                    text="Apply")
+                ops.src_density = sc.muv_world_scale_uv_src_density
+                ops.origin = sc.muv_world_scale_uv_origin
+                ops.same_density = False
+                ops.show_dialog = False
+                ops.tgt_scaling_factor = \
+                    sc.muv_world_scale_uv_tgt_scaling_factor
+
+            elif sc.muv_world_scale_uv_mode == 'PROPORTIONAL_TO_MESH':
+                sp = box.split(percentage=0.4)
+                col = sp.column(align=True)
+                col.label("Source:")
+                sp = sp.split(percentage=1.0)
+                col = sp.column(align=True)
+                col.operator(
+                    world_scale_uv.MUV_OT_WorldScaleUV_Measure.bl_idname,
+                    text="Measure")
+
+                sp = box.split(percentage=0.7)
+                col = sp.column(align=True)
+                col.prop(sc, "muv_world_scale_uv_src_mesh_area",
+                         text="Mesh Area")
+                col.prop(sc, "muv_world_scale_uv_src_uv_area", text="UV Area")
+                col.prop(sc, "muv_world_scale_uv_src_density", text="Density")
+                col.enabled = False
+                sp = sp.split(percentage=1.0)
+                col = sp.column(align=True)
+                col.label("cm2")
+                col.label("px2")
+                col.label("px2/cm2")
+                col.enabled = False
+
+                box.separator()
+                box.prop(sc, "muv_world_scale_uv_origin", text="Origin")
+                ops = box.operator(
+                    MUV_OT_WorldScaleUV_ApplyProportionalToMesh.bl_idname,
+                    text="Apply")
+                ops.src_density = sc.muv_world_scale_uv_src_density
+                ops.src_uv_area = sc.muv_world_scale_uv_src_uv_area
+                ops.src_mesh_area = sc.muv_world_scale_uv_src_mesh_area
+                ops.origin = sc.muv_world_scale_uv_origin
+                ops.show_dialog = False
+
+        box = layout.box()
+        box.prop(sc, "muv_preserve_uv_aspect_enabled",
+                 text="Preserve UV Aspect")
+        if sc.muv_preserve_uv_aspect_enabled:
+            row = box.row()
+            ops = row.operator(
+                preserve_uv_aspect.MUV_OT_PreserveUVAspect.bl_idname,
+                text="Change Image")
+            ops.dest_img_name = sc.muv_preserve_uv_aspect_tex_image
+            ops.origin = sc.muv_preserve_uv_aspect_origin
+            row.prop(sc, "muv_preserve_uv_aspect_tex_image", text="")
+            box.prop(sc, "muv_preserve_uv_aspect_origin", text="Origin")
+
+        box = layout.box()
+        box.prop(sc, "muv_texture_lock_enabled", text="Texture Lock")
+        if sc.muv_texture_lock_enabled:
+            row = box.row(align=True)
+            col = row.column(align=True)
+            col.label("Normal Mode:")
+            col = row.column(align=True)
+            col.operator(
+                texture_lock.MUV_OT_TextureLock_Lock.bl_idname,
+                text="Lock"
+                if not texture_lock.MUV_OT_TextureLock_Lock.is_ready(context)
+                else "ReLock")
+            ops = col.operator(
+                texture_lock.MUV_OT_TextureLock_Unlock.bl_idname,
+                text="Unlock")
+            ops.connect = sc.muv_texture_lock_connect
+            col.prop(sc, "muv_texture_lock_connect", text="Connect")
+
+            row = box.row(align=True)
+            row.label("Interactive Mode:")
+            box.prop(
+                sc, "muv_texture_lock_lock",
+                text="Unlock"
+                if texture_lock.MUV_OT_TextureLock_Intr.is_running(context)
+                else "Lock",
+                icon='RESTRICT_VIEW_OFF'
+                if texture_lock.MUV_OT_TextureLock_Intr.is_running(context)
+                else 'RESTRICT_VIEW_ON')
+
+        box = layout.box()
+        box.prop(sc, "muv_texture_wrap_enabled", text="Texture Wrap")
+        if sc.muv_texture_wrap_enabled:
+            row = box.row(align=True)
+            row.operator(texture_wrap.MUV_OT_TextureWrap_Refer.bl_idname,
+                         text="Refer")
+            row.operator(texture_wrap.MUV_OT_TextureWrap_Set.bl_idname,
+                         text="Set")
+            box.prop(sc, "muv_texture_wrap_set_and_refer")
+            box.prop(sc, "muv_texture_wrap_selseq")
+
+        box = layout.box()
+        box.prop(sc, "muv_uv_sculpt_enabled", text="UV Sculpt")
+        if sc.muv_uv_sculpt_enabled:
+            box.prop(
+                sc, "muv_uv_sculpt_enable",
+                text="Disable"if uv_sculpt.MUV_OT_UVSculpt.is_running(context)
+                else "Enable",
+                icon='RESTRICT_VIEW_OFF'
+                if uv_sculpt.MUV_OT_UVSculpt.is_running(context)
+                else 'RESTRICT_VIEW_ON')
+            col = box.column()
+            col.label("Brush:")
+            col.prop(sc, "muv_uv_sculpt_radius")
+            col.prop(sc, "muv_uv_sculpt_strength")
+            box.prop(sc, "muv_uv_sculpt_tools")
+            if sc.muv_uv_sculpt_tools == 'PINCH':
+                box.prop(sc, "muv_uv_sculpt_pinch_invert")
+            elif sc.muv_uv_sculpt_tools == 'RELAX':
+                box.prop(sc, "muv_uv_sculpt_relax_method")
+            box.prop(sc, "muv_uv_sculpt_show_brush")
diff --git a/uv_magic_uv/legacy/ui/view3d_uv_mapping.py b/uv_magic_uv/legacy/ui/view3d_uv_mapping.py
new file mode 100644
index 0000000000000000000000000000000000000000..3de86d0de00a0f698dd86a002e39cca95ba8c2a0
--- /dev/null
+++ b/uv_magic_uv/legacy/ui/view3d_uv_mapping.py
@@ -0,0 +1,116 @@
+# <pep8-80 compliant>
+
+# ##### 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 #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bpy
+
+from ..op import (
+    uvw,
+    texture_projection,
+    unwrap_constraint,
+)
+from ..op.texture_projection import (
+    MUV_OT_TextureProjection
+)
+from ...utils.bl_class_registry import BlClassRegistry
+
+__all__ = [
+    'MUV_PT_View3D_UVMapping',
+]
+
+
+@BlClassRegistry(legacy=True)
+class MUV_PT_View3D_UVMapping(bpy.types.Panel):
+    """
+    Panel class: UV Mapping on Property Panel on View3D
+    """
+
+    bl_space_type = 'VIEW_3D'
+    bl_region_type = 'TOOLS'
+    bl_label = "UV Mapping"
+    bl_category = "Magic UV"
+    bl_context = 'mesh_edit'
+    bl_options = {'DEFAULT_CLOSED'}
+
+    def draw_header(self, _):
+        layout = self.layout
+        layout.label(text="", icon='IMAGE_COL')
+
+    def draw(self, context):
+        sc = context.scene
+        layout = self.layout
+
+        box = layout.box()
+        box.prop(sc, "muv_unwrap_constraint_enabled", text="Unwrap Constraint")
+        if sc.muv_unwrap_constraint_enabled:
+            ops = box.operator(
+                unwrap_constraint.MUV_OT_UnwrapConstraint.bl_idname,
+                text="Unwrap")
+            ops.u_const = sc.muv_unwrap_constraint_u_const
+            ops.v_const = sc.muv_unwrap_constraint_v_const
+            row = box.row(align=True)
+            row.prop(sc, "muv_unwrap_constraint_u_const", text="U-Constraint")
+            row.prop(sc, "muv_unwrap_constraint_v_const", text="V-Constraint")
+
+        box = layout.box()
+        box.prop(sc, "muv_texture_projection_enabled",
+                 text="Texture Projection")
+        if sc.muv_texture_projection_enabled:
+            row = box.row()
+            row.prop(
+                sc, "muv_texture_projection_enable",
+                text="Disable"
+                if MUV_OT_TextureProjection.is_running(context)
+                else "Enable",
+                icon='RESTRICT_VIEW_OFF'
+                if MUV_OT_TextureProjection.is_running(context)
+                else 'RESTRICT_VIEW_ON')
+            row.prop(sc, "muv_texture_projection_tex_image", text="")
+            box.prop(sc, "muv_texture_projection_tex_transparency",
+                     text="Transparency")
+            col = box.column(align=True)
+            row = col.row()
+            row.prop(sc, "muv_texture_projection_adjust_window",
+                     text="Adjust Window")
+            if not sc.muv_texture_projection_adjust_window:
+                row.prop(sc, "muv_texture_projection_tex_magnitude",
+                         text="Magnitude")
+            col.prop(sc, "muv_texture_projection_apply_tex_aspect",
+                     text="Texture Aspect Ratio")
+            col.prop(sc, "muv_texture_projection_assign_uvmap",
+                     text="Assign UVMap")
+            box.operator(
+                texture_projection.MUV_OT_TextureProjection_Project.bl_idname,
+                text="Project")
+
+        box = layout.box()
+        box.prop(sc, "muv_uvw_enabled", text="UVW")
+        if sc.muv_uvw_enabled:
+            row = box.row(align=True)
+            ops = row.operator(uvw.MUV_OT_UVW_BoxMap.bl_idname, text="Box")
+            ops.assign_uvmap = sc.muv_uvw_assign_uvmap
+            ops = row.operator(uvw.MUV_OT_UVW_BestPlanerMap.bl_idname,
+                               text="Best Planner")
+            ops.assign_uvmap = sc.muv_uvw_assign_uvmap
+            box.prop(sc, "muv_uvw_assign_uvmap", text="Assign UVMap")
diff --git a/uv_magic_uv/op/__init__.py b/uv_magic_uv/op/__init__.py
index 9535b76d094b148741f5563ad34f356f810cad5a..2142c15771a51e7f94d76d039c4aef41d62a4832 100644
--- a/uv_magic_uv/op/__init__.py
+++ b/uv_magic_uv/op/__init__.py
@@ -25,50 +25,22 @@ __date__ = "17 Nov 2018"
 
 if "bpy" in locals():
     import importlib
-    importlib.reload(align_uv)
-    importlib.reload(align_uv_cursor)
     importlib.reload(copy_paste_uv)
     importlib.reload(copy_paste_uv_object)
     importlib.reload(copy_paste_uv_uvedit)
     importlib.reload(flip_rotate_uv)
     importlib.reload(mirror_uv)
     importlib.reload(move_uv)
-    importlib.reload(pack_uv)
-    importlib.reload(preserve_uv_aspect)
-    importlib.reload(select_uv)
-    importlib.reload(smooth_uv)
-    importlib.reload(texture_lock)
-    importlib.reload(texture_projection)
-    importlib.reload(texture_wrap)
     importlib.reload(transfer_uv)
-    importlib.reload(unwrap_constraint)
-    importlib.reload(uv_bounding_box)
-    importlib.reload(uv_inspection)
-    importlib.reload(uv_sculpt)
     importlib.reload(uvw)
-    importlib.reload(world_scale_uv)
 else:
-    from . import align_uv
-    from . import align_uv_cursor
     from . import copy_paste_uv
     from . import copy_paste_uv_object
     from . import copy_paste_uv_uvedit
     from . import flip_rotate_uv
     from . import mirror_uv
     from . import move_uv
-    from . import pack_uv
-    from . import preserve_uv_aspect
-    from . import select_uv
-    from . import smooth_uv
-    from . import texture_lock
-    from . import texture_projection
-    from . import texture_wrap
     from . import transfer_uv
-    from . import unwrap_constraint
-    from . import uv_bounding_box
-    from . import uv_inspection
-    from . import uv_sculpt
     from . import uvw
-    from . import world_scale_uv
 
 import bpy
diff --git a/uv_magic_uv/op/copy_paste_uv.py b/uv_magic_uv/op/copy_paste_uv.py
index cc1baa3009ae697eaef0473aa0750a60f96f9d1f..23bc8343853fa275a4d414e19a7ab68ea628de82 100644
--- a/uv_magic_uv/op/copy_paste_uv.py
+++ b/uv_magic_uv/op/copy_paste_uv.py
@@ -18,14 +18,14 @@
 #
 # ##### END GPL LICENSE BLOCK #####
 
-__author__ = "imdjs, Nutti <nutti.metro@gmail.com>"
+__author__ = "Nutti <nutti.metro@gmail.com>, Jace Priester"
 __status__ = "production"
 __version__ = "5.2"
 __date__ = "17 Nov 2018"
 
 
-import bpy
 import bmesh
+import bpy.utils
 from bpy.props import (
     StringProperty,
     BoolProperty,
@@ -33,151 +33,28 @@ from bpy.props import (
     EnumProperty,
 )
 
+from ..impl import copy_paste_uv_impl as impl
 from .. import common
-
+from ..utils.bl_class_registry import BlClassRegistry
+from ..utils.property_class_registry import PropertyClassRegistry
 
 __all__ = [
     'Properties',
-    'OpeartorCopyUV',
-    'MenuCopyUV',
-    'OperatorPasteUV',
-    'MenuPasteUV',
-    'OperatorSelSeqCopyUV',
-    'MenuSelSeqCopyUV',
-    'OperatorSelSeqPasteUV',
-    'MenuSelSeqPasteUV',
+    'MUV_OT_CopyPasteUV_CopyUV',
+    'MUV_MT_CopyPasteUV_CopyUV',
+    'MUV_OT_CopyPasteUV_PasteUV',
+    'MUV_MT_CopyPasteUV_PasteUV',
+    'MUV_OT_CopyPasteUV_SelSeqCopyUV',
+    'MUV_MT_CopyPasteUV_SelSeqCopyUV',
+    'MUV_OT_CopyPasteUV_SelSeqPasteUV',
+    'MUV_MT_CopyPasteUV_SelSeqPasteUV',
 ]
 
 
-def is_valid_context(context):
-    obj = context.object
-
-    # only edit mode is allowed to execute
-    if obj is None:
-        return False
-    if obj.type != 'MESH':
-        return False
-    if context.object.mode != 'EDIT':
-        return False
-
-    # only 'VIEW_3D' space is allowed to execute
-    for space in context.area.spaces:
-        if space.type == 'VIEW_3D':
-            break
-    else:
-        return False
-
-    return True
-
-
-def get_copy_uv_layers(ops_obj, bm):
-    uv_layers = []
-    if ops_obj.uv_map == "__default":
-        if not bm.loops.layers.uv:
-            ops_obj.report(
-                {'WARNING'}, "Object must have more than one UV map")
-            return None
-        uv_layers.append(bm.loops.layers.uv.verify())
-        ops_obj.report({'INFO'}, "Copy UV coordinate")
-    elif ops_obj.uv_map == "__all":
-        for uv in bm.loops.layers.uv.keys():
-            uv_layers.append(bm.loops.layers.uv[uv])
-        ops_obj.report({'INFO'}, "Copy UV coordinate (UV map: ALL)")
-    else:
-        uv_layers.append(bm.loops.layers.uv[ops_obj.uv_map])
-        ops_obj.report(
-            {'INFO'}, "Copy UV coordinate (UV map:{})".format(ops_obj.uv_map))
-
-    return uv_layers
-
-
-def get_paste_uv_layers(ops_obj, obj, bm, src_info):
-    uv_layers = []
-    if ops_obj.uv_map == "__default":
-        if not bm.loops.layers.uv:
-            ops_obj.report(
-                {'WARNING'}, "Object must have more than one UV map")
-            return None
-        uv_layers.append(bm.loops.layers.uv.verify())
-        ops_obj.report({'INFO'}, "Paste UV coordinate")
-    elif ops_obj.uv_map == "__new":
-        new_uv_map = common.create_new_uv_map(obj)
-        if not new_uv_map:
-            ops_obj.report({'WARNING'},
-                           "Reached to the maximum number of UV map")
-            return None
-        uv_layers.append(bm.loops.layers.uv[new_uv_map.name])
-        ops_obj.report(
-            {'INFO'}, "Paste UV coordinate (UV map:{})".format(new_uv_map))
-    elif ops_obj.uv_map == "__all":
-        for src_layer in src_info.keys():
-            if src_layer not in bm.loops.layers.uv.keys():
-                new_uv_map = common.create_new_uv_map(obj, src_layer)
-                if not new_uv_map:
-                    ops_obj.report({'WARNING'},
-                                   "Reached to the maximum number of UV map")
-                    return None
-            uv_layers.append(bm.loops.layers.uv[src_layer])
-        ops_obj.report({'INFO'}, "Paste UV coordinate (UV map: ALL)")
-    else:
-        uv_layers.append(bm.loops.layers.uv[ops_obj.uv_map])
-        ops_obj.report(
-            {'INFO'}, "Paste UV coordinate (UV map:{})".format(ops_obj.uv_map))
-
-    return uv_layers
-
-
-def paste_uv(ops_obj, bm, src_info, dest_info, uv_layers, strategy, flip,
-             rotate, copy_seams):
-    for slayer_name, dlayer in zip(src_info.keys(), uv_layers):
-        src_faces = src_info[slayer_name]
-        dest_faces = dest_info[dlayer.name]
-
-        for idx, dinfo in enumerate(dest_faces):
-            sinfo = None
-            if strategy == 'N_N':
-                sinfo = src_faces[idx]
-            elif strategy == 'N_M':
-                sinfo = src_faces[idx % len(src_faces)]
-
-            suv = sinfo["uvs"]
-            spuv = sinfo["pin_uvs"]
-            ss = sinfo["seams"]
-            if len(sinfo["uvs"]) != len(dinfo["uvs"]):
-                ops_obj.report({'WARNING'}, "Some faces are different size")
-                return -1
-
-            suvs_fr = [uv for uv in suv]
-            spuvs_fr = [pin_uv for pin_uv in spuv]
-            ss_fr = [s for s in ss]
-
-            # flip UVs
-            if flip is True:
-                suvs_fr.reverse()
-                spuvs_fr.reverse()
-                ss_fr.reverse()
-
-            # rotate UVs
-            for _ in range(rotate):
-                uv = suvs_fr.pop()
-                pin_uv = spuvs_fr.pop()
-                s = ss_fr.pop()
-                suvs_fr.insert(0, uv)
-                spuvs_fr.insert(0, pin_uv)
-                ss_fr.insert(0, s)
-
-            # paste UVs
-            for l, suv, spuv, ss in zip(bm.faces[idx].loops, suvs_fr,
-                                        spuvs_fr, ss_fr):
-                l[dlayer].uv = suv
-                l[dlayer].pin_uv = spuv
-                if copy_seams is True:
-                    l.edge.seam = ss
-
-    return 0
-
-
+@PropertyClassRegistry()
 class Properties:
+    idname = "copy_paste_uv"
+
     @classmethod
     def init_props(cls, scene):
         class Props():
@@ -225,7 +102,8 @@ class Properties:
         del scene.muv_copy_paste_uv_strategy
 
 
-class OpeartorCopyUV(bpy.types.Operator):
+@BlClassRegistry()
+class MUV_OT_CopyPasteUV_CopyUV(bpy.types.Operator):
     """
     Operation class: Copy UV coordinate
     """
@@ -235,14 +113,14 @@ class OpeartorCopyUV(bpy.types.Operator):
     bl_description = "Copy UV coordinate"
     bl_options = {'REGISTER', 'UNDO'}
 
-    uv_map = StringProperty(default="__default", options={'HIDDEN'})
+    uv_map: StringProperty(default="__default", options={'HIDDEN'})
 
     @classmethod
     def poll(cls, context):
         # we can not get area/space/region from console
         if common.is_console_mode():
             return True
-        return is_valid_context(context)
+        return impl.is_valid_context(context)
 
     def execute(self, context):
         props = context.scene.muv_props.copy_paste_uv
@@ -250,34 +128,24 @@ class OpeartorCopyUV(bpy.types.Operator):
         bm = common.create_bmesh(obj)
 
         # get UV layer
-        uv_layers = get_copy_uv_layers(self, bm)
+        uv_layers = impl.get_copy_uv_layers(self, bm, self.uv_map)
         if not uv_layers:
             return {'CANCELLED'}
 
         # get selected face
-        props.src_info = {}
-        for layer in uv_layers:
-            face_info = []
-            for face in bm.faces:
-                if face.select:
-                    info = {
-                        "uvs": [l[layer].uv.copy() for l in face.loops],
-                        "pin_uvs": [l[layer].pin_uv for l in face.loops],
-                        "seams": [l.edge.seam for l in face.loops],
-                    }
-                    face_info.append(info)
-            if not face_info:
-                self.report({'WARNING'}, "No faces are selected")
-                return {'CANCELLED'}
-            props.src_info[layer.name] = face_info
-
-        face_count = len([f for f in bm.faces if f.select])
+        src_info = impl.get_src_face_info(self, bm, uv_layers)
+        if src_info is None:
+            return {'CANCELLED'}
+        props.src_info = src_info
+
+        face_count = len(props.src_info[list(props.src_info.keys())[0]])
         self.report({'INFO'}, "{} face(s) are copied".format(face_count))
 
         return {'FINISHED'}
 
 
-class MenuCopyUV(bpy.types.Menu):
+@BlClassRegistry()
+class MUV_MT_CopyPasteUV_CopyUV(bpy.types.Menu):
     """
     Menu class: Copy UV coordinate
     """
@@ -288,7 +156,7 @@ class MenuCopyUV(bpy.types.Menu):
 
     @classmethod
     def poll(cls, context):
-        return is_valid_context(context)
+        return impl.is_valid_context(context)
 
     def draw(self, context):
         layout = self.layout
@@ -297,18 +165,21 @@ class MenuCopyUV(bpy.types.Menu):
         bm = common.create_bmesh(obj)
         uv_maps = bm.loops.layers.uv.keys()
 
-        ops = layout.operator(OpeartorCopyUV.bl_idname, text="[Default]")
+        ops = layout.operator(MUV_OT_CopyPasteUV_CopyUV.bl_idname,
+                              text="[Default]")
         ops.uv_map = "__default"
 
-        ops = layout.operator(OpeartorCopyUV.bl_idname, text="[All]")
+        ops = layout.operator(MUV_OT_CopyPasteUV_CopyUV.bl_idname,
+                              text="[All]")
         ops.uv_map = "__all"
 
         for m in uv_maps:
-            ops = layout.operator(OpeartorCopyUV.bl_idname, text=m)
+            ops = layout.operator(MUV_OT_CopyPasteUV_CopyUV.bl_idname, text=m)
             ops.uv_map = m
 
 
-class OperatorPasteUV(bpy.types.Operator):
+@BlClassRegistry()
+class MUV_OT_CopyPasteUV_PasteUV(bpy.types.Operator):
     """
     Operation class: Paste UV coordinate
     """
@@ -318,8 +189,8 @@ class OperatorPasteUV(bpy.types.Operator):
     bl_description = "Paste UV coordinate"
     bl_options = {'REGISTER', 'UNDO'}
 
-    uv_map = StringProperty(default="__default", options={'HIDDEN'})
-    strategy = EnumProperty(
+    uv_map: StringProperty(default="__default", options={'HIDDEN'})
+    strategy: EnumProperty(
         name="Strategy",
         description="Paste Strategy",
         items=[
@@ -328,18 +199,18 @@ class OperatorPasteUV(bpy.types.Operator):
         ],
         default="N_M"
     )
-    flip_copied_uv = BoolProperty(
+    flip_copied_uv: BoolProperty(
         name="Flip Copied UV",
         description="Flip Copied UV...",
         default=False
     )
-    rotate_copied_uv = IntProperty(
+    rotate_copied_uv: IntProperty(
         default=0,
         name="Rotate Copied UV",
         min=0,
         max=30
     )
-    copy_seams = BoolProperty(
+    copy_seams: BoolProperty(
         name="Seams",
         description="Copy Seams",
         default=True
@@ -354,7 +225,7 @@ class OperatorPasteUV(bpy.types.Operator):
         props = sc.muv_props.copy_paste_uv
         if not props.src_info:
             return False
-        return is_valid_context(context)
+        return impl.is_valid_context(context)
 
     def execute(self, context):
         props = context.scene.muv_props.copy_paste_uv
@@ -365,53 +236,34 @@ class OperatorPasteUV(bpy.types.Operator):
         bm = common.create_bmesh(obj)
 
         # get UV layer
-        uv_layers = get_paste_uv_layers(self, obj, bm, props.src_info)
+        uv_layers = impl.get_paste_uv_layers(self, obj, bm, props.src_info,
+                                             self.uv_map)
         if not uv_layers:
             return {'CANCELLED'}
 
         # get selected face
-        dest_face_count = 0
-        dest_info = {}
-        for layer in uv_layers:
-            face_info = []
-            for face in bm.faces:
-                if face.select:
-                    info = {
-                        "uvs": [l[layer].uv.copy() for l in face.loops],
-                    }
-                    face_info.append(info)
-            if not face_info:
-                self.report({'WARNING'}, "No faces are selected")
-                return {'CANCELLED'}
-            key = list(props.src_info.keys())[0]
-            src_face_count = len(props.src_info[key])
-            dest_face_count = len(face_info)
-            if self.strategy == 'N_N' and src_face_count != dest_face_count:
-                self.report(
-                    {'WARNING'},
-                    "Number of selected faces is different from copied" +
-                    "(src:{}, dest:{})"
-                    .format(src_face_count, dest_face_count))
-                return {'CANCELLED'}
-            dest_info[layer.name] = face_info
+        dest_info = impl.get_dest_face_info(self, bm, uv_layers,
+                                            props.src_info, self.strategy)
+        if dest_info is None:
+            return {'CANCELLED'}
 
         # paste
-        ret = paste_uv(self, bm, props.src_info, dest_info, uv_layers,
-                       self.strategy, self.flip_copied_uv,
-                       self.rotate_copied_uv, self.copy_seams)
+        ret = impl.paste_uv(self, bm, props.src_info, dest_info, uv_layers,
+                            self.strategy, self.flip_copied_uv,
+                            self.rotate_copied_uv, self.copy_seams)
         if ret:
             return {'CANCELLED'}
 
-        self.report({'INFO'}, "{} face(s) are pasted".format(dest_face_count))
+        face_count = len(props.src_info[list(dest_info.keys())[0]])
+        self.report({'INFO'}, "{} face(s) are pasted".format(face_count))
 
         bmesh.update_edit_mesh(obj.data)
-        if self.copy_seams is True:
-            obj.data.show_edge_seams = True
 
         return {'FINISHED'}
 
 
-class MenuPasteUV(bpy.types.Menu):
+@BlClassRegistry()
+class MUV_MT_CopyPasteUV_PasteUV(bpy.types.Menu):
     """
     Menu class: Paste UV coordinate
     """
@@ -426,7 +278,7 @@ class MenuPasteUV(bpy.types.Menu):
         props = sc.muv_props.copy_paste_uv
         if not props.src_info:
             return False
-        return is_valid_context(context)
+        return impl.is_valid_context(context)
 
     def draw(self, context):
         sc = context.scene
@@ -436,29 +288,33 @@ class MenuPasteUV(bpy.types.Menu):
         bm = common.create_bmesh(obj)
         uv_maps = bm.loops.layers.uv.keys()
 
-        ops = layout.operator(OperatorPasteUV.bl_idname, text="[Default]")
+        ops = layout.operator(MUV_OT_CopyPasteUV_PasteUV.bl_idname,
+                              text="[Default]")
         ops.uv_map = "__default"
         ops.copy_seams = sc.muv_copy_paste_uv_copy_seams
         ops.strategy = sc.muv_copy_paste_uv_strategy
 
-        ops = layout.operator(OperatorPasteUV.bl_idname, text="[New]")
+        ops = layout.operator(MUV_OT_CopyPasteUV_PasteUV.bl_idname,
+                              text="[New]")
         ops.uv_map = "__new"
         ops.copy_seams = sc.muv_copy_paste_uv_copy_seams
         ops.strategy = sc.muv_copy_paste_uv_strategy
 
-        ops = layout.operator(OperatorPasteUV.bl_idname, text="[All]")
+        ops = layout.operator(MUV_OT_CopyPasteUV_PasteUV.bl_idname,
+                              text="[All]")
         ops.uv_map = "__all"
         ops.copy_seams = sc.muv_copy_paste_uv_copy_seams
         ops.strategy = sc.muv_copy_paste_uv_strategy
 
         for m in uv_maps:
-            ops = layout.operator(OperatorPasteUV.bl_idname, text=m)
+            ops = layout.operator(MUV_OT_CopyPasteUV_PasteUV.bl_idname, text=m)
             ops.uv_map = m
             ops.copy_seams = sc.muv_copy_paste_uv_copy_seams
             ops.strategy = sc.muv_copy_paste_uv_strategy
 
 
-class OperatorSelSeqCopyUV(bpy.types.Operator):
+@BlClassRegistry()
+class MUV_OT_CopyPasteUV_SelSeqCopyUV(bpy.types.Operator):
     """
     Operation class: Copy UV coordinate by selection sequence
     """
@@ -468,14 +324,14 @@ class OperatorSelSeqCopyUV(bpy.types.Operator):
     bl_description = "Copy UV data by selection sequence"
     bl_options = {'REGISTER', 'UNDO'}
 
-    uv_map = StringProperty(default="__default", options={'HIDDEN'})
+    uv_map: StringProperty(default="__default", options={'HIDDEN'})
 
     @classmethod
     def poll(cls, context):
         # we can not get area/space/region from console
         if common.is_console_mode():
             return True
-        return is_valid_context(context)
+        return impl.is_valid_context(context)
 
     def execute(self, context):
         props = context.scene.muv_props.copy_paste_uv_selseq
@@ -483,34 +339,24 @@ class OperatorSelSeqCopyUV(bpy.types.Operator):
         bm = common.create_bmesh(obj)
 
         # get UV layer
-        uv_layers = get_copy_uv_layers(self, bm)
+        uv_layers = impl.get_copy_uv_layers(self, bm, self.uv_map)
         if not uv_layers:
             return {'CANCELLED'}
 
         # get selected face
-        props.src_info = {}
-        for layer in uv_layers:
-            face_info = []
-            for hist in bm.select_history:
-                if isinstance(hist, bmesh.types.BMFace) and hist.select:
-                    info = {
-                        "uvs": [l[layer].uv.copy() for l in hist.loops],
-                        "pin_uvs": [l[layer].pin_uv for l in hist.loops],
-                        "seams": [l.edge.seam for l in hist.loops],
-                    }
-                    face_info.append(info)
-            if not face_info:
-                self.report({'WARNING'}, "No faces are selected")
-                return {'CANCELLED'}
-            props.src_info[layer.name] = face_info
-
-        face_count = len([f for f in bm.faces if f.select])
+        src_info = impl.get_select_history_src_face_info(self, bm, uv_layers)
+        if src_info is None:
+            return {'CANCELLED'}
+        props.src_info = src_info
+
+        face_count = len(props.src_info[list(props.src_info.keys())[0]])
         self.report({'INFO'}, "{} face(s) are selected".format(face_count))
 
         return {'FINISHED'}
 
 
-class MenuSelSeqCopyUV(bpy.types.Menu):
+@BlClassRegistry()
+class MUV_MT_CopyPasteUV_SelSeqCopyUV(bpy.types.Menu):
     """
     Menu class: Copy UV coordinate by selection sequence
     """
@@ -521,7 +367,7 @@ class MenuSelSeqCopyUV(bpy.types.Menu):
 
     @classmethod
     def poll(cls, context):
-        return is_valid_context(context)
+        return impl.is_valid_context(context)
 
     def draw(self, context):
         layout = self.layout
@@ -529,18 +375,22 @@ class MenuSelSeqCopyUV(bpy.types.Menu):
         bm = common.create_bmesh(obj)
         uv_maps = bm.loops.layers.uv.keys()
 
-        ops = layout.operator(OperatorSelSeqCopyUV.bl_idname, text="[Default]")
+        ops = layout.operator(MUV_OT_CopyPasteUV_SelSeqCopyUV.bl_idname,
+                              text="[Default]")
         ops.uv_map = "__default"
 
-        ops = layout.operator(OperatorSelSeqCopyUV.bl_idname, text="[All]")
+        ops = layout.operator(MUV_OT_CopyPasteUV_SelSeqCopyUV.bl_idname,
+                              text="[All]")
         ops.uv_map = "__all"
 
         for m in uv_maps:
-            ops = layout.operator(OperatorSelSeqCopyUV.bl_idname, text=m)
+            ops = layout.operator(MUV_OT_CopyPasteUV_SelSeqCopyUV.bl_idname,
+                                  text=m)
             ops.uv_map = m
 
 
-class OperatorSelSeqPasteUV(bpy.types.Operator):
+@BlClassRegistry()
+class MUV_OT_CopyPasteUV_SelSeqPasteUV(bpy.types.Operator):
     """
     Operation class: Paste UV coordinate by selection sequence
     """
@@ -550,8 +400,8 @@ class OperatorSelSeqPasteUV(bpy.types.Operator):
     bl_description = "Paste UV coordinate by selection sequence"
     bl_options = {'REGISTER', 'UNDO'}
 
-    uv_map = StringProperty(default="__default", options={'HIDDEN'})
-    strategy = EnumProperty(
+    uv_map: StringProperty(default="__default", options={'HIDDEN'})
+    strategy: EnumProperty(
         name="Strategy",
         description="Paste Strategy",
         items=[
@@ -560,18 +410,18 @@ class OperatorSelSeqPasteUV(bpy.types.Operator):
         ],
         default="N_M"
     )
-    flip_copied_uv = BoolProperty(
+    flip_copied_uv: BoolProperty(
         name="Flip Copied UV",
         description="Flip Copied UV...",
         default=False
     )
-    rotate_copied_uv = IntProperty(
+    rotate_copied_uv: IntProperty(
         default=0,
         name="Rotate Copied UV",
         min=0,
         max=30
     )
-    copy_seams = BoolProperty(
+    copy_seams: BoolProperty(
         name="Seams",
         description="Copy Seams",
         default=True
@@ -586,7 +436,7 @@ class OperatorSelSeqPasteUV(bpy.types.Operator):
         props = sc.muv_props.copy_paste_uv_selseq
         if not props.src_info:
             return False
-        return is_valid_context(context)
+        return impl.is_valid_context(context)
 
     def execute(self, context):
         props = context.scene.muv_props.copy_paste_uv_selseq
@@ -597,53 +447,35 @@ class OperatorSelSeqPasteUV(bpy.types.Operator):
         bm = common.create_bmesh(obj)
 
         # get UV layer
-        uv_layers = get_paste_uv_layers(self, obj, bm, props.src_info)
+        uv_layers = impl.get_paste_uv_layers(self, obj, bm, props.src_info,
+                                             self.uv_map)
         if not uv_layers:
             return {'CANCELLED'}
 
         # get selected face
-        dest_face_count = 0
-        dest_info = {}
-        for layer in uv_layers:
-            face_info = []
-            for hist in bm.select_history:
-                if isinstance(hist, bmesh.types.BMFace) and hist.select:
-                    info = {
-                        "uvs": [l[layer].uv.copy() for l in hist.loops],
-                    }
-                    face_info.append(info)
-            if not face_info:
-                self.report({'WARNING'}, "No faces are selected")
-                return {'CANCELLED'}
-            key = list(props.src_info.keys())[0]
-            src_face_count = len(props.src_info[key])
-            dest_face_count = len(face_info)
-            if self.strategy == 'N_N' and src_face_count != dest_face_count:
-                self.report(
-                    {'WARNING'},
-                    "Number of selected faces is different from copied" +
-                    "(src:{}, dest:{})"
-                    .format(src_face_count, dest_face_count))
-                return {'CANCELLED'}
-            dest_info[layer.name] = face_info
+        dest_info = impl.get_select_history_dest_face_info(self, bm, uv_layers,
+                                                           props.src_info,
+                                                           self.strategy)
+        if dest_info is None:
+            return {'CANCELLED'}
 
         # paste
-        ret = paste_uv(self, bm, props.src_info, dest_info, uv_layers,
-                       self.strategy, self.flip_copied_uv,
-                       self.rotate_copied_uv, self.copy_seams)
+        ret = impl.paste_uv(self, bm, props.src_info, dest_info, uv_layers,
+                            self.strategy, self.flip_copied_uv,
+                            self.rotate_copied_uv, self.copy_seams)
         if ret:
             return {'CANCELLED'}
 
-        self.report({'INFO'}, "{} face(s) are pasted".format(dest_face_count))
+        face_count = len(props.src_info[list(dest_info.keys())[0]])
+        self.report({'INFO'}, "{} face(s) are pasted".format(face_count))
 
         bmesh.update_edit_mesh(obj.data)
-        if self.copy_seams is True:
-            obj.data.show_edge_seams = True
 
         return {'FINISHED'}
 
 
-class MenuSelSeqPasteUV(bpy.types.Menu):
+@BlClassRegistry()
+class MUV_MT_CopyPasteUV_SelSeqPasteUV(bpy.types.Menu):
     """
     Menu class: Paste UV coordinate by selection sequence
     """
@@ -658,7 +490,7 @@ class MenuSelSeqPasteUV(bpy.types.Menu):
         props = sc.muv_props.copy_paste_uv_selseq
         if not props.src_uvs or not props.src_pin_uvs:
             return False
-        return is_valid_context(context)
+        return impl.is_valid_context(context)
 
     def draw(self, context):
         sc = context.scene
@@ -668,24 +500,27 @@ class MenuSelSeqPasteUV(bpy.types.Menu):
         bm = common.create_bmesh(obj)
         uv_maps = bm.loops.layers.uv.keys()
 
-        ops = layout.operator(OperatorSelSeqPasteUV.bl_idname,
+        ops = layout.operator(MUV_OT_CopyPasteUV_SelSeqPasteUV.bl_idname,
                               text="[Default]")
         ops.uv_map = "__default"
         ops.copy_seams = sc.muv_copy_paste_uv_copy_seams
         ops.strategy = sc.muv_copy_paste_uv_strategy
 
-        ops = layout.operator(OperatorSelSeqPasteUV.bl_idname, text="[New]")
+        ops = layout.operator(MUV_OT_CopyPasteUV_SelSeqPasteUV.bl_idname,
+                              text="[New]")
         ops.uv_map = "__new"
         ops.copy_seams = sc.muv_copy_paste_uv_copy_seams
         ops.strategy = sc.muv_copy_paste_uv_strategy
 
-        ops = layout.operator(OperatorSelSeqPasteUV.bl_idname, text="[All]")
+        ops = layout.operator(MUV_OT_CopyPasteUV_SelSeqPasteUV.bl_idname,
+                              text="[All]")
         ops.uv_map = "__all"
         ops.copy_seams = sc.muv_copy_paste_uv_copy_seams
         ops.strategy = sc.muv_copy_paste_uv_strategy
 
         for m in uv_maps:
-            ops = layout.operator(OperatorSelSeqPasteUV.bl_idname, text=m)
+            ops = layout.operator(MUV_OT_CopyPasteUV_SelSeqPasteUV.bl_idname,
+                                  text=m)
             ops.uv_map = m
             ops.copy_seams = sc.muv_copy_paste_uv_copy_seams
             ops.strategy = sc.muv_copy_paste_uv_strategy
diff --git a/uv_magic_uv/op/copy_paste_uv_object.py b/uv_magic_uv/op/copy_paste_uv_object.py
index eb6d87c91b9adfb1719f578c4a65dd080be6c4e8..d9f42447a538735b5d8c0515cb729e8f249ea14a 100644
--- a/uv_magic_uv/op/copy_paste_uv_object.py
+++ b/uv_magic_uv/op/copy_paste_uv_object.py
@@ -23,27 +23,24 @@ __status__ = "production"
 __version__ = "5.2"
 __date__ = "17 Nov 2018"
 
-import bpy
 import bmesh
+import bpy
 from bpy.props import (
     StringProperty,
     BoolProperty,
 )
 
+from ..impl import copy_paste_uv_impl as impl
 from .. import common
-from .copy_paste_uv import (
-    get_copy_uv_layers,
-    get_paste_uv_layers,
-    paste_uv
-)
-
+from ..utils.bl_class_registry import BlClassRegistry
+from ..utils.property_class_registry import PropertyClassRegistry
 
 __all__ = [
     'Properties',
-    'OperatorCopyUV',
-    'MenuCopyUV',
-    'OperatorPasteUV',
-    'MenuPasteUV',
+    'MUV_OT_CopyPasteUVObject_CopyUV',
+    'MUV_MT_CopyPasteUVObject_CopyUV',
+    'MUV_OT_CopyPasteUVObject_PasteUV',
+    'MUV_MT_CopyPasteUVObject_PasteUV',
 ]
 
 
@@ -68,16 +65,10 @@ def is_valid_context(context):
     return True
 
 
-def memorize_view_3d_mode(fn):
-    def __memorize_view_3d_mode(self, context):
-        mode_orig = bpy.context.object.mode
-        result = fn(self, context)
-        bpy.ops.object.mode_set(mode=mode_orig)
-        return result
-    return __memorize_view_3d_mode
-
-
+@PropertyClassRegistry()
 class Properties:
+    idname = "copy_paste_uv_object"
+
     @classmethod
     def init_props(cls, scene):
         class Props():
@@ -97,7 +88,17 @@ class Properties:
         del scene.muv_copy_paste_uv_object_copy_seams
 
 
-class OperatorCopyUV(bpy.types.Operator):
+def memorize_view_3d_mode(fn):
+    def __memorize_view_3d_mode(self, context):
+        mode_orig = bpy.context.object.mode
+        result = fn(self, context)
+        bpy.ops.object.mode_set(mode=mode_orig)
+        return result
+    return __memorize_view_3d_mode
+
+
+@BlClassRegistry()
+class MUV_OT_CopyPasteUVObject_CopyUV(bpy.types.Operator):
     """
     Operation class: Copy UV coordinate among objects
     """
@@ -107,7 +108,7 @@ class OperatorCopyUV(bpy.types.Operator):
     bl_description = "Copy UV coordinate (Among Objects)"
     bl_options = {'REGISTER', 'UNDO'}
 
-    uv_map = StringProperty(default="__default", options={'HIDDEN'})
+    uv_map: StringProperty(default="__default", options={'HIDDEN'})
 
     @classmethod
     def poll(cls, context):
@@ -124,23 +125,15 @@ class OperatorCopyUV(bpy.types.Operator):
         bm = common.create_bmesh(obj)
 
         # get UV layer
-        uv_layers = get_copy_uv_layers(self, bm)
+        uv_layers = impl.get_copy_uv_layers(self, bm, self.uv_map)
         if not uv_layers:
             return {'CANCELLED'}
 
         # get selected face
-        props.src_info = {}
-        for layer in uv_layers:
-            face_info = []
-            for face in bm.faces:
-                if face.select:
-                    info = {
-                        "uvs": [l[layer].uv.copy() for l in face.loops],
-                        "pin_uvs": [l[layer].pin_uv for l in face.loops],
-                        "seams": [l.edge.seam for l in face.loops],
-                    }
-                    face_info.append(info)
-            props.src_info[layer.name] = face_info
+        src_info = impl.get_src_face_info(self, bm, uv_layers)
+        if src_info is None:
+            return {'CANCELLED'}
+        props.src_info = src_info
 
         self.report({'INFO'},
                     "{}'s UV coordinates are copied".format(obj.name))
@@ -148,7 +141,8 @@ class OperatorCopyUV(bpy.types.Operator):
         return {'FINISHED'}
 
 
-class MenuCopyUV(bpy.types.Menu):
+@BlClassRegistry()
+class MUV_MT_CopyPasteUVObject_CopyUV(bpy.types.Menu):
     """
     Menu class: Copy UV coordinate among objects
     """
@@ -164,20 +158,24 @@ class MenuCopyUV(bpy.types.Menu):
     def draw(self, _):
         layout = self.layout
         # create sub menu
-        uv_maps = bpy.context.active_object.data.uv_textures.keys()
+        uv_maps = bpy.context.active_object.data.uv_layers.keys()
 
-        ops = layout.operator(OperatorCopyUV.bl_idname, text="[Default]")
+        ops = layout.operator(MUV_OT_CopyPasteUVObject_CopyUV.bl_idname,
+                              text="[Default]")
         ops.uv_map = "__default"
 
-        ops = layout.operator(OperatorCopyUV.bl_idname, text="[All]")
+        ops = layout.operator(MUV_OT_CopyPasteUVObject_CopyUV.bl_idname,
+                              text="[All]")
         ops.uv_map = "__all"
 
         for m in uv_maps:
-            ops = layout.operator(OperatorCopyUV.bl_idname, text=m)
+            ops = layout.operator(MUV_OT_CopyPasteUVObject_CopyUV.bl_idname,
+                                  text=m)
             ops.uv_map = m
 
 
-class OperatorPasteUV(bpy.types.Operator):
+@BlClassRegistry()
+class MUV_OT_CopyPasteUVObject_PasteUV(bpy.types.Operator):
     """
     Operation class: Paste UV coordinate among objects
     """
@@ -187,8 +185,8 @@ class OperatorPasteUV(bpy.types.Operator):
     bl_description = "Paste UV coordinate (Among Objects)"
     bl_options = {'REGISTER', 'UNDO'}
 
-    uv_map = StringProperty(default="__default", options={'HIDDEN'})
-    copy_seams = BoolProperty(
+    uv_map: StringProperty(default="__default", options={'HIDDEN'})
+    copy_seams: BoolProperty(
         name="Seams",
         description="Copy Seams",
         default=True
@@ -213,52 +211,35 @@ class OperatorPasteUV(bpy.types.Operator):
             return {'CANCELLED'}
 
         for o in bpy.data.objects:
-            if not hasattr(o.data, "uv_textures") or not o.select:
+            if not hasattr(o.data, "uv_layers") or not o.select_get():
                 continue
 
             bpy.ops.object.mode_set(mode='OBJECT')
-            bpy.context.scene.objects.active = o
+            bpy.context.view_layer.objects.active = o
             bpy.ops.object.mode_set(mode='EDIT')
 
             obj = context.active_object
             bm = common.create_bmesh(obj)
 
             # get UV layer
-            uv_layers = get_paste_uv_layers(self, obj, bm, props.src_info)
+            uv_layers = impl.get_paste_uv_layers(self, obj, bm, props.src_info,
+                                                 self.uv_map)
             if not uv_layers:
                 return {'CANCELLED'}
 
             # get selected face
-            dest_info = {}
-            for layer in uv_layers:
-                face_info = []
-                for face in bm.faces:
-                    if face.select:
-                        info = {
-                            "uvs": [l[layer].uv.copy() for l in face.loops],
-                        }
-                        face_info.append(info)
-                key = list(props.src_info.keys())[0]
-                src_face_count = len(props.src_info[key])
-                dest_face_count = len(face_info)
-                if src_face_count != dest_face_count:
-                    self.report(
-                        {'WARNING'},
-                        "Number of selected faces is different from copied" +
-                        "(src:{}, dest:{})"
-                        .format(src_face_count, dest_face_count))
-                    return {'CANCELLED'}
-                dest_info[layer.name] = face_info
+            dest_info = impl.get_dest_face_info(self, bm, uv_layers,
+                                                props.src_info, 'N_N')
+            if dest_info is None:
+                return {'CANCELLED'}
 
             # paste
-            ret = paste_uv(self, bm, props.src_info, dest_info, uv_layers,
-                           'N_N', 0, 0, self.copy_seams)
+            ret = impl.paste_uv(self, bm, props.src_info, dest_info, uv_layers,
+                                'N_N', 0, 0, self.copy_seams)
             if ret:
                 return {'CANCELLED'}
 
             bmesh.update_edit_mesh(obj.data)
-            if self.copy_seams is True:
-                obj.data.show_edge_seams = True
 
             self.report(
                 {'INFO'}, "{}'s UV coordinates are pasted".format(obj.name))
@@ -266,7 +247,8 @@ class OperatorPasteUV(bpy.types.Operator):
         return {'FINISHED'}
 
 
-class MenuPasteUV(bpy.types.Menu):
+@BlClassRegistry()
+class MUV_MT_CopyPasteUVObject_PasteUV(bpy.types.Menu):
     """
     Menu class: Paste UV coordinate among objects
     """
@@ -289,22 +271,26 @@ class MenuPasteUV(bpy.types.Menu):
         # create sub menu
         uv_maps = []
         for obj in bpy.data.objects:
-            if hasattr(obj.data, "uv_textures") and obj.select:
-                uv_maps.extend(obj.data.uv_textures.keys())
+            if hasattr(obj.data, "uv_layers") and obj.select_get():
+                uv_maps.extend(obj.data.uv_layers.keys())
 
-        ops = layout.operator(OperatorPasteUV.bl_idname, text="[Default]")
+        ops = layout.operator(MUV_OT_CopyPasteUVObject_PasteUV.bl_idname,
+                              text="[Default]")
         ops.uv_map = "__default"
         ops.copy_seams = sc.muv_copy_paste_uv_object_copy_seams
 
-        ops = layout.operator(OperatorPasteUV.bl_idname, text="[New]")
+        ops = layout.operator(MUV_OT_CopyPasteUVObject_PasteUV.bl_idname,
+                              text="[New]")
         ops.uv_map = "__new"
         ops.copy_seams = sc.muv_copy_paste_uv_object_copy_seams
 
-        ops = layout.operator(OperatorPasteUV.bl_idname, text="[All]")
+        ops = layout.operator(MUV_OT_CopyPasteUVObject_PasteUV.bl_idname,
+                              text="[All]")
         ops.uv_map = "__all"
         ops.copy_seams = sc.muv_copy_paste_uv_object_copy_seams
 
         for m in uv_maps:
-            ops = layout.operator(OperatorPasteUV.bl_idname, text=m)
+            ops = layout.operator(MUV_OT_CopyPasteUVObject_PasteUV.bl_idname,
+                                  text=m)
             ops.uv_map = m
             ops.copy_seams = sc.muv_copy_paste_uv_object_copy_seams
diff --git a/uv_magic_uv/op/copy_paste_uv_uvedit.py b/uv_magic_uv/op/copy_paste_uv_uvedit.py
index e591b5f1ee579d1bc66ebdebca66228a26624e98..719687a6016e510212c10ac05f2c9fa8994bda7d 100644
--- a/uv_magic_uv/op/copy_paste_uv_uvedit.py
+++ b/uv_magic_uv/op/copy_paste_uv_uvedit.py
@@ -18,52 +18,29 @@
 #
 # ##### END GPL LICENSE BLOCK #####
 
-__author__ = "Nutti <nutti.metro@gmail.com>, Jace Priester"
+__author__ = "imdjs, Nutti <nutti.metro@gmail.com>"
 __status__ = "production"
 __version__ = "5.2"
 __date__ = "17 Nov 2018"
 
-import math
-from math import atan2, sin, cos
-
 import bpy
-import bmesh
-from mathutils import Vector
 
-from .. import common
+from ..utils.bl_class_registry import BlClassRegistry
+from ..utils.property_class_registry import PropertyClassRegistry
+from ..impl import copy_paste_uv_uvedit_impl as impl
 
 
 __all__ = [
     'Properties',
-    'OperatorCopyUV',
-    'OperatorPasteUV',
+    'MUV_OT_CopyPasteUVUVEdit_CopyUV',
+    'MUV_OT_CopyPasteUVUVEdit_PasteUV',
 ]
 
 
-def is_valid_context(context):
-    obj = context.object
-
-    # only edit mode is allowed to execute
-    if obj is None:
-        return False
-    if obj.type != 'MESH':
-        return False
-    if context.object.mode != 'EDIT':
-        return False
-
-    # 'IMAGE_EDITOR' and 'VIEW_3D' space is allowed to execute.
-    # If 'View_3D' space is not allowed, you can't find option in Tool-Shelf
-    # after the execution
-    for space in context.area.spaces:
-        if (space.type == 'IMAGE_EDITOR') or (space.type == 'VIEW_3D'):
-            break
-    else:
-        return False
-
-    return True
-
-
+@PropertyClassRegistry()
 class Properties:
+    idname = "copy_paste_uv_uvedit"
+
     @classmethod
     def init_props(cls, scene):
         class Props():
@@ -76,7 +53,8 @@ class Properties:
         del scene.muv_props.copy_paste_uv_uvedit
 
 
-class OperatorCopyUV(bpy.types.Operator):
+@BlClassRegistry()
+class MUV_OT_CopyPasteUVUVEdit_CopyUV(bpy.types.Operator):
     """
     Operation class: Copy UV coordinate on UV/Image Editor
     """
@@ -86,38 +64,19 @@ class OperatorCopyUV(bpy.types.Operator):
     bl_description = "Copy UV coordinate (only selected in UV/Image Editor)"
     bl_options = {'REGISTER', 'UNDO'}
 
+    def __init__(self):
+        self.__impl = impl.CopyUVImpl()
+
     @classmethod
     def poll(cls, context):
-        # we can not get area/space/region from console
-        if common.is_console_mode():
-            return True
-        return is_valid_context(context)
+        return impl.CopyUVImpl.poll(context)
 
     def execute(self, context):
-        props = context.scene.muv_props.copy_paste_uv_uvedit
-        obj = context.active_object
-        bm = bmesh.from_edit_mesh(obj.data)
-        uv_layer = bm.loops.layers.uv.verify()
-        if common.check_version(2, 73, 0) >= 0:
-            bm.faces.ensure_lookup_table()
-
-        props.src_uvs = []
-        for face in bm.faces:
-            if not face.select:
-                continue
-            skip = False
-            for l in face.loops:
-                if not l[uv_layer].select:
-                    skip = True
-                    break
-            if skip:
-                continue
-            props.src_uvs.append([l[uv_layer].uv.copy() for l in face.loops])
-
-        return {'FINISHED'}
-
-
-class OperatorPasteUV(bpy.types.Operator):
+        return self.__impl.execute(self, context)
+
+
+@BlClassRegistry()
+class MUV_OT_CopyPasteUVUVEdit_PasteUV(bpy.types.Operator):
     """
     Operation class: Paste UV coordinate on UV/Image Editor
     """
@@ -127,72 +86,12 @@ class OperatorPasteUV(bpy.types.Operator):
     bl_description = "Paste UV coordinate (only selected in UV/Image Editor)"
     bl_options = {'REGISTER', 'UNDO'}
 
+    def __init__(self):
+        self.__impl = impl.PasteUVImpl()
+
     @classmethod
     def poll(cls, context):
-        # we can not get area/space/region from console
-        if common.is_console_mode():
-            return True
-        sc = context.scene
-        props = sc.muv_props.copy_paste_uv_uvedit
-        if not props.src_uvs:
-            return False
-        return is_valid_context(context)
+        return impl.PasteUVImpl.poll(context)
 
     def execute(self, context):
-        props = context.scene.muv_props.copy_paste_uv_uvedit
-        obj = context.active_object
-        bm = bmesh.from_edit_mesh(obj.data)
-        uv_layer = bm.loops.layers.uv.verify()
-        if common.check_version(2, 73, 0) >= 0:
-            bm.faces.ensure_lookup_table()
-
-        dest_uvs = []
-        dest_face_indices = []
-        for face in bm.faces:
-            if not face.select:
-                continue
-            skip = False
-            for l in face.loops:
-                if not l[uv_layer].select:
-                    skip = True
-                    break
-            if skip:
-                continue
-            dest_face_indices.append(face.index)
-            uvs = [l[uv_layer].uv.copy() for l in face.loops]
-            dest_uvs.append(uvs)
-
-        for suvs, duvs in zip(props.src_uvs, dest_uvs):
-            src_diff = suvs[1] - suvs[0]
-            dest_diff = duvs[1] - duvs[0]
-
-            src_base = suvs[0]
-            dest_base = duvs[0]
-
-            src_rad = atan2(src_diff.y, src_diff.x)
-            dest_rad = atan2(dest_diff.y, dest_diff.x)
-            if src_rad < dest_rad:
-                radian = dest_rad - src_rad
-            elif src_rad > dest_rad:
-                radian = math.pi * 2 - (src_rad - dest_rad)
-            else:       # src_rad == dest_rad
-                radian = 0.0
-
-            ratio = dest_diff.length / src_diff.length
-            break
-
-        for suvs, fidx in zip(props.src_uvs, dest_face_indices):
-            for l, suv in zip(bm.faces[fidx].loops, suvs):
-                base = suv - src_base
-                radian_ref = atan2(base.y, base.x)
-                radian_fin = (radian + radian_ref)
-                length = base.length
-                turn = Vector((length * cos(radian_fin),
-                               length * sin(radian_fin)))
-                target_uv = Vector((turn.x * ratio, turn.y * ratio)) + \
-                    dest_base
-                l[uv_layer].uv = target_uv
-
-        bmesh.update_edit_mesh(obj.data)
-
-        return {'FINISHED'}
+        return self.__impl.execute(self, context)
diff --git a/uv_magic_uv/op/flip_rotate_uv.py b/uv_magic_uv/op/flip_rotate_uv.py
index 751bb8fb7b378ef885ad4ef11caf2b70b66c47e7..d16370528b1927d9df1fadf5bd713253036dd1a4 100644
--- a/uv_magic_uv/op/flip_rotate_uv.py
+++ b/uv_magic_uv/op/flip_rotate_uv.py
@@ -31,36 +31,20 @@ from bpy.props import (
 )
 
 from .. import common
-
+from ..utils.bl_class_registry import BlClassRegistry
+from ..utils.property_class_registry import PropertyClassRegistry
+from ..impl import flip_rotate_impl as impl
 
 __all__ = [
     'Properties',
-    'Operator',
+    'MUV_OT_FlipRotate',
 ]
 
 
-def is_valid_context(context):
-    obj = context.object
-
-    # only edit mode is allowed to execute
-    if obj is None:
-        return False
-    if obj.type != 'MESH':
-        return False
-    if context.object.mode != 'EDIT':
-        return False
-
-    # only 'VIEW_3D' space is allowed to execute
-    for space in context.area.spaces:
-        if space.type == 'VIEW_3D':
-            break
-    else:
-        return False
-
-    return True
-
-
+@PropertyClassRegistry()
 class Properties:
+    idname = "flip_rotate_uv"
+
     @classmethod
     def init_props(cls, scene):
         scene.muv_flip_rotate_uv_enabled = BoolProperty(
@@ -80,7 +64,8 @@ class Properties:
         del scene.muv_flip_rotate_uv_seams
 
 
-class Operator(bpy.types.Operator):
+@BlClassRegistry()
+class MUV_OT_FlipRotate(bpy.types.Operator):
     """
     Operation class: Flip and Rotate UV coordinate
     """
@@ -90,18 +75,18 @@ class Operator(bpy.types.Operator):
     bl_description = "Flip/Rotate UV coordinate"
     bl_options = {'REGISTER', 'UNDO'}
 
-    flip = BoolProperty(
+    flip: BoolProperty(
         name="Flip UV",
         description="Flip UV...",
         default=False
     )
-    rotate = IntProperty(
+    rotate: IntProperty(
         default=0,
         name="Rotate UV",
         min=0,
         max=30
     )
-    seams = BoolProperty(
+    seams: BoolProperty(
         name="Seams",
         description="Seams",
         default=True
@@ -112,7 +97,7 @@ class Operator(bpy.types.Operator):
         # we can not get area/space/region from console
         if common.is_console_mode():
             return True
-        return is_valid_context(context)
+        return impl.is_valid_context(context)
 
     def execute(self, context):
         self.report({'INFO'}, "Flip/Rotate UV")
@@ -122,61 +107,24 @@ class Operator(bpy.types.Operator):
             bm.faces.ensure_lookup_table()
 
         # get UV layer
-        if not bm.loops.layers.uv:
-            self.report({'WARNING'}, "Object must have more than one UV map")
+        uv_layer = impl.get_uv_layer(self, bm)
+        if not uv_layer:
             return {'CANCELLED'}
-        uv_layer = bm.loops.layers.uv.verify()
 
         # get selected face
-        dest_uvs = []
-        dest_pin_uvs = []
-        dest_seams = []
-        dest_face_indices = []
-        for face in bm.faces:
-            if face.select:
-                dest_face_indices.append(face.index)
-                uvs = [l[uv_layer].uv.copy() for l in face.loops]
-                pin_uvs = [l[uv_layer].pin_uv for l in face.loops]
-                seams = [l.edge.seam for l in face.loops]
-                dest_uvs.append(uvs)
-                dest_pin_uvs.append(pin_uvs)
-                dest_seams.append(seams)
-        if not dest_uvs or not dest_pin_uvs:
-            self.report({'WARNING'}, "No faces are selected")
+        src_info = impl.get_src_face_info(self, bm, [uv_layer], True)
+        if not src_info:
             return {'CANCELLED'}
-        self.report({'INFO'}, "%d face(s) are selected" % len(dest_uvs))
+
+        face_count = len(src_info[list(src_info.keys())[0]])
+        self.report({'INFO'}, "{} face(s) are selected".format(face_count))
 
         # paste
-        for idx, duvs, dpuvs, dss in zip(dest_face_indices, dest_uvs,
-                                         dest_pin_uvs, dest_seams):
-            duvs_fr = [uv for uv in duvs]
-            dpuvs_fr = [pin_uv for pin_uv in dpuvs]
-            dss_fr = [s for s in dss]
-            # flip UVs
-            if self.flip is True:
-                duvs_fr.reverse()
-                dpuvs_fr.reverse()
-                dss_fr.reverse()
-            # rotate UVs
-            for _ in range(self.rotate):
-                uv = duvs_fr.pop()
-                pin_uv = dpuvs_fr.pop()
-                s = dss_fr.pop()
-                duvs_fr.insert(0, uv)
-                dpuvs_fr.insert(0, pin_uv)
-                dss_fr.insert(0, s)
-            # paste UVs
-            for l, duv, dpuv, ds in zip(
-                    bm.faces[idx].loops, duvs_fr, dpuvs_fr, dss_fr):
-                l[uv_layer].uv = duv
-                l[uv_layer].pin_uv = dpuv
-                if self.seams is True:
-                    l.edge.seam = ds
-
-        self.report({'INFO'}, "%d face(s) are flipped/rotated" % len(dest_uvs))
+        ret = impl.paste_uv(self, bm, src_info, src_info, [uv_layer], 'N_N',
+                            self.flip, self.rotate, self.seams)
+        if ret:
+            return {'CANCELLED'}
 
         bmesh.update_edit_mesh(obj.data)
-        if self.seams is True:
-            obj.data.show_edge_seams = True
 
         return {'FINISHED'}
diff --git a/uv_magic_uv/op/mirror_uv.py b/uv_magic_uv/op/mirror_uv.py
index 11ad2bcaaf93a2667d17c8c952865debd3698f36..6793ca232e2bdcafbb4a95c02dbc1edc0cd3bbbb 100644
--- a/uv_magic_uv/op/mirror_uv.py
+++ b/uv_magic_uv/op/mirror_uv.py
@@ -29,40 +29,22 @@ from bpy.props import (
     FloatProperty,
     BoolProperty,
 )
-import bmesh
-from mathutils import Vector
 
-from .. import common
+from ..utils.bl_class_registry import BlClassRegistry
+from ..utils.property_class_registry import PropertyClassRegistry
+from ..impl import mirror_uv_impl as impl
 
 
 __all__ = [
     'Properties',
-    'Operator',
+    'MUV_OT_MirrorUV',
 ]
 
 
-def is_valid_context(context):
-    obj = context.object
-
-    # only edit mode is allowed to execute
-    if obj is None:
-        return False
-    if obj.type != 'MESH':
-        return False
-    if context.object.mode != 'EDIT':
-        return False
-
-    # only 'VIEW_3D' space is allowed to execute
-    for space in context.area.spaces:
-        if space.type == 'VIEW_3D':
-            break
-    else:
-        return False
-
-    return True
-
-
+@PropertyClassRegistry()
 class Properties:
+    idname = "mirror_uv"
+
     @classmethod
     def init_props(cls, scene):
         scene.muv_mirror_uv_enabled = BoolProperty(
@@ -87,7 +69,8 @@ class Properties:
         del scene.muv_mirror_uv_axis
 
 
-class Operator(bpy.types.Operator):
+@BlClassRegistry()
+class MUV_OT_MirrorUV(bpy.types.Operator):
     """
     Operation class: Mirror UV
     """
@@ -96,7 +79,7 @@ class Operator(bpy.types.Operator):
     bl_label = "Mirror UV"
     bl_options = {'REGISTER', 'UNDO'}
 
-    axis = EnumProperty(
+    axis: EnumProperty(
         items=(
             ('X', "X", "Mirror Along X axis"),
             ('Y', "Y", "Mirror Along Y axis"),
@@ -106,7 +89,7 @@ class Operator(bpy.types.Operator):
         description="Mirror Axis",
         default='X'
     )
-    error = FloatProperty(
+    error: FloatProperty(
         name="Error",
         description="Error threshold",
         default=0.001,
@@ -116,95 +99,12 @@ class Operator(bpy.types.Operator):
         soft_max=1.0
     )
 
-    def __is_vector_similar(self, v1, v2, error):
-        """
-        Check if two vectors are similar, within an error threshold
-        """
-        within_err_x = abs(v2.x - v1.x) < error
-        within_err_y = abs(v2.y - v1.y) < error
-        within_err_z = abs(v2.z - v1.z) < error
-
-        return within_err_x and within_err_y and within_err_z
-
-    def __mirror_uvs(self, uv_layer, src, dst, axis, error):
-        """
-        Copy UV coordinates from one UV face to another
-        """
-        for sl in src.loops:
-            suv = sl[uv_layer].uv.copy()
-            svco = sl.vert.co.copy()
-            for dl in dst.loops:
-                dvco = dl.vert.co.copy()
-                if axis == 'X':
-                    dvco.x = -dvco.x
-                elif axis == 'Y':
-                    dvco.y = -dvco.y
-                elif axis == 'Z':
-                    dvco.z = -dvco.z
-
-                if self.__is_vector_similar(svco, dvco, error):
-                    dl[uv_layer].uv = suv.copy()
-
-    def __get_face_center(self, face):
-        """
-        Get center coordinate of the face
-        """
-        center = Vector((0.0, 0.0, 0.0))
-        for v in face.verts:
-            center = center + v.co
-
-        return center / len(face.verts)
+    def __init__(self):
+        self.__impl = impl.MirrorUVImpl()
 
     @classmethod
     def poll(cls, context):
-        # we can not get area/space/region from console
-        if common.is_console_mode():
-            return True
-        return is_valid_context(context)
+        return impl.MirrorUVImpl.poll(context)
 
     def execute(self, context):
-        obj = context.active_object
-        bm = bmesh.from_edit_mesh(obj.data)
-
-        error = self.error
-        axis = self.axis
-
-        if common.check_version(2, 73, 0) >= 0:
-            bm.faces.ensure_lookup_table()
-        if not bm.loops.layers.uv:
-            self.report({'WARNING'}, "Object must have more than one UV map")
-            return {'CANCELLED'}
-        uv_layer = bm.loops.layers.uv.verify()
-
-        faces = [f for f in bm.faces if f.select]
-        for f_dst in faces:
-            count = len(f_dst.verts)
-            for f_src in bm.faces:
-                # check if this is a candidate to do mirror UV
-                if f_src.index == f_dst.index:
-                    continue
-                if count != len(f_src.verts):
-                    continue
-
-                # test if the vertices x values are the same sign
-                dst = self.__get_face_center(f_dst)
-                src = self.__get_face_center(f_src)
-                if (dst.x > 0 and src.x > 0) or (dst.x < 0 and src.x < 0):
-                    continue
-
-                # invert source axis
-                if axis == 'X':
-                    src.x = -src.x
-                elif axis == 'Y':
-                    src.y = -src.z
-                elif axis == 'Z':
-                    src.z = -src.z
-
-                # do mirror UV
-                if self.__is_vector_similar(dst, src, error):
-                    self.__mirror_uvs(
-                        uv_layer, f_src, f_dst, self.axis, self.error)
-
-        bmesh.update_edit_mesh(obj.data)
-
-        return {'FINISHED'}
+        return self.__impl.execute(self, context)
diff --git a/uv_magic_uv/op/move_uv.py b/uv_magic_uv/op/move_uv.py
index a229ae34ddc2bb8727a07631c9eb1aed2b31390d..653918d350e20c1f82e3f7b110efe73e7900dd10 100644
--- a/uv_magic_uv/op/move_uv.py
+++ b/uv_magic_uv/op/move_uv.py
@@ -24,40 +24,23 @@ __version__ = "5.2"
 __date__ = "17 Nov 2018"
 
 import bpy
-import bmesh
-from mathutils import Vector
 from bpy.props import BoolProperty
 
-from .. import common
+from ..impl import move_uv_impl as impl
+from ..utils.bl_class_registry import BlClassRegistry
+from ..utils.property_class_registry import PropertyClassRegistry
+
 
 __all__ = [
     'Properties',
-    'Operator',
+    'MUV_OT_MoveUV',
 ]
 
 
-def is_valid_context(context):
-    obj = context.object
-
-    # only edit mode is allowed to execute
-    if obj is None:
-        return False
-    if obj.type != 'MESH':
-        return False
-    if context.object.mode != 'EDIT':
-        return False
-
-    # only 'VIEW_3D' space is allowed to execute
-    for space in context.area.spaces:
-        if space.type == 'VIEW_3D':
-            break
-    else:
-        return False
-
-    return True
-
-
+@PropertyClassRegistry()
 class Properties:
+    idname = "move_uv"
+
     @classmethod
     def init_props(cls, scene):
         scene.muv_move_uv_enabled = BoolProperty(
@@ -71,7 +54,8 @@ class Properties:
         del scene.muv_move_uv_enabled
 
 
-class Operator(bpy.types.Operator):
+@BlClassRegistry()
+class MUV_OT_MoveUV(bpy.types.Operator):
     """
     Operator class: Move UV
     """
@@ -80,108 +64,19 @@ class Operator(bpy.types.Operator):
     bl_label = "Move UV"
     bl_options = {'REGISTER', 'UNDO'}
 
-    __running = False
-
     def __init__(self):
-        self.__topology_dict = []
-        self.__prev_mouse = Vector((0.0, 0.0))
-        self.__offset_uv = Vector((0.0, 0.0))
-        self.__prev_offset_uv = Vector((0.0, 0.0))
-        self.__first_time = True
-        self.__ini_uvs = []
-        self.__operating = False
+        self.__impl = impl.MoveUVImpl()
 
     @classmethod
     def poll(cls, context):
-        # we can not get area/space/region from console
-        if common.is_console_mode():
-            return False
-        if cls.is_running(context):
-            return False
-        return is_valid_context(context)
+        return impl.MoveUVImpl.poll(context)
 
     @classmethod
     def is_running(cls, _):
-        return cls.__running
-
-    def __find_uv(self, context):
-        bm = bmesh.from_edit_mesh(context.object.data)
-        topology_dict = []
-        uvs = []
-        active_uv = bm.loops.layers.uv.active
-        for fidx, f in enumerate(bm.faces):
-            for vidx, v in enumerate(f.verts):
-                if v.select:
-                    uvs.append(f.loops[vidx][active_uv].uv.copy())
-                    topology_dict.append([fidx, vidx])
-
-        return topology_dict, uvs
+        return impl.MoveUVImpl.is_running(_)
 
     def modal(self, context, event):
-        if self.__first_time is True:
-            self.__prev_mouse = Vector((
-                event.mouse_region_x, event.mouse_region_y))
-            self.__first_time = False
-            return {'RUNNING_MODAL'}
-
-        # move UV
-        div = 10000
-        self.__offset_uv += Vector((
-            (event.mouse_region_x - self.__prev_mouse.x) / div,
-            (event.mouse_region_y - self.__prev_mouse.y) / div))
-        ouv = self.__offset_uv
-        pouv = self.__prev_offset_uv
-        vec = Vector((ouv.x - ouv.y, ouv.x + ouv.y))
-        dv = vec - pouv
-        self.__prev_offset_uv = vec
-        self.__prev_mouse = Vector((
-            event.mouse_region_x, event.mouse_region_y))
-
-        # check if operation is started
-        if not self.__operating:
-            if event.type == 'LEFTMOUSE' and event.value == 'RELEASE':
-                self.__operating = True
-            return {'RUNNING_MODAL'}
-
-        # update UV
-        obj = context.object
-        bm = bmesh.from_edit_mesh(obj.data)
-        active_uv = bm.loops.layers.uv.active
-        for fidx, vidx in self.__topology_dict:
-            l = bm.faces[fidx].loops[vidx]
-            l[active_uv].uv = l[active_uv].uv + dv
-        bmesh.update_edit_mesh(obj.data)
-
-        # check mouse preference
-        if context.user_preferences.inputs.select_mouse == 'RIGHT':
-            confirm_btn = 'LEFTMOUSE'
-            cancel_btn = 'RIGHTMOUSE'
-        else:
-            confirm_btn = 'RIGHTMOUSE'
-            cancel_btn = 'LEFTMOUSE'
-
-        # cancelled
-        if event.type == cancel_btn and event.value == 'PRESS':
-            for (fidx, vidx), uv in zip(self.__topology_dict, self.__ini_uvs):
-                bm.faces[fidx].loops[vidx][active_uv].uv = uv
-            Operator.__running = False
-            return {'FINISHED'}
-        # confirmed
-        if event.type == confirm_btn and event.value == 'PRESS':
-            Operator.__running = False
-            return {'FINISHED'}
-
-        return {'RUNNING_MODAL'}
+        return self.__impl.modal(self, context, event)
 
     def execute(self, context):
-        Operator.__running = True
-        self.__operating = False
-        self.__first_time = True
-
-        context.window_manager.modal_handler_add(self)
-        self.__topology_dict, self.__ini_uvs = self.__find_uv(context)
-
-        if context.area:
-            context.area.tag_redraw()
-
-        return {'RUNNING_MODAL'}
+        return self.__impl.execute(self, context)
diff --git a/uv_magic_uv/op/transfer_uv.py b/uv_magic_uv/op/transfer_uv.py
index ef6fc3bebc43a034a16ce0367109b75c49648962..db05b34379ef8c6da680abc5292b4515802c23e5 100644
--- a/uv_magic_uv/op/transfer_uv.py
+++ b/uv_magic_uv/op/transfer_uv.py
@@ -23,43 +23,27 @@ __status__ = "production"
 __version__ = "5.2"
 __date__ = "17 Nov 2018"
 
-from collections import OrderedDict
-
 import bpy
 import bmesh
 from bpy.props import BoolProperty
 
 from .. import common
+from ..impl import transfer_uv_impl as impl
+from ..utils.bl_class_registry import BlClassRegistry
+from ..utils.property_class_registry import PropertyClassRegistry
 
 
 __all__ = [
-    'OperatorCopyUV',
-    'OperatorPasteUV',
+    'Properties',
+    'MUV_OT_TransferUV_CopyUV',
+    'MUV_OT_TransferUV_PasteUV',
 ]
 
 
-def is_valid_context(context):
-    obj = context.object
-
-    # only edit mode is allowed to execute
-    if obj is None:
-        return False
-    if obj.type != 'MESH':
-        return False
-    if context.object.mode != 'EDIT':
-        return False
-
-    # only 'VIEW_3D' space is allowed to execute
-    for space in context.area.spaces:
-        if space.type == 'VIEW_3D':
-            break
-    else:
-        return False
-
-    return True
-
-
+@PropertyClassRegistry()
 class Properties:
+    idname = "transfer_uv"
+
     @classmethod
     def init_props(cls, scene):
         class Props():
@@ -90,7 +74,8 @@ class Properties:
         del scene.muv_transfer_uv_copy_seams
 
 
-class OperatorCopyUV(bpy.types.Operator):
+@BlClassRegistry()
+class MUV_OT_TransferUV_CopyUV(bpy.types.Operator):
     """
         Operation class: Transfer UV copy
         Topological based copy
@@ -106,56 +91,30 @@ class OperatorCopyUV(bpy.types.Operator):
         # we can not get area/space/region from console
         if common.is_console_mode():
             return True
-        return is_valid_context(context)
+        return impl.is_valid_context(context)
 
     def execute(self, context):
         props = context.scene.muv_props.transfer_uv
-        active_obj = context.scene.objects.active
+        active_obj = context.active_object
         bm = bmesh.from_edit_mesh(active_obj.data)
-        if common.check_version(2, 73, 0) >= 0:
-            bm.faces.ensure_lookup_table()
+        bm.faces.ensure_lookup_table()
 
-        # get UV layer
-        if not bm.loops.layers.uv:
-            self.report({'WARNING'}, "Object must have more than one UV map")
+        uv_layer = impl.get_uv_layer(self, bm)
+        if uv_layer is None:
             return {'CANCELLED'}
-        uv_layer = bm.loops.layers.uv.verify()
 
-        props.topology_copied = []
-
-        # get selected faces
-        active_face = bm.faces.active
-        sel_faces = [face for face in bm.faces if face.select]
-        if len(sel_faces) != 2:
-            self.report({'WARNING'}, "Two faces must be selected")
-            return {'CANCELLED'}
-        if not active_face or active_face not in sel_faces:
-            self.report({'WARNING'}, "Two faces must be active")
-            return {'CANCELLED'}
-
-        # parse all faces according to selection
-        active_face_nor = active_face.normal.copy()
-        all_sorted_faces = main_parse(
-            self, uv_layer, sel_faces, active_face,
-            active_face_nor)
-
-        if all_sorted_faces:
-            for face_data in all_sorted_faces.values():
-                edges = face_data[1]
-                uv_loops = face_data[2]
-                uvs = [l.uv.copy() for l in uv_loops]
-                pin_uvs = [l.pin_uv for l in uv_loops]
-                seams = [e.seam for e in edges]
-                props.topology_copied.append([uvs, pin_uvs, seams])
-        else:
+        faces = impl.get_selected_src_faces(self, bm, uv_layer)
+        if faces is None:
             return {'CANCELLED'}
+        props.topology_copied = faces
 
         bmesh.update_edit_mesh(active_obj.data)
 
         return {'FINISHED'}
 
 
-class OperatorPasteUV(bpy.types.Operator):
+@BlClassRegistry()
+class MUV_OT_TransferUV_PasteUV(bpy.types.Operator):
     """
         Operation class: Transfer UV paste
         Topological based paste
@@ -166,12 +125,12 @@ class OperatorPasteUV(bpy.types.Operator):
     bl_description = "Transfer UV Paste UV (Topological based paste)"
     bl_options = {'REGISTER', 'UNDO'}
 
-    invert_normals = BoolProperty(
+    invert_normals: BoolProperty(
         name="Invert Normals",
         description="Invert Normals",
         default=False
     )
-    copy_seams = BoolProperty(
+    copy_seams: BoolProperty(
         name="Copy Seams",
         description="Copy Seams",
         default=True
@@ -186,253 +145,24 @@ class OperatorPasteUV(bpy.types.Operator):
         props = sc.muv_props.transfer_uv
         if not props.topology_copied:
             return False
-        return is_valid_context(context)
+        return impl.is_valid_context(context)
 
     def execute(self, context):
         props = context.scene.muv_props.transfer_uv
-        active_obj = context.scene.objects.active
+        active_obj = context.active_object
         bm = bmesh.from_edit_mesh(active_obj.data)
-        if common.check_version(2, 73, 0) >= 0:
-            bm.faces.ensure_lookup_table()
+        bm.faces.ensure_lookup_table()
 
         # get UV layer
-        if not bm.loops.layers.uv:
-            self.report({'WARNING'}, "Object must have more than one UV map")
+        uv_layer = impl.get_uv_layer(self, bm)
+        if uv_layer is None:
             return {'CANCELLED'}
-        uv_layer = bm.loops.layers.uv.verify()
 
-        # get selection history
-        all_sel_faces = [
-            e for e in bm.select_history
-            if isinstance(e, bmesh.types.BMFace) and e.select]
-        if len(all_sel_faces) % 2 != 0:
-            self.report({'WARNING'}, "Two faces must be selected")
+        ret = impl.paste_uv(self, bm, uv_layer, props.topology_copied,
+                            self.invert_normals, self.copy_seams)
+        if ret:
             return {'CANCELLED'}
 
-        # parse selection history
-        for i, _ in enumerate(all_sel_faces):
-            if (i == 0) or (i % 2 == 0):
-                continue
-            sel_faces = [all_sel_faces[i - 1], all_sel_faces[i]]
-            active_face = all_sel_faces[i]
-
-            # parse all faces according to selection history
-            active_face_nor = active_face.normal.copy()
-            if self.invert_normals:
-                active_face_nor.negate()
-            all_sorted_faces = main_parse(
-                self, uv_layer, sel_faces, active_face,
-                active_face_nor)
-
-            if all_sorted_faces:
-                # check amount of copied/pasted faces
-                if len(all_sorted_faces) != len(props.topology_copied):
-                    self.report(
-                        {'WARNING'},
-                        "Mesh has different amount of faces"
-                    )
-                    return {'CANCELLED'}
-
-                for j, face_data in enumerate(all_sorted_faces.values()):
-                    copied_data = props.topology_copied[j]
-
-                    # check amount of copied/pasted verts
-                    if len(copied_data[0]) != len(face_data[2]):
-                        bpy.ops.mesh.select_all(action='DESELECT')
-                        # select problematic face
-                        list(all_sorted_faces.keys())[j].select = True
-                        self.report(
-                            {'WARNING'},
-                            "Face have different amount of vertices"
-                        )
-                        return {'FINISHED'}
-
-                    for k, (edge, uvloop) in enumerate(zip(face_data[1],
-                                                           face_data[2])):
-                        uvloop.uv = copied_data[0][k]
-                        uvloop.pin_uv = copied_data[1][k]
-                        if self.copy_seams:
-                            edge.seam = copied_data[2][k]
-            else:
-                return {'CANCELLED'}
-
         bmesh.update_edit_mesh(active_obj.data)
-        if self.copy_seams:
-            active_obj.data.show_edge_seams = True
 
         return {'FINISHED'}
-
-
-def main_parse(
-        self, uv_layer, sel_faces,
-        active_face, active_face_nor):
-    all_sorted_faces = OrderedDict()  # This is the main stuff
-
-    used_verts = set()
-    used_edges = set()
-
-    faces_to_parse = []
-
-    # get shared edge of two faces
-    cross_edges = []
-    for edge in active_face.edges:
-        if edge in sel_faces[0].edges and edge in sel_faces[1].edges:
-            cross_edges.append(edge)
-
-    # parse two selected faces
-    if cross_edges and len(cross_edges) == 1:
-        shared_edge = cross_edges[0]
-        vert1 = None
-        vert2 = None
-
-        dot_n = active_face_nor.normalized()
-        edge_vec_1 = (shared_edge.verts[1].co - shared_edge.verts[0].co)
-        edge_vec_len = edge_vec_1.length
-        edge_vec_1 = edge_vec_1.normalized()
-
-        af_center = active_face.calc_center_median()
-        af_vec = shared_edge.verts[0].co + (edge_vec_1 * (edge_vec_len * 0.5))
-        af_vec = (af_vec - af_center).normalized()
-
-        if af_vec.cross(edge_vec_1).dot(dot_n) > 0:
-            vert1 = shared_edge.verts[0]
-            vert2 = shared_edge.verts[1]
-        else:
-            vert1 = shared_edge.verts[1]
-            vert2 = shared_edge.verts[0]
-
-        # get active face stuff and uvs
-        face_stuff = get_other_verts_edges(
-            active_face, vert1, vert2, shared_edge, uv_layer)
-        all_sorted_faces[active_face] = face_stuff
-        used_verts.update(active_face.verts)
-        used_edges.update(active_face.edges)
-
-        # get first selected face stuff and uvs as they share shared_edge
-        second_face = sel_faces[0]
-        if second_face is active_face:
-            second_face = sel_faces[1]
-        face_stuff = get_other_verts_edges(
-            second_face, vert1, vert2, shared_edge, uv_layer)
-        all_sorted_faces[second_face] = face_stuff
-        used_verts.update(second_face.verts)
-        used_edges.update(second_face.edges)
-
-        # first Grow
-        faces_to_parse.append(active_face)
-        faces_to_parse.append(second_face)
-
-    else:
-        self.report({'WARNING'}, "Two faces should share one edge")
-        return None
-
-    # parse all faces
-    while True:
-        new_parsed_faces = []
-        if not faces_to_parse:
-            break
-        for face in faces_to_parse:
-            face_stuff = all_sorted_faces.get(face)
-            new_faces = parse_faces(
-                face, face_stuff, used_verts, used_edges, all_sorted_faces,
-                uv_layer)
-            if new_faces == 'CANCELLED':
-                self.report({'WARNING'}, "More than 2 faces share edge")
-                return None
-
-            new_parsed_faces += new_faces
-        faces_to_parse = new_parsed_faces
-
-    return all_sorted_faces
-
-
-def parse_faces(
-        check_face, face_stuff, used_verts, used_edges, all_sorted_faces,
-        uv_layer):
-    """recurse faces around the new_grow only"""
-
-    new_shared_faces = []
-    for sorted_edge in face_stuff[1]:
-        shared_faces = sorted_edge.link_faces
-        if shared_faces:
-            if len(shared_faces) > 2:
-                bpy.ops.mesh.select_all(action='DESELECT')
-                for face_sel in shared_faces:
-                    face_sel.select = True
-                shared_faces = []
-                return 'CANCELLED'
-
-            clear_shared_faces = get_new_shared_faces(
-                check_face, sorted_edge, shared_faces, all_sorted_faces.keys())
-            if clear_shared_faces:
-                shared_face = clear_shared_faces[0]
-                # get vertices of the edge
-                vert1 = sorted_edge.verts[0]
-                vert2 = sorted_edge.verts[1]
-
-                common.debug_print(face_stuff[0], vert1, vert2)
-                if face_stuff[0].index(vert1) > face_stuff[0].index(vert2):
-                    vert1 = sorted_edge.verts[1]
-                    vert2 = sorted_edge.verts[0]
-
-                common.debug_print(shared_face.verts, vert1, vert2)
-                new_face_stuff = get_other_verts_edges(
-                    shared_face, vert1, vert2, sorted_edge, uv_layer)
-                all_sorted_faces[shared_face] = new_face_stuff
-                used_verts.update(shared_face.verts)
-                used_edges.update(shared_face.edges)
-
-                if common.is_debug_mode():
-                    shared_face.select = True  # test which faces are parsed
-
-                new_shared_faces.append(shared_face)
-
-    return new_shared_faces
-
-
-def get_new_shared_faces(orig_face, shared_edge, check_faces, used_faces):
-    shared_faces = []
-
-    for face in check_faces:
-        is_shared_edge = shared_edge in face.edges
-        not_used = face not in used_faces
-        not_orig = face is not orig_face
-        not_hide = face.hide is False
-        if is_shared_edge and not_used and not_orig and not_hide:
-            shared_faces.append(face)
-
-    return shared_faces
-
-
-def get_other_verts_edges(face, vert1, vert2, first_edge, uv_layer):
-    face_edges = [first_edge]
-    face_verts = [vert1, vert2]
-    face_loops = []
-
-    other_edges = [edge for edge in face.edges if edge not in face_edges]
-
-    for _ in range(len(other_edges)):
-        found_edge = None
-        # get sorted verts and edges
-        for edge in other_edges:
-            if face_verts[-1] in edge.verts:
-                other_vert = edge.other_vert(face_verts[-1])
-
-                if other_vert not in face_verts:
-                    face_verts.append(other_vert)
-
-                found_edge = edge
-                if found_edge not in face_edges:
-                    face_edges.append(edge)
-                break
-
-        other_edges.remove(found_edge)
-
-    # get sorted uvs
-    for vert in face_verts:
-        for loop in face.loops:
-            if loop.vert is vert:
-                face_loops.append(loop[uv_layer])
-                break
-
-    return [face_verts, face_edges, face_loops]
diff --git a/uv_magic_uv/op/uvw.py b/uv_magic_uv/op/uvw.py
index 44858187ae7aa8d894c6f61e00f8a09598ae7f22..c97e0e042e2f1b3f3c47264306a381ed393d96ff 100644
--- a/uv_magic_uv/op/uvw.py
+++ b/uv_magic_uv/op/uvw.py
@@ -23,8 +23,6 @@ __status__ = "production"
 __version__ = "5.2"
 __date__ = "17 Nov 2018"
 
-from math import sin, cos, pi
-
 import bpy
 import bmesh
 from bpy.props import (
@@ -32,40 +30,24 @@ from bpy.props import (
     FloatVectorProperty,
     BoolProperty
 )
-from mathutils import Vector
 
 from .. import common
+from ..impl import uvw_impl as impl
+from ..utils.bl_class_registry import BlClassRegistry
+from ..utils.property_class_registry import PropertyClassRegistry
 
 
 __all__ = [
     'Properties',
-    'OperatorBoxMap',
-    'OperatorBestPlanerMap',
+    'MUV_OT_UVW_BoxMap',
+    'MUV_OT_UVW_BestPlanerMap',
 ]
 
 
-def is_valid_context(context):
-    obj = context.object
-
-    # only edit mode is allowed to execute
-    if obj is None:
-        return False
-    if obj.type != 'MESH':
-        return False
-    if context.object.mode != 'EDIT':
-        return False
-
-    # only 'VIEW_3D' space is allowed to execute
-    for space in context.area.spaces:
-        if space.type == 'VIEW_3D':
-            break
-    else:
-        return False
-
-    return True
-
-
+@PropertyClassRegistry()
 class Properties:
+    idname = "uvw"
+
     @classmethod
     def init_props(cls, scene):
         scene.muv_uvw_enabled = BoolProperty(
@@ -85,32 +67,33 @@ class Properties:
         del scene.muv_uvw_assign_uvmap
 
 
-class OperatorBoxMap(bpy.types.Operator):
+@BlClassRegistry()
+class MUV_OT_UVW_BoxMap(bpy.types.Operator):
     bl_idname = "uv.muv_uvw_operator_box_map"
     bl_label = "Box Map"
     bl_options = {'REGISTER', 'UNDO'}
 
-    size = FloatProperty(
+    size: FloatProperty(
         name="Size",
         default=1.0,
         precision=4
     )
-    rotation = FloatVectorProperty(
+    rotation: FloatVectorProperty(
         name="XYZ Rotation",
         size=3,
         default=(0.0, 0.0, 0.0)
     )
-    offset = FloatVectorProperty(
+    offset: FloatVectorProperty(
         name="XYZ Offset",
         size=3,
         default=(0.0, 0.0, 0.0)
     )
-    tex_aspect = FloatProperty(
+    tex_aspect: FloatProperty(
         name="Texture Aspect",
         default=1.0,
         precision=4
     )
-    assign_uvmap = BoolProperty(
+    assign_uvmap: BoolProperty(
         name="Assign UVMap",
         description="Assign UVMap when no UVmaps are available",
         default=True
@@ -121,7 +104,7 @@ class OperatorBoxMap(bpy.types.Operator):
         # we can not get area/space/region from console
         if common.is_console_mode():
             return True
-        return is_valid_context(context)
+        return impl.is_valid_context(context)
 
     def execute(self, context):
         obj = context.active_object
@@ -130,102 +113,43 @@ class OperatorBoxMap(bpy.types.Operator):
             bm.faces.ensure_lookup_table()
 
         # get UV layer
-        if not bm.loops.layers.uv:
-            if self.assign_uvmap:
-                bm.loops.layers.uv.new()
-            else:
-                self.report(
-                    {'WARNING'}, "Object must have more than one UV map")
-                return {'CANCELLED'}
-        uv_layer = bm.loops.layers.uv.verify()
-
-        scale = 1.0 / self.size
-
-        sx = 1.0 * scale
-        sy = 1.0 * scale
-        sz = 1.0 * scale
-        ofx = self.offset[0]
-        ofy = self.offset[1]
-        ofz = self.offset[2]
-        rx = self.rotation[0] * pi / 180.0
-        ry = self.rotation[1] * pi / 180.0
-        rz = self.rotation[2] * pi / 180.0
-        aspect = self.tex_aspect
-
-        sel_faces = [f for f in bm.faces if f.select]
-
-        # update UV coordinate
-        for f in sel_faces:
-            n = f.normal
-            for l in f.loops:
-                co = l.vert.co
-                x = co.x * sx
-                y = co.y * sy
-                z = co.z * sz
-
-                # X-plane
-                if abs(n[0]) >= abs(n[1]) and abs(n[0]) >= abs(n[2]):
-                    if n[0] >= 0.0:
-                        u = (y - ofy) * cos(rx) + (z - ofz) * sin(rx)
-                        v = -(y * aspect - ofy) * sin(rx) +\
-                            (z * aspect - ofz) * cos(rx)
-                    else:
-                        u = -(y - ofy) * cos(rx) + (z - ofz) * sin(rx)
-                        v = (y * aspect - ofy) * sin(rx) +\
-                            (z * aspect - ofz) * cos(rx)
-                # Y-plane
-                elif abs(n[1]) >= abs(n[0]) and abs(n[1]) >= abs(n[2]):
-                    if n[1] >= 0.0:
-                        u = -(x - ofx) * cos(ry) + (z - ofz) * sin(ry)
-                        v = (x * aspect - ofx) * sin(ry) +\
-                            (z * aspect - ofz) * cos(ry)
-                    else:
-                        u = (x - ofx) * cos(ry) + (z - ofz) * sin(ry)
-                        v = -(x * aspect - ofx) * sin(ry) +\
-                            (z * aspect - ofz) * cos(ry)
-                # Z-plane
-                else:
-                    if n[2] >= 0.0:
-                        u = (x - ofx) * cos(rz) + (y - ofy) * sin(rz)
-                        v = -(x * aspect - ofx) * sin(rz) +\
-                            (y * aspect - ofy) * cos(rz)
-                    else:
-                        u = -(x - ofx) * cos(rz) - (y + ofy) * sin(rz)
-                        v = -(x * aspect + ofx) * sin(rz) +\
-                            (y * aspect - ofy) * cos(rz)
-
-                l[uv_layer].uv = Vector((u, v))
+        uv_layer = impl.get_uv_layer(self, bm, self.assign_uvmap)
+        if not uv_layer:
+            return {'CANCELLED'}
 
+        impl.apply_box_map(bm, uv_layer, self.size, self.offset,
+                           self.rotation, self.tex_aspect)
         bmesh.update_edit_mesh(obj.data)
 
         return {'FINISHED'}
 
 
-class OperatorBestPlanerMap(bpy.types.Operator):
+@BlClassRegistry()
+class MUV_OT_UVW_BestPlanerMap(bpy.types.Operator):
     bl_idname = "uv.muv_uvw_operator_best_planer_map"
     bl_label = "Best Planer Map"
     bl_options = {'REGISTER', 'UNDO'}
 
-    size = FloatProperty(
+    size: FloatProperty(
         name="Size",
         default=1.0,
         precision=4
     )
-    rotation = FloatProperty(
+    rotation: FloatProperty(
         name="XY Rotation",
         default=0.0
     )
-    offset = FloatVectorProperty(
+    offset: FloatVectorProperty(
         name="XY Offset",
         size=2,
         default=(0.0, 0.0)
     )
-    tex_aspect = FloatProperty(
+    tex_aspect: FloatProperty(
         name="Texture Aspect",
         default=1.0,
         precision=4
     )
-    assign_uvmap = BoolProperty(
+    assign_uvmap: BoolProperty(
         name="Assign UVMap",
         description="Assign UVMap when no UVmaps are available",
         default=True
@@ -236,7 +160,7 @@ class OperatorBestPlanerMap(bpy.types.Operator):
         # we can not get area/space/region from console
         if common.is_console_mode():
             return True
-        return is_valid_context(context)
+        return impl.is_valid_context(context)
 
     def execute(self, context):
         obj = context.active_object
@@ -245,44 +169,12 @@ class OperatorBestPlanerMap(bpy.types.Operator):
             bm.faces.ensure_lookup_table()
 
         # get UV layer
-        if not bm.loops.layers.uv:
-            if self.assign_uvmap:
-                bm.loops.layers.uv.new()
-            else:
-                self.report(
-                    {'WARNING'}, "Object must have more than one UV map")
-                return {'CANCELLED'}
-
-        uv_layer = bm.loops.layers.uv.verify()
-
-        scale = 1.0 / self.size
-
-        sx = 1.0 * scale
-        sy = 1.0 * scale
-        ofx = self.offset[0]
-        ofy = self.offset[1]
-        rz = self.rotation * pi / 180.0
-        aspect = self.tex_aspect
-
-        sel_faces = [f for f in bm.faces if f.select]
-
-        # calculate average of normal
-        n_ave = Vector((0.0, 0.0, 0.0))
-        for f in sel_faces:
-            n_ave = n_ave + f.normal
-        q = n_ave.rotation_difference(Vector((0.0, 0.0, 1.0)))
-
-        # update UV coordinate
-        for f in sel_faces:
-            for l in f.loops:
-                co = q * l.vert.co
-                x = co.x * sx
-                y = co.y * sy
-
-                u = x * cos(rz) - y * sin(rz) + ofx
-                v = -x * aspect * sin(rz) - y * aspect * cos(rz) + ofy
+        uv_layer = impl.get_uv_layer(self, bm, self.assign_uvmap)
+        if not uv_layer:
+            return {'CANCELLED'}
 
-                l[uv_layer].uv = Vector((u, v))
+        impl.apply_planer_map(bm, uv_layer, self.size, self.offset,
+                              self.rotation, self.tex_aspect)
 
         bmesh.update_edit_mesh(obj.data)
 
diff --git a/uv_magic_uv/preferences.py b/uv_magic_uv/preferences.py
index 376258d0c2e03ce294cb18ae2a51468775f8a4d4..3ba943761dfc76fa5340bbf90025f4abadbd8398 100644
--- a/uv_magic_uv/preferences.py
+++ b/uv_magic_uv/preferences.py
@@ -33,11 +33,10 @@ from bpy.props import (
 )
 from bpy.types import AddonPreferences
 
-from . import ui
 from . import op
+from . import ui
 from . import addon_updater_ops
 
-
 __all__ = [
     'add_builtin_menu',
     'remove_builtin_menu',
@@ -50,100 +49,50 @@ def view3d_uvmap_menu_fn(self, context):
     sc = context.scene
 
     layout.separator()
-    layout.label("Copy/Paste UV", icon='IMAGE_COL')
+    layout.label(text="Copy/Paste UV", icon='IMAGE')
     # Copy/Paste UV
-    layout.menu(ui.VIEW3D_MT_uv_map.MenuCopyPasteUV.bl_idname,
+    layout.menu(ui.VIEW3D_MT_uv_map.MUV_MT_CopyPasteUV.bl_idname,
                 text="Copy/Paste UV")
     # Transfer UV
-    layout.menu(ui.VIEW3D_MT_uv_map.MenuTransferUV.bl_idname,
+    layout.menu(ui.VIEW3D_MT_uv_map.MUV_MT_TransferUV.bl_idname,
                 text="Transfer UV")
 
     layout.separator()
-    layout.label("UV Manipulation", icon='IMAGE_COL')
+    layout.label(text="UV Manipulation", icon='IMAGE')
     # Flip/Rotate UV
-    ops = layout.operator(op.flip_rotate_uv.Operator.bl_idname,
+    ops = layout.operator(op.flip_rotate_uv.MUV_OT_FlipRotate.bl_idname,
                           text="Flip/Rotate UV")
     ops.seams = sc.muv_flip_rotate_uv_seams
     # Mirror UV
-    ops = layout.operator(op.mirror_uv.Operator.bl_idname, text="Mirror UV")
+    ops = layout.operator(op.mirror_uv.MUV_OT_MirrorUV.bl_idname,
+                          text="Mirror UV")
     ops.axis = sc.muv_mirror_uv_axis
     # Move UV
-    layout.operator(op.move_uv.Operator.bl_idname, text="Move UV")
-    # World Scale UV
-    layout.menu(ui.VIEW3D_MT_uv_map.MenuWorldScaleUV.bl_idname,
-                text="World Scale UV")
-    # Preserve UV
-    layout.menu(ui.VIEW3D_MT_uv_map.MenuPreserveUVAspect.bl_idname,
-                text="Preserve UV")
-    # Texture Lock
-    layout.menu(ui.VIEW3D_MT_uv_map.MenuTextureLock.bl_idname,
-                text="Texture Lock")
-    # Texture Wrap
-    layout.menu(ui.VIEW3D_MT_uv_map.MenuTextureWrap.bl_idname,
-                text="Texture Wrap")
-    # UV Sculpt
-    layout.prop(sc, "muv_uv_sculpt_enable", text="UV Sculpt")
+    layout.operator(op.move_uv.MUV_OT_MoveUV.bl_idname, text="Move UV")
 
     layout.separator()
-    layout.label("UV Mapping", icon='IMAGE_COL')
-    # Unwrap Constraint
-    ops = layout.operator(op.unwrap_constraint.Operator.bl_idname,
-                          text="Unwrap Constraint")
-    ops.u_const = sc.muv_unwrap_constraint_u_const
-    ops.v_const = sc.muv_unwrap_constraint_v_const
-    # Texture Projection
-    layout.menu(ui.VIEW3D_MT_uv_map.MenuTextureProjection.bl_idname,
-                text="Texture Projection")
     # UVW
-    layout.menu(ui.VIEW3D_MT_uv_map.MenuUVW.bl_idname, text="UVW")
+    layout.menu(ui.VIEW3D_MT_uv_map.MUV_MT_UVW.bl_idname, text="UVW")
 
 
 def view3d_object_menu_fn(self, _):
     layout = self.layout
 
     layout.separator()
+    layout.label(text="Copy/Paste UV", icon='IMAGE')
     # Copy/Paste UV (Among Objecct)
-    layout.menu(ui.VIEW3D_MT_object.MenuCopyPasteUVObject.bl_idname,
+    layout.menu(ui.VIEW3D_MT_object.MUV_MT_CopyPasteUV_Object.bl_idname,
                 text="Copy/Paste UV")
-    layout.label("Copy/Paste UV", icon='IMAGE_COL')
 
 
-def image_uvs_menu_fn(self, context):
+def image_uvs_menu_fn(self, _):
     layout = self.layout
-    sc = context.scene
-
-    layout.separator()
-    # Align UV Cursor
-    layout.menu(ui.IMAGE_MT_uvs.MenuAlignUVCursor.bl_idname,
-                text="Align UV Cursor")
-    # UV Bounding Box
-    layout.prop(sc, "muv_uv_bounding_box_show", text="UV Bounding Box")
-    # UV Inspection
-    layout.menu(ui.IMAGE_MT_uvs.MenuUVInspection.bl_idname,
-                text="UV Inspection")
-    layout.label("Editor Enhancement", icon='IMAGE_COL')
-
-    layout.separator()
-    # Align UV
-    layout.menu(ui.IMAGE_MT_uvs.MenuAlignUV.bl_idname, text="Align UV")
-    # Smooth UV
-    ops = layout.operator(op.smooth_uv.Operator.bl_idname, text="Smooth")
-    ops.transmission = sc.muv_smooth_uv_transmission
-    ops.select = sc.muv_smooth_uv_select
-    ops.mesh_infl = sc.muv_smooth_uv_mesh_infl
-    # Select UV
-    layout.menu(ui.IMAGE_MT_uvs.MenuSelectUV.bl_idname, text="Select UV")
-    # Pack UV
-    ops = layout.operator(op.pack_uv.Operator.bl_idname, text="Pack UV")
-    ops.allowable_center_deviation = sc.muv_pack_uv_allowable_center_deviation
-    ops.allowable_size_deviation = sc.muv_pack_uv_allowable_size_deviation
-    layout.label("UV Manipulation", icon='IMAGE_COL')
 
     layout.separator()
     # Copy/Paste UV (on UV/Image Editor)
-    layout.menu(ui.IMAGE_MT_uvs.MenuCopyPasteUVUVEdit.bl_idname,
+    layout.label(text="Copy/Paste UV", icon='IMAGE')
+    layout.menu(ui.IMAGE_MT_uvs.MUV_MT_CopyPasteUV_UVEdit.bl_idname,
                 text="Copy/Paste UV")
-    layout.label("Copy/Paste UV", icon='IMAGE_COL')
 
 
 def add_builtin_menu():
@@ -154,14 +103,14 @@ def add_builtin_menu():
 
 def remove_builtin_menu():
     bpy.types.IMAGE_MT_uvs.remove(image_uvs_menu_fn)
-    bpy.types.VIEW3D_MT_object.remove(view3d_object_menu_fn)
+    bpy.types.VIEW3D_MT_object.append(view3d_object_menu_fn)
     bpy.types.VIEW3D_MT_uv_map.remove(view3d_uvmap_menu_fn)
 
 
 class Preferences(AddonPreferences):
     """Preferences class: Preferences for this add-on"""
 
-    bl_idname = __package__
+    bl_idname = "uv_magic_uv"
 
     def update_enable_builtin_menu(self, _):
         if self['enable_builtin_menu']:
diff --git a/uv_magic_uv/properites.py b/uv_magic_uv/properites.py
index e4634e5139932ef792c86b08adf9a72d6e4b253a..60ce26eb10922b797645122d854ad52666941767 100644
--- a/uv_magic_uv/properites.py
+++ b/uv_magic_uv/properites.py
@@ -24,30 +24,7 @@ __version__ = "5.2"
 __date__ = "17 Nov 2018"
 
 
-from .op import (
-    align_uv,
-    align_uv_cursor,
-    copy_paste_uv,
-    copy_paste_uv_object,
-    copy_paste_uv_uvedit,
-    flip_rotate_uv,
-    mirror_uv,
-    move_uv,
-    pack_uv,
-    preserve_uv_aspect,
-    select_uv,
-    smooth_uv,
-    texture_lock,
-    texture_projection,
-    texture_wrap,
-    transfer_uv,
-    unwrap_constraint,
-    uv_bounding_box,
-    uv_inspection,
-    uv_sculpt,
-    uvw,
-    world_scale_uv,
-)
+from .utils.property_class_registry import PropertyClassRegistry
 
 
 __all__ = [
@@ -77,53 +54,9 @@ class MUV_Prefs():
 
 def init_props(scene):
     scene.muv_props = MUV_Properties()
-
-    align_uv.Properties.init_props(scene)
-    align_uv_cursor.Properties.init_props(scene)
-    copy_paste_uv.Properties.init_props(scene)
-    copy_paste_uv_object.Properties.init_props(scene)
-    copy_paste_uv_uvedit.Properties.init_props(scene)
-    flip_rotate_uv.Properties.init_props(scene)
-    mirror_uv.Properties.init_props(scene)
-    move_uv.Properties.init_props(scene)
-    pack_uv.Properties.init_props(scene)
-    preserve_uv_aspect.Properties.init_props(scene)
-    select_uv.Properties.init_props(scene)
-    smooth_uv.Properties.init_props(scene)
-    texture_lock.Properties.init_props(scene)
-    texture_projection.Properties.init_props(scene)
-    texture_wrap.Properties.init_props(scene)
-    transfer_uv.Properties.init_props(scene)
-    unwrap_constraint.Properties.init_props(scene)
-    uv_bounding_box.Properties.init_props(scene)
-    uv_inspection.Properties.init_props(scene)
-    uv_sculpt.Properties.init_props(scene)
-    uvw.Properties.init_props(scene)
-    world_scale_uv.Properties.init_props(scene)
+    PropertyClassRegistry.init_props(scene)
 
 
 def clear_props(scene):
-    align_uv.Properties.del_props(scene)
-    align_uv_cursor.Properties.del_props(scene)
-    copy_paste_uv.Properties.del_props(scene)
-    copy_paste_uv_object.Properties.del_props(scene)
-    copy_paste_uv_uvedit.Properties.del_props(scene)
-    flip_rotate_uv.Properties.del_props(scene)
-    mirror_uv.Properties.del_props(scene)
-    move_uv.Properties.del_props(scene)
-    pack_uv.Properties.del_props(scene)
-    preserve_uv_aspect.Properties.del_props(scene)
-    select_uv.Properties.del_props(scene)
-    smooth_uv.Properties.del_props(scene)
-    texture_lock.Properties.del_props(scene)
-    texture_projection.Properties.del_props(scene)
-    texture_wrap.Properties.del_props(scene)
-    transfer_uv.Properties.del_props(scene)
-    unwrap_constraint.Properties.del_props(scene)
-    uv_bounding_box.Properties.del_props(scene)
-    uv_inspection.Properties.del_props(scene)
-    uv_sculpt.Properties.del_props(scene)
-    uvw.Properties.del_props(scene)
-    world_scale_uv.Properties.del_props(scene)
-
+    PropertyClassRegistry.del_props(scene)
     del scene.muv_props
diff --git a/uv_magic_uv/ui/IMAGE_MT_uvs.py b/uv_magic_uv/ui/IMAGE_MT_uvs.py
index 9beb7e2ff46bce1237965fa575c8626d962fbea4..e7dda379cba8e375de9e8f7b826fd45f6ab14ac2 100644
--- a/uv_magic_uv/ui/IMAGE_MT_uvs.py
+++ b/uv_magic_uv/ui/IMAGE_MT_uvs.py
@@ -24,23 +24,19 @@ __version__ = "5.2"
 __date__ = "17 Nov 2018"
 
 import bpy
-from ..op import copy_paste_uv_uvedit
-from ..op import align_uv
-from ..op import uv_inspection
-from ..op import align_uv_cursor
-from ..op import select_uv
 
+from ..op import (
+    copy_paste_uv_uvedit,
+)
+from ..utils.bl_class_registry import BlClassRegistry
 
 __all__ = [
-    'MenuCopyPasteUVUVEdit',
-    'MenuAlignUV',
-    'MenuSelectUV',
-    'MenuAlignUVCursor',
-    'MenuUVInspection',
+    'MUV_MT_CopyPasteUV_UVEdit',
 ]
 
 
-class MenuCopyPasteUVUVEdit(bpy.types.Menu):
+@BlClassRegistry()
+class MUV_MT_CopyPasteUV_UVEdit(bpy.types.Menu):
     """
     Menu class: Master menu of Copy/Paste UV coordinate on UV/ImageEditor
     """
@@ -52,135 +48,9 @@ class MenuCopyPasteUVUVEdit(bpy.types.Menu):
     def draw(self, _):
         layout = self.layout
 
-        layout.operator(copy_paste_uv_uvedit.OperatorCopyUV.bl_idname,
-                        text="Copy")
-        layout.operator(copy_paste_uv_uvedit.OperatorPasteUV.bl_idname,
-                        text="Paste")
-
-
-class MenuAlignUV(bpy.types.Menu):
-    """
-    Menu class: Master menu of Align UV
-    """
-
-    bl_idname = "uv.muv_align_uv_menu"
-    bl_label = "Align UV"
-    bl_description = "Align UV"
-
-    def draw(self, context):
-        layout = self.layout
-        sc = context.scene
-
-        ops = layout.operator(align_uv.OperatorCircle.bl_idname, text="Circle")
-        ops.transmission = sc.muv_align_uv_transmission
-        ops.select = sc.muv_align_uv_select
-
-        ops = layout.operator(align_uv.OperatorStraighten.bl_idname,
-                              text="Straighten")
-        ops.transmission = sc.muv_align_uv_transmission
-        ops.select = sc.muv_align_uv_select
-        ops.vertical = sc.muv_align_uv_vertical
-        ops.horizontal = sc.muv_align_uv_horizontal
-
-        ops = layout.operator(align_uv.OperatorAxis.bl_idname,
-                              text="XY-axis")
-        ops.transmission = sc.muv_align_uv_transmission
-        ops.select = sc.muv_align_uv_select
-        ops.vertical = sc.muv_align_uv_vertical
-        ops.horizontal = sc.muv_align_uv_horizontal
-        ops.location = sc.muv_align_uv_location
-
-
-class MenuSelectUV(bpy.types.Menu):
-    """
-    Menu class: Master menu of Select UV
-    """
-
-    bl_idname = "uv.muv_select_uv_menu"
-    bl_label = "Select UV"
-    bl_description = "Select UV"
-
-    def draw(self, _):
-        layout = self.layout
-
-        layout.operator(select_uv.OperatorSelectOverlapped.bl_idname,
-                        text="Overlapped")
-        layout.operator(select_uv.OperatorSelectFlipped.bl_idname,
-                        text="Flipped")
-
-
-class MenuAlignUVCursor(bpy.types.Menu):
-    """
-    Menu class: Master menu of Align UV Cursor
-    """
-
-    bl_idname = "uv.muv_align_uv_cursor_menu"
-    bl_label = "Align UV Cursor"
-    bl_description = "Align UV cursor"
-
-    def draw(self, context):
-        layout = self.layout
-        sc = context.scene
-
-        ops = layout.operator(align_uv_cursor.Operator.bl_idname,
-                              text="Left Top")
-        ops.position = 'LEFT_TOP'
-        ops.base = sc.muv_align_uv_cursor_align_method
-
-        ops = layout.operator(align_uv_cursor.Operator.bl_idname,
-                              text="Middle Top")
-        ops.position = 'MIDDLE_TOP'
-        ops.base = sc.muv_align_uv_cursor_align_method
-
-        ops = layout.operator(align_uv_cursor.Operator.bl_idname,
-                              text="Right Top")
-        ops.position = 'RIGHT_TOP'
-        ops.base = sc.muv_align_uv_cursor_align_method
-
-        ops = layout.operator(align_uv_cursor.Operator.bl_idname,
-                              text="Left Middle")
-        ops.position = 'LEFT_MIDDLE'
-        ops.base = sc.muv_align_uv_cursor_align_method
-
-        ops = layout.operator(align_uv_cursor.Operator.bl_idname,
-                              text="Center")
-        ops.position = 'CENTER'
-        ops.base = sc.muv_align_uv_cursor_align_method
-
-        ops = layout.operator(align_uv_cursor.Operator.bl_idname,
-                              text="Right Middle")
-        ops.position = 'RIGHT_MIDDLE'
-        ops.base = sc.muv_align_uv_cursor_align_method
-
-        ops = layout.operator(align_uv_cursor.Operator.bl_idname,
-                              text="Left Bottom")
-        ops.position = 'LEFT_BOTTOM'
-        ops.base = sc.muv_align_uv_cursor_align_method
-
-        ops = layout.operator(align_uv_cursor.Operator.bl_idname,
-                              text="Middle Bottom")
-        ops.position = 'MIDDLE_BOTTOM'
-        ops.base = sc.muv_align_uv_cursor_align_method
-
-        ops = layout.operator(align_uv_cursor.Operator.bl_idname,
-                              text="Right Bottom")
-        ops.position = 'RIGHT_BOTTOM'
-        ops.base = sc.muv_align_uv_cursor_align_method
-
-
-class MenuUVInspection(bpy.types.Menu):
-    """
-    Menu class: Master menu of UV Inspection
-    """
-
-    bl_idname = "uv.muv_uv_inspection_menu"
-    bl_label = "UV Inspection"
-    bl_description = "UV Inspection"
-
-    def draw(self, context):
-        layout = self.layout
-        sc = context.scene
-
-        layout.prop(sc, "muv_uv_inspection_show", text="UV Inspection")
-        layout.operator(uv_inspection.OperatorUpdate.bl_idname,
-                        text="Update")
+        layout.operator(
+            copy_paste_uv_uvedit.MUV_OT_CopyPasteUVUVEdit_CopyUV.bl_idname,
+            text="Copy")
+        layout.operator(
+            copy_paste_uv_uvedit.MUV_OT_CopyPasteUVUVEdit_PasteUV.bl_idname,
+            text="Paste")
diff --git a/uv_magic_uv/ui/VIEW3D_MT_object.py b/uv_magic_uv/ui/VIEW3D_MT_object.py
index c73157cc2cea69fb45cfe6a6420991750bdc131c..318cd82c6ea6506a1176da5413359906ff4e116a 100644
--- a/uv_magic_uv/ui/VIEW3D_MT_object.py
+++ b/uv_magic_uv/ui/VIEW3D_MT_object.py
@@ -24,15 +24,17 @@ __version__ = "5.2"
 __date__ = "17 Nov 2018"
 
 import bpy
-from ..op import copy_paste_uv_object
 
+from ..op import copy_paste_uv_object
+from ..utils.bl_class_registry import BlClassRegistry
 
 __all__ = [
-    'MenuCopyPasteUVObject',
+    'MUV_MT_CopyPasteUV_Object',
 ]
 
 
-class MenuCopyPasteUVObject(bpy.types.Menu):
+@BlClassRegistry()
+class MUV_MT_CopyPasteUV_Object(bpy.types.Menu):
     """
     Menu class: Master menu of Copy/Paste UV coordinate among object
     """
@@ -44,7 +46,9 @@ class MenuCopyPasteUVObject(bpy.types.Menu):
     def draw(self, _):
         layout = self.layout
 
-        layout.menu(copy_paste_uv_object.MenuCopyUV.bl_idname,
-                    text="Copy")
-        layout.menu(copy_paste_uv_object.MenuPasteUV.bl_idname,
-                    text="Paste")
+        layout.menu(
+            copy_paste_uv_object.MUV_MT_CopyPasteUVObject_CopyUV.bl_idname,
+            text="Copy")
+        layout.menu(
+            copy_paste_uv_object.MUV_MT_CopyPasteUVObject_PasteUV.bl_idname,
+            text="Paste")
diff --git a/uv_magic_uv/ui/VIEW3D_MT_uv_map.py b/uv_magic_uv/ui/VIEW3D_MT_uv_map.py
index bb59c12c7dff289c674218eca6be2237cf16d3b4..c5698504450765dd7aa3dcd02729c9862ff11159 100644
--- a/uv_magic_uv/ui/VIEW3D_MT_uv_map.py
+++ b/uv_magic_uv/ui/VIEW3D_MT_uv_map.py
@@ -23,30 +23,24 @@ __status__ = "production"
 __version__ = "5.2"
 __date__ = "17 Nov 2018"
 
-import bpy
-from ..op import copy_paste_uv
-from ..op import transfer_uv
-from ..op import texture_lock
-from ..op import world_scale_uv
-from ..op import uvw
-from ..op import texture_projection
-from ..op import texture_wrap
-from ..op import preserve_uv_aspect
+import bpy.utils
 
+from ..op import (
+    copy_paste_uv,
+    transfer_uv,
+    uvw,
+)
+from ..utils.bl_class_registry import BlClassRegistry
 
 __all__ = [
-    'MenuCopyPasteUV',
-    'MenuTransferUV',
-    'MenuTextureLock',
-    'MenuWorldScaleUV',
-    'MenuTextureWrap',
-    'MenuUVW',
-    'MenuTextureProjection',
-    'MenuPreserveUVAspect',
+    'MUV_MT_CopyPasteUV',
+    'MUV_MT_TransferUV',
+    'MUV_MT_UVW',
 ]
 
 
-class MenuCopyPasteUV(bpy.types.Menu):
+@BlClassRegistry()
+class MUV_MT_CopyPasteUV(bpy.types.Menu):
     """
     Menu class: Master menu of Copy/Paste UV coordinate
     """
@@ -58,20 +52,23 @@ class MenuCopyPasteUV(bpy.types.Menu):
     def draw(self, _):
         layout = self.layout
 
-        layout.label("Default")
-        layout.menu(copy_paste_uv.MenuCopyUV.bl_idname, text="Copy")
-        layout.menu(copy_paste_uv.MenuPasteUV.bl_idname, text="Paste")
+        layout.label(text="Default")
+        layout.menu(copy_paste_uv.MUV_MT_CopyPasteUV_CopyUV.bl_idname,
+                    text="Copy")
+        layout.menu(copy_paste_uv.MUV_MT_CopyPasteUV_PasteUV.bl_idname,
+                    text="Paste")
 
         layout.separator()
 
-        layout.label("Selection Sequence")
-        layout.menu(copy_paste_uv.MenuSelSeqCopyUV.bl_idname,
+        layout.label(text="Selection Sequence")
+        layout.menu(copy_paste_uv.MUV_MT_CopyPasteUV_SelSeqCopyUV.bl_idname,
                     text="Copy")
-        layout.menu(copy_paste_uv.MenuSelSeqPasteUV.bl_idname,
+        layout.menu(copy_paste_uv.MUV_MT_CopyPasteUV_SelSeqPasteUV.bl_idname,
                     text="Paste")
 
 
-class MenuTransferUV(bpy.types.Menu):
+@BlClassRegistry()
+class MUV_MT_TransferUV(bpy.types.Menu):
     """
     Menu class: Master menu of Transfer UV coordinate
     """
@@ -84,99 +81,16 @@ class MenuTransferUV(bpy.types.Menu):
         layout = self.layout
         sc = context.scene
 
-        layout.operator(transfer_uv.OperatorCopyUV.bl_idname, text="Copy")
-        ops = layout.operator(transfer_uv.OperatorPasteUV.bl_idname,
+        layout.operator(transfer_uv.MUV_OT_TransferUV_CopyUV.bl_idname,
+                        text="Copy")
+        ops = layout.operator(transfer_uv.MUV_OT_TransferUV_PasteUV.bl_idname,
                               text="Paste")
         ops.invert_normals = sc.muv_transfer_uv_invert_normals
         ops.copy_seams = sc.muv_transfer_uv_copy_seams
 
 
-class MenuTextureLock(bpy.types.Menu):
-    """
-    Menu class: Master menu of Texture Lock
-    """
-
-    bl_idname = "uv.muv_texture_lock_menu"
-    bl_label = "Texture Lock"
-    bl_description = "Lock texture when vertices of mesh (Preserve UV)"
-
-    def draw(self, context):
-        layout = self.layout
-        sc = context.scene
-
-        layout.label("Normal Mode")
-        layout.operator(texture_lock.OperatorLock.bl_idname,
-                        text="Lock"
-                        if not texture_lock.OperatorLock.is_ready(context)
-                        else "ReLock")
-        ops = layout.operator(texture_lock.OperatorUnlock.bl_idname,
-                              text="Unlock")
-        ops.connect = sc.muv_texture_lock_connect
-
-        layout.separator()
-
-        layout.label("Interactive Mode")
-        layout.prop(sc, "muv_texture_lock_lock", text="Lock")
-
-
-class MenuWorldScaleUV(bpy.types.Menu):
-    """
-    Menu class: Master menu of world scale UV
-    """
-
-    bl_idname = "uv.muv_world_scale_uv_menu"
-    bl_label = "World Scale UV"
-    bl_description = ""
-
-    def draw(self, context):
-        layout = self.layout
-        sc = context.scene
-
-        layout.operator(world_scale_uv.OperatorMeasure.bl_idname,
-                        text="Measure")
-
-        layout.operator(world_scale_uv.OperatorApplyManual.bl_idname,
-                        text="Apply (Manual)")
-
-        ops = layout.operator(
-            world_scale_uv.OperatorApplyScalingDensity.bl_idname,
-            text="Apply (Same Desity)")
-        ops.src_density = sc.muv_world_scale_uv_src_density
-        ops.same_density = True
-
-        ops = layout.operator(
-            world_scale_uv.OperatorApplyScalingDensity.bl_idname,
-            text="Apply (Scaling Desity)")
-        ops.src_density = sc.muv_world_scale_uv_src_density
-        ops.same_density = False
-        ops.tgt_scaling_factor = sc.muv_world_scale_uv_tgt_scaling_factor
-
-        ops = layout.operator(
-            world_scale_uv.OperatorApplyProportionalToMesh.bl_idname,
-            text="Apply (Proportional to Mesh)")
-        ops.src_density = sc.muv_world_scale_uv_src_density
-        ops.src_uv_area = sc.muv_world_scale_uv_src_uv_area
-        ops.src_mesh_area = sc.muv_world_scale_uv_src_mesh_area
-        ops.origin = sc.muv_world_scale_uv_origin
-
-
-class MenuTextureWrap(bpy.types.Menu):
-    """
-    Menu class: Master menu of Texture Wrap
-    """
-
-    bl_idname = "uv.muv_texture_wrap_menu"
-    bl_label = "Texture Wrap"
-    bl_description = ""
-
-    def draw(self, _):
-        layout = self.layout
-
-        layout.operator(texture_wrap.OperatorRefer.bl_idname, text="Refer")
-        layout.operator(texture_wrap.OperatorSet.bl_idname, text="Set")
-
-
-class MenuUVW(bpy.types.Menu):
+@BlClassRegistry()
+class MUV_MT_UVW(bpy.types.Menu):
     """
     Menu class: Master menu of UVW
     """
@@ -189,48 +103,9 @@ class MenuUVW(bpy.types.Menu):
         layout = self.layout
         sc = context.scene
 
-        ops = layout.operator(uvw.OperatorBoxMap.bl_idname, text="Box")
+        ops = layout.operator(uvw.MUV_OT_UVW_BoxMap.bl_idname, text="Box")
         ops.assign_uvmap = sc.muv_uvw_assign_uvmap
 
-        ops = layout.operator(uvw.OperatorBestPlanerMap.bl_idname,
+        ops = layout.operator(uvw.MUV_OT_UVW_BestPlanerMap.bl_idname,
                               text="Best Planner")
         ops.assign_uvmap = sc.muv_uvw_assign_uvmap
-
-
-class MenuTextureProjection(bpy.types.Menu):
-    """
-    Menu class: Master menu of Texture Projection
-    """
-
-    bl_idname = "uv.muv_texture_projection_menu"
-    bl_label = "Texture Projection"
-    bl_description = ""
-
-    def draw(self, context):
-        layout = self.layout
-        sc = context.scene
-
-        layout.prop(sc, "muv_texture_projection_enable",
-                    text="Texture Projection")
-        layout.operator(texture_projection.OperatorProject.bl_idname,
-                        text="Project")
-
-
-class MenuPreserveUVAspect(bpy.types.Menu):
-    """
-    Menu class: Master menu of Preserve UV Aspect
-    """
-
-    bl_idname = "uv.muv_preserve_uv_aspect_menu"
-    bl_label = "Preserve UV Aspect"
-    bl_description = ""
-
-    def draw(self, context):
-        layout = self.layout
-        sc = context.scene
-
-        for key in bpy.data.images.keys():
-            ops = layout.operator(
-                preserve_uv_aspect.Operator.bl_idname, text=key)
-            ops.dest_img_name = key
-            ops.origin = sc.muv_preserve_uv_aspect_origin
diff --git a/uv_magic_uv/ui/__init__.py b/uv_magic_uv/ui/__init__.py
index b377ed2343eb6db5c9678e47456dc722fcf30373..5f7e0c5ef62402521134aa07473cd60e903ef919 100644
--- a/uv_magic_uv/ui/__init__.py
+++ b/uv_magic_uv/ui/__init__.py
@@ -25,26 +25,22 @@ __date__ = "17 Nov 2018"
 
 if "bpy" in locals():
     import importlib
-    importlib.reload(view3d_copy_paste_uv_objectmode)
     importlib.reload(view3d_copy_paste_uv_editmode)
+    importlib.reload(view3d_copy_paste_uv_objectmode)
     importlib.reload(view3d_uv_manipulation)
     importlib.reload(view3d_uv_mapping)
     importlib.reload(uvedit_copy_paste_uv)
-    importlib.reload(uvedit_uv_manipulation)
-    importlib.reload(uvedit_editor_enhancement)
-    importlib.reload(VIEW3D_MT_uv_map)
     importlib.reload(VIEW3D_MT_object)
+    importlib.reload(VIEW3D_MT_uv_map)
     importlib.reload(IMAGE_MT_uvs)
 else:
-    from . import view3d_copy_paste_uv_objectmode
     from . import view3d_copy_paste_uv_editmode
+    from . import view3d_copy_paste_uv_objectmode
     from . import view3d_uv_manipulation
     from . import view3d_uv_mapping
     from . import uvedit_copy_paste_uv
-    from . import uvedit_uv_manipulation
-    from . import uvedit_editor_enhancement
-    from . import VIEW3D_MT_uv_map
     from . import VIEW3D_MT_object
+    from . import VIEW3D_MT_uv_map
     from . import IMAGE_MT_uvs
 
 import bpy
diff --git a/uv_magic_uv/ui/uvedit_copy_paste_uv.py b/uv_magic_uv/ui/uvedit_copy_paste_uv.py
index 271277a086bd695e4438d2f55dafdc1eb31bf6e8..e21a5abd336d6af9c7eb259582a29de1ad89a452 100644
--- a/uv_magic_uv/ui/uvedit_copy_paste_uv.py
+++ b/uv_magic_uv/ui/uvedit_copy_paste_uv.py
@@ -26,20 +26,21 @@ __date__ = "17 Nov 2018"
 import bpy
 
 from ..op import copy_paste_uv_uvedit
-
+from ..utils.bl_class_registry import BlClassRegistry
 
 __all__ = [
-    'PanelCopyPasteUV',
+    'MUV_PT_UVEdit_CopyPasteUV',
 ]
 
 
-class PanelCopyPasteUV(bpy.types.Panel):
+@BlClassRegistry()
+class MUV_PT_UVEdit_CopyPasteUV(bpy.types.Panel):
     """
     Panel class: Copy/Paste UV on Property Panel on UV/ImageEditor
     """
 
     bl_space_type = 'IMAGE_EDITOR'
-    bl_region_type = 'TOOLS'
+    bl_region_type = 'UI'
     bl_label = "Copy/Paste UV"
     bl_category = "Magic UV"
     bl_context = 'mesh_edit'
@@ -47,13 +48,15 @@ class PanelCopyPasteUV(bpy.types.Panel):
 
     def draw_header(self, _):
         layout = self.layout
-        layout.label(text="", icon='IMAGE_COL')
+        layout.label(text="", icon='IMAGE')
 
     def draw(self, _):
         layout = self.layout
 
         row = layout.row(align=True)
-        row.operator(copy_paste_uv_uvedit.OperatorCopyUV.bl_idname,
-                     text="Copy")
-        row.operator(copy_paste_uv_uvedit.OperatorPasteUV.bl_idname,
-                     text="Paste")
+        row.operator(
+            copy_paste_uv_uvedit.MUV_OT_CopyPasteUVUVEdit_CopyUV.bl_idname,
+            text="Copy")
+        row.operator(
+            copy_paste_uv_uvedit.MUV_OT_CopyPasteUVUVEdit_PasteUV.bl_idname,
+            text="Paste")
diff --git a/uv_magic_uv/ui/view3d_copy_paste_uv_editmode.py b/uv_magic_uv/ui/view3d_copy_paste_uv_editmode.py
index c5906ca02b85187f18c83a715e084bd3af4ceecc..14fba24a2c7e80e254726308417ae4e6e30d93f6 100644
--- a/uv_magic_uv/ui/view3d_copy_paste_uv_editmode.py
+++ b/uv_magic_uv/ui/view3d_copy_paste_uv_editmode.py
@@ -25,22 +25,25 @@ __date__ = "17 Nov 2018"
 
 import bpy
 
-from ..op import copy_paste_uv
-from ..op import transfer_uv
-
+from ..op import (
+    copy_paste_uv,
+    transfer_uv,
+)
+from ..utils.bl_class_registry import BlClassRegistry
 
 __all__ = [
-    'PanelCopyPasteUVEditMode',
+    'MUV_PT_CopyPasteUVEditMode',
 ]
 
 
-class PanelCopyPasteUVEditMode(bpy.types.Panel):
+@BlClassRegistry()
+class MUV_PT_CopyPasteUVEditMode(bpy.types.Panel):
     """
     Panel class: Copy/Paste UV on Property Panel on View3D
     """
 
     bl_space_type = 'VIEW_3D'
-    bl_region_type = 'TOOLS'
+    bl_region_type = 'UI'
     bl_label = "Copy/Paste UV"
     bl_category = "Magic UV"
     bl_context = 'mesh_edit'
@@ -48,7 +51,7 @@ class PanelCopyPasteUVEditMode(bpy.types.Panel):
 
     def draw_header(self, _):
         layout = self.layout
-        layout.label(text="", icon='IMAGE_COL')
+        layout.label(text="", icon='IMAGE')
 
     def draw(self, context):
         sc = context.scene
@@ -59,15 +62,17 @@ class PanelCopyPasteUVEditMode(bpy.types.Panel):
         if sc.muv_copy_paste_uv_enabled:
             row = box.row(align=True)
             if sc.muv_copy_paste_uv_mode == 'DEFAULT':
-                row.menu(copy_paste_uv.MenuCopyUV.bl_idname,
+                row.menu(copy_paste_uv.MUV_MT_CopyPasteUV_CopyUV.bl_idname,
                          text="Copy")
-                row.menu(copy_paste_uv.MenuPasteUV.bl_idname,
+                row.menu(copy_paste_uv.MUV_MT_CopyPasteUV_PasteUV.bl_idname,
                          text="Paste")
             elif sc.muv_copy_paste_uv_mode == 'SEL_SEQ':
-                row.menu(copy_paste_uv.MenuSelSeqCopyUV.bl_idname,
-                         text="Copy")
-                row.menu(copy_paste_uv.MenuSelSeqPasteUV.bl_idname,
-                         text="Paste")
+                row.menu(
+                    copy_paste_uv.MUV_MT_CopyPasteUV_SelSeqCopyUV.bl_idname,
+                    text="Copy")
+                row.menu(
+                    copy_paste_uv.MUV_MT_CopyPasteUV_SelSeqPasteUV.bl_idname,
+                    text="Paste")
             box.prop(sc, "muv_copy_paste_uv_mode", expand=True)
             box.prop(sc, "muv_copy_paste_uv_copy_seams", text="Seams")
             box.prop(sc, "muv_copy_paste_uv_strategy", text="Strategy")
@@ -76,8 +81,9 @@ class PanelCopyPasteUVEditMode(bpy.types.Panel):
         box.prop(sc, "muv_transfer_uv_enabled", text="Transfer UV")
         if sc.muv_transfer_uv_enabled:
             row = box.row(align=True)
-            row.operator(transfer_uv.OperatorCopyUV.bl_idname, text="Copy")
-            ops = row.operator(transfer_uv.OperatorPasteUV.bl_idname,
+            row.operator(transfer_uv.MUV_OT_TransferUV_CopyUV.bl_idname,
+                         text="Copy")
+            ops = row.operator(transfer_uv.MUV_OT_TransferUV_PasteUV.bl_idname,
                                text="Paste")
             ops.invert_normals = sc.muv_transfer_uv_invert_normals
             ops.copy_seams = sc.muv_transfer_uv_copy_seams
diff --git a/uv_magic_uv/ui/view3d_copy_paste_uv_objectmode.py b/uv_magic_uv/ui/view3d_copy_paste_uv_objectmode.py
index a9203d87ee989eafc7768377ee22d4536890d6d8..6dd0d3b43584e8bd479da565dbe9b6de34381e95 100644
--- a/uv_magic_uv/ui/view3d_copy_paste_uv_objectmode.py
+++ b/uv_magic_uv/ui/view3d_copy_paste_uv_objectmode.py
@@ -26,20 +26,21 @@ __date__ = "17 Nov 2018"
 import bpy
 
 from ..op import copy_paste_uv_object
-
+from ..utils.bl_class_registry import BlClassRegistry
 
 __all__ = [
-    'PanelCopyPasteUVObjectMode',
+    'MUV_PT_View3D_Object_CopyPasteUV',
 ]
 
 
-class PanelCopyPasteUVObjectMode(bpy.types.Panel):
+@BlClassRegistry()
+class MUV_PT_View3D_Object_CopyPasteUV(bpy.types.Panel):
     """
     Panel class: Copy/Paste UV on Property Panel on View3D
     """
 
     bl_space_type = 'VIEW_3D'
-    bl_region_type = 'TOOLS'
+    bl_region_type = 'UI'
     bl_label = "Copy/Paste UV"
     bl_category = "Magic UV"
     bl_context = 'objectmode'
@@ -47,16 +48,18 @@ class PanelCopyPasteUVObjectMode(bpy.types.Panel):
 
     def draw_header(self, _):
         layout = self.layout
-        layout.label(text="", icon='IMAGE_COL')
+        layout.label(text="", icon='IMAGE')
 
     def draw(self, context):
         sc = context.scene
         layout = self.layout
 
         row = layout.row(align=True)
-        row.menu(copy_paste_uv_object.MenuCopyUV.bl_idname,
-                 text="Copy")
-        row.menu(copy_paste_uv_object.MenuPasteUV.bl_idname,
-                 text="Paste")
+        row.menu(
+            copy_paste_uv_object.MUV_MT_CopyPasteUVObject_CopyUV.bl_idname,
+            text="Copy")
+        row.menu(
+            copy_paste_uv_object.MUV_MT_CopyPasteUVObject_PasteUV.bl_idname,
+            text="Paste")
         layout.prop(sc, "muv_copy_paste_uv_object_copy_seams",
                     text="Seams")
diff --git a/uv_magic_uv/ui/view3d_uv_manipulation.py b/uv_magic_uv/ui/view3d_uv_manipulation.py
index be0bcf575bdf334ede14eaf368ede9563054dda0..365a0dc8a795a15755ec868655bfbd45e287eef9 100644
--- a/uv_magic_uv/ui/view3d_uv_manipulation.py
+++ b/uv_magic_uv/ui/view3d_uv_manipulation.py
@@ -25,28 +25,26 @@ __date__ = "17 Nov 2018"
 
 import bpy
 
-from ..op import flip_rotate_uv
-from ..op import mirror_uv
-from ..op import move_uv
-from ..op import preserve_uv_aspect
-from ..op import texture_lock
-from ..op import texture_wrap
-from ..op import uv_sculpt
-from ..op import world_scale_uv
-
+from ..op import (
+    flip_rotate_uv,
+    mirror_uv,
+    move_uv,
+)
+from ..utils.bl_class_registry import BlClassRegistry
 
 __all__ = [
-    'PanelUVManipulation',
+    'MUV_PT_View3D_UVManipulation',
 ]
 
 
-class PanelUVManipulation(bpy.types.Panel):
+@BlClassRegistry()
+class MUV_PT_View3D_UVManipulation(bpy.types.Panel):
     """
     Panel class: UV Manipulation on Property Panel on View3D
     """
 
     bl_space_type = 'VIEW_3D'
-    bl_region_type = 'TOOLS'
+    bl_region_type = 'UI'
     bl_label = "UV Manipulation"
     bl_category = "Magic UV"
     bl_context = 'mesh_edit'
@@ -54,7 +52,7 @@ class PanelUVManipulation(bpy.types.Panel):
 
     def draw_header(self, _):
         layout = self.layout
-        layout.label(text="", icon='IMAGE_COL')
+        layout.label(text="", icon='IMAGE')
 
     def draw(self, context):
         sc = context.scene
@@ -64,7 +62,7 @@ class PanelUVManipulation(bpy.types.Panel):
         box.prop(sc, "muv_flip_rotate_uv_enabled", text="Flip/Rotate UV")
         if sc.muv_flip_rotate_uv_enabled:
             row = box.row()
-            ops = row.operator(flip_rotate_uv.Operator.bl_idname,
+            ops = row.operator(flip_rotate_uv.MUV_OT_FlipRotate.bl_idname,
                                text="Flip/Rotate")
             ops.seams = sc.muv_flip_rotate_uv_seams
             row.prop(sc, "muv_flip_rotate_uv_seams", text="Seams")
@@ -73,7 +71,8 @@ class PanelUVManipulation(bpy.types.Panel):
         box.prop(sc, "muv_mirror_uv_enabled", text="Mirror UV")
         if sc.muv_mirror_uv_enabled:
             row = box.row()
-            ops = row.operator(mirror_uv.Operator.bl_idname, text="Mirror")
+            ops = row.operator(mirror_uv.MUV_OT_MirrorUV.bl_idname,
+                               text="Mirror")
             ops.axis = sc.muv_mirror_uv_axis
             row.prop(sc, "muv_mirror_uv_axis", text="")
 
@@ -81,191 +80,9 @@ class PanelUVManipulation(bpy.types.Panel):
         box.prop(sc, "muv_move_uv_enabled", text="Move UV")
         if sc.muv_move_uv_enabled:
             col = box.column()
-            if not move_uv.Operator.is_running(context):
-                col.operator(move_uv.Operator.bl_idname, icon='PLAY',
+            if not move_uv.MUV_OT_MoveUV.is_running(context):
+                col.operator(move_uv.MUV_OT_MoveUV.bl_idname, icon='PLAY',
                              text="Start")
             else:
-                col.operator(move_uv.Operator.bl_idname, icon='PAUSE',
+                col.operator(move_uv.MUV_OT_MoveUV.bl_idname, icon='PAUSE',
                              text="Stop")
-
-        box = layout.box()
-        box.prop(sc, "muv_world_scale_uv_enabled", text="World Scale UV")
-        if sc.muv_world_scale_uv_enabled:
-            box.prop(sc, "muv_world_scale_uv_mode", text="")
-
-            if sc.muv_world_scale_uv_mode == 'MANUAL':
-                sp = box.split(percentage=0.5)
-                col = sp.column()
-                col.prop(sc, "muv_world_scale_uv_tgt_texture_size",
-                         text="Texture Size")
-                sp = sp.split(percentage=1.0)
-                col = sp.column()
-                col.label("Density:")
-                col.prop(sc, "muv_world_scale_uv_tgt_density", text="")
-                box.prop(sc, "muv_world_scale_uv_origin", text="Origin")
-                ops = box.operator(
-                    world_scale_uv.OperatorApplyManual.bl_idname, text="Apply")
-                ops.tgt_density = sc.muv_world_scale_uv_tgt_density
-                ops.tgt_texture_size = sc.muv_world_scale_uv_tgt_texture_size
-                ops.origin = sc.muv_world_scale_uv_origin
-                ops.show_dialog = False
-
-            elif sc.muv_world_scale_uv_mode == 'SAME_DENSITY':
-                sp = box.split(percentage=0.4)
-                col = sp.column(align=True)
-                col.label("Source:")
-                sp = sp.split(percentage=1.0)
-                col = sp.column(align=True)
-                col.operator(world_scale_uv.OperatorMeasure.bl_idname,
-                             text="Measure")
-
-                sp = box.split(percentage=0.7)
-                col = sp.column(align=True)
-                col.prop(sc, "muv_world_scale_uv_src_density", text="Density")
-                col.enabled = False
-                sp = sp.split(percentage=1.0)
-                col = sp.column(align=True)
-                col.label("px2/cm2")
-
-                box.separator()
-                box.prop(sc, "muv_world_scale_uv_origin", text="Origin")
-                ops = box.operator(
-                    world_scale_uv.OperatorApplyScalingDensity.bl_idname,
-                    text="Apply")
-                ops.src_density = sc.muv_world_scale_uv_src_density
-                ops.origin = sc.muv_world_scale_uv_origin
-                ops.same_density = True
-                ops.show_dialog = False
-
-            elif sc.muv_world_scale_uv_mode == 'SCALING_DENSITY':
-                sp = box.split(percentage=0.4)
-                col = sp.column(align=True)
-                col.label("Source:")
-                sp = sp.split(percentage=1.0)
-                col = sp.column(align=True)
-                col.operator(world_scale_uv.OperatorMeasure.bl_idname,
-                             text="Measure")
-
-                sp = box.split(percentage=0.7)
-                col = sp.column(align=True)
-                col.prop(sc, "muv_world_scale_uv_src_density", text="Density")
-                col.enabled = False
-                sp = sp.split(percentage=1.0)
-                col = sp.column(align=True)
-                col.label("px2/cm2")
-
-                box.separator()
-                box.prop(sc, "muv_world_scale_uv_tgt_scaling_factor",
-                         text="Scaling Factor")
-                box.prop(sc, "muv_world_scale_uv_origin", text="Origin")
-                ops = box.operator(
-                    world_scale_uv.OperatorApplyScalingDensity.bl_idname,
-                    text="Apply")
-                ops.src_density = sc.muv_world_scale_uv_src_density
-                ops.origin = sc.muv_world_scale_uv_origin
-                ops.same_density = False
-                ops.show_dialog = False
-                ops.tgt_scaling_factor = \
-                    sc.muv_world_scale_uv_tgt_scaling_factor
-
-            elif sc.muv_world_scale_uv_mode == 'PROPORTIONAL_TO_MESH':
-                sp = box.split(percentage=0.4)
-                col = sp.column(align=True)
-                col.label("Source:")
-                sp = sp.split(percentage=1.0)
-                col = sp.column(align=True)
-                col.operator(world_scale_uv.OperatorMeasure.bl_idname,
-                             text="Measure")
-
-                sp = box.split(percentage=0.7)
-                col = sp.column(align=True)
-                col.prop(sc, "muv_world_scale_uv_src_mesh_area",
-                         text="Mesh Area")
-                col.prop(sc, "muv_world_scale_uv_src_uv_area", text="UV Area")
-                col.prop(sc, "muv_world_scale_uv_src_density", text="Density")
-                col.enabled = False
-                sp = sp.split(percentage=1.0)
-                col = sp.column(align=True)
-                col.label("cm2")
-                col.label("px2")
-                col.label("px2/cm2")
-                col.enabled = False
-
-                box.separator()
-                box.prop(sc, "muv_world_scale_uv_origin", text="Origin")
-                ops = box.operator(
-                    world_scale_uv.OperatorApplyProportionalToMesh.bl_idname,
-                    text="Apply")
-                ops.src_density = sc.muv_world_scale_uv_src_density
-                ops.src_uv_area = sc.muv_world_scale_uv_src_uv_area
-                ops.src_mesh_area = sc.muv_world_scale_uv_src_mesh_area
-                ops.origin = sc.muv_world_scale_uv_origin
-                ops.show_dialog = False
-
-        box = layout.box()
-        box.prop(sc, "muv_preserve_uv_aspect_enabled",
-                 text="Preserve UV Aspect")
-        if sc.muv_preserve_uv_aspect_enabled:
-            row = box.row()
-            ops = row.operator(
-                preserve_uv_aspect.Operator.bl_idname,
-                text="Change Image")
-            ops.dest_img_name = sc.muv_preserve_uv_aspect_tex_image
-            ops.origin = sc.muv_preserve_uv_aspect_origin
-            row.prop(sc, "muv_preserve_uv_aspect_tex_image", text="")
-            box.prop(sc, "muv_preserve_uv_aspect_origin", text="Origin")
-
-        box = layout.box()
-        box.prop(sc, "muv_texture_lock_enabled", text="Texture Lock")
-        if sc.muv_texture_lock_enabled:
-            row = box.row(align=True)
-            col = row.column(align=True)
-            col.label("Normal Mode:")
-            col = row.column(align=True)
-            col.operator(texture_lock.OperatorLock.bl_idname,
-                         text="Lock"
-                         if not texture_lock.OperatorLock.is_ready(context)
-                         else "ReLock")
-            ops = col.operator(texture_lock.OperatorUnlock.bl_idname,
-                               text="Unlock")
-            ops.connect = sc.muv_texture_lock_connect
-            col.prop(sc, "muv_texture_lock_connect", text="Connect")
-
-            row = box.row(align=True)
-            row.label("Interactive Mode:")
-            box.prop(sc, "muv_texture_lock_lock",
-                     text="Unlock"
-                     if texture_lock.OperatorIntr.is_running(context)
-                     else "Lock",
-                     icon='RESTRICT_VIEW_OFF'
-                     if texture_lock.OperatorIntr.is_running(context)
-                     else 'RESTRICT_VIEW_ON')
-
-        box = layout.box()
-        box.prop(sc, "muv_texture_wrap_enabled", text="Texture Wrap")
-        if sc.muv_texture_wrap_enabled:
-            row = box.row(align=True)
-            row.operator(texture_wrap.OperatorRefer.bl_idname, text="Refer")
-            row.operator(texture_wrap.OperatorSet.bl_idname, text="Set")
-            box.prop(sc, "muv_texture_wrap_set_and_refer")
-            box.prop(sc, "muv_texture_wrap_selseq")
-
-        box = layout.box()
-        box.prop(sc, "muv_uv_sculpt_enabled", text="UV Sculpt")
-        if sc.muv_uv_sculpt_enabled:
-            box.prop(sc, "muv_uv_sculpt_enable",
-                     text="Disable"if uv_sculpt.Operator.is_running(context)
-                     else "Enable",
-                     icon='RESTRICT_VIEW_OFF'
-                     if uv_sculpt.Operator.is_running(context)
-                     else 'RESTRICT_VIEW_ON')
-            col = box.column()
-            col.label("Brush:")
-            col.prop(sc, "muv_uv_sculpt_radius")
-            col.prop(sc, "muv_uv_sculpt_strength")
-            box.prop(sc, "muv_uv_sculpt_tools")
-            if sc.muv_uv_sculpt_tools == 'PINCH':
-                box.prop(sc, "muv_uv_sculpt_pinch_invert")
-            elif sc.muv_uv_sculpt_tools == 'RELAX':
-                box.prop(sc, "muv_uv_sculpt_relax_method")
-            box.prop(sc, "muv_uv_sculpt_show_brush")
diff --git a/uv_magic_uv/ui/view3d_uv_mapping.py b/uv_magic_uv/ui/view3d_uv_mapping.py
index 2aa62c26d37ddcffd01c347de7f751a20e92031d..c596008ebb8534c5429000e2eed9389953ad5925 100644
--- a/uv_magic_uv/ui/view3d_uv_mapping.py
+++ b/uv_magic_uv/ui/view3d_uv_mapping.py
@@ -25,23 +25,24 @@ __date__ = "17 Nov 2018"
 
 import bpy
 
-from ..op import texture_projection
-from ..op import unwrap_constraint
-from ..op import uvw
-
+from ..op import (
+    uvw,
+)
+from ..utils.bl_class_registry import BlClassRegistry
 
 __all__ = [
-    'UVMapping',
+    'MUV_PT_View3D_UVMapping',
 ]
 
 
-class UVMapping(bpy.types.Panel):
+@BlClassRegistry()
+class MUV_PT_View3D_UVMapping(bpy.types.Panel):
     """
     Panel class: UV Mapping on Property Panel on View3D
     """
 
     bl_space_type = 'VIEW_3D'
-    bl_region_type = 'TOOLS'
+    bl_region_type = 'UI'
     bl_label = "UV Mapping"
     bl_category = "Magic UV"
     bl_context = 'mesh_edit'
@@ -49,60 +50,19 @@ class UVMapping(bpy.types.Panel):
 
     def draw_header(self, _):
         layout = self.layout
-        layout.label(text="", icon='IMAGE_COL')
+        layout.label(text="", icon='IMAGE')
 
     def draw(self, context):
         sc = context.scene
         layout = self.layout
 
-        box = layout.box()
-        box.prop(sc, "muv_unwrap_constraint_enabled", text="Unwrap Constraint")
-        if sc.muv_unwrap_constraint_enabled:
-            ops = box.operator(
-                unwrap_constraint.Operator.bl_idname,
-                text="Unwrap")
-            ops.u_const = sc.muv_unwrap_constraint_u_const
-            ops.v_const = sc.muv_unwrap_constraint_v_const
-            row = box.row(align=True)
-            row.prop(sc, "muv_unwrap_constraint_u_const", text="U-Constraint")
-            row.prop(sc, "muv_unwrap_constraint_v_const", text="V-Constraint")
-
-        box = layout.box()
-        box.prop(sc, "muv_texture_projection_enabled",
-                 text="Texture Projection")
-        if sc.muv_texture_projection_enabled:
-            row = box.row()
-            row.prop(sc, "muv_texture_projection_enable",
-                     text="Disable"
-                     if texture_projection.Operator.is_running(context)
-                     else "Enable",
-                     icon='RESTRICT_VIEW_OFF'
-                     if texture_projection.Operator.is_running(context)
-                     else 'RESTRICT_VIEW_ON')
-            row.prop(sc, "muv_texture_projection_tex_image", text="")
-            box.prop(sc, "muv_texture_projection_tex_transparency",
-                     text="Transparency")
-            col = box.column(align=True)
-            row = col.row()
-            row.prop(sc, "muv_texture_projection_adjust_window",
-                     text="Adjust Window")
-            if not sc.muv_texture_projection_adjust_window:
-                row.prop(sc, "muv_texture_projection_tex_magnitude",
-                         text="Magnitude")
-            col.prop(sc, "muv_texture_projection_apply_tex_aspect",
-                     text="Texture Aspect Ratio")
-            col.prop(sc, "muv_texture_projection_assign_uvmap",
-                     text="Assign UVMap")
-            box.operator(texture_projection.OperatorProject.bl_idname,
-                         text="Project")
-
         box = layout.box()
         box.prop(sc, "muv_uvw_enabled", text="UVW")
         if sc.muv_uvw_enabled:
             row = box.row(align=True)
-            ops = row.operator(uvw.OperatorBoxMap.bl_idname, text="Box")
+            ops = row.operator(uvw.MUV_OT_UVW_BoxMap.bl_idname, text="Box")
             ops.assign_uvmap = sc.muv_uvw_assign_uvmap
-            ops = row.operator(uvw.OperatorBestPlanerMap.bl_idname,
+            ops = row.operator(uvw.MUV_OT_UVW_BestPlanerMap.bl_idname,
                                text="Best Planner")
             ops.assign_uvmap = sc.muv_uvw_assign_uvmap
             box.prop(sc, "muv_uvw_assign_uvmap", text="Assign UVMap")
diff --git a/uv_magic_uv/utils/__init__.py b/uv_magic_uv/utils/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..4ce9d9079326be15d662765d96a9c88d1350beca
--- /dev/null
+++ b/uv_magic_uv/utils/__init__.py
@@ -0,0 +1,34 @@
+# <pep8-80 compliant>
+
+# ##### 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 #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+if "bpy" in locals():
+    import importlib
+    importlib.reload(bl_class_registry)
+    importlib.reload(property_class_registry)
+else:
+    from . import bl_class_registry
+    from . import property_class_registry
+
+import bpy
diff --git a/uv_magic_uv/utils/bl_class_registry.py b/uv_magic_uv/utils/bl_class_registry.py
new file mode 100644
index 0000000000000000000000000000000000000000..d173061566fd0369e68c57fec302d4f542325fe7
--- /dev/null
+++ b/uv_magic_uv/utils/bl_class_registry.py
@@ -0,0 +1,84 @@
+# <pep8-80 compliant>
+
+# ##### 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 #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+import bpy
+
+from .. import common
+
+__all__ = [
+    'BlClassRegistry',
+]
+
+
+class BlClassRegistry:
+    class_list = []
+
+    def __init__(self, *_, **kwargs):
+        self.legacy = kwargs.get('legacy', False)
+
+    def __call__(self, cls):
+        if hasattr(cls, "bl_idname"):
+            BlClassRegistry.add_class(cls.bl_idname, cls, self.legacy)
+        else:
+            bl_idname = "{}{}{}{}".format(cls.bl_space_type,
+                                          cls.bl_region_type,
+                                          cls.bl_context, cls.bl_label)
+            BlClassRegistry.add_class(bl_idname, cls, self.legacy)
+        return cls
+
+    @classmethod
+    def add_class(cls, bl_idname, op_class, legacy):
+        for class_ in cls.class_list:
+            if (class_["bl_idname"] == bl_idname) and \
+               (class_["legacy"] == legacy):
+                raise RuntimeError("{} is already registered"
+                                   .format(bl_idname))
+
+        new_op = {
+            "bl_idname": bl_idname,
+            "class": op_class,
+            "legacy": legacy,
+        }
+        cls.class_list.append(new_op)
+        common.debug_print("{} is registered.".format(bl_idname))
+
+    @classmethod
+    def register(cls):
+        for class_ in cls.class_list:
+            bpy.utils.register_class(class_["class"])
+            common.debug_print("{} is registered to Blender."
+                               .format(class_["bl_idname"]))
+
+    @classmethod
+    def unregister(cls):
+        for class_ in cls.class_list:
+            bpy.utils.unregister_class(class_["class"])
+            common.debug_print("{} is unregistered from Blender."
+                               .format(class_["bl_idname"]))
+
+    @classmethod
+    def cleanup(cls):
+        cls.class_list = []
+        common.debug_print("Cleanup registry.")
diff --git a/uv_magic_uv/utils/property_class_registry.py b/uv_magic_uv/utils/property_class_registry.py
new file mode 100644
index 0000000000000000000000000000000000000000..20df03422af86198fba12c33d3d493b0a9fabd66
--- /dev/null
+++ b/uv_magic_uv/utils/property_class_registry.py
@@ -0,0 +1,72 @@
+# <pep8-80 compliant>
+
+# ##### 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 #####
+
+__author__ = "Nutti <nutti.metro@gmail.com>"
+__status__ = "production"
+__version__ = "5.2"
+__date__ = "17 Nov 2018"
+
+from .. import common
+
+__all__ = [
+    'PropertyClassRegistry',
+]
+
+
+class PropertyClassRegistry:
+    class_list = []
+
+    def __init__(self, *_, **kwargs):
+        self.legacy = kwargs.get('legacy', False)
+
+    def __call__(self, cls):
+        PropertyClassRegistry.add_class(cls.idname, cls, self.legacy)
+        return cls
+
+    @classmethod
+    def add_class(cls, idname, prop_class, legacy):
+        for class_ in cls.class_list:
+            if (class_["idname"] == idname) and (class_["legacy"] == legacy):
+                raise RuntimeError("{} is already registered".format(idname))
+
+        new_op = {
+            "idname": idname,
+            "class": prop_class,
+            "legacy": legacy,
+        }
+        cls.class_list.append(new_op)
+        common.debug_print("{} is registered.".format(idname))
+
+    @classmethod
+    def init_props(cls, scene):
+        for class_ in cls.class_list:
+            class_["class"].init_props(scene)
+            common.debug_print("{} is initialized.".format(class_["idname"]))
+
+    @classmethod
+    def del_props(cls, scene):
+        for class_ in cls.class_list:
+            class_["class"].del_props(scene)
+            common.debug_print("{} is cleared.".format(class_["idname"]))
+
+    @classmethod
+    def cleanup(cls):
+        cls.class_list = []
+        common.debug_print("Cleanup registry.")