root/sweep/trunk/src/sample-display.c

Revision 688, 69.8 kB (checked in by erikd, 2 years ago)

Clean up large number of '#if 0' and '#if 1' code blocks.

Remove code chunks surrounded by '#if 0' .. '#endif'.
Remove '#if 1' and '#endif' around code chunks that are actually being used.

  • 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  *
6  * This widget was originally based on sample-display by Michael Krause
7  * in ``The Real Soundtracker'', Copyright (C) 1998-1999 Michael Krause
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #  include <config.h>
26 #endif
27
28 #include <stdlib.h>
29 #include <math.h>
30 #include <time.h>
31
32 #include "sample-display.h"
33
34 #include <gdk/gdkkeysyms.h>
35 #include <gtk/gtk.h>
36
37 #include <sweep/sweep_i18n.h>
38
39 #include "sweep_app.h"
40 #include "sample.h"
41 #include "head.h"
42 #include "cursors.h"
43 #include "play.h"
44 #include "callbacks.h"
45 #include "edit.h"
46 #include "undo_dialog.h"
47
48 /*#define DEBUG*/
49
50 /*#define DOUBLE_BUFFER */
51
52 /*#define ALWAYS_REDRAW_ALL*/
53
54 /*#define LEGACY_DRAW_MODE*/
55
56 #define SEL_SCRUBS
57
58 extern GdkCursor * sweep_cursors[];
59
60 /* Maximum number of samples to consider per pixel */
61 #define STEP_MAX 32
62
63
64 /* Whether or not to compile in support for
65  * drawing the crossing vectors
66  */
67 /* #define DRAW_CROSSING_VECTORS */
68
69 #define PIXEL_TO_OFFSET(p) \
70   ((sw_framecount_t)(((gdouble)(p)) * (((gdouble)(s->view->end - s->view->start)) / ((gdouble)s->width))))
71
72 #define XPOS_TO_OFFSET(x) \
73   CLAMP(s->view->start + PIXEL_TO_OFFSET(x), 0, s->view->sample->sounddata->nr_frames)
74
75 #define SAMPLE_TO_PIXEL(n) \
76   ((sw_framecount_t)((gdouble)(n) * (gdouble)s->width / (gdouble)(s->view->end - s->view->start)))
77
78 #define OFFSET_TO_XPOS(o) \
79   SAMPLE_TO_PIXEL((o) - s->view->start)
80
81 #define OFFSET_RANGE(l, x) ((x) < 0 ? 0 : ((x) >= (l) ? (l) - 1 : (x)))
82
83 #define SET_CURSOR(w, c) \
84   gdk_window_set_cursor ((w)->window, sweep_cursors[SWEEP_CURSOR_##c])
85
86 #define YPOS_TO_CHANNEL(y) \
87   (s->view->sample->sounddata->format->channels * y / s->height)
88
89 #define CHANNEL_HEIGHT \
90   (s->height / s->view->sample->sounddata->format->channels)
91
92 #define YPOS_TO_VALUE(y) \
93   ((sw_audio_t)(CHANNEL_HEIGHT/2 - (y - (YPOS_TO_CHANNEL(y) * CHANNEL_HEIGHT)))/(CHANNEL_HEIGHT/2))
94
95 #define MARCH_INTERVAL 300
96 #define PULSE_INTERVAL 450
97 #define HAND_SCROLL_INTERVAL 50
98
99 extern sw_view * last_tmp_view;
100
101 static int last_button; /* button index which started the last selection;
102                          * This is global to allow comparison for
103                          * last_tmp_view
104                          */
105
106 static const int default_colors[] = {
107   200, 200, 193,  /* bg */
108   199, 203, 158,  /* fg */
109   0, 0xaa, 0, /* play (mask) */
110   220, 230, 255, /* user */
111   100, 100, 100, /* zero */
112   240, 230, 240, /* sel box */
113   110, 110, 100, /* tmp_sel XOR mask */
114   108, 115, 134,    /* sel bg */
115   166, 166, 154, /* minmax */
116   240, 250, 240, /* highlight */
117   81, 101, 81,   /* lowlight */
118   230, 0, 0,     /* rec */
119 };
120
121 static const int bg_colors[] = {
122   250, 250, 237,    /* black bg */
123   200, 200, 193,    /* red bg */
124   147, 147, 140,    /* orange bg */
125   160, 160, 150,    /* yellow bg */
126   250, 250, 237,    /* blue bg */
127   160, 160, 150,    /* white bg */
128   0, 0, 0,          /* greenscreen bg */
129   60, 70, 170,          /* bluescreen bg */
130 };
131
132 static const int fg_colors[] = {
133   80, 80, 60,    /* black fg */
134   220, 80, 40,   /* red fg */
135   220, 170, 120, /* orange fg */
136   199, 203, 158, /* yellow fg */
137   128, 138, 184, /* blue fg */
138   230, 240, 255, /* white fg */
139   0, 220, 0,     /* greenscreen fg */
140   240, 240, 240,     /* bluescreen fg */
141 };
142
143 /* Values for s->selecting */
144 enum {
145   SELECTING_NOTHING = 0,
146   SELECTING_SELECTION_START,
147   SELECTING_SELECTION_END,
148   SELECTING_PAN_WINDOW,
149   SELECTING_PLAYMARKER,
150   SELECTING_PENCIL,
151   SELECTING_NOISE,
152   SELECTING_HAND,
153 };
154
155 enum {
156   SELECTION_MODE_NONE = 0, /* Not selecting; used for consistency check. */
157   SELECTION_MODE_REPLACE,
158   SELECTION_MODE_INTERSECT,
159   SELECTION_MODE_SUBTRACT,
160   SELECTION_MODE_MAX
161 };
162
163 enum {
164   SIG_SELECTION_CHANGED,
165   SIG_WINDOW_CHANGED,
166   SIG_MOUSE_OFFSET_CHANGED,
167   LAST_SIGNAL
168 };
169
170 static gchar * selection_mode_names[SELECTION_MODE_MAX] = {
171   N_(""), /* NONE */
172   N_("New selection"), /* REPLACE */
173   N_("Selection: add/modify region"), /* INTERSECT */
174   N_("Selection: subtract region"), /* SUBTRACT */
175 };
176
177 #define IS_INITIALIZED(s) (s->view != NULL)
178
179 static guint sample_display_signals[LAST_SIGNAL] = { 0 };
180
181 static gint8 sel_dash_list[2] = { 4, 4 }; /* Equivalent to GDK's default
182                                           *  dash list.
183                                           */
184
185 void
186 sample_display_refresh (SampleDisplay *s)
187 {
188   sw_sample * sample;
189
190   g_return_if_fail(s != NULL);
191   g_return_if_fail(IS_SAMPLE_DISPLAY(s));
192
193   if(!IS_INITIALIZED(s))
194     return;
195
196   sample = s->view->sample;
197
198   s->old_user_offset_x = s->user_offset_x =
199     OFFSET_TO_XPOS(sample->user_offset);
200
201   s->old_play_offset_x = s->play_offset_x =
202     OFFSET_TO_XPOS(sample->play_head->offset);
203
204   if (sample->rec_head != NULL) {
205     s->old_rec_offset_x = s->rec_offset_x =
206       OFFSET_TO_XPOS(sample->rec_head->offset);
207   }
208
209   gtk_widget_queue_draw_area (GTK_WIDGET(s), 0, 0, s->width, s->height);
210 }
211
212 sw_framecount_t
213 sample_display_get_mouse_offset (SampleDisplay * s)
214 {
215   int x, y;
216   GdkModifierType state;
217
218   gdk_window_get_pointer (GTK_WIDGET(s)->window, &x, &y, &state);
219
220   return XPOS_TO_OFFSET(x);
221 }
222
223 void
224 sample_display_set_view (SampleDisplay *s, sw_view *view)
225 {
226   g_return_if_fail(s != NULL);
227   g_return_if_fail(IS_SAMPLE_DISPLAY(s));
228
229   s->view = view;
230   s->old_user_offset_x = -1;
231   s->user_offset_x = -1;
232   s->old_play_offset_x = -1;
233   s->play_offset_x = -1;
234
235   /*  gtk_signal_emit(GTK_OBJECT(s), sample_display_signals[SIG_WINDOW_CHANGED], s->view->start, s->view->start + (s->view->end - s->view->start));*/
236        
237   s->selecting = SELECTING_NOTHING;
238   s->selection_mode = SELECTION_MODE_NONE;
239
240   gtk_widget_queue_draw(GTK_WIDGET(s));
241 }
242
243 void
244 sample_display_refresh_user_marker (SampleDisplay *s)
245 {
246   sw_sample * sample;
247   gint x, width;
248
249   g_return_if_fail(s != NULL);
250   g_return_if_fail(IS_SAMPLE_DISPLAY(s));
251
252   if(!IS_INITIALIZED(s))
253     return;
254
255   sample = s->view->sample;
256
257   s->user_offset_x = OFFSET_TO_XPOS(sample->user_offset);
258
259   /* paint changed user cursor pos */
260   if (s->old_user_offset_x != s->user_offset_x) {
261     x = CLAMP (s->old_user_offset_x - 15, 0, s->width);
262     width = MIN (s->width - x, 29);
263     gtk_widget_queue_draw_area (GTK_WIDGET(s), x, 0, width, s->height);
264   }
265  
266   /* paint cursor */
267   if (s->user_offset_x >= 0 && s->user_offset_x <= s->width) {
268     x = CLAMP (s->user_offset_x - 15, 0, s->width);
269     width = MIN (s->width - x, 29);
270
271     gtk_widget_queue_draw_area (GTK_WIDGET(s), x, 0, width, s->height);
272
273     s->old_user_offset_x = s->user_offset_x;
274   }
275 }
276
277 void
278 sample_display_refresh_play_marker (SampleDisplay *s)
279 {
280   sw_sample * sample;
281   sw_head * head;
282   gint x, width;
283
284   g_return_if_fail(s != NULL);
285   g_return_if_fail(IS_SAMPLE_DISPLAY(s));
286
287   if(!IS_INITIALIZED(s))
288     return;
289
290   sample = s->view->sample;
291   head = sample->play_head;
292
293   s->play_offset_x = OFFSET_TO_XPOS(head->offset);
294
295   /* paint play offset */
296   if (s->old_play_offset_x != s->play_offset_x) {
297     x = CLAMP (s->old_play_offset_x - 15, 0, s->width);
298     width = MIN (s->width - x, 29);
299     gtk_widget_queue_draw_area (GTK_WIDGET(s), x, 0, width, s->height);
300   }
301  
302   if (s->play_offset_x >= 0 && s->play_offset_x <= s->width) {
303     x = CLAMP (s->play_offset_x - 15, 0, s->width);
304     width = MIN (s->width - x, 29);
305     gtk_widget_queue_draw_area (GTK_WIDGET(s), x, 0, width, s->height);
306
307     s->old_play_offset_x = s->play_offset_x;
308   }
309 }
310
311 void
312 sample_display_refresh_rec_marker (SampleDisplay *s)
313 {
314   sw_sample * sample;
315   sw_head * rec_head;
316   gint x, width;
317
318   g_return_if_fail(s != NULL);
319   g_return_if_fail(IS_SAMPLE_DISPLAY(s));
320
321   if(!IS_INITIALIZED(s))
322     return;
323
324   sample = s->view->sample;
325   rec_head = sample->rec_head;
326
327   if (rec_head == NULL) return;
328
329   s->rec_offset_x = OFFSET_TO_XPOS(rec_head->offset);
330
331   /* paint rec offset */
332   if (s->old_rec_offset_x != s->rec_offset_x) {
333     x = CLAMP (s->old_rec_offset_x - 15, 0, s->width);
334     width = MIN (s->width - x, 29);
335     gtk_widget_queue_draw_area (GTK_WIDGET(s), x, 0, width, s->height);
336   }
337  
338   if (s->rec_offset_x >= 0 && s->rec_offset_x <= s->width) {
339     x = CLAMP (s->rec_offset_x - 15, 0, s->width);
340     width = MIN (s->width - x, 29);
341     gtk_widget_queue_draw_area (GTK_WIDGET(s), x, 0, width, s->height);
342
343     s->old_rec_offset_x = s->rec_offset_x;
344   }
345 }
346
347 static void
348 sample_display_refresh_sels (SampleDisplay * s)
349 {
350   int x, x2;
351   sw_sample * sample;
352   GList * gl;
353   sw_sel * sel;
354
355   g_return_if_fail(s != NULL);
356   g_return_if_fail(IS_SAMPLE_DISPLAY(s));
357
358   if(!IS_INITIALIZED(s))
359     return;
360
361   sample = s->view->sample;
362
363   /* paint marching ants */
364    
365   /* real selection */
366   for (gl = sample->sounddata->sels; gl; gl = gl->next) {
367     sel = (sw_sel *)gl->data;
368      
369     x = OFFSET_TO_XPOS(sel->sel_start);
370     x2 = OFFSET_TO_XPOS(sel->sel_end);
371
372     if ((x >= 0) && (x <= s->width)) {
373       gtk_widget_queue_draw_area (GTK_WIDGET(s), x, 0, 1, s->height);
374     }
375     if ((x2 >= 0) && (x2 <= s->width)) {
376       gtk_widget_queue_draw_area (GTK_WIDGET(s), x2, 0, 1, s->height);
377     }
378
379     if ((x <= s->width) && (x2 >= 0)) {
380       x = CLAMP (x, 0, s->width);
381       x2 = CLAMP (x2, 0, s->width);
382       gtk_widget_queue_draw_area (GTK_WIDGET(s), x, 0, x2 - x, 1);
383       gtk_widget_queue_draw_area (GTK_WIDGET(s), x, s->height - 1, x2 - x, 1);
384     }
385   }
386    
387   /* temporary selection */
388   sel = sample->tmp_sel;
389
390   if (sel) {
391     x = OFFSET_TO_XPOS(sel->sel_start);
392     x2 = OFFSET_TO_XPOS(sel->sel_end);
393
394     if ((x >= 0) && (x <= s->width)) {
395       gtk_widget_queue_draw_area (GTK_WIDGET(s), x, 0, 1, s->height);
396     }
397     if ((x2 >= 0) && (x2 <= s->width)) {
398       gtk_widget_queue_draw_area (GTK_WIDGET(s), x2, 0, 1, s->height);
399     }
400
401     if ((x <= s->width) && (x2 >= 0)) {
402       x = CLAMP (x, 0, s->width);
403       x2 = CLAMP (x2, 0, s->width);
404       gtk_widget_queue_draw_area (GTK_WIDGET(s), x, 0, x2 - x, 1);
405       gtk_widget_queue_draw_area (GTK_WIDGET(s), x, s->height - 1, x2 - x, 1);
406     }
407   }
408 }
409
410 void
411 sample_display_set_cursor (SampleDisplay * s, GdkCursor * cursor)
412 {
413   gdk_window_set_cursor (GTK_WIDGET(s)->window, cursor);
414 }
415
416 void
417 sample_display_set_default_cursor (SampleDisplay * s)
418 {
419   GdkCursor * cursor;
420
421   switch (s->view->current_tool) {
422   case TOOL_SELECT:
423     cursor = sweep_cursors[SWEEP_CURSOR_CROSSHAIR];
424     break;
425   case TOOL_ZOOM:
426     cursor = sweep_cursors[SWEEP_CURSOR_ZOOM_IN];
427     break;
428   case TOOL_MOVE:
429     cursor = sweep_cursors[SWEEP_CURSOR_MOVE];
430     break;
431   case TOOL_SCRUB:
432     cursor = sweep_cursors[SWEEP_CURSOR_NEEDLE];
433     break;
434   case TOOL_PENCIL:
435     cursor = sweep_cursors[SWEEP_CURSOR_PENCIL];
436     break;
437   case TOOL_NOISE:
438     cursor = sweep_cursors[SWEEP_CURSOR_NOISE];
439     break;
440   case TOOL_HAND:
441     cursor = sweep_cursors[SWEEP_CURSOR_HAND_OPEN];
442     break;
443   default:
444     cursor = NULL;
445     break;
446   }
447
448   gdk_window_set_cursor (GTK_WIDGET(s)->window, cursor);
449 }
450
451 static void
452 sample_display_set_intersect_cursor (SampleDisplay * s)
453 {
454   sw_sample * sample = s->view->sample;
455   GtkWidget * widget = GTK_WIDGET(s);
456
457   /* Check if there are other selection regions.
458    * NB. This assumes that tmp_sel has already been
459    * set.
460    */
461   if (sample_sel_nr_regions(sample) > 0) {
462     SET_CURSOR(widget, HORIZ_PLUS);
463   } else {
464     SET_CURSOR(widget, HORIZ);
465   }
466 }
467
468 static void
469 sample_display_set_subtract_cursor (SampleDisplay * s)
470 {
471   sw_sample * sample = s->view->sample;
472   GtkWidget * widget = GTK_WIDGET(s);
473
474   /* Check if there are other selection regions.
475    * NB. This assumes that tmp_sel has already been
476    * set.
477    */
478   if (sample_sel_nr_regions(sample) > 0) {
479     SET_CURSOR(widget, HORIZ_MINUS);
480   } else {
481     SET_CURSOR(widget, HORIZ);
482   }
483 }
484
485 void
486 sample_display_set_window (SampleDisplay *s,
487                            sw_framecount_t start,
488                            sw_framecount_t end)
489 {
490   sw_framecount_t len, vlen;
491
492   g_return_if_fail(s != NULL);
493   g_return_if_fail(IS_SAMPLE_DISPLAY(s));
494
495   len = s->view->sample->sounddata->nr_frames;
496   vlen = end - start;
497  
498   g_return_if_fail(end >= start);
499
500
501   if (vlen > len) {
502     /* Align to middle if entire length of sample is visible */
503     start = (len - vlen) / 2;
504     end = start + vlen;
505   } else if (vlen == 0 && len > 0) {
506     /* Zoom normal if window is zero but there is data; eg. after pasting
507      * into an empty buffer */
508     start = 0;
509     end = MIN (len, s->width * 1024);
510   }
511
512   s->view->start = start;
513   s->view->end = end;
514
515   sample_display_refresh_user_marker (s);
516
517   g_signal_emit_by_name(GTK_OBJECT(s), "window-changed");
518
519   s->mouse_offset = XPOS_TO_OFFSET (s->mouse_x);
520   g_signal_emit_by_name(GTK_OBJECT(s),
521                   "mouse-offset-changed");
522
523   gtk_widget_queue_draw(GTK_WIDGET(s));
524 }
525
526 static void
527 sample_display_init_display (SampleDisplay *s,
528                              int w,
529                              int h)
530 {
531   GdkWindow * window;
532   GdkVisual * visual;
533   sw_framecount_t len, vlen, vlendelta;
534
535
536   /* If the window was already displaying data before this event
537    * was received, we are handling a resize event.
538    * We want to ensure that the relative size of displayed data
539    * remains constant over the resize -- the window simply 'uncovers'
540    * or 'covers' the visualisation.
541    */
542   if (s->width > 1) {
543
544     len = s->view->sample->sounddata->nr_frames;
545     vlen = s->view->end - s->view->start;
546
547     /* If we're already viewing EXACTLY the entire sample,
548      * and dealing with a widening of the window, then
549      * allow the visualisation to stretch. */
550     if (s->view->start == 0 && vlen == len && w > s->width)
551       goto stretch;
552
553     /*
554      * Funky integer error minimisation: this gives non-lossy results,
555      *  as opposed to just using:
556      *
557      * s->view->end = s->view->start +
558      *   (s->view->end - s->view->start) * w / s->width;
559      *
560      * However there is a noticeable waver when resizing the display
561      * when zoomed in far enough that individual samples are visible.
562      *
563      * The alternative is to represent s->view->end (from which the
564      * visible length is determined) as a floating point number.
565      */
566
567     vlendelta = vlen * (w - s->width) / s->width;
568
569     if (vlen+vlendelta > len) {
570       s->view->start = (len - (vlen+vlendelta)) / 2;
571       s->view->end = s->view->start + vlen + vlendelta;
572     } else if (s->view->start < 0) {
573       s->view->end += vlendelta - s->view->start;
574       s->view->start = 0;
575     } else if (s->view->end > len) {
576       s->view->start = s->view->end - vlen - vlendelta;
577       s->view->end = len;
578     } else {
579       s->view->end += vlendelta;
580     }
581
582     g_signal_emit_by_name(GTK_OBJECT(s), "window-changed");
583   }
584
585  stretch:
586  
587   s->width = w;
588   s->height = h;
589
590   window = GTK_WIDGET(s)->window;
591   visual = gdk_rgb_get_visual();
592
593 #if DOUBLE_BUFFER
594   if(s->backing_pixmap) {
595     g_object_unref(s->backing_pixmap);
596   }
597   s->backing_pixmap = gdk_pixmap_new (GTK_WIDGET(s)->window, 
598                                       w, h, visual->depth);
599 #endif
600
601 }
602
603 static void
604 sample_display_size_request (GtkWidget *widget,
605                              GtkRequisition *requisition)
606 {
607   requisition->width = 40;
608   requisition->height = 20;
609 }
610
611 static void
612 sample_display_size_allocate (GtkWidget *widget,
613                               GtkAllocation *allocation)
614 {
615   SampleDisplay *s;
616
617   g_return_if_fail (widget != NULL);
618   g_return_if_fail (IS_SAMPLE_DISPLAY (widget));
619   g_return_if_fail (allocation != NULL);
620
621   widget->allocation = *allocation;
622   if (GTK_WIDGET_REALIZED (widget)) {
623     s = SAMPLE_DISPLAY (widget);
624
625     gdk_window_move_resize (widget->window,
626                             allocation->x, allocation->y,
627                             allocation->width, allocation->height);
628
629     sample_display_init_display(s, allocation->width, allocation->height);
630   }
631 }
632
633 static void
634 sample_display_realize (GtkWidget *widget)
635 {
636   GdkWindowAttr attributes;
637   gint attributes_mask;
638   SampleDisplay *s;
639   gint i;
640
641   g_return_if_fail (widget != NULL);
642   g_return_if_fail (IS_SAMPLE_DISPLAY (widget));
643
644   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
645   s = SAMPLE_DISPLAY(widget);
646
647   attributes.x = widget->allocation.x;
648   attributes.y = widget->allocation.y;
649   attributes.width = widget->allocation.width;
650   attributes.height = widget->allocation.height;
651   attributes.wclass = GDK_INPUT_OUTPUT;
652   attributes.window_type = GDK_WINDOW_CHILD;
653   attributes.event_mask = GDK_ALL_EVENTS_MASK;
654
655   attributes.visual = gtk_widget_get_visual (widget);
656   attributes.colormap = gtk_widget_get_colormap (widget);
657
658   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
659   widget->window = gdk_window_new (widget->parent->window,
660                                    &attributes, attributes_mask);
661
662   widget->style = gtk_style_attach (widget->style, widget->window);
663
664   s->width = 0;
665
666   s->bg_gc = gdk_gc_new(widget->window);
667   gdk_gc_set_foreground(s->bg_gc, &SAMPLE_DISPLAY_CLASS(GTK_WIDGET_GET_CLASS(widget))->colors[SAMPLE_DISPLAYCOL_BG]);
668
669   s->fg_gc = gdk_gc_new(widget->window);
670   gdk_gc_set_foreground(s->fg_gc, &SAMPLE_DISPLAY_CLASS(GTK_WIDGET_GET_CLASS(widget))->colors[SAMPLE_DISPLAYCOL_FG]);
671
672   s->zeroline_gc = gdk_gc_new(widget->window);
673   gdk_gc_set_foreground(s->zeroline_gc, &SAMPLE_DISPLAY_CLASS(GTK_WIDGET_GET_CLASS(widget))->colors[SAMPLE_DISPLAYCOL_ZERO]);
674
675   s->play_gc = gdk_gc_new(widget->window);
676   gdk_gc_set_foreground(s->play_gc, &SAMPLE_DISPLAY_CLASS(GTK_WIDGET_GET_CLASS(widget))->colors[SAMPLE_DISPLAYCOL_PLAY]);
677   gdk_gc_set_function (s->play_gc, GDK_OR_REVERSE);
678
679   s->user_gc = gdk_gc_new(widget->window);
680   gdk_gc_set_foreground(s->user_gc, &SAMPLE_DISPLAY_CLASS(GTK_WIDGET_GET_CLASS(widget))->colors[SAMPLE_DISPLAYCOL_PAUSE]);
681
682   s->rec_gc = gdk_gc_new(widget->window);
683   gdk_gc_set_foreground(s->rec_gc, &SAMPLE_DISPLAY_CLASS(GTK_WIDGET_GET_CLASS(widget))->colors[SAMPLE_DISPLAYCOL_REC]);
684  
685   s->sel_gc = gdk_gc_new(widget->window);
686   gdk_gc_set_foreground(s->sel_gc, &SAMPLE_DISPLAY_CLASS(GTK_WIDGET_GET_CLASS(widget))->colors[SAMPLE_DISPLAYCOL_SEL]);
687   gdk_gc_set_line_attributes(s->sel_gc, 1 /* line width */,
688                              GDK_LINE_DOUBLE_DASH,
689                              GDK_CAP_BUTT,
690                              GDK_JOIN_MITER);
691
692   s->tmp_sel_gc = gdk_gc_new(widget->window);
693   gdk_gc_set_foreground(s->tmp_sel_gc, &SAMPLE_DISPLAY_CLASS(GTK_WIDGET_GET_CLASS(widget))->colors[SAMPLE_DISPLAYCOL_TMP_SEL]);
694   gdk_gc_set_function (s->tmp_sel_gc, GDK_XOR);
695
696   s->crossing_gc = gdk_gc_new(widget->window);
697   gdk_gc_set_foreground(s->crossing_gc, &SAMPLE_DISPLAY_CLASS(GTK_WIDGET_GET_CLASS(widget))->colors[SAMPLE_DISPLAYCOL_CROSSING]);
698
699   s->minmax_gc = gdk_gc_new (widget->window);
700   gdk_gc_set_foreground(s->minmax_gc, &SAMPLE_DISPLAY_CLASS(GTK_WIDGET_GET_CLASS(widget))->colors[SAMPLE_DISPLAYCOL_MINMAX]);
701
702   s->highlight_gc = gdk_gc_new (widget->window);
703   gdk_gc_set_foreground(s->highlight_gc, &SAMPLE_DISPLAY_CLASS(GTK_WIDGET_GET_CLASS(widget))->colors[SAMPLE_DISPLAYCOL_HIGHLIGHT]);
704
705   s->lowlight_gc = gdk_gc_new (widget->window);
706   gdk_gc_set_foreground(s->lowlight_gc, &SAMPLE_DISPLAY_CLASS(GTK_WIDGET_GET_CLASS(widget))->colors[SAMPLE_DISPLAYCOL_LOWLIGHT]);
707
708   for (i = 0; i < VIEW_COLOR_MAX; i++) {
709     s->bg_gcs[i] = gdk_gc_new (widget->window);
710     gdk_gc_set_foreground(s->bg_gcs[i], &SAMPLE_DISPLAY_CLASS(GTK_WIDGET_GET_CLASS(widget))->bg_colors[i]);
711
712     s->fg_gcs[i] = gdk_gc_new (widget->window);
713     gdk_gc_set_foreground(s->fg_gcs[i], &SAMPLE_DISPLAY_CLASS(GTK_WIDGET_GET_CLASS(widget))->fg_colors[i]);
714   }
715
716   sample_display_init_display(s, attributes.width, attributes.height);
717
718   sample_display_set_default_cursor (s);
719
720   gdk_window_set_user_data (widget->window, widget);
721 }
722
723
724 static void
725 sample_display_draw_data_channel (GdkDrawable * win,
726                                   const SampleDisplay * s,
727                                   int x,
728                                   int y,
729                                   int width,
730                                   int height,
731                                   int channel)
732 {
733   GList * gl;
734   GdkGC * gc, * fg_gc;
735   sw_sel * sel;
736   int x1, x2, y1;
737   sw_audio_t vhigh, vlow;
738   sw_audio_intermediate_t totpos, totneg;
739   sw_audio_t d, maxpos, avgpos, minneg, avgneg;
740   sw_audio_t prev_maxpos, prev_minneg;
741   sw_framecount_t i, step, nr_frames, nr_pos, nr_neg;
742   sw_sample * sample;
743   const int channels = s->view->sample->sounddata->format->channels;
744
745   sample = s->view->sample;
746
747   fg_gc = s->fg_gcs[sample->color];
748
749   gdk_draw_rectangle(win, s->bg_gcs[sample->color],
750                      TRUE, x, y, width, height);
751
752   /* Draw real selection */
753   for (gl = sample->sounddata->sels; gl; gl = gl->next) {
754     sel = (sw_sel *)gl->data;
755
756     x1 = OFFSET_TO_XPOS(sel->sel_start);
757     x1 = CLAMP(x1, x, x+width);
758
759     x2 = OFFSET_TO_XPOS(sel->sel_end);
760     x2 = CLAMP(x2, x, x+width);
761
762     if (x2 - x1 > 1){
763       gdk_draw_rectangle (win, s->crossing_gc, TRUE,
764                           x1, y, x2 - x1, y + height -1);
765     }
766   }
767
768   /* Draw temporary selection */
769   sel = sample->tmp_sel;
770
771   if (sel) {
772     if (sel->sel_start != sel->sel_end) {
773       x1 = OFFSET_TO_XPOS(sel->sel_start);
774       x1 = CLAMP(x1, x, x+width);
775      
776       x2 = OFFSET_TO_XPOS(sel->sel_end);
777       x2 = CLAMP(x2, x, x+width);
778      
779       if (x2 - x1 > 1) {
780         gdk_draw_rectangle (win, s->tmp_sel_gc, TRUE,
781                             x1, y, x2 - x1, y + height -1);
782       }
783     }
784   }
785
786   vhigh = s->view->vhigh;
787   vlow = s->view->vlow;
788
789 #define YPOS(v) CLAMP(y + height - ((((v) - vlow) * height) \
790                                / (vhigh - vlow)), y, y+height)
791
792   /* Draw zero and 6db lines */
793   y1 = YPOS(0.5);
794   gdk_draw_line(win, s->zeroline_gc,
795                 x, y1, x + width - 1, y1);
796
797   y1 = YPOS(0.0);
798   gdk_draw_line(win, s->zeroline_gc,
799                 x, y1, x + width - 1, y1);
800
801   y1 = YPOS(-0.5);
802   gdk_draw_line(win, s->zeroline_gc,
803                 x, y1, x + width - 1, y1);
804
805   totpos = totneg = 0.0;
806   maxpos = minneg = prev_maxpos = prev_minneg = 0.0;
807
808   nr_frames = sample->sounddata->nr_frames;
809
810   /* 'step' ensures that no more than STEP_MAX values get looked at
811    * per pixel */
812   step = MAX (1, PIXEL_TO_OFFSET(1)/STEP_MAX);
813
814 #ifdef LEGACY_DRAW_MODE
815   {
816     int py, ty;
817     sw_audio_t peak;
818
819     py = y+height/2;
820
821     while (width >= 0) {
822       peak = 0;
823       for (i = OFFSET_RANGE(nr_frames, XPOS_TO_OFFSET(x));
824            i < OFFSET_RANGE(nr_frames, XPOS_TO_OFFSET(x+1));
825            i+=step) {
826         d = ((sw_audio_t *)sample->sounddata->data)[i*channels + channel];
827         if (fabs(d) > fabs(peak)) peak = d;
828       }
829
830       ty = YPOS(peak);
831
832       gdk_draw_line (win, fg_gc, x-1, py, x, ty);
833
834       py = ty;
835
836       x++;
837       width--;
838     }
839   }
840
841 #else
842
843   for (i = OFFSET_RANGE (nr_frames, XPOS_TO_OFFSET(x-1));
844        i < OFFSET_RANGE (nr_frames, XPOS_TO_OFFSET(x));
845        i += step) {
846     d = ((sw_audio_t *)sample->sounddata->data)[i*channels + channel];
847     if (d >= 0) {
848       if (d > prev_maxpos) prev_maxpos = d;
849     } else {
850       if (d < prev_minneg) prev_minneg = d;
851     }
852   }
853
854   while(width >= 0) {
855     nr_pos = nr_neg = 0;
856     totpos = totneg = 0;
857    
858     maxpos = minneg = 0;
859
860     /* lock the sounddata against destructive ops to make sure
861      * sounddata->data doesn't change under us */
862     g_mutex_lock (sample->ops_mutex);
863
864     for (i = OFFSET_RANGE(nr_frames, XPOS_TO_OFFSET(x));
865          i < OFFSET_RANGE(nr_frames, XPOS_TO_OFFSET(x+1));
866          i+=step) {
867       d = ((sw_audio_t *)sample->sounddata->data)[i*channels + channel];
868       if (d >= 0) {
869         if (d > maxpos) maxpos = d;
870         totpos += d;
871         nr_pos++;
872       } else {
873         if (d < minneg) minneg = d;
874         totneg += d;
875         nr_neg++;
876       }
877     }
878
879     g_mutex_unlock (sample->ops_mutex);
880    
881     if (nr_pos > 0) {
882       avgpos = totpos / nr_pos;
883     } else {
884       avgpos = 0;
885     }
886
887     if (nr_neg > 0) {
888       avgneg = totneg / nr_neg;
889     } else {
890       avgneg = 0;
891     }
892
893     gdk_draw_line(win, s->minmax_gc,
894                   x, YPOS(maxpos),
895                   x, YPOS(minneg));
896
897     gc = maxpos > prev_maxpos ? s->highlight_gc : s->lowlight_gc;
898     gdk_draw_line(win, gc,
899                   x, YPOS(prev_maxpos),
900                   x, YPOS(maxpos));
901
902     gc = minneg > prev_minneg ? s->lowlight_gc : s->highlight_gc;
903     gdk_draw_line(win, gc,
904                   x, YPOS(prev_minneg),
905                   x, YPOS(minneg));
906
907     gdk_draw_line(win, fg_gc,
908                   x, YPOS(avgpos),
909                   x, YPOS(avgneg));
910
911     prev_maxpos = maxpos;
912     prev_minneg = minneg;
913
914     x++;
915     width--;
916   }
917 #endif
918
919 }
920
921 static void
922 sample_display_draw_data (GdkDrawable *win, const SampleDisplay *s,
923                           int x, int width)
924 {
925   const int sh = s->height;
926   int start_x, end_x, i, cy, cheight, cerr;
927   const int channels = s->view->sample->sounddata->format->channels;
928
929   if (width == 0)
930     return;
931
932   g_return_if_fail(x >= 0);
933   g_return_if_fail(x + width <= s->width);
934
935 #ifdef DEBUG
936   g_print("draw_data: view %u --> %u, drawing x=%d, width=%d\n",
937           s->view->start, s->view->end, x, width);
938 #endif
939
940   start_x = OFFSET_TO_XPOS(0);
941   end_x = OFFSET_TO_XPOS(s->view->sample->sounddata->nr_frames);
942
943   if (start_x > x + width || end_x < x) {
944     gtk_style_apply_default_background (GTK_WIDGET(s)->style, win,
945                                         TRUE, GTK_STATE_NORMAL,
946                                         NULL,
947                                         x, 0,
948                                         width, sh);
949     return;
950   }
951
952   if (start_x > x) {
953     gtk_style_apply_default_background (GTK_WIDGET(s)->style, win,
954                                         TRUE, GTK_STATE_NORMAL,
955                                         NULL,
956                                         x, 0,
957                                         start_x - x, sh);
958
959     if (start_x > 0)
960       gdk_draw_line (win, s->lowlight_gc, start_x-1, 0, start_x-1, sh);
961
962     x = start_x;
963   }
964
965   if (end_x < x + width) {
966     gtk_style_apply_default_background (GTK_WIDGET(s)->style, win,
967                                         TRUE, GTK_STATE_NORMAL,
968                                         NULL,
969                                         end_x, 0,
970                                         x + width - end_x, sh);
971
972     gdk_draw_line (win, s->highlight_gc,
973                    end_x, 0, end_x, sh);
974
975     width = end_x - x;
976   }
977
978   cheight = sh / channels;
979   cerr = sh - (channels * cheight);
980   if (cerr == channels - 1) {
981     cheight++;
982     cerr = 0;
983   }
984   cy = 0;
985
986   for (i = 0; i < channels; i++) {
987     if (i >= 0) {
988       gtk_style_apply_default_background (GTK_WIDGET(s)->style, win,
989                                           TRUE, GTK_STATE_NORMAL, NULL,
990                                           x, cy, width, 1);
991       cy += 1;
992     }
993
994     sample_display_draw_data_channel (win, s,
995                                       x, cy, width, cheight-2, i);
996     cy += cheight-2;
997
998     gtk_style_apply_default_background (GTK_WIDGET(s)->style, win,
999                                         TRUE, GTK_STATE_NORMAL, NULL,
1000                                         x, cy, width, 1);
1001     cy += 1;
1002
1003   }
1004
1005   if (cy < sh) {
1006     gtk_style_apply_default_background (GTK_WIDGET(s)->style, win,
1007                                         TRUE, GTK_STATE_NORMAL, NULL,
1008                                         x, cy, width, sh-cy);
1009   }
1010
1011 }
1012
1013 /*** CROSSING VECTORS ***/
1014
1015 #ifdef DRAW_CROSSING_VECTORS
1016
1017 static void
1018 sample_display_draw_crossing_vector (GdkDrawable * win,
1019                                      GdkGC * gc,
1020                                      const SampleDisplay * s,
1021                                      int x)
1022 {
1023   sw_sample * sample = s->view->sample;
1024   const int sh = s->height;
1025   int cx1, cx2, cy1, cy2;
1026
1027 #define VRAD 8
1028
1029   cx1 = ( x > VRAD ? VRAD : 0 );
1030   cx2 = ( x < sample->sounddata->nr_frames - VRAD ? VRAD : 0 );
1031
1032   cy1 = ((sw_audio_t *)sample->sounddata->data)[OFFSET_RANGE(sample->sounddata->nr_frames, XPOS_TO_OFFSET(x)) - cx1];
1033   cy2 = ((sw_audio_t *)sample->sounddata->data)[OFFSET_RANGE(sample->sounddata->nr_frames, XPOS_TO_OFFSET(x)) + cx2];
1034  
1035   gdk_draw_line(win, s->crossing_gc,
1036                 x - cx1, (((cy1 + 1.0) * sh) / 2.0),
1037                 x + cx2, (((cy2 + 1.0) * sh) / 2.0));
1038 }       
1039                              
1040 #endif
1041
1042 /*** MARCHING ANTS ***/
1043
1044 /*
1045  * sample_display_sel_box_march_ants ()
1046  *
1047  * gtk_idle function to move the marching ants used by
1048  * selections.
1049  */
1050 static gint
1051 sd_march_ants (gpointer data)
1052 {
1053   SampleDisplay * s = (SampleDisplay *)data;
1054   GdkGC * gc = s->sel_gc;
1055   static int dash_offset = 0;
1056
1057   gdk_gc_set_dashes (gc, dash_offset, sel_dash_list, 2);
1058
1059   dash_offset++;
1060   dash_offset %= 8;
1061
1062   sample_display_refresh_sels (s);
1063
1064   return TRUE;
1065 }
1066
1067 static void
1068 sd_start_marching_ants_timeout (SampleDisplay * s)
1069 {
1070   if (s->marching_tag > 0)
1071     g_source_remove (s->marching_tag);
1072
1073   s->marching_tag = g_timeout_add (MARCH_INTERVAL,
1074                                      (GSourceFunc)sd_march_ants,
1075                                      s);
1076 }
1077
1078 void
1079 sample_display_start_marching_ants (SampleDisplay * s)
1080 {
1081   sd_start_marching_ants_timeout (s);
1082   s->marching = TRUE;
1083 }
1084
1085 static void
1086 sd_stop_marching_ants_timeout (SampleDisplay * s)
1087 {
1088   if (s->marching_tag > 0)
1089     g_source_remove (s->marching_tag);
1090
1091   s->marching_tag = 0;
1092 }
1093
1094 void
1095 sample_display_stop_marching_ants (SampleDisplay * s)
1096 {
1097   sd_stop_marching_ants_timeout (s);
1098   s->marching = FALSE;
1099 }
1100
1101 /*** SELECTION BOXES ***/
1102
1103 static void
1104 sample_display_draw_sel_box(GdkDrawable * win,
1105                             GdkGC * gc,
1106                             const SampleDisplay * s,
1107                             int x,
1108                             int width,
1109                             int draw_left, int draw_right)
1110 {
1111   if (width <= 0) {
1112     gdk_draw_line (win, gc, x, 0, x, s->height - 1);
1113     return;
1114   }
1115
1116   /* Must draw individual lines for these: if you optimise by
1117    * drawing a rectangle where possible, the marching ants go
1118    * crazy. We don't want that to happen, they are cute.
1119    */
1120
1121   gdk_draw_line(win, gc,
1122                 x, 0,
1123                 x+width, 0);
1124   gdk_draw_line(win, gc,
1125                   x, s->height - 1,
1126                 x+width, s->height - 1);
1127   if (draw_left) {
1128     gdk_draw_line(win, gc,
1129                   x, 0,
1130                   x, s->height - 1);
1131    
1132 #ifdef DRAW_CROSSING_VECTORS
1133     /* crossing vector */
1134     sample_display_draw_crossing_vector (win, gc, s, x);
1135 #endif
1136   }
1137   if (draw_right) {
1138     gdk_draw_line(win, gc,
1139                   x+width, 0,
1140                   x+width, s->height - 1);
1141    
1142 #ifdef DRAW_CROSSING_VECTORS
1143     /* crossing vector */
1144     sample_display_draw_crossing_vector (win, gc, s, x+width);
1145 #endif
1146   }
1147 }
1148
1149 static void
1150 sample_display_draw_sel (GdkDrawable * win,
1151                          const SampleDisplay * s,
1152                          int x_min, int x_max)
1153 {
1154   sw_sample * sample = s->view->sample;
1155   GList * gl;
1156   sw_sel * sel;
1157   int x, x2;
1158   int l_end, r_end; /* draw left + right ends of sel */
1159
1160   /* Draw real selection */
1161   for (gl = sample->sounddata->sels; gl; gl = gl->next) {
1162     sel = (sw_sel *)gl->data;
1163
1164     x = OFFSET_TO_XPOS(sel->sel_start);
1165     x2 = OFFSET_TO_XPOS(sel->sel_end);
1166
1167     if (x > x_max) break;
1168     if (x2 < x_min) continue;
1169
1170     l_end = (x >= x_min) && (x <= x_max);
1171     x = CLAMP (x, x_min, x_max);
1172    
1173     r_end = (x2 >= x_min) && (x2 <= x_max);
1174     x2 = CLAMP (x2, x_min, x_max);
1175    
1176     /* draw the selection */
1177     sample_display_draw_sel_box(win, s->sel_gc,
1178                                 s, x, x2 - x - 1,
1179                                 l_end, r_end /* draw_ends */);
1180
1181
1182   }
1183
1184   /* Draw temporary selection */
1185   sel = sample->tmp_sel;
1186
1187   if (sel) {
1188     x = OFFSET_TO_XPOS(sel->sel_start);
1189     l_end = (x >= x_min) && (x <= x_max);
1190     x = CLAMP (x, x_min, x_max);
1191
1192     x2 = OFFSET_TO_XPOS(sel->sel_end);
1193     r_end = (x2 >= x_min) && (x2 <= x_max);
1194     x2 = CLAMP (x2, x_min, x_max);
1195
1196     /* draw the selection */
1197     sample_display_draw_sel_box(win, s->tmp_sel_gc,
1198                                 s, x, x2 - x - 1,
1199                                 l_end, r_end /* draw_ends */);
1200   }
1201 }
1202
1203
1204 static gint
1205 sd_pulse_cursor (gpointer data)
1206 {
1207   SampleDisplay * s = (SampleDisplay *)data;
1208
1209   if (s->pulse)
1210     gdk_gc_set_function (s->user_gc, GDK_NOOP);
1211   else
1212     gdk_gc_set_function (s->user_gc, GDK_COPY);
1213
1214   s->pulse = (!s->pulse);
1215
1216   sample_display_refresh_user_marker (s);
1217
1218   return TRUE;
1219 }
1220
1221 void
1222 sample_display_start_cursor_pulse (SampleDisplay * s)
1223 {
1224   gdk_gc_set_function (s->user_gc, GDK_NOOP);
1225
1226   sample_display_refresh_user_marker (s);
1227
1228   s->pulsing_tag = g_timeout_add (PULSE_INTERVAL,
1229                                     (GSourceFunc)sd_pulse_cursor, s);
1230 }
1231
1232 void
1233 sample_display_stop_cursor_pulse (SampleDisplay * s)
1234 {
1235   if (s->pulsing_tag > 0)
1236     g_source_remove (s->pulsing_tag);
1237
1238   s->pulsing_tag = 0;
1239
1240   gdk_gc_set_function (s->user_gc, GDK_COPY);
1241
1242   sample_display_refresh_user_marker (s);
1243 }
1244
1245 static void
1246 sample_display_draw_user_offset (GdkDrawable * win, GdkGC * gc,
1247                                  SampleDisplay * s, int x,
1248                                  int x_min, int x_max)
1249 {
1250   GdkPoint poly[4];
1251   gboolean fill;
1252
1253   if(x >= x_min && x <= x_max) {
1254     gdk_draw_line(win, s->zeroline_gc,
1255                   x-2, 0,
1256                   x-2, s->height);
1257
1258     gdk_draw_line(win, gc,
1259                   x-1, 0,
1260                   x-1, s->height);
1261     gdk_draw_line(win, gc,
1262                   x+1, 0,
1263                   x+1, s->height);
1264
1265     gdk_draw_line(win, s->zeroline_gc,
1266                   x+2, 0,
1267                   x+2, s->height);
1268
1269
1270
1271     if (!s->view->sample->play_head->going) {
1272       fill = !s->view->sample->play_head->mute;
1273
1274       if (x < 20) {
1275         poly[0].x = 11;
1276         poly[1].x = 11;
1277         poly[2].x = 14;
1278         poly[3].x = 14;
1279       } else {
1280         poly[0].x = x - 8;
1281         poly[1].x = x - 8;
1282         poly[2].x = x - 5;
1283         poly[3].x = x - 5;
1284       }
1285
1286       poly[0].y = 4;
1287       poly[1].y = 14;
1288       poly[2].y = 14;
1289       poly[3].y = 4;
1290
1291       gdk_draw_polygon (win, gc, fill, poly, 4);
1292       gdk_draw_polygon (win, s->zeroline_gc, FALSE, poly, 4);
1293
1294       poly[0].x -= 5;
1295       poly[1].x -= 5;
1296       poly[2].x -= 5;
1297       poly[3].x -= 5;
1298
1299       gdk_draw_polygon (win, gc, fill, poly, 4);
1300       gdk_draw_polygon (win, s->zeroline_gc, FALSE, poly, 4);
1301     }
1302   }
1303 }
1304
1305 static void
1306 sample_display_draw_play_offset (GdkDrawable * win, GdkGC * gc,
1307                                  SampleDisplay * s, int x,
1308                                  int x_min, int x_max)
1309 {
1310   sw_sample * sample;
1311   GdkPoint poly[4];
1312   sw_head * head;
1313
1314   if(x >= x_min && x <= x_max) {
1315     gdk_draw_rectangle(win, gc, TRUE,
1316                        x-1, 0,
1317                        3, s->height);
1318
1319     sample = s->view->sample;
1320     head = sample->play_head;
1321
1322     if (head->going) {
1323       if (x < 20) {
1324         if (head->reverse) {
1325           poly[0].x = 14;
1326           poly[1].x = 14;
1327           poly[2].x = 6;
1328         } else {
1329           poly[0].x = 6;
1330           poly[1].x = 6;
1331           poly[2].x = 14;
1332         }
1333       } else {
1334         if (head->reverse) {
1335           poly[0].x = x - 5;
1336           poly[1].x = x - 5;
1337           poly[2].x = x - 13;
1338         } else {
1339           poly[0].x = x - 13;
1340           poly[1].x = x - 13;
1341           poly[2].x = x - 5;
1342         }
1343       }
1344
1345       poly[0].y = 4;
1346       poly[1].y = 14;
1347       poly[2].y = 9;
1348      
1349       gdk_draw_polygon (win, gc, !head->mute, poly, 3);
1350       gdk_draw_polygon (win, s->zeroline_gc, FALSE, poly, 3);
1351     }
1352   }
1353 }
1354
1355 static void
1356 sample_display_draw_rec_offset (GdkDrawable * win, GdkGC * gc,
1357                                 SampleDisplay * s, int x,
1358                                 int x_min, int x_max)
1359 {
1360   sw_sample * sample;
1361
1362   if(x >= x_min && x <= x_max) {
1363     gdk_draw_line(win, s->zeroline_gc,
1364                   x-2, 0,
1365                   x-2, s->height);
1366
1367     gdk_draw_line(win, gc,
1368                   x-1, 0,
1369                   x-1, s->height);
1370     gdk_draw_line(win, gc,
1371                   x+1, 0,
1372                   x+1, s->height);
1373
1374     gdk_draw_line(win, s->zeroline_gc,
1375                   x+2, 0,
1376                   x+2, s->height);
1377
1378     sample = s->view->sample;
1379
1380     if (x < 20) x = 19;
1381     gdk_draw_arc (win, gc, TRUE, x-14, 15, 8, 8, 0, 360 * 64);
1382     gdk_draw_arc (win, s->zeroline_gc, FALSE, x-14, 15, 8, 8, 0, 360 * 64);
1383
1384   }
1385 }
1386
1387
1388 /*** DRAW ***/
1389
1390 static void
1391 sample_display_draw (GtkWidget *widget, GdkRectangle *area)
1392 {
1393   SampleDisplay *s = SAMPLE_DISPLAY(widget);
1394   sw_sample * sample = s->view->sample;
1395   GdkDrawable * drawable;
1396
1397   /*  g_return_if_fail(area->x >= 0);*/
1398   if (area->x < 0) return;
1399   if (area->y < 0) return;
1400
1401   if(area->width == 0)
1402     return;
1403
1404   if(area->x + area->width > s->width)
1405     return;
1406
1407 #ifdef DEBUG
1408     g_print ("sample_display_draw: (%d, %d) [%d, %d]\n",
1409              area->x, area->y, area->width, area->height);
1410 #endif
1411
1412   if(!IS_INITIALIZED(s)) {
1413     gtk_style_apply_default_background (GTK_WIDGET(s)->style, widget->window,
1414                                         TRUE, GTK_STATE_NORMAL,
1415                                         NULL, 0, 0, s->width, s->height);
1416   } else {
1417     const int x_min = area->x;
1418     const int x_max = area->x + area->width;
1419
1420 #ifdef DOUBLE_BUFFER
1421     drawable = s->backing_pixmap;
1422 #else
1423     drawable = widget->window;
1424 #endif
1425
1426     /* draw the sample graph */
1427     sample_display_draw_data(drawable, s, x_min, x_max - x_min);
1428
1429     /* draw the selection bounds */
1430     sample_display_draw_sel (drawable, s, x_min, x_max);
1431
1432
1433     /* draw the offset cursors */
1434
1435     if(!sample->play_head->going) {
1436       /* Draw user offset */
1437       sample_display_draw_user_offset (drawable, s->user_gc,
1438                                        s, s->user_offset_x,
1439                                        x_min, x_max);
1440     } else {
1441       /* Draw play offset */
1442       sample_display_draw_play_offset (drawable, s->play_gc,
1443                                        s, s->play_offset_x,
1444                                        x_min, x_max);
1445       /* Draw user offset */
1446       sample_display_draw_user_offset (drawable, s->user_gc,
1447                                        s, s->user_offset_x,
1448                                        x_min, x_max);
1449
1450     }
1451
1452     /* Draw rec offset */
1453     if (sample->rec_head /*&& sample->rec_head->transport_mode != SWEEP_TRANSPORT_STOP*/ ) {
1454       sample_display_draw_rec_offset (drawable, s->rec_gc,
1455                                       s, s->rec_offset_x,
1456                                       x_min, x_max);
1457     }
1458
1459 #ifdef DOUBLE_BUFFER
1460     gdk_draw_pixmap(widget->window, s->fg_gc, s->backing_pixmap,
1461                     area->x, area->y,
1462                     area->x, area->y,
1463                     area->width, area->height);
1464 #endif
1465   }
1466 }
1467
1468 /*** EVENT HANDLERS ***/
1469
1470
1471 static gint
1472 sample_display_expose (GtkWidget *widget,
1473                        GdkEventExpose *event)
1474 {
1475   GdkRectangle * a;
1476   a = &event->area;
1477
1478 #ifdef DEBUG
1479   g_print ("received expose event for (%d, %d) [%d, %d]; %d follow\n",
1480            a->x, a->y, a->width, a->height, event->count);
1481 #endif
1482
1483   sample_display_draw (widget, a);
1484
1485   return FALSE;
1486 }
1487
1488 static gint
1489 sample_display_hand_scroll (SampleDisplay * s)
1490 {
1491   gint new_win_start, win_length;
1492   gfloat step;
1493  
1494   win_length = s->view->end - s->view->start;
1495
1496   step = win_length * 1.0 / s->width;
1497
1498   new_win_start = s->view->start + s->hand_scroll_delta * step;
1499  
1500   new_win_start = CLAMP(new_win_start, 0,
1501                         s->view->sample->sounddata->nr_frames -
1502                         (s->view->end - s->view->start));
1503
1504   if(new_win_start != s->view->start) {
1505     sample_display_set_window (s,
1506                                new_win_start,
1507                                new_win_start + win_length);
1508   } else {
1509           s->hand_scroll_delta = 0;
1510   }
1511 /*
1512   g_print ("s->delta: %i new_win_start: %i\n", s->hand_scroll_delta, new_win_start);
1513 */
1514   s->hand_scroll_delta *= 0.98;
1515
1516   return (s->hand_scroll_delta != 0);
1517 }
1518
1519 static gint
1520 sample_display_scroll_left (gpointer data)
1521 {
1522   SampleDisplay * s = (SampleDisplay *)data;
1523   int new_win_start, win_length;
1524  
1525   win_length = s->view->end - s->view->start;
1526   new_win_start = s->view->start - win_length/8;
1527  
1528   new_win_start = CLAMP(new_win_start, 0,
1529                         s->view->sample->sounddata->nr_frames -
1530                         (s->view->end - s->view->start));
1531  
1532   if(new_win_start != s->view->start) {
1533     sample_display_set_window (s,
1534                                new_win_start,
1535                                new_win_start + win_length);
1536   }
1537
1538   s->view->sample->tmp_sel->sel_start = new_win_start;
1539
1540   return (new_win_start > 0);
1541 }
1542
1543 static gint
1544 sample_display_scroll_right (gpointer data)
1545 {
1546   SampleDisplay * s = (SampleDisplay *)data;
1547   int new_win_start, win_length;
1548  
1549   win_length = s->view->end - s->view->start;
1550   new_win_start = s->view->start + win_length/8;
1551  
1552   new_win_start = CLAMP(new_win_start, 0,
1553                         s->view->sample->sounddata->nr_frames -
1554                         (s->view->end - s->view->start));
1555  
1556   if(new_win_start != s->view->start) {
1557     sample_display_set_window (s,
1558                                new_win_start,
1559                                new_win_start + win_length);
1560   }
1561
1562   s->view->sample->tmp_sel->sel_end = s->view->end;
1563
1564   return (new_win_start >= (s->view->end - win_length));
1565 }
1566
1567 static void
1568 sample_display_handle_playmarker_motion (SampleDisplay * s, int x, int y)
1569 {
1570   sw_sample * sample;
1571   sw_framecount_t offset;
1572
1573   sample = s->view->sample;
1574   offset = XPOS_TO_OFFSET(x);
1575
1576   sample_set_playmarker (sample, offset, TRUE);
1577 }
1578
1579 void
1580 sample_display_clear_sel (SampleDisplay * s)
1581 {
1582   sample_clear_tmp_sel (s->view->sample);
1583   s->selecting = SELECTING_NOTHING;
1584   s->selection_mode = SELECTION_MODE_NONE;
1585   sample_display_set_default_cursor (s);
1586   sample_clear_tmp_message (s->view->sample);
1587   g_signal_emit_by_name(GTK_OBJECT(s),
1588                   "selection-changed");
1589 }
1590
1591 static void
1592 sample_display_handle_sel_motion (SampleDisplay *s,
1593                               int x,
1594                               int y,
1595                               int just_clicked)
1596 {
1597   sw_sample * sample;
1598   sw_sel * sel;
1599   int o;
1600   int ss, se;
1601   gboolean scroll_left = FALSE, scroll_right = FALSE;
1602
1603   if (s->view->current_tool != TOOL_SELECT) return;
1604
1605   if(!s->selecting)
1606     return;
1607
1608   if (!s->view->sample)
1609     return;
1610
1611   if (!s->view->sample->tmp_sel)
1612     return;
1613
1614   sample = s->view->sample;
1615
1616   if (sample->edit_mode != SWEEP_EDIT_MODE_READY) {
1617     sample_display_clear_sel (s);
1618     return;
1619   }
1620
1621   o = XPOS_TO_OFFSET(x);
1622
1623 #ifdef DEBUG
1624   if (o < 0) {
1625     g_print ("OI! setting an offset < 0!\n");
1626   }
1627 #endif
1628
1629   if (!sample->play_head->going) {
1630     sample_set_playmarker (sample, o, TRUE);
1631   }
1632
1633   sel = sample->tmp_sel;
1634
1635   ss = sel->sel_start;
1636   se = sel->sel_end;
1637
1638   if(x < 0) {
1639     scroll_left = TRUE;
1640     x = 0;
1641   } else if(x >= s->width - 1) {
1642     scroll_right = TRUE;
1643     x = s->width - 1;
1644   }
1645
1646   if (just_clicked) {
1647     ss = o;
1648     se = o+1;
1649   } else {
1650     switch (s->selecting) {
1651     case SELECTING_SELECTION_START:
1652       if (o < se) {
1653         ss = o;
1654       } else {
1655         if (o != ss+1) ss = se;
1656         se = o;
1657         s->selecting = SELECTING_SELECTION_END;      }
1658       break;
1659     case SELECTING_SELECTION_END:
1660       if (o > ss) {
1661         se = o;
1662       } else {
1663         if (o != se-1) se = ss;
1664         ss = o;
1665         s->selecting = SELECTING_SELECTION_START;
1666       }
1667       break;
1668     default:
1669       g_assert_not_reached ();
1670       break;
1671     }
1672   }
1673
1674   if(sel->sel_start != ss || sel->sel_end != se || just_clicked) {
1675     sel->sel_start = ss;
1676     sel->sel_end = se;
1677     g_signal_emit_by_name(GTK_OBJECT(s),
1678                     "selection-changed");
1679   }
1680
1681   if (scroll_left && s->scroll_left_tag == 0) {
1682     if (s->scroll_right_tag != 0) {
1683       g_source_remove (s->scroll_right_tag);
1684       s->scroll_right_tag = 0;
1685     }
1686
1687     s->scroll_left_tag = g_timeout_add (100, sample_display_scroll_left,
1688                                           (gpointer)s);
1689
1690   } else if (scroll_right && s->scroll_right_tag == 0) {
1691     if (s->scroll_left_tag != 0) {
1692       g_source_remove (s->scroll_left_tag);
1693       s->scroll_left_tag = 0;
1694     }
1695
1696     s->scroll_right_tag = g_timeout_add (100, sample_display_scroll_right,
1697                                            (gpointer)s);
1698
1699   } else if (!scroll_right && !scroll_left) {
1700     if (s->scroll_right_tag != 0) {
1701       g_source_remove (s->scroll_right_tag);
1702       s->scroll_right_tag = 0;
1703     }
1704     if (s->scroll_left_tag != 0) {
1705       g_source_remove (s->scroll_left_tag);
1706       s->scroll_left_tag = 0;
1707     }
1708   }
1709 }
1710
1711 /* Handle middle mousebutton display window panning */
1712 static void
1713 sample_display_handle_move_motion (SampleDisplay *s, int x, int y)
1714 {
1715   sw_sample * sample = s->view->sample;
1716   sw_framecount_t vlen, offset_xpos, new_offset;
1717   int new_win_start;
1718
1719   vlen = s->view->end - s->view->start;
1720
1721   offset_xpos = OFFSET_TO_XPOS(sample->user_offset);
1722
1723   new_win_start =
1724     s->selecting_wins0 + (s->selecting_x0 - x) * vlen / s->width;
1725
1726   new_win_start = CLAMP(new_win_start, 0,
1727                         sample->sounddata->nr_frames - vlen);
1728
1729   new_offset = new_win_start + offset_xpos * vlen / s->width;
1730  
1731   sample_set_scrubbing (sample, TRUE);
1732   sample_set_playmarker (sample, new_offset, TRUE);
1733
1734   if(new_win_start != s->view->start) {
1735     sample_display_set_window (s,
1736                                new_win_start,
1737                                new_win_start + vlen);
1738   }
1739 }
1740
1741 static void
1742 sample_display_handle_pencil_motion (SampleDisplay * s, int x, int y)
1743 {
1744   sw_sample * sample;
1745   sw_framecount_t offset;
1746   int channel, channels;
1747   sw_audio_t value;
1748   sw_audio_t * sampledata;
1749
1750   offset = XPOS_TO_OFFSET(x);
1751
1752   if (offset < s->view->start || offset > s->view->end) return;
1753
1754   sample = s->view->sample;
1755   sampledata = (sw_audio_t *)sample->sounddata->data;
1756   channels = sample->sounddata->format->channels;
1757
1758   y = CLAMP (y, 0, s->height);
1759
1760   channel = YPOS_TO_CHANNEL(y);
1761   value = YPOS_TO_VALUE(y);
1762   sampledata[offset*channels + channel] = value;
1763
1764   sample_refresh_views (sample);
1765 }
1766
1767 static void
1768 sample_display_handle_hand_motion (SampleDisplay * s, int x, int y)
1769 {
1770   gdouble move, vstart, vend;
1771   gdouble step = (gdouble)(s->view->end - s->view->start) / ((gdouble)s->width);
1772   GtkAdjustment * adj = GTK_ADJUSTMENT(s->view->adj);
1773   gint delta;
1774
1775   delta = s->view->hand_offset - x;
1776
1777   s->hand_scroll_delta *= 0.9;
1778
1779   if (abs (delta) > abs (s->hand_scroll_delta))
1780           s->hand_scroll_delta = delta;
1781  
1782   if (s->view->hand_offset != x){
1783     move = s->view->hand_offset - x;
1784     move *= step;
1785
1786     vstart = s->view->start + move;
1787     vend = s->view->end + move;
1788
1789     if (vstart < 0){
1790         vstart = 0;
1791         vend = adj->page_size;
1792     }
1793     if (vend > s->view->sample->sounddata->nr_frames){
1794         vstart = s->view->sample->sounddata->nr_frames - adj->page_size;
1795         vend = s->view->sample->sounddata->nr_frames;
1796     }
1797    
1798         vstart = ceil(vstart + (move < 0 ? 0.5 : -0.5));
1799     vend = ceil(vend + (move < 0 ? 0.5 : -0.5));
1800
1801     if (s->view->start != vstart && s->view->end != vend)
1802             s->view->hand_offset = x;
1803
1804     s->view->start = vstart;
1805     s->view->end = vend;
1806
1807     view_refresh_display(s->view);
1808
1809     gtk_adjustment_set_value( GTK_ADJUSTMENT(s->view->adj), vstart);
1810   }
1811 }
1812
1813 static void
1814 sample_display_handle_noise_motion (SampleDisplay * s, int x, int y)
1815 {
1816   sw_sample * sample;
1817   sw_framecount_t offset;
1818   int channel;
1819   sw_audio_t value, oldvalue;
1820   sw_audio_t * sampledata;
1821
1822   offset = XPOS_TO_OFFSET(x);
1823
1824   if (offset < s->view->start || offset > s->view->end) return;
1825
1826   sample = s->view->sample;
1827   sampledata = (sw_audio_t *)sample->sounddata->data;
1828
1829   y = CLAMP (y, 0, s->height);
1830
1831   value = 2.0 * (random() - RAND_MAX/2) / (sw_audio_t)RAND_MAX;
1832
1833   if (sample->sounddata->format->channels == 1) {
1834     oldvalue = sampledata[offset];
1835   } else {
1836     channel = YPOS_TO_CHANNEL(y);
1837     offset = offset*2 + channel;
1838     oldvalue = sampledata[offset];
1839   }
1840
1841   sampledata[offset] = CLAMP(oldvalue * 0.8 + value * 0.2,
1842                              SW_AUDIO_T_MIN, SW_AUDIO_T_MAX);
1843
1844   sample_refresh_views (sample);
1845 }
1846
1847 static void
1848 sample_display_handle_sel_button_press (SampleDisplay * s, int x, int y,
1849                                         GdkModifierType state)
1850 {
1851   sw_sample * sample;
1852   GList * gl;
1853   sw_sel * sel, * tmp_sel = NULL;
1854   int xss, xse;
1855   int min_xs;
1856   gboolean just_clicked = TRUE;
1857
1858   sample = s->view->sample;
1859
1860   if (sample->edit_mode != SWEEP_EDIT_MODE_READY) {
1861     sample_display_clear_sel (s);
1862     return;
1863   }
1864
1865   for (gl = sample->sounddata->sels; gl; gl = gl->next) {
1866
1867     /* If the cursor is near the current start or end of
1868      * the selection, move that.
1869      */
1870
1871     sel = (sw_sel *)gl->data;
1872          
1873     xss = OFFSET_TO_XPOS(sel->sel_start);
1874     xse = OFFSET_TO_XPOS(sel->sel_end);
1875          
1876     if(abs(x-xss) < 5) {
1877       sample_set_tmp_sel (sample, s->view, sel);
1878       s->selecting = SELECTING_SELECTION_START;
1879       s->selection_mode = SELECTION_MODE_INTERSECT;
1880       sample_display_set_intersect_cursor (s);
1881       just_clicked = FALSE;
1882       goto motion;
1883     } else if(abs(x-xse) < 5) {
1884       sample_set_tmp_sel (sample, s->view, sel);
1885       s->selecting = SELECTING_SELECTION_END;
1886       s->selection_mode = SELECTION_MODE_INTERSECT;
1887       sample_display_set_intersect_cursor (s);
1888       just_clicked = FALSE;
1889       goto motion;
1890     }
1891   }
1892
1893   /* If shift is held down, move the closest selection edge to the mouse */
1894
1895   if ((state & GDK_SHIFT_MASK) && (sample->sounddata->sels != NULL)) {
1896     min_xs = G_MAXINT;
1897
1898     for (gl = sample->sounddata->sels; gl; gl = gl->next) {
1899       sel = (sw_sel *)gl->data;
1900      
1901       xss = OFFSET_TO_XPOS(sel->sel_start);
1902       xse = OFFSET_TO_XPOS(sel->sel_end);
1903
1904       if (abs(x-xss) > min_xs) break;
1905
1906       tmp_sel = sel;
1907
1908       min_xs = abs(x-xss);
1909
1910       s->selecting = SELECTING_SELECTION_START;
1911
1912       if (abs(x-xse) > min_xs) break;
1913
1914       min_xs = abs(x-xse);
1915
1916       s->selecting = SELECTING_SELECTION_END;
1917     }
1918
1919     sample_set_tmp_sel (sample, s->view, tmp_sel);
1920     s->selection_mode = SELECTION_MODE_INTERSECT;
1921     sample_display_set_intersect_cursor (s);
1922     just_clicked = FALSE;
1923     goto motion;
1924   }
1925
1926   /* Otherwise, start a new selection region. */
1927        
1928   sample_set_tmp_sel_1(sample, s->view,
1929                        XPOS_TO_OFFSET(x),
1930                        XPOS_TO_OFFSET(x)+1);
1931        
1932   s->selecting = SELECTING_SELECTION_END;
1933
1934   if(state & GDK_CONTROL_MASK) {
1935     s->selection_mode = SELECTION_MODE_INTERSECT;
1936     sample_display_set_intersect_cursor (s);
1937   } else if (state & GDK_MOD1_MASK) /* how to get ALT? */{
1938     s->selection_mode = SELECTION_MODE_SUBTRACT;
1939     sample_display_set_subtract_cursor (s);
1940   } else {
1941     s->selection_mode = SELECTION_MODE_REPLACE;
1942     SET_CURSOR(GTK_WIDGET(s), HORIZ);
1943   }
1944
1945   just_clicked = TRUE;
1946
1947  motion:
1948
1949   sample_set_tmp_message (sample, _(selection_mode_names[s->selection_mode]));
1950   sample_set_progress_ready (sample);
1951
1952   sample_display_handle_sel_motion (s, x, y, just_clicked);
1953 }
1954
1955 static gint
1956 sample_display_on_playmarker (SampleDisplay * s, gint x, gint y)
1957 {
1958   gint xp = OFFSET_TO_XPOS(s->view->sample->user_offset);
1959
1960   if (abs(x-xp) < 5 || ((abs(x-xp) < 15) && (y < 17)))
1961     return TRUE;
1962  
1963   return FALSE;
1964 }
1965
1966 static gint
1967 sample_display_on_sel (SampleDisplay * s, gint x, gint y)
1968 {
1969   GList * gl;
1970   sw_sel * sel;
1971   int xss, xse;
1972
1973   for (gl = s->view->sample->sounddata->sels; gl; gl = gl->next) {
1974     sel = (sw_sel *)gl->data;
1975
1976     xss = OFFSET_TO_XPOS(sel->sel_start);
1977     xse = OFFSET_TO_XPOS(sel->sel_end);
1978
1979     if(abs(x-xss) < 5 || abs(x-xse) < 5)
1980       return TRUE;   
1981   }
1982
1983   return FALSE;
1984 }
1985
1986 static gint
1987 sample_display_scroll_event(GtkWidget *widget,
1988                                  GdkEventScroll *event)
1989 {
1990   SampleDisplay *s;
1991        
1992   s = SAMPLE_DISPLAY(widget);
1993        
1994   if (event->direction == GDK_SCROLL_UP) {    /* mouse wheel scroll up */
1995         view_zoom_in (s->view, 2.0);
1996         return TRUE;
1997   }  else if (event->direction == GDK_SCROLL_DOWN) {   /* mouse wheel scroll down */
1998         view_zoom_out (s->view, 2.0);
1999         return TRUE;
2000   }
2001   return FALSE; /* redundant? */
2002 }
2003
2004 static gint
2005 sample_display_button_press (GtkWidget      *widget,
2006                              GdkEventButton *event)
2007 {
2008   SampleDisplay *s;
2009   GdkModifierType state;
2010   sw_sample * sample;
2011   int x, y;
2012   int o;
2013
2014   g_return_val_if_fail (widget != NULL, FALSE);
2015   g_return_val_if_fail (IS_SAMPLE_DISPLAY (widget), FALSE);
2016   g_return_val_if_fail (event != NULL, FALSE);
2017
2018   s = SAMPLE_DISPLAY(widget);
2019  
2020   if(!IS_INITIALIZED(s))
2021     return TRUE;
2022
2023   gtk_widget_grab_focus (widget);
2024
2025   sample = s->view->sample;
2026
2027   if (s->meta_down && s->view->current_tool == TOOL_SCRUB &&
2028       s->selecting == SELECTING_PLAYMARKER) {
2029     gdk_window_get_pointer (event->window, &x, &y, &state);
2030     sample_display_handle_playmarker_motion (s, x, y);
2031   } else
2032   if(s->selecting && event->button != last_button) {
2033     /* Cancel the current operation if a different button is pressed. */
2034     sample_display_clear_sel (s);
2035   } else
2036   if (last_tmp_view && last_tmp_view != s->view && event->button != last_button) {
2037     view_clear_last_tmp_view ();
2038   } else {
2039     last_button = event->button;
2040     gdk_window_get_pointer (event->window, &x, &y, &state);
2041    
2042     if(last_button == 1) {
2043
2044       if (XPOS_TO_OFFSET(x) < 0 ||
2045           XPOS_TO_OFFSET(x) > sample->sounddata->nr_frames)
2046         return TRUE;
2047
2048       switch (s->view->current_tool) {
2049       case TOOL_SCRUB:
2050         s->selecting = SELECTING_PLAYMARKER;
2051         SET_CURSOR(widget, NEEDLE);
2052         sample_set_scrubbing (s->view->sample, TRUE);
2053         if (sample->play_head->going) {
2054           head_set_restricted (sample->play_head, FALSE);
2055           sample_refresh_playmode (sample);
2056         } else {
2057           play_view_all (s->view);
2058         }
2059         sample_display_handle_playmarker_motion (s, x, y);
2060         return TRUE;
2061         break;
2062       case TOOL_SELECT:
2063         /* If the cursor is near a sel, move that */
2064         if (sample_display_on_sel (s, x, y)) {
2065           sample_display_handle_sel_button_press (s, x, y, state);
2066         } else {
2067           sample_display_handle_sel_button_press (s, x, y, state);
2068         }
2069 #ifdef SEL_SCRUBS
2070         /* scrub along the changing selection edge, unless we're already
2071          * playing. NB. the play_head->scrubbing state is used by the
2072          * motion and release callbacks here to determine whether or not
2073          * to scrub the moving edge, and whether or not to stop playback
2074          * upon release. */
2075         if (sample->play_head->going) {
2076           sample_set_scrubbing (sample, FALSE);
2077           sample_refresh_playmode (sample);
2078         } else {
2079           /*sample_set_monitor (sample, TRUE);*/
2080           sample_set_scrubbing (sample, TRUE);
2081           play_view_all (s->view);
2082           sample_display_handle_playmarker_motion (s, x, y);
2083         }
2084 #endif
2085         break;
2086       case TOOL_HAND:
2087             s->selecting = SELECTING_HAND;
2088             s->view->hand_offset = x;
2089             s->hand_scroll_delta = 0;
2090             if (s->hand_scroll_tag){
2091                    g_source_remove (s->hand_scroll_tag);
2092                    s->hand_scroll_tag = 0;
2093             }
2094             SET_CURSOR(widget, HAND_CLOSE);
2095             sample_display_handle_hand_motion (s, x, y);
2096         break;
2097       case TOOL_ZOOM:
2098             o = XPOS_TO_OFFSET(x);
2099             view_center_on (s->view, o);
2100             if (state & GDK_SHIFT_MASK) {
2101               view_zoom_out (s->view, 2.0);
2102             } else {
2103               view_zoom_in (s->view, 2.0);
2104             }
2105         break;
2106       case TOOL_PENCIL:
2107         s->selecting = SELECTING_PENCIL;
2108         sample_display_handle_pencil_motion (s, x, y);
2109         break;
2110       case TOOL_NOISE:
2111         s->selecting = SELECTING_NOISE;
2112         sample_display_handle_noise_motion (s, x, y);
2113         break;
2114       default:
2115         break;
2116       }
2117
2118     } else if(last_button == 2) {
2119       s->selecting = SELECTING_PAN_WINDOW;
2120       gdk_window_get_pointer (event->window, &s->selecting_x0, NULL, NULL);
2121       s->selecting_wins0 = s->view->start;
2122       SET_CURSOR(widget, MOVE);
2123       sample_set_scrubbing (s->view->sample, TRUE);
2124     } else if(last_button == 3) {
2125       if(s->view && s->view->menu) {
2126         view_popup_context_menu (s->view, 3, event->time);
2127       }
2128     }
2129   }
2130            
2131   return TRUE;
2132 }
2133
2134 void
2135 sample_display_sink_tmp_sel (SampleDisplay * s)
2136 {
2137   sw_sample * sample = s->view->sample;
2138   sw_sel * t;
2139
2140   s->selecting = SELECTING_NOTHING;
2141
2142   if (s->scroll_right_tag != 0) {
2143     g_source_remove (s->scroll_right_tag);
2144     s->scroll_right_tag = 0;
2145   }
2146   if (s->scroll_left_tag != 0) {
2147     g_source_remove (s->scroll_left_tag);
2148     s->scroll_left_tag = 0;
2149   }
2150
2151   t = sample->tmp_sel;
2152
2153   if (t->sel_end == (t->sel_start + 1)) {
2154     if (!sample->play_head->going) {
2155       sample_set_playmarker (sample, t->sel_start, TRUE);
2156     }
2157     sample_clear_tmp_sel (sample);
2158   } else {
2159
2160     if (s->selecting == SELECTING_SELECTION_START) {
2161       sample_set_playmarker (sample, t->sel_start, TRUE);
2162     } else if (s->selecting == SELECTING_SELECTION_END) {
2163       sample_set_playmarker (sample, t->sel_end, TRUE);
2164     }
2165
2166     if(s->selection_mode == SELECTION_MODE_REPLACE) {
2167       sample_selection_replace_with_tmp_sel (sample);
2168     } else if (s->selection_mode == SELECTION_MODE_SUBTRACT) {
2169       sample_selection_subtract_tmp_sel (sample);
2170     } else {
2171       sample_selection_insert_tmp_sel (sample);
2172     }
2173     s->selection_mode = SELECTION_MODE_NONE;
2174
2175     g_signal_emit_by_name(GTK_OBJECT(s),
2176                     "selection-changed");
2177   }
2178 }
2179
2180 static gint
2181 sample_display_button_release (GtkWidget      *widget,
2182                                GdkEventButton *event)
2183 {
2184   SampleDisplay *s;
2185 #ifdef SEL_SCRUBS
2186   GdkModifierType state;
2187   int x, y;
2188 #endif
2189
2190   g_return_val_if_fail (widget != NULL, FALSE);
2191   g_return_val_if_fail (IS_SAMPLE_DISPLAY (widget), FALSE);
2192   g_return_val_if_fail (event != NULL, FALSE);
2193    
2194   s = SAMPLE_DISPLAY(widget);
2195
2196   switch (s->view->current_tool) {
2197   case TOOL_SELECT:
2198    
2199     /* If the user has released the button they were selecting with,
2200      * sink this sample's temporary selection.
2201      */
2202     if (s->selecting && event->button == last_button) {
2203       switch (s->selecting) {
2204       case SELECTING_SELECTION_START:
2205       case SELECTING_SELECTION_END:
2206         sample_display_sink_tmp_sel (s);
2207         break;
2208       default:
2209         break;
2210       }
2211     }
2212    
2213     /* If the user has released the button in a sweep window different
2214      * to that used for selection, then sink the appropriate temporary
2215      * selection.
2216      */
2217     if (last_tmp_view != s->view) {
2218       view_sink_last_tmp_view();
2219     }
2220
2221 #ifdef SEL_SCRUBS
2222     if (s->view->sample->play_head->scrubbing) {
2223       gdk_window_get_pointer (event->window, &x, &y, &state);
2224       pause_playback (s->view->sample);
2225       sample_display_handle_playmarker_motion (s, x, y);
2226     }
2227 #endif
2228
2229     break;
2230   case TOOL_SCRUB:
2231     if (s->meta_down) return TRUE;
2232     break;
2233   case TOOL_HAND:
2234     s->view->hand_offset = -1;
2235
2236     s->hand_scroll_tag = g_timeout_add (HAND_SCROLL_INTERVAL,
2237                     (GSourceFunc)sample_display_hand_scroll,
2238                     s);
2239
2240     break;
2241   case TOOL_MOVE:
2242     break;
2243   case TOOL_ZOOM:
2244     break;
2245   default:
2246     break;
2247   }
2248
2249   if (s->meta_down && s->selecting == SELECTING_PLAYMARKER)
2250     return TRUE;
2251
2252   s->selecting = SELECTING_NOTHING;
2253
2254   sample_set_scrubbing (s->view->sample, FALSE);
2255
2256   sample_display_set_default_cursor (s);
2257    
2258   return FALSE;
2259 }
2260
2261 static gint
2262 sample_display_motion_notify (GtkWidget *widget,
2263                               GdkEventMotion *event)
2264 {
2265   SampleDisplay *s;
2266   gint x, y;
2267   GdkModifierType state;
2268   sw_framecount_t o;
2269
2270   s = SAMPLE_DISPLAY(widget);
2271
2272   if(!IS_INITIALIZED(s))
2273     return FALSE;
2274
2275   if (event->is_hint) {
2276     gdk_window_get_pointer (event->window, &x, &y, &state);
2277   } else {
2278     x = event->x;
2279     y = event->y;
2280     state = event->state;
2281   }
2282
2283   o = XPOS_TO_OFFSET(x);
2284   s->mouse_x = x;
2285   s->mouse_offset = o;
2286   g_signal_emit_by_name(GTK_OBJECT(s),
2287                   "mouse-offset-changed");
2288
2289   if (s->selecting) {
2290     if (s->meta_down && s->selecting == SELECTING_PLAYMARKER) {
2291       sample_display_handle_playmarker_motion (s, x, y);
2292     } else if((state & GDK_BUTTON1_MASK) && last_button == 1) {
2293       switch (s->selecting) {
2294       case SELECTING_PLAYMARKER:
2295         sample_display_handle_playmarker_motion (s, x, y);
2296         break;
2297       case SELECTING_HAND:
2298             sample_display_handle_hand_motion (s, x, y);
2299             break;
2300       case SELECTING_PENCIL:
2301         sample_display_handle_pencil_motion (s, x, y);
2302         break;
2303       case SELECTING_NOISE:
2304         sample_display_handle_noise_motion (s, x, y);
2305         break;
2306       default:
2307         sample_display_handle_sel_motion(s, x, y, 0);
2308 #ifdef SEL_SCRUBS
2309         if (s->view->sample->play_head->scrubbing) {
2310           sample_display_handle_playmarker_motion (s, x, y);
2311         }
2312 #endif
2313         break;
2314       }
2315     } else if((state & GDK_BUTTON2_MASK) && last_button == 2) {
2316       sample_display_handle_move_motion (s, x, y);
2317     } else {
2318       /*    sample_display_clear_sel (s);*/
2319       if (s->selecting == SELECTING_SELECTION_START ||
2320           s->selecting == SELECTING_SELECTION_END) {
2321         /* XXX: Need to sink_tmp_sel here instead for consistency ???
2322          *  It seems to be clearing fast tmp_sels now, but at least not
2323          * leaving them lying around.*/
2324         sample_display_sink_tmp_sel (s);
2325
2326         sample_display_set_default_cursor (s);
2327       }
2328     }
2329
2330   } else {
2331     if (s->view->current_tool == TOOL_SELECT &&
2332         sample_display_on_sel (s, x, y)) {
2333       SET_CURSOR(widget, HORIZ);
2334     } else if (s->view->current_tool == TOOL_SELECT &&
2335                sample_display_on_playmarker (s, x, y)) {
2336       SET_CURSOR(widget, NEEDLE);
2337     } else {
2338
2339       if (o > 0 && o < s->view->sample->sounddata->nr_frames)
2340         sample_display_set_default_cursor (SAMPLE_DISPLAY(widget));
2341       else
2342         gdk_window_set_cursor (widget->window, NULL);
2343     }
2344   }
2345    
2346   return FALSE;
2347 }
2348
2349 static gint
2350 sample_display_enter_notify (GtkWidget *widget,
2351                              GdkEventCrossing *event)
2352 {
2353   gtk_widget_grab_focus (widget);
2354   return TRUE;
2355 }
2356
2357 static gint
2358 sample_display_leave_notify (GtkWidget *widget,
2359                              GdkEventCrossing *event)
2360 {
2361   SampleDisplay *s;
2362  
2363   s = SAMPLE_DISPLAY(widget);
2364   s->mouse_offset = -1;
2365   g_signal_emit_by_name(GTK_OBJECT(s),
2366                  "mouse-offset-changed");
2367
2368   return TRUE;
2369 }
2370
2371 static gint
2372 sample_display_focus_in (GtkWidget * widget, GdkEventFocus * event)
2373 {
2374   SampleDisplay * s;
2375
2376   g_return_val_if_fail (widget != NULL, FALSE);
2377   g_return_val_if_fail (IS_SAMPLE_DISPLAY(widget), FALSE);
2378
2379   GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
2380  /*
2381   * FIXME: nonexistant in GTK+-2.0
2382   * docs say draw in the expose function. draw what though?
2383   *     
2384   *     gtk_widget_draw_focus (widget);
2385   */
2386
2387   s = SAMPLE_DISPLAY(widget);
2388
2389   if (s->marching) {
2390     sd_start_marching_ants_timeout (s);
2391   }
2392
2393   sample_display_start_cursor_pulse (s);
2394
2395   undo_dialog_set_sample (s->view->sample);
2396
2397   return FALSE;
2398 }
2399
2400 static gint
2401 sample_display_focus_out (GtkWidget * widget, GdkEventFocus * event)
2402 {
2403   SampleDisplay * s;
2404
2405   g_return_val_if_fail (widget != NULL, FALSE);
2406   g_return_val_if_fail (IS_SAMPLE_DISPLAY(widget), FALSE);
2407
2408   GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
2409  /*
2410   * FIXME: nonexistant in GTK+-2.0
2411   * docs say draw in the expose function. draw what though?
2412   *     
2413   *     gtk_widget_draw_focus (widget);
2414   */
2415
2416   s = SAMPLE_DISPLAY(widget);
2417
2418   sd_stop_marching_ants_timeout (s);
2419
2420   sample_display_stop_cursor_pulse (s);
2421
2422   return FALSE; 
2423 }
2424
2425 static gint
2426 sample_display_key_press (GtkWidget * widget, GdkEventKey * event)
2427 {
2428   SampleDisplay * s = SAMPLE_DISPLAY(widget);
2429   sw_view * view = s->view;
2430   sw_sample * sample;
2431   sw_framecount_t vlen, move_delta = 0, sel_t;
2432   GList * gl;
2433   sw_sel * sel;
2434   int x, y, xss, xse;
2435
2436   sample = view->sample;
2437   vlen = view->end - view->start;
2438
2439   /*g_print ("key 0x%X pressed\n", event->keyval);*/
2440
2441   switch (event->keyval) {
2442   case GDK_Meta_L:
2443   case GDK_Super_L:
2444   case GDK_Multi_key:
2445     if (sample->edit_mode == SWEEP_EDIT_MODE_ALLOC) break;
2446
2447     s->meta_down = TRUE;
2448
2449     if (s->selecting == SELECTING_NOTHING) {
2450       s->selecting = SELECTING_PLAYMARKER;
2451       SET_CURSOR(widget, NEEDLE);
2452       sample_set_scrubbing (sample, TRUE);
2453     }
2454
2455     if (s->selecting == SELECTING_PLAYMARKER) {
2456       gdk_window_get_pointer (widget->window, &x, &y, NULL);
2457       sample_display_handle_playmarker_motion (s, x, y);
2458       if (!sample->play_head->going) {
2459         play_view_all (s->view);
2460       }
2461     }
2462
2463     return TRUE;
2464     break;
2465   case GDK_Menu:
2466     if(view->menu) {
2467       gtk_menu_popup(GTK_MENU(view->menu),
2468                      NULL, NULL, NULL,
2469                      NULL, 3, event->time);
2470     }
2471     return TRUE;
2472     break;
2473   case GDK_BackSpace:
2474     if (sample->edit_mode == SWEEP_EDIT_MODE_READY)
2475       do_clear (sample);
2476     return TRUE;
2477     break;
2478   case GDK_Delete:
2479     if (sample->edit_mode == SWEEP_EDIT_MODE_READY)
2480       do_delete (sample);
2481     return TRUE;
2482     break;
2483   case GDK_less:
2484     if (sample->edit_mode == SWEEP_EDIT_MODE_READY)
2485       select_shift_left_cb (widget, s);
2486     return TRUE;
2487   case GDK_greater:
2488     if (sample->edit_mode == SWEEP_EDIT_MODE_READY)
2489       select_shift_right_cb (widget, s);
2490     return TRUE;
2491   case GDK_Up:
2492   case GDK_KP_Up:
2493     if (event->state & GDK_CONTROL_MASK) {
2494       zoom_1to1_cb (GTK_WIDGET(s), s);
2495     } else if (event->state & GDK_SHIFT_MASK) {
2496       view_vzoom_in (view, 1.2);
2497     } else {
2498       view_zoom_in (view, 2.0);
2499     }
2500     return TRUE;
2501     break;
2502   case GDK_Down:
2503   case GDK_KP_Down:
2504     if (event->state & GDK_CONTROL_MASK) {
2505       zoom_norm_cb (GTK_WIDGET(s), s);
2506     } else if (event->state & GDK_SHIFT_MASK) {
2507       view_vzoom_out (view, 1.2);
2508     } else {
2509       view_zoom_out (view, 2.0);
2510     }
2511     return TRUE;
2512     break;
2513   case GDK_Left:
2514   case GDK_KP_Left:
2515     move_delta = MIN(-1,  -vlen/s->width);
2516     if (event->state & GDK_CONTROL_MASK) {
2517       if (!(event->state & GDK_SHIFT_MASK)) {
2518         sample_set_offset_next_bound_left (sample);
2519         return TRUE;
2520       }
2521       move_delta *= 10;
2522     }
2523     break;
2524   case GDK_Right:
2525   case GDK_KP_Right:
2526     move_delta = MAX(1,  vlen/s->width);
2527     if (event->state & GDK_CONTROL_MASK) {
2528       if (!(event->state & GDK_SHIFT_MASK)) {
2529         sample_set_offset_next_bound_right (sample);
2530         return TRUE;
2531       }
2532       move_delta *= 10;
2533     }
2534     break;
2535   default: /* Random other key pressed */
2536     return FALSE;
2537     break;
2538   }
2539
2540   /* Handle movement only from here on */
2541
2542   if ((event->state & GDK_SHIFT_MASK) &&
2543       (sample->edit_mode == SWEEP_EDIT_MODE_READY)) {
2544     sel = sample->tmp_sel;
2545    
2546     switch (s->selecting) {
2547     case SELECTING_SELECTION_START:
2548       sel->sel_start += move_delta;
2549       if (sel->sel_start > sel->sel_end) {
2550         sel_t = sel->sel_start;
2551         sel->sel_start = sel->sel_end;
2552         sel->sel_end = sel_t;
2553         s->selecting = SELECTING_SELECTION_END;
2554       }
2555       break;
2556     case SELECTING_SELECTION_END:
2557       sample->tmp_sel->sel_end += move_delta;
2558       if (sel->sel_start > sel->sel_end) {
2559         sel_t = sel->sel_start;
2560         sel->sel_start = sel->sel_end;
2561         sel->sel_end = sel_t;
2562         s->selecting = SELECTING_SELECTION_START;
2563       }
2564       break;
2565     default:
2566       last_button = 0;
2567
2568       x = OFFSET_TO_XPOS(sample->user_offset);
2569
2570       if ((sel = sample->tmp_sel) != NULL) {
2571
2572         xss = OFFSET_TO_XPOS(sel->sel_start);
2573         xse = OFFSET_TO_XPOS(sel->sel_end);
2574          
2575         if(abs(x-xss) < 5) {
2576           s->selecting = SELECTING_SELECTION_START;
2577           sel->sel_start += move_delta;
2578           if (sel->sel_start > sel->sel_end) {
2579             sel_t = sel->sel_start;
2580             sel->sel_start = sel->sel_end;
2581             sel->sel_end = sel_t;
2582             s->selecting = SELECTING_SELECTION_END;
2583           }
2584           break;
2585         } else if(abs(x-xse) < 5) {
2586           s->selecting = SELECTING_SELECTION_END;
2587           sel->sel_end += move_delta;
2588           if (sel->sel_start > sel->sel_end) {
2589             sel_t = sel->sel_start;
2590             sel->sel_start = sel->sel_end;
2591             sel->sel_end = sel_t;
2592             s->selecting = SELECTING_SELECTION_START;
2593           }
2594           break;
2595         }
2596       }
2597
2598       if (s->selecting != SELECTING_SELECTION_START &&
2599           s->selecting != SELECTING_SELECTION_END) {
2600        
2601         for (gl = sample->sounddata->sels; gl; gl = gl->next) {
2602          
2603          
2604           /* If the cursor is near the current start or end of
2605            * the selection, move that.
2606            */
2607          
2608           sel = (sw_sel *)gl->data;
2609          
2610           xss = OFFSET_TO_XPOS(sel->sel_start);
2611           xse = OFFSET_TO_XPOS(sel->sel_end);
2612          
2613           if(abs(x-xss) < 5) {
2614             sample_set_tmp_sel (sample, s->view, sel);
2615             s->selecting = SELECTING_SELECTION_START;
2616             s->selection_mode = SELECTION_MODE_INTERSECT;
2617             sample_display_set_intersect_cursor (s);
2618             break;
2619           } else if(abs(x-xse) < 5) {
2620             sample_set_tmp_sel (sample, s->view, sel);
2621             s->selecting = SELECTING_SELECTION_END;
2622             s->selection_mode = SELECTION_MODE_INTERSECT;
2623             sample_display_set_intersect_cursor (s);
2624             break;
2625           }
2626         }
2627       }
2628      
2629       if (s->selecting != SELECTING_SELECTION_START &&
2630           s->selecting != SELECTING_SELECTION_END) {
2631
2632         sample_set_tmp_sel_1 (sample, view, sample->user_offset,
2633                               sample->user_offset + move_delta);
2634         s->selecting = SELECTING_SELECTION_START;
2635         s->selection_mode = SELECTION_MODE_REPLACE;
2636         SET_CURSOR (GTK_WIDGET(s), HORIZ);
2637       }
2638       break;
2639     }
2640     g_signal_emit_by_name(GTK_OBJECT(s),
2641                     "selection-changed");
2642   } else if (s->selecting == SELECTING_SELECTION_START ||
2643              s->selecting == SELECTING_SELECTION_END) {
2644     sample_display_sink_tmp_sel (s);
2645     s->selecting = SELECTING_NOTHING;
2646     sample_display_set_default_cursor (s);
2647   }
2648
2649   sample_set_playmarker (sample, sample->user_offset + move_delta, TRUE);
2650
2651   return TRUE;
2652 }
2653
2654 static gint
2655 sample_display_key_release (GtkWidget * widget, GdkEventKey * event)
2656 {
2657   SampleDisplay * s = SAMPLE_DISPLAY(widget);
2658   GdkModifierType state;
2659
2660   switch (event->keyval) {
2661   case GDK_Meta_L:
2662   case GDK_Super_L:
2663   case GDK_Multi_key:
2664     s->meta_down = FALSE;
2665
2666     gdk_window_get_pointer (widget->window, NULL, NULL, &state);
2667
2668     /* Don't cancel scrubbing if the mouse is down for it */
2669     if ((state & GDK_BUTTON1_MASK) && s->view->current_tool == TOOL_SCRUB)
2670       return TRUE;
2671
2672     if (s->selecting == SELECTING_PLAYMARKER) {
2673       s->selecting = SELECTING_NOTHING;
2674       sample_set_scrubbing (s->view->sample, FALSE);
2675       sample_display_set_default_cursor (s);
2676     }
2677
2678     return TRUE;
2679     break;
2680   default:
2681     break;
2682   }
2683
2684   return FALSE;
2685 }
2686
2687 static gint
2688 sample_display_destroy (GtkWidget * widget, GdkEventAny * event)
2689 {
2690   gtk_widget_queue_draw(widget);
2691   return 0;
2692 }
2693
2694 static void
2695 sample_display_class_init (SampleDisplayClass *class)
2696 {
2697   GtkObjectClass *object_class;
2698   GtkWidgetClass *widget_class;
2699   int n;
2700   const int *p;
2701   GdkColor *c;
2702
2703   object_class = (GtkObjectClass*) class;
2704   widget_class = (GtkWidgetClass*) class;
2705
2706   widget_class->realize = sample_display_realize;
2707   widget_class->size_allocate = sample_display_size_allocate;
2708   widget_class->expose_event = sample_display_expose;
2709   widget_class->size_request = sample_display_size_request;
2710   widget_class->button_press_event = sample_display_button_press;
2711   widget_class->button_release_event = sample_display_button_release;
2712   widget_class->scroll_event = sample_display_scroll_event;
2713   widget_class->motion_notify_event = sample_display_motion_notify;
2714   widget_class->enter_notify_event = sample_display_enter_notify;
2715   widget_class->leave_notify_event = sample_display_leave_notify;
2716   widget_class->key_press_event = sample_display_key_press;
2717   widget_class->key_release_event = sample_display_key_release;
2718   widget_class->focus_in_event = sample_display_focus_in;
2719   widget_class->focus_out_event = sample_display_focus_out;
2720
2721   widget_class->destroy_event = sample_display_destroy;
2722
2723   sample_display_signals[SIG_SELECTION_CHANGED] =
2724     g_signal_new ("selection-changed",
2725                                                                   G_TYPE_FROM_CLASS (object_class),
2726                                       G_SIGNAL_RUN_FIRST,
2727                                       G_STRUCT_OFFSET (SampleDisplayClass, selection_changed),
2728                                   NULL,
2729                                   NULL,               
2730                                                                   g_cclosure_marshal_VOID__VOID,
2731                                   G_TYPE_NONE, 0);
2732  
2733
2734   sample_display_signals[SIG_WINDOW_CHANGED] =
2735     g_signal_new ("window-changed",
2736                                                                   G_TYPE_FROM_CLASS (object_class),
2737                                       G_SIGNAL_RUN_FIRST,
2738                                       G_STRUCT_OFFSET (SampleDisplayClass, window_changed),
2739                                   NULL,
2740                                   NULL,               
2741                                                                   g_cclosure_marshal_VOID__VOID,
2742                                   G_TYPE_NONE, 0);
2743  
2744
2745   sample_display_signals[SIG_MOUSE_OFFSET_CHANGED] =
2746     g_signal_new ("mouse-offset-changed",
2747                                                                   G_TYPE_FROM_CLASS (object_class),
2748                                       G_SIGNAL_RUN_FIRST,
2749                                       G_STRUCT_OFFSET (SampleDisplayClass, mouse_offset_changed),
2750                                   NULL,
2751                                   NULL,               
2752                                                                   g_cclosure_marshal_VOID__VOID,
2753                                   G_TYPE_NONE, 0);
2754
2755   class->selection_changed = NULL;
2756   class->window_changed = NULL;
2757   class->mouse_offset_changed = NULL;
2758
2759   for(n = 0, p = default_colors, c = class->colors;
2760       n < SAMPLE_DISPLAYCOL_LAST; n++, c++) {
2761     c->red = *p++ * 65535 / 255;
2762     c->green = *p++ * 65535 / 255;
2763     c->blue = *p++ * 65535 / 255;
2764     c->pixel = (glong)((c->red & 0xff00)*256 +
2765                         (c->green & 0xff00) +
2766                         (c->blue & 0xff00)/256);
2767     gdk_colormap_alloc_color(gdk_colormap_get_system(), c, TRUE, TRUE);
2768   }
2769
2770   for(n = 0, p = bg_colors, c = class->bg_colors;
2771       n < VIEW_COLOR_MAX; n++, c++) {
2772     c->red = *p++ * 65535 / 255;
2773     c->green = *p++ * 65535 / 255;
2774     c->blue = *p++ * 65535 / 255;
2775     c->pixel = (glong)((c->red & 0xff00)*256 +
2776                         (c->green & 0xff00) +
2777                         (c->blue & 0xff00)/256);
2778     gdk_colormap_alloc_color(gdk_colormap_get_system(), c, TRUE, TRUE);
2779   }
2780
2781   for(n = 0, p = fg_colors, c = class->fg_colors;
2782       n < VIEW_COLOR_MAX; n++, c++) {
2783     c->red = *p++ * 65535 / 255;
2784     c->green = *p++ * 65535 / 255;
2785     c->blue = *p++ * 65535 / 255;
2786     c->pixel = (glong)((c->red & 0xff00)*256 +
2787                         (c->green & 0xff00) +
2788                         (c->blue & 0xff00)/256);
2789     gdk_colormap_alloc_color(gdk_colormap_get_system(), c, TRUE, TRUE);
2790   }
2791 }
2792
2793 static void
2794 sample_display_init (SampleDisplay *s)
2795 {
2796   GTK_WIDGET_SET_FLAGS (GTK_WIDGET(s), GTK_CAN_FOCUS);
2797
2798   s->backing_pixmap = NULL;
2799   s->view = NULL;
2800   s->selecting = SELECTING_NOTHING;
2801   s->selection_mode = SELECTION_MODE_NONE;
2802   s->marching_tag = 0;
2803   s->marching = FALSE;
2804   s->pulsing_tag = 0;
2805   s->pulse = FALSE;
2806   s->hand_scroll_tag = 0;
2807   s->mouse_x = 0;
2808   s->mouse_offset = 0;
2809   s->scroll_left_tag = 0;
2810   s->scroll_right_tag = 0;
2811 }
2812
2813
2814
2815
2816 GType
2817 sample_display_get_type (void)
2818 {
2819   static GType sample_display_type = 0;
2820
2821   if (!sample_display_type)
2822         {
2823       static const GTypeInfo sample_display_info =
2824     {
2825       sizeof(SampleDisplayClass),
2826            NULL, /* base_init */
2827            NULL, /* base_finalize */
2828            (GClassInitFunc) sample_display_class_init,
2829            NULL, /* class_finalize */
2830            NULL, /* class_data */
2831        sizeof (SampleDisplay),           
2832            0,    /* n_preallocs */
2833            (GInstanceInitFunc) sample_display_init,
2834                  
2835     };
2836
2837                 sample_display_type = g_type_register_static (GTK_TYPE_WIDGET,
2838                                                                                                   "SampleDisplay", &sample_display_info, 0);
2839                
2840   }
2841
2842   return sample_display_type;
2843 }
2844
2845 GtkWidget*
2846 sample_display_new (void)
2847 {
2848   return GTK_WIDGET (g_object_new (sample_display_get_type (), NULL));
2849 }
Note: See TracBrowser for help on using the browser.