root/sweep/trunk/src/db_ruler.c

Revision 691, 14.5 kB (checked in by erikd, 2 years ago)

Remove needless #defines of buffer lengths.

Code had many instances of:

#undef BUF_LEN
#define BUF_LEN 64
char buf[BUF_LEN];
snprintf (buf, BUF_LEN, ....);

which can be replaced with:

char buf[64];
snprintf (buf, sizeof (buf), ....);

which is far cleaner and more difficult to get wrong.

  • 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  * db_ruler, modified from hruler in GTK+ 1.2.x
5  * by Conrad Parker 2002 for Sweep.
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */
21
22 /*
23  * GTK - The GIMP Toolkit
24  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
25  */
26
27 /*
28  * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
29  * file for a list of people on the GTK+ Team.  See the ChangeLog
30  * files for a list of changes.  These files are distributed with
31  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
32  */
33
34
35 #ifdef HAVE_CONFIG_H
36 #  include <config.h>
37 #endif
38
39 #include <math.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include "db_ruler.h"
43
44 #include <sweep/sweep_typeconvert.h>
45
46 #include "print.h"
47
48 #define RULER_WIDTH           32
49 #define RULER_HEIGHT          14
50 #define MINIMUM_INCR          2
51 #define MAXIMUM_SUBDIVIDE     3
52 #define MAXIMUM_SCALES        21
53
54 #define ROUND(x) ((int) ((x) + 0.5))
55
56
57 static void db_ruler_class_init    (DbRulerClass *klass);
58 static void db_ruler_init          (DbRuler      *db_ruler);
59 static void db_ruler_realize (GtkWidget * widget);
60 static gint
61 db_ruler_button_press  (GtkWidget * widget, GdkEventButton * event);
62 static gint
63 db_ruler_button_release  (GtkWidget * widget, GdkEventButton * event);
64 static gint
65 db_ruler_motion_notify (GtkWidget *widget, GdkEventMotion *event);
66 static gint
67 db_ruler_leave_notify (GtkWidget * widget, GdkEventCrossing * event);
68
69 static gboolean db_ruler_scroll_event (GtkWidget * widget, GdkEventScroll *event);
70
71 static void db_ruler_draw_ticks    (GtkRuler       *ruler);
72 static void db_ruler_draw_pos      (GtkRuler       *ruler);
73
74 static GtkWidgetClass * parent_class = NULL;
75
76 enum {
77   CHANGED_SIGNAL,
78   LAST_SIGNAL
79 };
80
81 static gint db_ruler_signals[LAST_SIGNAL] = { 0 };
82
83 GType
84 db_ruler_get_type (void)
85 {
86   static GType db_ruler_type = 0;
87
88   if (!db_ruler_type)
89     {
90       static const GTypeInfo db_ruler_info =
91       {
92                  
93         sizeof (DbRulerClass),
94         NULL, /* base_init */
95         NULL, /* base_finalize */
96         (GClassInitFunc) db_ruler_class_init,
97         NULL, /* class_finalize */
98         NULL, /* class_data */
99         sizeof (DbRuler),
100         0,    /* n_preallocs */
101         (GInstanceInitFunc) db_ruler_init,       
102
103       };
104
105       db_ruler_type = g_type_register_static (GTK_TYPE_RULER, "DbRuler", &db_ruler_info, 0);
106     }
107
108   return db_ruler_type;
109 }
110
111 static void
112 db_ruler_class_init (DbRulerClass *klass)
113 {
114   GtkObjectClass *object_class;
115   GtkWidgetClass *widget_class;
116   GtkRulerClass *ruler_class;
117
118   object_class = (GtkObjectClass *) klass;
119   widget_class = (GtkWidgetClass*) klass;
120   ruler_class = (GtkRulerClass*) klass;
121
122   widget_class->realize = db_ruler_realize;
123   widget_class->button_press_event = db_ruler_button_press;
124   widget_class->motion_notify_event = db_ruler_motion_notify;
125   widget_class->button_release_event = db_ruler_button_release;
126   widget_class->leave_notify_event = db_ruler_leave_notify;
127   widget_class->scroll_event = db_ruler_scroll_event;
128
129   ruler_class->draw_ticks = db_ruler_draw_ticks;
130   ruler_class->draw_pos = db_ruler_draw_pos;
131
132   parent_class = gtk_type_class (gtk_widget_get_type ());
133
134   db_ruler_signals[CHANGED_SIGNAL] = g_signal_new ("changed",
135                                                                   G_TYPE_FROM_CLASS (klass),
136                                       G_SIGNAL_RUN_FIRST,
137                                       G_STRUCT_OFFSET (DbRulerClass, changed),
138                                   NULL,
139                                   NULL,               
140                                                                   g_cclosure_marshal_VOID__VOID,
141                                   G_TYPE_NONE, 0);
142   klass->changed = NULL;
143 }
144
145 static gfloat ruler_scale[MAXIMUM_SCALES] =
146 { 0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1,
147   3, 6, 12, 15, 30, 60, 300, 600, 1800, 3600, 18000, 36000 };
148
149 static gint subdivide[MAXIMUM_SUBDIVIDE] = { 1, 5, 20 };
150
151 static void
152 db_ruler_init (DbRuler *db_ruler)
153 {
154   GtkWidget *widget;
155
156   widget = GTK_WIDGET (db_ruler);
157   widget->requisition.width = widget->style->xthickness * 2 + RULER_WIDTH;
158   widget->requisition.height = widget->style->ythickness * 2 + 1;
159
160   DB_RULER(db_ruler)->dragging = FALSE;
161 }
162
163 static void
164 db_ruler_realize (GtkWidget * widget)
165 {
166   GtkRuler * ruler;
167   GdkWindowAttr attributes;
168   gint attributes_mask;
169   GdkVisual * visual;
170
171   g_return_if_fail (widget != NULL);
172   g_return_if_fail (GTK_IS_DB_RULER (widget));
173
174   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
175
176   ruler = GTK_RULER(widget);
177
178   attributes.x = widget->allocation.x;
179   attributes.y = widget->allocation.y;
180   attributes.width = widget->allocation.width;
181   attributes.height = widget->allocation.height;
182   attributes.wclass = GDK_INPUT_OUTPUT;
183   attributes.window_type = GDK_WINDOW_CHILD;
184   attributes.event_mask = gtk_widget_get_events (widget)
185     | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
186     | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK
187     | GDK_LEAVE_NOTIFY_MASK | GDK_SCROLL_MASK;
188
189   attributes.visual = gtk_widget_get_visual (widget);
190   attributes.colormap = gtk_widget_get_colormap (widget);
191
192   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
193   widget->window = gdk_window_new (widget->parent->window,
194                                    &attributes, attributes_mask);
195
196   widget->style = gtk_style_attach (widget->style, widget->window);
197
198   gtk_style_set_background(widget->style, widget->window, GTK_STATE_NORMAL);
199
200   gdk_window_set_user_data(widget->window, widget);
201
202   visual = gdk_window_get_visual (widget->window);
203
204   if (ruler->backing_store != NULL) {
205     gdk_pixmap_unref (ruler->backing_store);
206   }
207
208   ruler->backing_store = gdk_pixmap_new (widget->window,
209                                          widget->allocation.width,
210                                          widget->allocation.height,
211                                          visual->depth);
212
213   ruler->non_gr_exp_gc = gdk_gc_new (widget->window);
214   gdk_gc_copy (ruler->non_gr_exp_gc, widget->style->fg_gc[GTK_STATE_NORMAL]);
215 }
216
217 GtkWidget*
218 db_ruler_new (void)
219 {
220   return GTK_WIDGET (g_object_new (db_ruler_get_type (), NULL));
221 }
222
223 static gint
224 db_ruler_motion_notify (GtkWidget *widget, GdkEventMotion *event)
225 {
226   GtkRuler *ruler;
227   gfloat delta;
228   GdkModifierType state;
229   gint y, ydelta;
230
231   g_return_val_if_fail (widget != NULL, FALSE);
232   g_return_val_if_fail (GTK_IS_DB_RULER (widget), FALSE);
233   g_return_val_if_fail (event != NULL, FALSE);
234
235   ruler = GTK_RULER (widget);
236
237   if (event->is_hint)
238     gdk_window_get_pointer (widget->window, NULL, &y, &state);
239   else
240     y = event->y;
241
242   if (DB_RULER(widget)->dragging && (state & GDK_BUTTON1_MASK)) {
243     ydelta = DB_RULER(widget)->y - y;
244
245     delta = (((ruler->lower - ruler->upper) * ydelta) / widget->allocation.height);
246
247     gtk_ruler_set_range (ruler, ruler->lower + delta, ruler->upper + delta,
248                          (ruler->upper - ruler->lower)/2.0, 2.0);
249
250     g_signal_emit_by_name (ruler, "changed");
251
252     gtk_ruler_draw_ticks (ruler);
253   }
254
255   DB_RULER(widget)->y = y;
256
257   ruler->position =
258     ((ruler->lower - ruler->upper) * y) / widget->allocation.height
259     - ruler->lower;
260
261   /*  Make sure the ruler has been allocated already  */
262   if (ruler->backing_store != NULL)
263     gtk_ruler_draw_pos (ruler);
264
265   return FALSE;
266 }
267
268 static gint
269 db_ruler_button_press (GtkWidget * widget, GdkEventButton * event)
270 {
271    int y;
272
273   gdk_window_get_pointer (event->window, NULL, &y, NULL);
274  
275   switch (event->button) {
276   case 1:
277     DB_RULER(widget)->y = y;
278     DB_RULER(widget)->dragging = TRUE;
279     break;
280   default:
281     break;
282   }
283
284   return TRUE;
285 }
286
287 static gint
288 db_ruler_button_release (GtkWidget * widget, GdkEventButton * event)
289 {
290   DB_RULER(widget)->dragging = FALSE;
291
292   return TRUE;
293 }
294
295 static gboolean
296 db_ruler_scroll_event (GtkWidget *widget, GdkEventScroll *event)
297 {
298   GtkRuler * ruler = GTK_RULER(widget);
299   float delta;
300
301   if (event->direction == GDK_SCROLL_UP) {    /* mouse wheel scroll up */
302        
303         delta = ruler->upper - ruler->lower;
304     gtk_ruler_set_range (ruler, ruler->lower + delta/8, ruler->upper - delta/8,
305                          (ruler->upper - ruler->lower)/2.0, 2.0);   
306     g_signal_emit_by_name (ruler, "changed");
307         return TRUE;
308          
309   }  else if (event->direction == GDK_SCROLL_DOWN) {   /* mouse wheel scroll down */
310        
311     delta = ruler->upper - ruler->lower;
312     gtk_ruler_set_range (ruler, ruler->lower - delta/8, ruler->upper + delta/8,
313                          (ruler->upper - ruler->lower)/2.0, 2.0);   
314     g_signal_emit_by_name (ruler, "changed");
315         return TRUE;
316   }
317   return FALSE; /* redundant? */
318 }
319
320 static gint
321 db_ruler_leave_notify (GtkWidget * widget, GdkEventCrossing * event)
322 {
323   DB_RULER(widget)->dragging = FALSE;
324
325   return TRUE;
326 }
327
328 static void
329 db_ruler_draw_ticks (GtkRuler *ruler)
330 {
331   GtkWidget *widget;
332   GdkGC *gc, *bg_gc;
333   gint i;
334   gint width, height;
335   gint xthickness;
336   gint ythickness;
337   gint length, ideal_length;
338   gfloat lower, upper;          /* Upper and lower limits, in ruler units */
339   gfloat increment, abs_increment; /* Number of pixels per unit */
340   gint scale;                   /* Number of units per major unit */
341   gfloat subd_incr;
342   gfloat start, end, cur;
343   gchar unit_str[32];
344   gint digit_height;
345   gint digit_offset;
346   gint text_height;
347   gint pos;
348   PangoLayout *layout;
349   PangoRectangle logical_rect, ink_rect;
350
351   g_return_if_fail (ruler != NULL);
352   g_return_if_fail (GTK_IS_DB_RULER (ruler));
353
354   if (!GTK_WIDGET_DRAWABLE (ruler))
355     return;
356
357   widget = GTK_WIDGET (ruler);
358
359   gc = widget->style->fg_gc[GTK_STATE_NORMAL];
360   bg_gc = widget->style->bg_gc[GTK_STATE_NORMAL];
361
362   xthickness = widget->style->xthickness;
363   ythickness = widget->style->ythickness;
364
365   width = widget->allocation.width - ( xthickness * 2) ;
366   /*  height = widget->allocation.height - ythickness * 2;*/
367   height = widget->allocation.height;
368
369   layout = gtk_widget_create_pango_layout (widget, "012456789dBinf-");
370   pango_layout_get_extents (layout, &ink_rect, &logical_rect);
371  
372   digit_height = PANGO_PIXELS (ink_rect.height) + 2;
373   digit_offset = ink_rect.y;
374
375   gtk_paint_box (widget->style, ruler->backing_store,
376                  GTK_STATE_NORMAL, GTK_SHADOW_OUT,
377                  NULL, widget, "db_ruler",
378                  0, 0,
379                  widget->allocation.width, widget->allocation.height);
380
381   gdk_draw_line (ruler->backing_store, gc,
382                  width + xthickness,
383                  ythickness,
384                  width + xthickness,
385                  height - ythickness);
386
387 upper = ruler->upper / ruler->metric->pixels_per_unit;
388 lower = ruler->lower / ruler->metric->pixels_per_unit;
389
390 //   upper = ruler->upper;
391 //   lower = ruler->lower;
392
393   if ((upper - lower) == 0)
394     return;
395
396   increment = (gfloat) height / (upper - lower);
397   abs_increment = (gfloat) fabs((double)increment);
398
399 // * strlen (unit_str)
400 text_height =  digit_height + 1;
401
402   for (scale = 0; scale < MAXIMUM_SCALES; scale++)
403  /*   if (ruler->metric->ruler_scale[scale] * fabs(increment) > 2 * text_height)
404       break;*/
405 if (ruler_scale[scale] * abs_increment > 2 * digit_height)
406       break;
407   if (scale == MAXIMUM_SCALES)
408     scale = MAXIMUM_SCALES - 1;
409
410   /* drawing starts here */
411
412   pango_layout_set_text (layout, "dB", 2);
413   pango_layout_get_extents (layout, NULL, &logical_rect);
414
415   gtk_paint_layout (widget->style,
416                   ruler->backing_store,
417                   GTK_WIDGET_STATE (widget),
418                                   FALSE,
419                   NULL,
420                   widget,
421                   "vruler",
422                   2,
423                   (digit_height/2),
424                   layout);
425
426   length = 0;
427   for (i = MAXIMUM_SUBDIVIDE - 1; i >= 0; i--)
428     {
429       subd_incr = (gfloat) ruler_scale[scale] /
430                   (gfloat) subdivide[i];
431       if (subd_incr * fabs(increment) <= MINIMUM_INCR)
432         continue;
433
434       /* Calculate the length of the tickmarks. Make sure that
435        * this length increases for each set of ticks
436        */
437       ideal_length = 8 / (i + 1) - 1;
438       if (ideal_length > ++length)
439         length = ideal_length;
440
441       if (lower < upper)
442         {
443           start = floor (lower / subd_incr) * subd_incr;
444           end   = ceil  (upper / subd_incr) * subd_incr;
445         }
446       else
447         {
448           start = floor (upper / subd_incr) * subd_incr;
449           end   = ceil  (lower / subd_incr) * subd_incr;
450         }
451
452       for (cur = start; cur <= end; cur += subd_incr)
453         {
454           pos = height - ROUND ((cur - lower) * increment);
455
456           gdk_draw_line (ruler->backing_store, gc,
457                          width + xthickness, pos,
458                          width - length + xthickness, pos);
459
460           /* draw label */
461           if (i == 0 && cur < upper && cur > lower) {
462             float a_cur = fabs(cur), db_cur;
463            
464             /* ensure inf. stays as 'inf.', not nearby large values */
465             if (a_cur < subd_incr/2) a_cur = 0.0;
466             db_cur = 20 * log10 (a_cur);
467            
468             if (db_cur > -10.0) {
469               snprintf (unit_str, sizeof (unit_str), "%1.1f", db_cur);
470             } else {
471               snprintf (unit_str, sizeof (unit_str), "%2.0f", db_cur);
472             }
473          
474                 pango_layout_set_text (layout, unit_str, -1);
475
476                 gtk_paint_layout (widget->style,
477                   ruler->backing_store,
478                   GTK_WIDGET_STATE (widget),
479                                   FALSE,
480                   NULL,
481                   widget,
482                   "vruler",
483                   2,
484                   pos - (digit_height),
485                   layout);
486             }
487         }
488        
489     }
490        
491 }
492
493 static void
494 db_ruler_draw_pos (GtkRuler *ruler)
495 {
496   GtkWidget *widget;
497   GdkGC *gc;
498   int i;
499   gint x, y;
500   gint width, height;
501   gint bs_width, bs_height;
502   gint xthickness;
503   gint ythickness;
504   gfloat increment;
505
506   g_return_if_fail (ruler != NULL);
507   g_return_if_fail (GTK_IS_DB_RULER (ruler));
508
509   if (GTK_WIDGET_DRAWABLE (ruler))
510     {
511       widget = GTK_WIDGET (ruler);
512
513       gc = widget->style->fg_gc[GTK_STATE_NORMAL];
514       xthickness = widget->style->xthickness;
515       ythickness = widget->style->ythickness;
516       width = widget->allocation.width - xthickness * 2;
517       height = widget->allocation.height;
518
519       bs_height = 7;
520       bs_width = 4;
521
522       if ((bs_width > 0) && (bs_height > 0))
523         {
524           /*  If a backing store exists, restore the ruler  */
525           if (ruler->backing_store && ruler->non_gr_exp_gc)
526             gdk_draw_drawable (ruler->widget.window,
527                              ruler->non_gr_exp_gc,
528                              ruler->backing_store,
529                              ruler->xsrc, ruler->ysrc,
530                              ruler->xsrc, ruler->ysrc,
531                              bs_width, bs_height);
532
533           increment = (gfloat) height / (ruler->upper - ruler->lower);
534
535           x = (width - bs_width) + xthickness - 2;
536           y = DB_RULER(ruler)->y;
537
538           for (i = 0; i < bs_width; i++)
539             gdk_draw_line (widget->window, gc,
540                            x + i, y + i,
541                            x + i, y + bs_height - 1 - i);
542
543           ruler->xsrc = x;
544           ruler->ysrc = y;
545         }
546     }
547 }
Note: See TracBrowser for help on using the browser.