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