| 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 |
|---|
| 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 |
|---|
| 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 |
|---|
| 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* |
|---|
| 129 |
time_ruler_new (void) |
|---|
| 130 |
{ |
|---|
| 131 |
return GTK_WIDGET (g_object_new (time_ruler_get_type (), NULL)); |
|---|
| 132 |
} |
|---|
| 133 |
|
|---|
| 134 |
static gint |
|---|
| 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 |
|---|
| 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 |
|---|
| 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 |
gchar unit_str[32]; |
|---|
| 187 |
gint digit_height; |
|---|
| 188 |
gint digit_offset; |
|---|
| 189 |
gint text_width; |
|---|
| 190 |
gint pos; |
|---|
| 191 |
PangoLayout *layout; |
|---|
| 192 |
PangoRectangle logical_rect, ink_rect; |
|---|
| 193 |
|
|---|
| 194 |
g_return_if_fail (ruler != NULL); |
|---|
| 195 |
g_return_if_fail (GTK_IS_TIME_RULER (ruler)); |
|---|
| 196 |
|
|---|
| 197 |
if (!GTK_WIDGET_DRAWABLE (ruler)) |
|---|
| 198 |
return; |
|---|
| 199 |
|
|---|
| 200 |
widget = GTK_WIDGET (ruler); |
|---|
| 201 |
|
|---|
| 202 |
gc = widget->style->fg_gc[GTK_STATE_NORMAL]; |
|---|
| 203 |
bg_gc = widget->style->bg_gc[GTK_STATE_NORMAL]; |
|---|
| 204 |
|
|---|
| 205 |
xthickness = widget->style->xthickness; |
|---|
| 206 |
ythickness = widget->style->ythickness; |
|---|
| 207 |
|
|---|
| 208 |
width = widget->allocation.width; |
|---|
| 209 |
height = widget->allocation.height - ythickness * 2; |
|---|
| 210 |
|
|---|
| 211 |
layout = gtk_widget_create_pango_layout (widget, "012456789"); |
|---|
| 212 |
pango_layout_get_extents (layout, &ink_rect, &logical_rect); |
|---|
| 213 |
|
|---|
| 214 |
digit_height = PANGO_PIXELS (ink_rect.height) + 2; |
|---|
| 215 |
digit_offset = ink_rect.y; |
|---|
| 216 |
|
|---|
| 217 |
|
|---|
| 218 |
gtk_paint_box (widget->style, ruler->backing_store, |
|---|
| 219 |
GTK_STATE_NORMAL, GTK_SHADOW_OUT, |
|---|
| 220 |
NULL, widget, "time_ruler", |
|---|
| 221 |
0, 0, |
|---|
| 222 |
widget->allocation.width, widget->allocation.height); |
|---|
| 223 |
|
|---|
| 224 |
|
|---|
| 225 |
gdk_draw_line (ruler->backing_store, gc, |
|---|
| 226 |
xthickness, |
|---|
| 227 |
height + ythickness, |
|---|
| 228 |
widget->allocation.width - xthickness, |
|---|
| 229 |
height + ythickness); |
|---|
| 230 |
|
|---|
| 231 |
upper = ruler->upper / TIME_RULER(ruler)->samplerate; |
|---|
| 232 |
lower = ruler->lower / TIME_RULER(ruler)->samplerate; |
|---|
| 233 |
|
|---|
| 234 |
if ((upper - lower) == 0) |
|---|
| 235 |
return; |
|---|
| 236 |
|
|---|
| 237 |
increment = (gdouble) width / (upper - lower); |
|---|
| 238 |
abs_increment = (gdouble) fabs((double)increment); |
|---|
| 239 |
|
|---|
| 240 |
/* determine the scale |
|---|
| 241 |
* We calculate the text size as for the vruler instead of using |
|---|
| 242 |
* text_width = gdk_string_width(font, unit_str), so that the result |
|---|
| 243 |
* for the scale looks consistent with an accompanying vruler |
|---|
| 244 |
*/ |
|---|
| 245 |
scale = ceil (ruler->max_size / TIME_RULER(ruler)->samplerate); |
|---|
| 246 |
snprint_time (unit_str, sizeof (unit_str), (sw_time_t)scale); |
|---|
| 247 |
|
|---|
| 248 |
text_width = strlen (unit_str) * digit_height + 1; |
|---|
| 249 |
|
|---|
| 250 |
for (scale = 0; scale < MAXIMUM_SCALES; scale++) |
|---|
| 251 |
if (ruler_scale[scale] * abs_increment > 2 * text_width) |
|---|
| 252 |
break; |
|---|
| 253 |
|
|---|
| 254 |
if (scale == MAXIMUM_SCALES) |
|---|
| 255 |
scale = MAXIMUM_SCALES - 1; |
|---|
| 256 |
|
|---|
| 257 |
/* drawing starts here */ |
|---|
| 258 |
length = 0; |
|---|
| 259 |
for (i = MAXIMUM_SUBDIVIDE - 1; i >= 0; i--) |
|---|
| 260 |
{ |
|---|
| 261 |
subd_incr = (gdouble) ruler_scale[scale] / |
|---|
| 262 |
(gdouble) subdivide[i]; |
|---|
| 263 |
if (subd_incr * fabs(increment) <= MINIMUM_INCR) |
|---|
| 264 |
continue; |
|---|
| 265 |
|
|---|
| 266 |
/* Calculate the length of the tickmarks. Make sure that |
|---|
| 267 |
* this length increases for each set of ticks |
|---|
| 268 |
*/ |
|---|
| 269 |
ideal_length = height / (i + 1) - 1; |
|---|
| 270 |
if (ideal_length > ++length) |
|---|
| 271 |
length = ideal_length; |
|---|
| 272 |
|
|---|
| 273 |
if (lower < upper) |
|---|
| 274 |
{ |
|---|
| 275 |
start = floor (lower / subd_incr) * subd_incr; |
|---|
| 276 |
end = ceil (upper / subd_incr) * subd_incr; |
|---|
| 277 |
} |
|---|
| 278 |
else |
|---|
| 279 |
{ |
|---|
| 280 |
start = floor (upper / subd_incr) * subd_incr; |
|---|
| 281 |
end = ceil (lower / subd_incr) * subd_incr; |
|---|
| 282 |
} |
|---|
| 283 |
|
|---|
| 284 |
|
|---|
| 285 |
for (cur = start; cur <= end; cur += subd_incr) |
|---|
| 286 |
{ |
|---|
| 287 |
pos = ROUND ((cur - lower) * increment); |
|---|
| 288 |
|
|---|
| 289 |
gdk_draw_line (ruler->backing_store, gc, |
|---|
| 290 |
pos, height + ythickness, |
|---|
| 291 |
pos, height - length + ythickness); |
|---|
| 292 |
|
|---|
| 293 |
/* draw label */ |
|---|
| 294 |
if (i == 0) |
|---|
| 295 |
{ |
|---|
| 296 |
snprint_time (unit_str, sizeof (unit_str), (sw_time_t)cur); |
|---|
| 297 |
pango_layout_set_text (layout, unit_str, -1); |
|---|
| 298 |
pango_layout_get_extents (layout, NULL, &logical_rect); |
|---|
| 299 |
|
|---|
| 300 |
gtk_paint_layout (widget->style, |
|---|
| 301 |
ruler->backing_store, |
|---|
| 302 |
GTK_WIDGET_STATE (widget), |
|---|
| 303 |
FALSE, |
|---|
| 304 |
NULL, |
|---|
| 305 |
widget, |
|---|
| 306 |
"vruler", |
|---|
| 307 |
pos +2, |
|---|
| 308 |
2, |
|---|
| 309 |
layout); |
|---|
| 310 |
} |
|---|
| 311 |
} |
|---|
| 312 |
} |
|---|
| 313 |
} |
|---|
| 314 |
|
|---|
| 315 |
static void |
|---|
| 316 |
time_ruler_draw_pos (GtkRuler *ruler) |
|---|
| 317 |
{ |
|---|
| 318 |
GtkWidget *widget; |
|---|
| 319 |
GdkGC *gc; |
|---|
| 320 |
int i; |
|---|
| 321 |
gint x, y; |
|---|
| 322 |
gint width, height; |
|---|
| 323 |
gint bs_width, bs_height; |
|---|
| 324 |
gint xthickness; |
|---|
| 325 |
gint ythickness; |
|---|
| 326 |
gfloat increment; |
|---|
| 327 |
|
|---|
| 328 |
g_return_if_fail (ruler != NULL); |
|---|
| 329 |
g_return_if_fail (GTK_IS_TIME_RULER (ruler)); |
|---|
| 330 |
|
|---|
| 331 |
if (GTK_WIDGET_DRAWABLE (ruler)) |
|---|
| 332 |
{ |
|---|
| 333 |
widget = GTK_WIDGET (ruler); |
|---|
| 334 |
|
|---|
| 335 |
gc = widget->style->fg_gc[GTK_STATE_NORMAL]; |
|---|
| 336 |
xthickness = widget->style->xthickness; |
|---|
| 337 |
ythickness = widget->style->ythickness; |
|---|
| 338 |
width = widget->allocation.width; |
|---|
| 339 |
height = widget->allocation.height - ythickness * 2; |
|---|
| 340 |
|
|---|
| 341 |
bs_width = height / 2; |
|---|
| 342 |
bs_width |= 1; /* make sure it's odd */ |
|---|
| 343 |
bs_height = bs_width / 2 + 1; |
|---|
| 344 |
|
|---|
| 345 |
if ((bs_width > 0) && (bs_height > 0)) |
|---|
| 346 |
{ |
|---|
| 347 |
/* If a backing store exists, restore the ruler */ |
|---|
| 348 |
if (ruler->backing_store && ruler->non_gr_exp_gc) |
|---|
| 349 |
gdk_draw_drawable (ruler->widget.window, |
|---|
| 350 |
ruler->non_gr_exp_gc, |
|---|
| 351 |
ruler->backing_store, |
|---|
| 352 |
ruler->xsrc, ruler->ysrc, |
|---|
| 353 |
ruler->xsrc, ruler->ysrc, |
|---|
| 354 |
bs_width, bs_height); |
|---|
| 355 |
|
|---|
| 356 |
increment = (gfloat) width / (ruler->upper - ruler->lower); |
|---|
| 357 |
|
|---|
| 358 |
x = ROUND ((ruler->position - ruler->lower) * increment) + |
|---|
| 359 |
(xthickness - bs_width) / 2 - 1; |
|---|
| 360 |
y = (height + bs_height) / 2 + ythickness; |
|---|
| 361 |
|
|---|
| 362 |
for (i = 0; i < bs_height; i++) |
|---|
| 363 |
gdk_draw_line (widget->window, gc, |
|---|
| 364 |
x + i, y + i, |
|---|
| 365 |
x + bs_width - 1 - i, y + i); |
|---|
| 366 |
|
|---|
| 367 |
|
|---|
| 368 |
ruler->xsrc = x; |
|---|
| 369 |
ruler->ysrc = y; |
|---|
| 370 |
} |
|---|
| 371 |
} |
|---|
| 372 |
} |
|---|
| 373 |
|
|---|
| 374 |
void |
|---|
| 375 |
time_ruler_set_format (TimeRuler * time_ruler, sw_format * f) |
|---|
| 376 |
{ |
|---|
| 377 |
time_ruler->samplerate = f->rate; |
|---|
| 378 |
} |
|---|