Camera tracking integration
authorSergey Sharybin <sergey.vfx@gmail.com>
Fri, 22 Jul 2011 16:18:51 +0000 (16:18 +0000)
committerSergey Sharybin <sergey.vfx@gmail.com>
Fri, 22 Jul 2011 16:18:51 +0000 (16:18 +0000)
===========================

- Operator "Join Selected Tracks".
  This operator joins all selected tracks to active track.
  Hotkey is Ctrl-J.
- Operator "Select Grouped".
  Now it's easy to select all locked/keyframes/etc markers.
  Hotkey is Shit-G.
- Operator to jump to beginning/end of active path.
  Hotkeys are Shift+Ctrl+Arrow Left/Right.
- Operator to copy color from active track to all tracks.

release/scripts/startup/bl_ui/space_clip.py
source/blender/blenkernel/BKE_tracking.h
source/blender/blenkernel/intern/tracking.c
source/blender/editors/space_clip/clip_intern.h
source/blender/editors/space_clip/space_clip.c
source/blender/editors/space_clip/tracking_ops.c

index dfea34071ec1b96b2984e5f75c2d348bc3ad6c64..e2195847f469d5b68babbcaee65cc1a4ee6d4308 100644 (file)
@@ -161,6 +161,7 @@ class CLIP_PT_tools(bpy.types.Panel):
             op = col.operator("clip.clear_track_path", text="Clear Track Path")
             op.action = 'ALL'
 
+            layout.operator("clip.join_tracks")
             layout.operator("clip.detect_features")
             layout.operator("clip.apply_follow_track")
 
@@ -217,8 +218,10 @@ class CLIP_PT_track(bpy.types.Panel):
         row = layout.row(align=True)
         label = bpy.types.CLIP_MT_track_color_presets.bl_label
         row.menu('CLIP_MT_track_color_presets', text=label)
+        row.menu('CLIP_MT_track_color_specials', text="", icon="DOWNARROW_HLT")
         row.operator("clip.track_color_preset_add", text="", icon="ZOOMIN")
-        op = row.operator("clip.track_color_preset_add", text="", icon="ZOOMOUT")
+        op = row.operator("clip.track_color_preset_add", \
+            text="", icon="ZOOMOUT")
         op.remove_active = True
 
         row = layout.row()
@@ -234,7 +237,6 @@ class CLIP_PT_track(bpy.types.Panel):
         row.prop(act_track, "use_blue_channel", text="Blue")
 
 
-
 class CLIP_PT_track_settings(bpy.types.Panel):
     bl_space_type = 'CLIP_EDITOR'
     bl_region_type = 'UI'
@@ -497,12 +499,40 @@ class CLIP_MT_select(bpy.types.Menu):
 
         sc = context.space_data
 
+        layout.menu("CLIP_MT_select_grouped")
         layout.operator("clip.select_border")
         layout.operator("clip.select_circle")
         layout.operator("clip.select_all", text="Select/Deselect all")
         layout.operator("clip.select_all", text="Inverse").action = 'INVERT'
 
 
+class CLIP_MT_select_grouped(bpy.types.Menu):
+    bl_label = "Select Grouped"
+
+    def draw(self, context):
+        layout = self.layout
+
+        sc = context.space_data
+
+        op = layout.operator("clip.select_grouped", text="Select Keyframed")
+        op.group = 'KEYFRAMED'
+
+        op = layout.operator("clip.select_grouped", text="Select Estimated")
+        op.group = 'ESTIMATED'
+
+        op = layout.operator("clip.select_grouped", text="Select Tracked")
+        op.group = 'TRACKED'
+
+        op = layout.operator("clip.select_grouped", text="Select Locked")
+        op.group = 'LOCKED'
+
+        op = layout.operator("clip.select_grouped", text="Select Disabled")
+        op.group = 'DISABLED'
+
+        op = layout.operator("clip.select_grouped", text="Select by Color")
+        op.group = 'COLOR'
+
+
 class CLIP_MT_tracking_specials(bpy.types.Menu):
     bl_label = "Specials"
 
@@ -548,5 +578,14 @@ class CLIP_MT_track_color_presets(bpy.types.Menu):
     draw = bpy.types.Menu.draw_preset
 
 
+class CLIP_MT_track_color_specials(bpy.types.Menu):
+    bl_label = "Track Color Specials"
+
+    def draw(self, context):
+        layout = self.layout
+
+        layout.operator('clip.track_copy_color', icon='COPY_ID')
+
+
 if __name__ == "__main__":  # only for live edit.
     bpy.utils.register_module(__name__)
index 75cb457734ccb1a046a482e1e2fc534357751b7e..2956e10025141a0820a10c364f04ac9339d7c763 100644 (file)
@@ -56,6 +56,8 @@ int BKE_tracking_has_marker(struct MovieTrackingTrack *track, int framenr);
 void BKE_tracking_free_track(struct MovieTrackingTrack *track);
 struct MovieTrackingTrack *BKE_tracking_copy_track(struct MovieTrackingTrack *track);
 void BKE_tracking_clear_path(struct MovieTrackingTrack *track, int ref_frame, int action);
+int BKE_tracking_test_join_tracks(struct MovieTrackingTrack *dst_track, struct MovieTrackingTrack *src_track);
+void BKE_tracking_join_tracks(struct MovieTrackingTrack *dst_track, struct MovieTrackingTrack *src_track);
 void BKE_tracking_free(struct MovieTracking *tracking);
 
 struct ImBuf *BKE_tracking_acquire_pattern_imbuf(struct ImBuf *ibuf, struct MovieTrackingTrack *track,
index 05809121a722b754f147fa9a64c3b9299ea8f5d4..54af98b92a2e5258d6a05a37c006a282d1fd9d61 100644 (file)
@@ -375,6 +375,51 @@ void BKE_tracking_clear_path(MovieTrackingTrack *track, int ref_frame, int actio
        }
 }
 
+int BKE_tracking_test_join_tracks(MovieTrackingTrack *dst_track, MovieTrackingTrack *src_track)
+{
+       int i, a= 0, b= 0, tot= dst_track->markersnr+src_track->markersnr;
+
+       for(i= 0; i<tot; i++) {
+               if(b>=dst_track->markersnr || a>=src_track->markersnr)
+                       break;
+
+               if(src_track->markers[a].framenr<dst_track->markers[b].framenr)
+                       a++;
+               else if(src_track->markers[a].framenr>dst_track->markers[b].framenr)
+                       b++;
+               else
+                       return 0;
+       }
+
+       return 1;
+}
+
+void BKE_tracking_join_tracks(MovieTrackingTrack *dst_track, MovieTrackingTrack *src_track)
+{
+       int i, a= 0, b= 0, tot= dst_track->markersnr+src_track->markersnr;
+       MovieTrackingMarker *markers;
+
+       markers= MEM_callocN(tot*sizeof(MovieTrackingMarker), "tracking joined tracks");
+
+       for(i= 0; i<tot; i++) {
+               if(b>=dst_track->markersnr) {
+                       markers[i]= src_track->markers[a++];
+               }
+               else if(a>=src_track->markersnr) {
+                       markers[i]= dst_track->markers[b++];
+               }
+               else if(src_track->markers[a].framenr<dst_track->markers[b].framenr)
+                       markers[i]= src_track->markers[a++];
+               else
+                       markers[i]= dst_track->markers[b++];
+       }
+
+       MEM_freeN(dst_track->markers);
+
+       dst_track->markers= markers;
+       dst_track->markersnr= tot;
+}
+
 void BKE_tracking_free(MovieTracking *tracking)
 {
        MovieTrackingTrack *track;
index 3626d6cbe6117159ffa968db20a3b2a44dec04dd..165587526e5797907c054b4ffb1322b742064bab 100644 (file)
@@ -62,6 +62,7 @@ void CLIP_OT_select(struct wmOperatorType *ot);
 void CLIP_OT_select_all(struct wmOperatorType *ot);
 void CLIP_OT_select_border(struct wmOperatorType *ot);
 void CLIP_OT_select_circle(struct wmOperatorType *ot);
+void CLIP_OT_select_grouped(struct wmOperatorType *ot);
 
 void CLIP_OT_add_marker(struct wmOperatorType *ot);
 void CLIP_OT_delete_track(struct wmOperatorType *ot);
@@ -72,6 +73,7 @@ void CLIP_OT_solve_camera(struct wmOperatorType *ot);
 void CLIP_OT_clear_reconstruction(struct wmOperatorType *ot);
 
 void CLIP_OT_clear_track_path(struct wmOperatorType *ot);
+void CLIP_OT_join_tracks(struct wmOperatorType *ot);
 
 void CLIP_OT_disable_markers(struct wmOperatorType *ot);
 void CLIP_OT_hide_tracks(struct wmOperatorType *ot);
@@ -87,6 +89,9 @@ void CLIP_OT_set_center_principal(struct wmOperatorType *ot);
 
 void CLIP_OT_slide_marker(struct wmOperatorType *ot);
 
+void CLIP_OT_frame_jump(struct wmOperatorType *ot);
+void CLIP_OT_track_copy_color(struct wmOperatorType *ot);
+
 void CLIP_OT_detect_features(struct wmOperatorType *ot);
 
 /* clip_draw.c */
index 771008b0a01c4fb5dadf53952443458c0623a132..1e066102cdaaa01e2a423e4c97a95e1abd5a8199 100644 (file)
@@ -230,6 +230,7 @@ static void clip_operatortypes(void)
        WM_operatortype_append(CLIP_OT_select_all);
        WM_operatortype_append(CLIP_OT_select_border);
        WM_operatortype_append(CLIP_OT_select_circle);
+       WM_operatortype_append(CLIP_OT_select_grouped);
 
        WM_operatortype_append(CLIP_OT_add_marker);
        WM_operatortype_append(CLIP_OT_delete_track);
@@ -252,9 +253,13 @@ static void clip_operatortypes(void)
        WM_operatortype_append(CLIP_OT_set_center_principal);
 
        WM_operatortype_append(CLIP_OT_clear_track_path);
+       WM_operatortype_append(CLIP_OT_join_tracks);
+       WM_operatortype_append(CLIP_OT_track_copy_color);
 
        WM_operatortype_append(CLIP_OT_slide_marker);
 
+       WM_operatortype_append(CLIP_OT_frame_jump);
+
        WM_operatortype_append(CLIP_OT_detect_features);
 }
 
@@ -301,6 +306,7 @@ static void clip_keymap(struct wmKeyConfig *keyconf)
        RNA_enum_set(WM_keymap_add_item(keymap, "CLIP_OT_select_all", IKEY, KM_PRESS, KM_CTRL, 0)->ptr, "action", SEL_INVERT);
        WM_keymap_add_item(keymap, "CLIP_OT_select_border", BKEY, KM_PRESS, 0, 0);
        WM_keymap_add_item(keymap, "CLIP_OT_select_circle", CKEY, KM_PRESS, 0, 0);
+       WM_keymap_add_menu(keymap, "CLIP_MT_select_grouped", GKEY, KM_PRESS, KM_SHIFT, 0);
 
        WM_keymap_add_item(keymap, "CLIP_OT_add_marker_slide", LEFTMOUSE, KM_PRESS, KM_CTRL, 0);
 
@@ -346,6 +352,12 @@ static void clip_keymap(struct wmKeyConfig *keyconf)
        kmi= WM_keymap_add_item(keymap, "CLIP_OT_lock_tracks", LKEY, KM_PRESS, KM_ALT, 0);
        RNA_enum_set(kmi->ptr, "action", 1);
 
+       WM_keymap_add_item(keymap, "CLIP_OT_frame_jump", LEFTARROWKEY, KM_PRESS, KM_CTRL|KM_SHIFT, 0);
+       kmi= WM_keymap_add_item(keymap, "CLIP_OT_frame_jump", RIGHTARROWKEY, KM_PRESS, KM_CTRL|KM_SHIFT, 0);
+       RNA_boolean_set(kmi->ptr, "end", 1);
+
+       WM_keymap_add_item(keymap, "CLIP_OT_join_tracks", JKEY, KM_PRESS, KM_CTRL, 0);
+
        transform_keymap_for_space(keyconf, keymap, SPACE_CLIP);
 }
 
index 98a00a192ef56c716dbc5b03fc05b4d9b755bb02..64d3d634b755f8378886c89cae4afe782572d185 100644 (file)
@@ -49,6 +49,7 @@
 #include "BKE_object.h"
 #include "BKE_report.h"
 #include "BKE_scene.h"
+#include "BKE_sound.h"
 
 #include "WM_api.h"
 #include "WM_types.h"
@@ -127,17 +128,17 @@ static void add_marker(SpaceClip *sc, float x, float y)
 {
        MovieClip *clip= ED_space_clip(sc);
        MovieTrackingTrack *track;
-       int width, height, select= 0;
+       int width, height, sel= 0;
 
        ED_space_clip_size(sc, &width, &height);
 
        track= BKE_tracking_add_track(&clip->tracking, x, y, sc->user.framenr, width, height);
 
-       select= TRACK_AREA_POINT;
-       if(sc->flag&SC_SHOW_MARKER_PATTERN) select|= TRACK_AREA_PAT;
-       if(sc->flag&SC_SHOW_MARKER_SEARCH) select|= TRACK_AREA_SEARCH;
+       sel= TRACK_AREA_POINT;
+       if(sc->flag&SC_SHOW_MARKER_PATTERN) sel|= TRACK_AREA_PAT;
+       if(sc->flag&SC_SHOW_MARKER_SEARCH) sel|= TRACK_AREA_SEARCH;
 
-       BKE_movieclip_select_track(clip, track, select, 0);
+       BKE_movieclip_select_track(clip, track, sel, 0);
        BKE_movieclip_set_selection(clip, MCLIP_SEL_TRACK, track);
 }
 
@@ -743,6 +744,91 @@ void CLIP_OT_select_all(wmOperatorType *ot)
        WM_operator_properties_select_all(ot);
 }
 
+/********************** select grouped opertaotr *********************/
+
+static int select_groped_exec(bContext *C, wmOperator *op)
+{
+       SpaceClip *sc= CTX_wm_space_clip(C);
+       MovieClip *clip= ED_space_clip(sc);
+       MovieTrackingTrack *track, *sel;
+       MovieTrackingMarker *marker;
+       int group= RNA_enum_get(op->ptr, "group");
+       int sel_type;
+
+       BKE_movieclip_last_selection(clip, &sel_type, (void**)&sel);
+
+       track= clip->tracking.tracks.first;
+       while(track) {
+               int ok= 0;
+
+               marker= BKE_tracking_get_marker(track, sc->user.framenr);
+
+               if(group==0) { /* Keyframed */
+                       ok= marker->framenr==sc->user.framenr && (marker->flag&MARKER_TRACKED)==0;
+               }
+               else if(group==1) { /* Estimated */
+                       ok= marker->framenr!=sc->user.framenr;
+               }
+               else if(group==2) { /* tracked */
+                       ok= marker->framenr==sc->user.framenr && (marker->flag&MARKER_TRACKED);
+               }
+               else if(group==3) { /* locked */
+                       ok= track->flag&TRACK_LOCKED;
+               }
+               else if(group==4) { /* disabled */
+                       ok= marker->flag&MARKER_DISABLED;
+               }
+               else if(group==5) { /* color */
+                       if(sel_type==MCLIP_SEL_TRACK) {
+                               ok= (track->flag&TRACK_CUSTOMCOLOR) == (sel->flag&TRACK_CUSTOMCOLOR);
+
+                               if(ok && track->flag&TRACK_CUSTOMCOLOR)
+                                       ok= equals_v3v3(track->color, sel->color);
+                       }
+               }
+
+               if(ok) {
+                       track->flag|= SELECT;
+                       if(sc->flag&SC_SHOW_MARKER_PATTERN) track->pat_flag|= SELECT;;
+                       if(sc->flag&SC_SHOW_MARKER_SEARCH) track->search_flag|= SELECT;;
+               }
+
+               track= track->next;
+       }
+
+       WM_event_add_notifier(C, NC_MOVIECLIP|ND_DISPLAY, clip);
+
+       return OPERATOR_FINISHED;
+}
+
+void CLIP_OT_select_grouped(wmOperatorType *ot)
+{
+       static EnumPropertyItem select_group_items[] = {
+                       {0, "KEYFRAMED", 0, "Keyframed tracks", "Select all keyframed tracks"},
+                       {1, "ESTIMATED", 0, "Estimated tracks", "Select all estimated tracks"},
+                       {2, "TRACKED", 0, "Tracked tracks", "Select all tracked tracks"},
+                       {3, "LOCKED", 0, "Locked tracks", "Select all locked tracks"},
+                       {4, "DISABLED", 0, "Disabled tracks", "Select all disabled tracks"},
+                       {5, "COLOR", 0, "Tracks with same color", "Select all tracks with same color as actiev track"},
+                       {0, NULL, 0, NULL, NULL}
+       };
+
+       /* identifiers */
+       ot->name= "Join Tracks";
+       ot->description= "Joint Selected Tracks";
+       ot->idname= "CLIP_OT_select_grouped";
+
+       /* api callbacks */
+       ot->exec= select_groped_exec;
+       ot->poll= space_clip_frame_poll;
+
+       /* flags */
+       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+       /* proeprties */
+       RNA_def_enum(ot->srna, "group", select_group_items, TRACK_CLEAR_REMAINED, "Action", "Clear action to execute");
+}
+
 /********************** track operator *********************/
 
 typedef struct TrackMarkersJob {
@@ -1181,7 +1267,6 @@ void CLIP_OT_clear_track_path(wmOperatorType *ot)
 
        /* proeprties */
        RNA_def_enum(ot->srna, "action", clear_path_actions, TRACK_CLEAR_REMAINED, "Action", "Clear action to execute");
-
 }
 
 /********************** disable markers operator *********************/
@@ -1695,6 +1780,7 @@ static int mouse_on_corner(SpaceClip *sc, MovieTrackingTrack *track, float size,
 {
        int inside= 0;
        float nco[2], crn[2], dx, dy;
+       float odx, ody;
 
        nco[0]= co[0]/width;
        nco[1]= co[1]/height;
@@ -1702,24 +1788,27 @@ static int mouse_on_corner(SpaceClip *sc, MovieTrackingTrack *track, float size,
        dx= size/width/sc->zoom;
        dy= size/height/sc->zoom;
 
-       dx=MIN2(dx, (track->search_max[0]-track->search_min[0])/5);
-       dy=MIN2(dy, (track->search_max[1]-track->search_min[1])/5);
+       odx= 5.0f/width/sc->zoom;
+       ody= 5.0f/height/sc->zoom;
+
+       dx=MIN2(dx, (track->search_max[0]-track->search_min[0])/3);
+       dy=MIN2(dy, (track->search_max[1]-track->search_min[1])/3);
 
        if(corner==0) {
                crn[0]= pos[0]+max[0];
                crn[1]= pos[1]+min[1];
 
-               inside= nco[0]>=crn[0]-dx && nco[0]<=crn[0] && nco[1]>=crn[1] && nco[1]<=crn[1]+dy;
+               inside= nco[0]>=crn[0]-dx && nco[0]<=crn[0]+odx && nco[1]>=crn[1]-ody && nco[1]<=crn[1]+dy;
        } else if(corner==1) {
                crn[0]= pos[0]+min[0];
                crn[1]= pos[1]+max[1];
 
-               inside= nco[0]>=crn[0] && nco[0]<=crn[0]+dx && nco[1]>=crn[1]-dy && nco[1]<=crn[1];
+               inside= nco[0]>=crn[0]-odx && nco[0]<=crn[0]+dx && nco[1]>=crn[1]-dy && nco[1]<=crn[1]+ody;
        } else {
                crn[0]= pos[0]+min[0];
                crn[1]= pos[1]+max[1];
 
-               inside= nco[0]>=crn[0]-dx && nco[0]<=crn[0] && nco[1]>=crn[1] && nco[1]<=crn[1]+dy;
+               inside= nco[0]>=crn[0]-dx && nco[0]<=crn[0]+odx && nco[1]>=crn[1]-ody && nco[1]<=crn[1]+dy;
        }
 
        return inside;
@@ -1788,9 +1877,9 @@ static int slide_marker_invoke(bContext *C, wmOperator *op, wmEvent *event)
                                }
 
                                if(!op->customdata && sc->flag&SC_SHOW_MARKER_PATTERN) {
-                                       if(mouse_on_corner(sc, track, 10.0f, co, 2, marker->pos, track->pat_min, track->pat_max, width, height))
+                                       if(mouse_on_corner(sc, track, 15.0f, co, 2, marker->pos, track->pat_min, track->pat_max, width, height))
                                                op->customdata= create_slide_marker_data(sc, track, marker, event, TRACK_AREA_PAT, SLIDE_ACTION_POS, width, height);
-                                       else if(mouse_on_corner(sc, track, 10.0f, co, 0, marker->pos, track->pat_min, track->pat_max, width, height))
+                                       else if(mouse_on_corner(sc, track, 15.0f, co, 0, marker->pos, track->pat_min, track->pat_max, width, height))
                                                op->customdata= create_slide_marker_data(sc, track, marker, event, TRACK_AREA_PAT, SLIDE_ACTION_SIZE, width, height);
                                }
 
@@ -2070,6 +2159,118 @@ void CLIP_OT_detect_features(wmOperatorType *ot)
        ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 }
 
+/********************** frame jump opertaotr *********************/
+
+static int frame_jump_exec(bContext *C, wmOperator *op)
+{
+       Scene *scene= CTX_data_scene(C);
+       SpaceClip *sc= CTX_wm_space_clip(C);
+       MovieClip *clip= ED_space_clip(sc);
+       MovieTrackingTrack *track;
+       int end= RNA_boolean_get(op->ptr, "end");
+       int sel_type, delta= end ? 1 : -1;
+
+       BKE_movieclip_last_selection(clip, &sel_type, (void**)&track);
+
+       if(sel_type!=MCLIP_SEL_TRACK)
+               return OPERATOR_CANCELLED;
+
+       while(BKE_tracking_has_marker(track, sc->user.framenr+delta)) {
+               sc->user.framenr+= delta;
+       }
+
+       if(CFRA!=sc->user.framenr) {
+               CFRA= sc->user.framenr;
+               sound_seek_scene(C);
+
+               WM_event_add_notifier(C, NC_SCENE|ND_FRAME, scene);
+       }
+
+       WM_event_add_notifier(C, NC_MOVIECLIP|ND_DISPLAY, NULL);
+
+       return OPERATOR_FINISHED;
+}
+
+void CLIP_OT_frame_jump(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name= "Jump to Frame";
+       ot->description= "Jump to special frame";
+       ot->idname= "CLIP_OT_frame_jump";
+
+       /* api callbacks */
+       ot->exec= frame_jump_exec;
+       ot->poll= space_clip_frame_poll;
+
+       /* flags */
+       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+       /* properties */
+       RNA_def_boolean(ot->srna, "end", 0, "Last Frame", "Jump to the last frame of current track.");
+}
+
+/********************** join tracks opertaotr *********************/
+
+static int join_tracks_exec(bContext *C, wmOperator *op)
+{
+       SpaceClip *sc= CTX_wm_space_clip(C);
+       MovieClip *clip= ED_space_clip(sc);
+       MovieTrackingTrack *act_track, *track, *next;
+       int sel_type;
+
+       BKE_movieclip_last_selection(clip, &sel_type, (void**)&act_track);
+
+       if(sel_type!=MCLIP_SEL_TRACK) {
+               BKE_report(op->reports, RPT_ERROR, "No active track to join to");
+               return OPERATOR_CANCELLED;
+       }
+
+       track= clip->tracking.tracks.first;
+       while(track) {
+               if(TRACK_SELECTED(track) && track!=act_track) {
+                       if(!BKE_tracking_test_join_tracks(act_track, track)) {
+                               BKE_report(op->reports, RPT_ERROR, "Some selected tracks have got keyframed markers to the same frame");
+                               return OPERATOR_CANCELLED;
+                       }
+               }
+
+               track= track->next;
+       }
+
+       track= clip->tracking.tracks.first;
+       while(track) {
+               next= track->next;
+
+               if(TRACK_SELECTED(track) && track!=act_track) {
+                       BKE_tracking_join_tracks(act_track, track);
+
+                       BKE_tracking_free_track(track);
+                       BLI_freelinkN(&clip->tracking.tracks, track);
+               }
+
+               track= next;
+       }
+
+       WM_event_add_notifier(C, NC_MOVIECLIP|NA_EDITED, clip);
+
+       return OPERATOR_FINISHED;
+}
+
+void CLIP_OT_join_tracks(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name= "Join Tracks";
+       ot->description= "Joint Selected Tracks";
+       ot->idname= "CLIP_OT_join_tracks";
+
+       /* api callbacks */
+       ot->exec= join_tracks_exec;
+       ot->poll= space_clip_frame_poll;
+
+       /* flags */
+       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
 /********************** lock tracks operator *********************/
 
 static int lock_tracks_exec(bContext *C, wmOperator *op)
@@ -2119,3 +2320,51 @@ void CLIP_OT_lock_tracks(wmOperatorType *ot)
        /* properties */
        RNA_def_enum(ot->srna, "action", actions_items, 0, "Action", "Lock action to execute");
 }
+
+/********************** track copy color operator *********************/
+
+static int track_copy_color_exec(bContext *C, wmOperator *UNUSED(op))
+{
+       SpaceClip *sc= CTX_wm_space_clip(C);
+       MovieClip *clip= ED_space_clip(sc);
+       MovieTrackingTrack *track, *sel;
+       int sel_type;
+
+       BKE_movieclip_last_selection(clip, &sel_type, (void**)&sel);
+
+       if(sel_type!=MCLIP_SEL_TRACK)
+               return OPERATOR_CANCELLED;
+
+       track= clip->tracking.tracks.first;
+       while(track) {
+               if(TRACK_SELECTED(track)) {
+                       track->flag&= ~TRACK_CUSTOMCOLOR;
+
+                       if(sel->flag&TRACK_CUSTOMCOLOR) {
+                               copy_v3_v3(track->color, sel->color);
+                               track->flag|= TRACK_CUSTOMCOLOR;
+                       }
+               }
+
+               track= track->next;
+       }
+
+       WM_event_add_notifier(C, NC_MOVIECLIP|ND_DISPLAY, clip);
+
+       return OPERATOR_FINISHED;
+}
+
+void CLIP_OT_track_copy_color(wmOperatorType *ot)
+{
+       /* identifiers */
+       ot->name= "Copy Color";
+       ot->description= "Copy color to all selected tracks";
+       ot->idname= "CLIP_OT_track_copy_color";
+
+       /* api callbacks */
+       ot->exec= track_copy_color_exec;
+       ot->poll= space_clip_tracking_poll;
+
+       /* flags */
+       ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}