1 /*
2  * Sweep, a sound wave editor.
3  *
4  * time_ruler, modified from hruler in GTK+ 1.2.x
5  * by Conrad Parker 2000 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 "time_ruler.h"
43 
44 #include <sweep/sweep_typeconvert.h>
45 
46 #include "print.h"
47 
48 #define RULER_HEIGHT          14
49 #define MINIMUM_INCR          5
50 #define MAXIMUM_SUBDIVIDE     5
51 #define MAXIMUM_SCALES        21
52 
53 #define ROUND(x) ((int) ((x) + 0.5))
54 
55 
56 static void time_ruler_class_init    (TimeRulerClass *klass);
57 static void time_ruler_init          (TimeRuler      *time_ruler);
58 static gint time_ruler_button_press  (GtkWidget * widget,
59 				      GdkEventButton * event);
60 static gint time_ruler_motion_notify (GtkWidget      *widget,
61 				      GdkEventMotion *event);
62 static void time_ruler_draw_ticks    (GtkRuler       *ruler);
63 static void time_ruler_draw_pos      (GtkRuler       *ruler);
64 
65 GType
time_ruler_get_type(void)66 time_ruler_get_type (void)
67 {
68   static GType time_ruler_type = 0;
69 
70   if (!time_ruler_type)
71     {
72       static const GTypeInfo time_ruler_info =
73       {
74 
75 	sizeof (TimeRulerClass),
76 	NULL, /* base_init */
77 	NULL, /* base_finalize */
78 	(GClassInitFunc) time_ruler_class_init,
79 	NULL, /* class_finalize */
80 	NULL, /* class_data */
81 	sizeof (TimeRuler),
82 	0,    /* n_preallocs */
83 	(GInstanceInitFunc) time_ruler_init,
84 
85       };
86 
87       time_ruler_type = g_type_register_static (GTK_TYPE_RULER, "TimeRuler", &time_ruler_info, 0);
88     }
89 
90   return time_ruler_type;
91 }
92 
93 static void
time_ruler_class_init(TimeRulerClass * klass)94 time_ruler_class_init (TimeRulerClass *klass)
95 {
96   GtkWidgetClass *widget_class;
97   GtkRulerClass *ruler_class;
98 
99   widget_class = (GtkWidgetClass*) klass;
100   ruler_class = (GtkRulerClass*) klass;
101 
102   /*  widget_class->realize = time_ruler_realize;*/
103   widget_class->button_press_event = time_ruler_button_press;
104   widget_class->motion_notify_event = time_ruler_motion_notify;
105 
106   ruler_class->draw_ticks = time_ruler_draw_ticks;
107   ruler_class->draw_pos = time_ruler_draw_pos;
108 }
109 
110 static gfloat ruler_scale[MAXIMUM_SCALES] =
111 { 0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1,
112   2.5, 5, 10, 15, 30, 60, 300, 600, 1800, 3600, 18000, 36000 };
113 
114 static gint subdivide[MAXIMUM_SUBDIVIDE] = { 1, 2, 5, 10, 100 };
115 
116 static void
time_ruler_init(TimeRuler * time_ruler)117 time_ruler_init (TimeRuler *time_ruler)
118 {
119   GtkWidget *widget;
120 
121   time_ruler->samplerate = 44100;
122 
123   widget = GTK_WIDGET (time_ruler);
124   widget->requisition.width = widget->style->xthickness * 2 + 1;
125   widget->requisition.height = widget->style->ythickness * 2 + RULER_HEIGHT;
126 }
127 
128 GtkWidget*
time_ruler_new(void)129 time_ruler_new (void)
130 {
131   return GTK_WIDGET (g_object_new (time_ruler_get_type (), NULL));
132 }
133 
134 static gint
time_ruler_motion_notify(GtkWidget * widget,GdkEventMotion * event)135 time_ruler_motion_notify (GtkWidget *widget, GdkEventMotion *event)
136 {
137   GtkRuler *ruler;
138   gint x;
139 
140   g_return_val_if_fail (widget != NULL, FALSE);
141   g_return_val_if_fail (GTK_IS_TIME_RULER (widget), FALSE);
142   g_return_val_if_fail (event != NULL, FALSE);
143 
144   ruler = GTK_RULER (widget);
145 
146   if (event->is_hint)
147     gdk_window_get_pointer (widget->window, &x, NULL, NULL);
148   else
149     x = event->x;
150 
151   ruler->position = ruler->lower + ((ruler->upper - ruler->lower) * x) / widget->allocation.width;
152 
153   /*  Make sure the ruler has been allocated already  */
154   if (ruler->backing_store != NULL)
155     gtk_ruler_draw_pos (ruler);
156 
157   return FALSE;
158 }
159 
160 static gint
time_ruler_button_press(GtkWidget * widget,GdkEventButton * event)161 time_ruler_button_press (GtkWidget * widget, GdkEventButton * event)
162 {
163   GdkModifierType state;
164   int x, y;
165 
166   gdk_window_get_pointer (event->window, &x, &y, &state);
167 
168   return TRUE;
169 }
170 
171 static void
time_ruler_draw_ticks(GtkRuler * ruler)172 time_ruler_draw_ticks (GtkRuler *ruler)
173 {
174   GtkWidget *widget;
175   GdkGC *gc, *bg_gc;
176   gint i;
177   gint width, height;
178   gint xthickness;
179   gint ythickness;
180   gint length, ideal_length;
181   gdouble lower, upper;		/* Upper and lower limits, in ruler units */
182   gdouble increment, abs_increment; /* Number of pixels per unit */
183   gint scale;			/* Number of units per major unit */
184   gdouble subd_incr;
185   gdouble start, end, cur;
186 #define UNIT_STR_LEN 32
187   gchar unit_str[UNIT_STR_LEN];
188   gint digit_height;
189   gint digit_offset;
190   gint text_width;
191   gint pos;
192   PangoLayout *layout;
193   PangoRectangle logical_rect, ink_rect;
194 
195   g_return_if_fail (ruler != NULL);
196   g_return_if_fail (GTK_IS_TIME_RULER (ruler));
197 
198   if (!GTK_WIDGET_DRAWABLE (ruler))
199     return;
200 
201   widget = GTK_WIDGET (ruler);
202 
203   gc = widget->style->fg_gc[GTK_STATE_NORMAL];
204   bg_gc = widget->style->bg_gc[GTK_STATE_NORMAL];
205 
206   xthickness = widget->style->xthickness;
207   ythickness = widget->style->ythickness;
208 
209   width = widget->allocation.width;
210   height = widget->allocation.height - ythickness * 2;
211 
212   layout = gtk_widget_create_pango_layout (widget, "012456789");
213   pango_layout_get_extents (layout, &ink_rect, &logical_rect);
214 
215   digit_height = PANGO_PIXELS (ink_rect.height) + 2;
216   digit_offset = ink_rect.y;
217 
218 
219   gtk_paint_box (widget->style, ruler->backing_store,
220 		 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
221 		 NULL, widget, "time_ruler",
222 		 0, 0,
223 		 widget->allocation.width, widget->allocation.height);
224 
225 
226   gdk_draw_line (ruler->backing_store, gc,
227 		 xthickness,
228 		 height + ythickness,
229 		 widget->allocation.width - xthickness,
230 		 height + ythickness);
231 
232   upper = ruler->upper / TIME_RULER(ruler)->samplerate;
233   lower = ruler->lower / TIME_RULER(ruler)->samplerate;
234 
235   if ((upper - lower) == 0)
236     return;
237 
238   increment = (gdouble) width / (upper - lower);
239   abs_increment = (gdouble) fabs((double)increment);
240 
241   /* determine the scale
242    *  We calculate the text size as for the vruler instead of using
243    *  text_width = gdk_string_width(font, unit_str), so that the result
244    *  for the scale looks consistent with an accompanying vruler
245    */
246   scale = ceil (ruler->max_size / TIME_RULER(ruler)->samplerate);
247   snprint_time (unit_str, UNIT_STR_LEN, (sw_time_t)scale);
248   /*  snprint_time_smpte (unit_str, UNIT_STR_LEN, (sw_time_t)scale, 10.0);*/
249 
250  text_width = strlen (unit_str) * digit_height + 1;
251 
252   for (scale = 0; scale < MAXIMUM_SCALES; scale++)
253     if (ruler_scale[scale] * abs_increment > 2 * text_width)
254       break;
255 
256   if (scale == MAXIMUM_SCALES)
257     scale = MAXIMUM_SCALES - 1;
258 
259   /* drawing starts here */
260   length = 0;
261   for (i = MAXIMUM_SUBDIVIDE - 1; i >= 0; i--)
262     {
263       subd_incr = (gdouble) ruler_scale[scale] /
264 	          (gdouble) subdivide[i];
265       if (subd_incr * fabs(increment) <= MINIMUM_INCR)
266 	continue;
267 
268       /* Calculate the length of the tickmarks. Make sure that
269        * this length increases for each set of ticks
270        */
271       ideal_length = height / (i + 1) - 1;
272       if (ideal_length > ++length)
273 	length = ideal_length;
274 
275       if (lower < upper)
276 	{
277 	  start = floor (lower / subd_incr) * subd_incr;
278 	  end   = ceil  (upper / subd_incr) * subd_incr;
279 	}
280       else
281 	{
282 	  start = floor (upper / subd_incr) * subd_incr;
283 	  end   = ceil  (lower / subd_incr) * subd_incr;
284 	}
285 
286 
287       for (cur = start; cur <= end; cur += subd_incr)
288 	{
289 	  pos = ROUND ((cur - lower) * increment);
290 
291 	  gdk_draw_line (ruler->backing_store, gc,
292 			 pos, height + ythickness,
293 			 pos, height - length + ythickness);
294 
295 	  /* draw label */
296 	  if (i == 0)
297 	    {
298 #if 1
299 	      snprint_time (unit_str, UNIT_STR_LEN, (sw_time_t)cur);
300 #else
301 	      snprint_time_smpte (unit_str, UNIT_STR_LEN, (sw_time_t)cur, 10.0);
302 #endif
303   		  pango_layout_set_text (layout, unit_str, -1);
304  		  pango_layout_get_extents (layout, NULL, &logical_rect);
305 
306  		  gtk_paint_layout (widget->style,
307                   ruler->backing_store,
308                   GTK_WIDGET_STATE (widget),
309 				  FALSE,
310                   NULL,
311                   widget,
312                   "vruler",
313                   pos +2,
314                   2,
315                   layout);
316 	    }
317 	 }
318    }
319 }
320 
321 static void
time_ruler_draw_pos(GtkRuler * ruler)322 time_ruler_draw_pos (GtkRuler *ruler)
323 {
324   GtkWidget *widget;
325   GdkGC *gc;
326   int i;
327   gint x, y;
328   gint width, height;
329   gint bs_width, bs_height;
330   gint xthickness;
331   gint ythickness;
332   gfloat increment;
333 
334   g_return_if_fail (ruler != NULL);
335   g_return_if_fail (GTK_IS_TIME_RULER (ruler));
336 
337   if (GTK_WIDGET_DRAWABLE (ruler))
338     {
339       widget = GTK_WIDGET (ruler);
340 
341       gc = widget->style->fg_gc[GTK_STATE_NORMAL];
342       xthickness = widget->style->xthickness;
343       ythickness = widget->style->ythickness;
344       width = widget->allocation.width;
345       height = widget->allocation.height - ythickness * 2;
346 
347       bs_width = height / 2;
348       bs_width |= 1;  /* make sure it's odd */
349       bs_height = bs_width / 2 + 1;
350 
351       if ((bs_width > 0) && (bs_height > 0))
352 	{
353 	  /*  If a backing store exists, restore the ruler  */
354 	  if (ruler->backing_store && ruler->non_gr_exp_gc)
355 	    gdk_draw_drawable (ruler->widget.window,
356 			     ruler->non_gr_exp_gc,
357 			     ruler->backing_store,
358 			     ruler->xsrc, ruler->ysrc,
359 			     ruler->xsrc, ruler->ysrc,
360 			     bs_width, bs_height);
361 
362 	  increment = (gfloat) width / (ruler->upper - ruler->lower);
363 
364 	  x = ROUND ((ruler->position - ruler->lower) * increment) +
365 	    (xthickness - bs_width) / 2 - 1;
366 	  y = (height + bs_height) / 2 + ythickness;
367 
368 	  for (i = 0; i < bs_height; i++)
369 	    gdk_draw_line (widget->window, gc,
370 			   x + i, y + i,
371 			   x + bs_width - 1 - i, y + i);
372 
373 
374 	  ruler->xsrc = x;
375 	  ruler->ysrc = y;
376 	}
377     }
378 }
379 
380 void
time_ruler_set_format(TimeRuler * time_ruler,sw_format * f)381 time_ruler_set_format (TimeRuler * time_ruler, sw_format * f)
382 {
383   time_ruler->samplerate = f->rate;
384 }
385