1 /*
2 * Copyright (C) 2004 2005 2008 2010, Magnus Hjorth
3 *
4 * This file is part of mhWaveEdit.
5 *
6 * mhWaveEdit is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * mhWaveEdit is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with mhWaveEdit; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 #include <config.h>
22
23 #include <string.h>
24 #include <stdio.h>
25
26 #include "statusbar.h"
27 #include "mainloop.h"
28 #include "gettext.h"
29
30 #define LEFT_MARGIN 4
31
32 gboolean status_bar_roll_cursor;
33
34 static GtkWidgetClass *parent_class;
35 static int progress_count = 0;
36
37 enum { PROGRESS_BEGIN_SIGNAL, PROGRESS_END_SIGNAL, LAST_SIGNAL };
38 static guint status_bar_signals[LAST_SIGNAL] = { 0 };
39
status_bar_expose(GtkWidget * widget,GdkEventExpose * event,gpointer user_data)40 static void status_bar_expose(GtkWidget *widget, GdkEventExpose *event,
41 gpointer user_data)
42 {
43 StatusBar *bar = STATUSBAR(user_data);
44 GdkGC *gc;
45
46 if (bar->mode == 1) {
47 gc = get_gc(PROGRESS,widget);
48 gdk_gc_set_clip_rectangle(gc,&(event->area));
49 gdk_draw_rectangle(widget->window, gc, TRUE,
50 widget->allocation.x,event->area.y,
51 bar->bar_width,event->area.height);
52
53 gdk_gc_set_clip_mask(gc,NULL);
54 }
55 }
56
status_bar_init(GtkObject * obj)57 static void status_bar_init(GtkObject *obj)
58 {
59 StatusBar *sb = STATUSBAR(obj);
60 GtkContainer *cont = GTK_CONTAINER(obj);
61 GtkFixed *fix = GTK_FIXED(obj);
62 GtkWidget *da;
63
64 da = gtk_label_new("");
65 sb->da = da;
66 gtk_signal_connect(GTK_OBJECT(da),"expose_event",
67 GTK_SIGNAL_FUNC(status_bar_expose),obj);
68 gtk_fixed_put(fix,da,0,0);
69
70 sb->mode = 2;
71 sb->rate = 0;
72 sb->fresh_label = GTK_LABEL(gtk_label_new(_("(no file loaded)")));
73 sb->progress_label = GTK_LABEL(gtk_label_new(""));
74 sb->cursor = GTK_LABEL(gtk_label_new(""));
75 sb->view = GTK_LABEL(gtk_label_new(""));
76 sb->sel = GTK_LABEL(gtk_label_new(""));
77 gtk_fixed_put(fix,GTK_WIDGET(sb->fresh_label),LEFT_MARGIN,2);
78 gtk_fixed_put(fix,GTK_WIDGET(sb->progress_label),LEFT_MARGIN,2);
79 gtk_fixed_put(fix,GTK_WIDGET(sb->cursor),LEFT_MARGIN,2);
80 gtk_container_add(cont,GTK_WIDGET(sb->view));
81 gtk_container_add(cont,GTK_WIDGET(sb->sel));
82 gtk_widget_show(GTK_WIDGET(sb->fresh_label));
83 }
84
status_bar_size_allocate(GtkWidget * widget,GtkAllocation * allocation)85 static void status_bar_size_allocate(GtkWidget *widget,
86 GtkAllocation *allocation)
87 {
88 StatusBar *sb = STATUSBAR(widget);
89 GtkWidget *daw = GTK_WIDGET(sb->da);
90 if (daw->allocation.height != allocation->height ||
91 daw->allocation.width != allocation->width) {
92 gtk_widget_set_usize(daw,allocation->width,allocation->height);
93 }
94 parent_class->size_allocate(widget,allocation);
95 }
96
status_bar_size_request(GtkWidget * widget,GtkRequisition * requisition)97 static void status_bar_size_request(GtkWidget *widget,
98 GtkRequisition *requisition)
99 {
100 parent_class->size_request(widget,requisition);
101 requisition->width = 10;
102 }
103
status_bar_class_init(GtkObjectClass * klass)104 static void status_bar_class_init(GtkObjectClass *klass)
105 {
106 parent_class = GTK_WIDGET_CLASS(gtk_type_class(gtk_fixed_get_type()));
107 GTK_WIDGET_CLASS(klass)->size_allocate = status_bar_size_allocate;
108 GTK_WIDGET_CLASS(klass)->size_request = status_bar_size_request;
109 STATUSBAR_CLASS(klass)->progress_begin = NULL;
110 STATUSBAR_CLASS(klass)->progress_end = NULL;
111 status_bar_signals[PROGRESS_BEGIN_SIGNAL] =
112 gtk_signal_new("progress-begin", GTK_RUN_FIRST,GTK_CLASS_TYPE(klass),
113 GTK_SIGNAL_OFFSET(StatusBarClass,progress_begin),
114 gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0);
115 status_bar_signals[PROGRESS_END_SIGNAL] =
116 gtk_signal_new("progress-end", GTK_RUN_FIRST, GTK_CLASS_TYPE(klass),
117 GTK_SIGNAL_OFFSET(StatusBarClass,progress_end),
118 gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0);
119 gtk_object_class_add_signals(klass,status_bar_signals,LAST_SIGNAL);
120 }
121
status_bar_get_type(void)122 GtkType status_bar_get_type(void)
123 {
124 static GtkType id = 0;
125 if (!id) {
126 GtkTypeInfo info = {
127 "StatusBar",
128 sizeof(StatusBar),
129 sizeof(StatusBarClass),
130 (GtkClassInitFunc)status_bar_class_init,
131 (GtkObjectInitFunc)status_bar_init
132 };
133 id = gtk_type_unique(gtk_fixed_get_type(),&info);
134 }
135 return id;
136 }
137
status_bar_new(void)138 GtkWidget *status_bar_new(void)
139 {
140 return GTK_WIDGET(gtk_type_new(status_bar_get_type()));
141 }
142
status_bar_set_mode(StatusBar * sb,gint mode)143 static void status_bar_set_mode(StatusBar *sb, gint mode)
144 {
145 gint old_mode;
146 if (mode == sb->mode) return;
147 old_mode = sb->mode;
148 switch (sb->mode) {
149 case 0:
150 gtk_widget_hide(GTK_WIDGET(sb->cursor));
151 gtk_widget_hide(GTK_WIDGET(sb->view));
152 gtk_widget_hide(GTK_WIDGET(sb->sel));
153 break;
154 case 1:
155 gtk_widget_hide(GTK_WIDGET(sb->progress_label));
156 progress_count --;
157 break;
158 case 2:
159 gtk_widget_hide(GTK_WIDGET(sb->fresh_label));
160 break;
161 }
162 switch (mode) {
163 case 0:
164 /* The labels aren't shown here since they may need to be
165 * laid out one by one. Instead, that is handled in
166 * status_bar_set_info. */
167 break;
168 case 1:
169 gtk_widget_show(GTK_WIDGET(sb->progress_label));
170 sb->progress_cur = 0;
171 sb->progress_max = 1;
172 sb->bar_width = 0;
173 progress_count ++;
174 sb->progress_break = FALSE;
175 break;
176 case 2:
177 gtk_widget_show(GTK_WIDGET(sb->fresh_label));
178 break;
179 }
180 sb->mode = mode;
181 if (old_mode == 1)
182 gtk_signal_emit(GTK_OBJECT(sb),
183 status_bar_signals[PROGRESS_END_SIGNAL]);
184 if (mode == 1)
185 gtk_signal_emit(GTK_OBJECT(sb),
186 status_bar_signals[PROGRESS_BEGIN_SIGNAL]);
187 gtk_widget_queue_draw(GTK_WIDGET(sb));
188 }
189
status_bar_reset(StatusBar * sb)190 void status_bar_reset(StatusBar *sb)
191 {
192 status_bar_set_mode(sb,2);
193 }
194
status_bar_set_info(StatusBar * sb,off_t cursorpos,gboolean is_rolling,off_t viewstart,off_t viewend,off_t selstart,off_t selend,off_t samplerate,off_t maxvalue)195 void status_bar_set_info(StatusBar *sb, off_t cursorpos, gboolean is_rolling,
196 off_t viewstart, off_t viewend, off_t selstart,
197 off_t selend, off_t samplerate, off_t maxvalue)
198 {
199 gchar buf[256];
200 gboolean cdif=FALSE,vdif=FALSE,sdif=FALSE,mdif;
201 guint p;
202 GtkRequisition req;
203
204 sb->rate = samplerate;
205
206 /* What needs to be updated? */
207 if (sb->mode != 0) {
208 mdif = TRUE;
209 } else {
210 if (XOR(is_rolling,sb->rolling))
211 cdif = TRUE;
212 else if (is_rolling && status_bar_roll_cursor)
213 cdif = (cursorpos < sb->cur) || (cursorpos > sb->cur+samplerate/20);
214 else
215 cdif = (!is_rolling && sb->cur != cursorpos);
216
217 vdif = (sb->vs != viewstart) || (sb->ve != viewend);
218 if (selstart == selend)
219 sdif = (sb->ss != sb->se);
220 else
221 sdif = (sb->ss != selstart) || (sb->se != selend);
222 if (maxvalue > sb->max) {
223 mdif = TRUE;
224 } else
225 mdif = FALSE;
226 }
227
228 /* Hide other labels */
229 status_bar_set_mode(sb,0);
230
231 p = LEFT_MARGIN;
232 /* Update cursor info */
233 if (cdif || mdif) {
234 if (is_rolling && !status_bar_roll_cursor)
235 strcpy(buf,_("Cursor: running"));
236 else {
237 strcpy(buf,_("Cursor: "));
238 get_time(samplerate,cursorpos,maxvalue,buf+strlen(buf),
239 default_time_mode);
240 }
241 gtk_label_set_text(sb->cursor,buf);
242 sb->cur = cursorpos;
243 sb->rolling = is_rolling;
244 }
245 if (mdif) {
246 /* gtk_fixed_move(sb->fixed,GTK_WIDGET(sb->cursor),p,2); */
247 gtk_widget_show(GTK_WIDGET(sb->cursor));
248 gtk_widget_size_request(GTK_WIDGET(sb->cursor),&req);
249 /* add some extra margin to be able to handle
250 * larger values and a LOT of extra margin if using
251 * 'Samples' display mode (slightly hackish I admit)
252 */
253 if (default_time_mode != 2)
254 p = p + req.width + req.width/4;
255 else
256 p = p + req.width + req.width/2;
257 }
258 /* Update view info */
259 if (vdif || mdif) {
260 g_snprintf(buf,150,_("View: [ %s - %s ]"),
261 get_time(samplerate,viewstart,maxvalue,buf+150,
262 default_time_mode),
263 get_time(samplerate,viewend,maxvalue,buf+200,
264 default_time_mode));
265 gtk_label_set_text(sb->view,buf);
266 sb->vs = viewstart;
267 sb->ve = viewend;
268 }
269 if (mdif) {
270 gtk_fixed_move(GTK_FIXED(sb),GTK_WIDGET(sb->view),p,2);
271 gtk_widget_show(GTK_WIDGET(sb->view));
272 gtk_widget_size_request(GTK_WIDGET(sb->view),&req);
273 if (default_time_mode != 2)
274 p = p + req.width + 10;
275 else
276 p = p + req.width + req.width/4;
277 }
278 /* Update selection info */
279 if (sdif || mdif) {
280 if (selstart != selend) {
281 g_snprintf(buf,150,_("Selection: %s+%s"),
282 get_time(samplerate,selstart,maxvalue,buf+150,
283 default_time_mode),
284 get_time(samplerate,selend-selstart,maxvalue,
285 buf+200,default_time_mode));
286 gtk_label_set_text(sb->sel,buf);
287 } else
288 gtk_label_set_text(sb->sel,"");
289 sb->ss = selstart;
290 sb->se = selend;
291 }
292 if (mdif) {
293 gtk_fixed_move(GTK_FIXED(sb),GTK_WIDGET(sb->sel),p,2);
294 gtk_widget_show(GTK_WIDGET(sb->sel));
295 }
296 /* Update the max value */
297 sb->max = maxvalue;
298 }
299
status_bar_begin_progress(StatusBar * sb,off_t progress_length,gchar * description)300 void status_bar_begin_progress(StatusBar *sb, off_t progress_length,
301 gchar *description)
302 {
303 gchar *c;
304 if (description == NULL && sb->mode != 1) description=_("Processing data");
305 if (description != NULL) {
306 c = g_strdup_printf(_("%s... (Press ESC to cancel)"),description);
307 gtk_label_set_text(sb->progress_label,c);
308 g_free(c);
309 }
310 status_bar_set_mode(sb,1);
311 sb->progress_max = progress_length;
312 sb->progress_cur = 0;
313 }
314
status_bar_end_progress(StatusBar * sb)315 void status_bar_end_progress(StatusBar *sb)
316 {
317 if (sb != NULL) {
318 if (sb->rate != 0)
319 status_bar_set_info(sb,sb->cur,sb->rolling,sb->vs,sb->ve,sb->ss,sb->se,
320 sb->rate,sb->max);
321 else
322 status_bar_reset(sb);
323 }
324 }
325
status_bar_progress(StatusBar * sb,off_t progress)326 gboolean status_bar_progress(StatusBar *sb, off_t progress)
327 {
328 guint bw,obw;
329 gfloat f;
330 sb->progress_cur += progress;
331 f = ((gfloat)sb->progress_cur)/((gfloat)sb->progress_max);
332 if (f > 1.0) f = 1.0;
333 f *= (gfloat)(GTK_WIDGET(sb)->allocation.width);
334 bw = (guint)f;
335 /* Usually, bw should be larger than sb->bar_width since we've done
336 * progress, but it can be smaller if we just shrunk the window. */
337 if (bw > sb->bar_width) {
338 obw = sb->bar_width;
339 gtk_widget_queue_draw_area(GTK_WIDGET(sb),sb->da->allocation.x+obw,
340 sb->da->allocation.y,bw-obw,
341 sb->da->allocation.height);
342 } else if (bw < sb->bar_width) {
343 gtk_widget_queue_draw(GTK_WIDGET(sb));
344 }
345 sb->bar_width = bw;
346 idle_work_flag = FALSE;
347 while (!idle_work_flag) mainloop();
348 return sb->progress_break;
349 }
350
status_bar_break_progress(StatusBar * sb)351 void status_bar_break_progress(StatusBar *sb)
352 {
353 sb->progress_break = TRUE;
354 }
355
status_bar_progress_count(void)356 int status_bar_progress_count(void)
357 {
358 return progress_count;
359 }
360