root/sweep/trunk/src/sweep_undo.c

Revision 701, 17.9 kB (checked in by erikd, 2 years ago)

Replace deprecated gtk_timeout_add/remove functions with their replacements.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1 /*
2  * Sweep, a sound wave editor.
3  *
4  * Copyright (C) 2000 Conrad Parker
5  * Copyright (C) 2002 Commonwealth Scientific and Industrial Research
6  * Organisation (CSIRO), Australia
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #  include <config.h>
25 #endif
26
27 #include <stdio.h>
28 #include <string.h>
29 #include <glib.h>
30 #include <pthread.h>
31
32 #include <sweep/sweep_i18n.h>
33 #include <sweep/sweep_undo.h>
34 #include <sweep/sweep_sample.h>
35 #include <sweep/sweep_sounddata.h>
36 #include <sweep/sweep_selection.h>
37
38 #include "sweep_app.h"
39 #include "edit.h"
40 #include "undo_dialog.h"
41 #include "head.h"
42 #include "play.h"
43 #include "file_dialogs.h"
44 #include "question_dialogs.h"
45
46 #ifdef LIMITED_UNDO
47 /* Nr. of undo operations remembered */
48 #define UNDO_LEVELS 99
49 #endif
50
51 /* Within each sample s, maintain:
52  *   s->current_undo == s->current_redo->prev
53  *   s->current_redo == s->current_undo->next
54  */
55
56 /*#define DEBUG*/
57
58 static void
59 op_main (sw_sample * sample)
60 {
61   GList * gl;
62   sw_op_instance * inst;
63
64 #ifdef DEBUG
65   g_print ("%d: Hello from op_main %d!\n", getpid(), getpid());
66 #endif
67
68   while (sample->edit_state != SWEEP_EDIT_STATE_CANCEL &&
69          (gl = sample->pending_ops) != NULL) {
70
71     g_mutex_lock (sample->edit_mutex);
72     while (sample->edit_state != SWEEP_EDIT_STATE_PENDING &&
73            sample->edit_state != SWEEP_EDIT_STATE_CANCEL) {
74       g_cond_wait (sample->pending_cond, sample->edit_mutex);
75     }
76
77     if (sample->edit_state == SWEEP_EDIT_STATE_CANCEL) {
78 #ifdef DEBUG
79       g_print ("Caught an early cancelmoose; pending is %p\n",
80                sample->pending_ops);
81       fflush (stdout);
82 #endif
83     } else {
84       gboolean was_going = FALSE;
85      
86       inst = (sw_op_instance *)gl->data;
87
88       g_assert (sample->edit_state == SWEEP_EDIT_STATE_PENDING);
89      
90       sample->edit_state = SWEEP_EDIT_STATE_BUSY;
91       sample->pending_ops = g_list_remove_link (sample->pending_ops, gl);
92      
93       g_mutex_unlock (sample->edit_mutex);
94      
95       if (inst->op->edit_mode == SWEEP_EDIT_MODE_ALLOC) {
96         g_mutex_lock (sample->play_mutex);
97         if ((was_going = sample->play_head->going)) {
98           head_set_stop_offset (sample->play_head, sample->user_offset);
99           head_set_going (sample->play_head, FALSE);
100         }
101         g_mutex_unlock (sample->play_mutex);
102       }
103
104       /* XXX: this is fubar -- change to SweepFunction ?? or change all to
105        * have sample as first arg ... */
106       inst->op->_do_ ((sw_sample *)inst, (void *)inst);
107
108       g_mutex_lock (sample->edit_mutex);
109      
110 #ifdef DEBUG
111       if (sample->edit_state == SWEEP_EDIT_STATE_CANCEL) {
112         g_print ("Caught a late cancelmoose; pending is %p\n",
113                  sample->pending_ops);
114         fflush (stdout);
115       }  else {
116         g_print ("%d: post-op edit state is %d\n", getpid(),
117                  sample->edit_state);
118       }
119 #endif
120      
121     }
122
123     sample->edit_state = SWEEP_EDIT_STATE_DONE;
124
125     g_mutex_unlock (sample->edit_mutex);
126   }
127
128
129   g_mutex_lock (sample->edit_mutex);
130
131 #ifdef DEBUG
132   if (sample->edit_state == SWEEP_EDIT_STATE_CANCEL) {
133     g_print ("%d: Let me save you from scrubby!.\n", getpid());
134     sample->edit_state = SWEEP_EDIT_STATE_DONE;
135   } else {
136     g_print ("%d: exit edit state is %d\n", getpid(), sample->edit_state);
137   }
138
139   g_print ("%d: yada yada that's all!\n", getpid());
140
141 #endif
142
143   sample->ops_thread = (pthread_t) -1;
144
145   g_mutex_unlock (sample->edit_mutex);
146
147 }
148
149 static void
150 prepare_op (sw_op_instance * inst)
151 {
152   sw_sample * sample = inst->sample;
153
154   gchar buf[128];
155
156   g_snprintf (buf, sizeof (buf), "%s (%%p%%%%)", inst->description);
157   sample_set_progress_text (sample, buf);
158  
159   sample_set_edit_mode (sample, inst->op->edit_mode);
160   sample_set_progress_percent (sample, 0);
161
162   if (sample->ops_thread == (pthread_t) -1) {
163     pthread_create (&sample->ops_thread, NULL, (void *) (*op_main), sample);
164   }
165 }
166
167 gint
168 update_edit_progress (gpointer data)
169 {
170   sw_sample * sample = (sw_sample *)data;
171   sw_op_instance * inst;
172
173   if (sample->edit_state == SWEEP_EDIT_STATE_BUSY) {
174     sample_refresh_progress_percent (sample);
175     if (sample->edit_mode == SWEEP_EDIT_MODE_META ||
176         sample->edit_mode == SWEEP_EDIT_MODE_FILTER)
177       sample_refresh_views (sample);
178
179     return TRUE;
180   }
181
182   if (sample->edit_state == SWEEP_EDIT_STATE_DONE) {
183     undo_dialog_refresh_history (sample);
184
185     if (sample->sounddata->sels == NULL) {
186       sample_stop_marching_ants (sample);
187     }
188
189     sample_refresh_views (sample);
190
191     if (sample->pending_ops == NULL)
192       sample_set_edit_state (sample, SWEEP_EDIT_STATE_IDLE);
193   }
194
195   if (sample->pending_ops != NULL) {
196     inst = (sw_op_instance *)sample->pending_ops->data;
197     prepare_op (inst);
198     sample_set_edit_state (sample, SWEEP_EDIT_STATE_PENDING);
199   }
200
201   if (sample->edit_state == SWEEP_EDIT_STATE_IDLE) {
202     sample_refresh_views (sample);
203
204     sample->op_progress_tag = -1;
205     return FALSE;
206   }
207
208   if (sample->edit_state == SWEEP_EDIT_STATE_CANCEL) {
209     sample_set_progress_text (sample, "Scrubby has you.");
210   }
211
212   return TRUE;
213 }
214
215 static void
216 sw_op_instance_clear (sw_op_instance * inst)
217 {
218   if (!inst) return;
219
220   if (inst->do_data) {
221     if (inst->op->purge_do) inst->op->purge_do (inst->do_data);
222   }
223   if (inst->undo_data) {
224     if (inst->op->purge_undo) inst->op->purge_undo (inst->undo_data);
225     else g_free (inst->undo_data);
226   }
227   if (inst->redo_data && inst->redo_data != inst->undo_data) {
228     if (inst->op->purge_redo) inst->op->purge_redo (inst->redo_data);
229     else g_free (inst->redo_data);
230
231   }
232
233   inst->do_data = NULL;
234   inst->undo_data = NULL;
235   inst->redo_data = NULL;
236 }
237
238 sw_op_instance *
239 sw_op_instance_new (sw_sample * sample, char * desc, sw_operation * op)
240 {
241   sw_op_instance * inst;
242
243   inst = g_malloc (sizeof(sw_op_instance));
244   inst->sample = sample;
245   inst->description = strdup (desc);
246   inst->op = op;
247   inst->do_data = NULL;
248   inst->undo_data = NULL;
249   inst->redo_data = NULL;
250
251   return inst;
252 }
253
254 void
255 trim_registered_ops (sw_sample * s, int length)
256 {
257   GList * gl;
258
259   while (g_list_length (s->registered_ops) > length) {
260     gl = g_list_first (s->registered_ops);
261     s->registered_ops = g_list_remove_link (s->registered_ops, gl);
262
263     sw_op_instance_clear (gl->data);
264     g_list_free (gl);
265   }
266
267   if (s->registered_ops == NULL)
268     s->current_undo = NULL;
269 }
270
271 static void
272 schedule_operation_do (sw_op_instance * inst)
273 {
274   sw_sample * sample = inst->sample;
275
276   g_mutex_lock (sample->edit_mutex);
277   sample->pending_ops = g_list_append (sample->pending_ops, inst);
278   g_mutex_unlock (sample->edit_mutex);
279
280   if (sample->op_progress_tag == -1) {
281     sample->op_progress_tag =
282       g_timeout_add (30, (GtkFunction)update_edit_progress,
283                        (gpointer)sample);
284   }
285 }
286
287 static void
288 schedule_operation_ok_cb (GtkWidget * widget, gpointer data)
289 {
290   sw_op_instance * inst = (sw_op_instance *)data;
291   sw_sample * sample = inst->sample;
292
293   sample->edit_ignore_mtime = TRUE;
294
295   schedule_operation_do (inst);
296 }
297
298 void
299 schedule_operation (sw_sample * sample, char * description,
300                     sw_operation * operation, void * do_data)
301 {
302   sw_op_instance * inst;
303   char buf[512];
304
305   inst = sw_op_instance_new (sample, description, operation);
306   inst->do_data = do_data;
307
308   if (operation->edit_mode != SWEEP_EDIT_MODE_META &&
309       !sample->edit_ignore_mtime && sample_mtime_changed (sample)) {
310
311     snprintf (buf, sizeof (buf),
312               _("%s\n has changed on disk.\n\n"
313                 "Do you want to continue editing this buffer?"),
314               sample->pathname);
315    
316     question_dialog_new (sample, _("File modified"), buf,
317                          _("Continue editing"), _("Reread from disk"),
318                          G_CALLBACK (schedule_operation_ok_cb), inst,
319                          G_CALLBACK (sample_revert_ok_cb), sample,
320                          SWEEP_EDIT_MODE_ALLOC);
321   } else {
322     schedule_operation_do (inst);
323   }
324 }
325
326 void
327 register_operation (sw_sample * s, sw_op_instance * inst)
328 {
329   GList * trash, * gl;
330   sw_op_instance * inst2;
331
332 #ifdef DEBUG
333   printf("Registering %s\n", inst->description);
334 #endif
335
336   g_mutex_lock (s->ops_mutex);
337
338   if (s->registered_ops && s->current_redo) {
339     /* Split the list here -- keep everything up to and
340      * including current_undo, and trash the rest.
341      */
342     s->current_redo->prev = NULL;
343
344     if (s->current_undo)
345       s->current_undo->next = NULL;
346
347     if (s->registered_ops == s->current_redo)
348       s->registered_ops = NULL;
349
350     trash = s->current_redo;
351     s->current_redo = NULL;
352
353     /* Free up the rest of the list */
354     for (gl = trash; gl; gl = gl->next) {
355       inst2 = (sw_op_instance *)gl->data;
356       sw_op_instance_clear (inst2);
357     }
358
359     /* Trash it */
360     g_list_free (trash);
361   }
362
363   s->registered_ops = g_list_append (s->registered_ops, inst);
364
365 #ifdef LIMITED_UNDO
366   trim_registered_ops (s, UNDO_LEVELS);
367 #endif
368
369   s->current_undo = g_list_find (s->registered_ops, inst);
370
371   s->active_op = NULL;
372
373   if (inst->op->edit_mode != SWEEP_EDIT_MODE_META) {
374     s->modified = TRUE;
375   }
376
377   g_mutex_unlock (s->ops_mutex);
378 }
379
380 static void
381 undo_operation (sw_sample * s, sw_op_instance * inst)
382 {
383   if (inst && inst->op->undo) {
384     inst->op->undo(s, inst->undo_data);
385   }
386 }
387
388 static void
389 redo_operation (sw_sample * s, sw_op_instance * inst)
390 {
391   if (inst && inst->op->redo) {
392     inst->op->redo(s, inst->redo_data);
393   }
394 }
395
396 static void
397 do_undo_current_thread (sw_op_instance * inst)
398 {
399   sw_sample * s = inst->sample;
400
401   if (s == NULL || s->current_undo == NULL) goto noop;
402
403   undo_operation (s, inst->do_data);
404  
405   g_mutex_lock (s->ops_mutex);
406   if (s->edit_state == SWEEP_EDIT_STATE_BUSY) {
407     s->current_redo = s->current_undo;
408     s->current_undo = s->current_undo->prev;
409   }
410   g_mutex_unlock (s->ops_mutex);
411
412   return;
413
414  noop:
415   sample_set_tmp_message (s, _("Nothing to undo"));
416 }
417
418 static sw_operation undo_filter_op = {
419   SWEEP_EDIT_MODE_FILTER,
420   (SweepCallback)do_undo_current_thread,
421   (SweepFunction)NULL,
422   (SweepCallback)NULL,
423   (SweepFunction)NULL,
424   (SweepCallback)NULL,
425   (SweepFunction)NULL,
426 };
427
428 static sw_operation undo_alloc_op = {
429   SWEEP_EDIT_MODE_ALLOC,
430   (SweepCallback)do_undo_current_thread,
431   (SweepFunction)NULL,
432   (SweepCallback)NULL,
433   (SweepFunction)NULL,
434   (SweepCallback)NULL,
435   (SweepFunction)NULL,
436 };
437
438 static void
439 schedule_undo_inst (sw_sample * sample, sw_op_instance * inst)
440 {
441   sw_operation * op;
442   gchar buf[128];
443
444   g_snprintf (buf, sizeof (buf), "Undo %s", inst->description);
445
446   if (inst->op->edit_mode == SWEEP_EDIT_MODE_FILTER)
447     op = &undo_filter_op;
448   else
449     op = &undo_alloc_op;
450
451   schedule_operation (sample, buf, op, inst);
452 }
453
454 void
455 undo_current (sw_sample * sample)
456 {
457   sw_op_instance * inst;
458
459   if (sample == NULL) return;
460   if (sample->edit_state != SWEEP_EDIT_STATE_IDLE) return;
461   if (sample->current_undo == NULL) goto noop;
462
463   inst = sample->current_undo->data;
464
465   if (inst->op->undo != NULL)
466     schedule_undo_inst (sample, inst);
467
468   return;
469
470  noop:
471   sample_set_tmp_message (sample, _("Nothing to undo"));
472   sample_set_progress_ready (sample);
473 }
474
475 static void
476 do_redo_current_thread (sw_op_instance * inst)
477 {
478   sw_sample * s = inst->sample;
479
480   if (s == NULL || s->current_redo == NULL) goto noop;
481
482   redo_operation (s, inst->do_data);
483
484   g_mutex_lock (s->ops_mutex);
485   if (s->edit_state == SWEEP_EDIT_STATE_BUSY) {
486     s->current_undo = s->current_redo;
487     s->current_redo = s->current_redo->next;
488   }
489   g_mutex_unlock (s->ops_mutex);
490
491   return;
492
493  noop:
494   sample_set_tmp_message (s, _("Nothing to redo"));
495 }
496
497 static sw_operation redo_filter_op = {
498   SWEEP_EDIT_MODE_FILTER,
499   (SweepCallback)do_redo_current_thread,
500   (SweepFunction)NULL,
501   (SweepCallback)NULL,
502   (SweepFunction)NULL,
503   (SweepCallback)NULL,
504   (SweepFunction)NULL,
505 };
506
507 static sw_operation redo_alloc_op = {
508   SWEEP_EDIT_MODE_ALLOC,
509   (SweepCallback)do_redo_current_thread,
510   (SweepFunction)NULL,
511   (SweepCallback)NULL,
512   (SweepFunction)NULL,
513   (SweepCallback)NULL,
514   (SweepFunction)NULL,
515 };
516
517 static void
518 schedule_redo_inst (sw_sample * sample, sw_op_instance * inst)
519 {
520   sw_operation * op;
521   gchar buf[128];
522
523   g_snprintf (buf, sizeof (buf), "Redo %s", inst->description);
524
525   if (inst->op->edit_mode == SWEEP_EDIT_MODE_FILTER)
526     op = &redo_filter_op;
527   else
528     op = &redo_alloc_op;
529
530   schedule_operation (sample, buf, op, inst);
531 }
532
533 void
534 redo_current (sw_sample * sample)
535 {
536   sw_op_instance * inst;
537
538   if (sample == NULL) return;
539   if (sample->edit_state != SWEEP_EDIT_STATE_IDLE) return;
540   if (sample->current_redo == NULL) goto noop;
541
542   inst = sample->current_redo->data;
543
544   if (inst->op->redo != NULL)
545     schedule_redo_inst (sample, inst);
546
547  noop:
548   sample_set_tmp_message (sample, _("Nothing to redo"));
549   sample_set_progress_ready (sample);
550 }
551
552 void
553 revert_op (sw_sample * sample, GList * op_gl)
554 {
555   GList * gl = NULL;
556   sw_op_instance * inst;
557   gboolean need_undo = FALSE;
558
559   if (sample == NULL) return;
560
561   for (gl = g_list_last (sample->registered_ops); gl; gl = gl->prev) {
562     inst = (sw_op_instance *)gl->data;
563
564     if (gl == sample->current_undo) {
565       need_undo = TRUE;
566     }
567
568     if (gl == op_gl) break;
569   }
570
571   if (need_undo) {
572     for (gl = sample->current_undo; gl != op_gl; gl = gl->prev) {
573       inst = (sw_op_instance *)gl->data;
574       schedule_undo_inst (sample, inst);
575     }
576   } else {
577     for (gl = sample->current_redo; gl != op_gl; gl = gl->next) {
578       inst = (sw_op_instance *)gl->data;
579       schedule_redo_inst (sample, inst);
580     }
581     if (gl != NULL) {
582       inst = (sw_op_instance *)gl->data;
583       schedule_redo_inst (sample, inst);
584     }
585   }
586 }
587
588
589 void
590 set_active_op (sw_sample * s, sw_op_instance * inst)
591 {
592   g_mutex_lock (s->ops_mutex);
593
594   g_assert (s->active_op == NULL);
595
596   s->active_op = inst;
597
598   g_mutex_unlock (s->ops_mutex);
599 }
600
601 #ifdef TRYCANCEL
602 static gint
603 try_cancel_active_op (gpointer data)
604 {
605   sw_sample * s = (sw_sample *)data;
606
607   if (g_mutex_trylock (s->ops_mutex)) {
608
609     if (s->active_op) {
610       undo_operation (s, s->active_op);
611     }
612    
613     s->active_op = NULL;
614    
615     sample_set_edit_state (s, SWEEP_EDIT_STATE_CANCEL);
616
617     g_mutex_unlock (s->ops_mutex);
618
619     return FALSE;
620   } else {
621     return TRUE;
622   }
623 }
624 #endif
625
626 void
627 cancel_active_op (sw_sample * s)
628 {
629 #ifdef TRYCANCEL
630
631   g_idle_add (GSourceFunc(try_cancel_active_op), (gpointer)s);
632
633 #else
634
635   g_mutex_lock (s->ops_mutex);
636
637    
638   if (s->active_op) {
639     undo_operation (s, s->active_op);
640
641     sample_set_tmp_message (s, "%s CANCELLED", s->active_op->description);
642
643     /* XXX: does this leak s->active_op here? */
644   }
645   s->active_op = NULL;
646  
647   g_mutex_lock (s->edit_mutex);
648
649   /*
650    * Signal to the ops thread to cancel or not perform the current operation.
651    * It is possible that the ops thread has already exited, in which case
652    * s->ops_thread will have been set to -1 on its departure, also within
653    * s->edit_mutex, in which case the signalled CANCEL would never be cleared.
654    */
655   if (s->ops_thread != (pthread_t) -1) {
656     s->edit_state = SWEEP_EDIT_STATE_CANCEL;
657   }
658
659   if (s->pending_ops) {
660     GList * gltmp = s->pending_ops;
661
662     s->pending_ops = NULL;
663
664     g_print ("XXX: cancel: need to remove pending ops\n");
665     g_list_free (gltmp);
666   }
667
668   g_mutex_unlock (s->edit_mutex);
669
670   /*  sample_set_edit_state (s, SWEEP_EDIT_STATE_CANCEL);*/
671  
672   g_mutex_unlock (s->ops_mutex);
673
674 #endif
675 }
676
677 /* Standard undo/redo functions */
678
679 sounddata_replace_data *
680 sounddata_replace_data_new (sw_sample * sample,
681                             sw_sounddata * old_sounddata,
682                             sw_sounddata * new_sounddata)
683 {
684   sounddata_replace_data * sr;
685
686   sr = g_malloc (sizeof(sounddata_replace_data));
687
688   sr->sample = sample;
689   sr->old_sounddata = old_sounddata;
690   sr->new_sounddata = new_sounddata;
691
692   old_sounddata->refcount++;
693   new_sounddata->refcount++;
694
695   return sr;
696 }
697
698 void
699 sounddata_replace_data_destroy (sounddata_replace_data * sr)
700 {
701   sounddata_destroy (sr->old_sounddata);
702   sounddata_destroy (sr->new_sounddata);
703
704   g_free (sr);
705 }
706
707 void
708 undo_by_sounddata_replace (sw_sample * s, sounddata_replace_data * sr)
709 {
710   g_mutex_lock (s->ops_mutex);
711   s->sounddata = sr->old_sounddata;
712   g_mutex_unlock (s->ops_mutex);
713 }
714
715 void
716 redo_by_sounddata_replace (sw_sample * s, sounddata_replace_data * sr)
717 {
718   g_mutex_lock (s->ops_mutex);
719   s->sounddata = sr->new_sounddata;
720   g_mutex_unlock (s->ops_mutex);
721 }
722
723
724 paste_over_data *
725 paste_over_data_new (sw_edit_buffer * old_eb, sw_edit_buffer * new_eb)
726 {
727   paste_over_data * p;
728
729   p = g_malloc (sizeof(paste_over_data));
730
731   p->old_eb = old_eb;
732   p->new_eb = new_eb;
733
734   return p;
735 };
736
737 void
738 paste_over_data_destroy (paste_over_data * p)
739 {
740   edit_buffer_destroy (p->old_eb);
741   if (p->new_eb != p->old_eb)
742     edit_buffer_destroy (p->new_eb);
743   g_free (p);
744 }
745
746 void
747 undo_by_paste_over (sw_sample * s, paste_over_data * p)
748 {
749   paste_over (s, p->old_eb);
750 }
751
752 void
753 redo_by_paste_over (sw_sample * s, paste_over_data * p)
754 {
755   paste_over (s, p->new_eb);
756 }
757
758 splice_data *
759 splice_data_new (sw_edit_buffer * eb, GList * sels)
760 {
761   splice_data * s;
762
763   s = g_malloc (sizeof(splice_data));
764
765   s->eb = eb;
766   s->sels = sels_copy (sels);
767
768   return s;
769 }
770
771 void
772 splice_data_destroy (splice_data * s)
773 {
774   edit_buffer_destroy (s->eb);
775   g_list_free (s->sels);
776   g_free (s);
777 }
778
779 void
780 undo_by_splice_in (sw_sample * s, splice_data * sp)
781 {
782   splice_in_eb (s, sp->eb);
783 }
784
785 void
786 redo_by_splice_out (sw_sample * s, splice_data * sp)
787 {
788   splice_out_sel (s);
789 }
790
791 void
792 undo_by_splice_out (sw_sample * s, splice_data * sp)
793 {
794   splice_out_sel (s);
795
796   s->sounddata->sels = sels_copy (sp->sels);
797 }
798
799 void
800 redo_by_splice_in (sw_sample * s, splice_data * sp)
801 {
802   splice_in_eb (s, sp->eb);
803 }
804
805 void
806 undo_by_splice_over (sw_sample * s, splice_data * sp)
807 {
808   s->sounddata->sels = sels_copy (sp->sels);
809   paste_over (s, sp->eb);
810 }
811
812 void
813 redo_by_splice_over (sw_sample * s, splice_data * sp)
814 {
815   s->sounddata->sels = sels_copy (sp->sels);
816   paste_over (s, sp->eb);
817 }
818
819 void
820 undo_by_crop_in (sw_sample * s, splice_data * sp)
821 {
822   crop_in (s, sp->eb);
823 }
824
825 void
826 redo_by_crop_out (sw_sample * s, splice_data * sp)
827 {
828   crop_out (s);
829 }
Note: See TracBrowser for help on using the browser.