You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

gvc-level-bar.c 32 KiB

4 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887
  1. /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
  2. *
  3. * Copyright (C) 2008 William Jon McCann <william.jon.mccann@gmail.com>
  4. * Copyright (C) 2014 Michal Ratajsky <michal.ratajsky@gmail.com>
  5. *
  6. * This program 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. * This program 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 this program; if not, write to the Free Software
  18. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
  19. *
  20. */
  21. #include <math.h>
  22. #include <glib.h>
  23. #include <glib/gi18n.h>
  24. #include <glib-object.h>
  25. #include <gtk/gtk.h>
  26. #define MATE_DESKTOP_USE_UNSTABLE_API
  27. #include <libmate-desktop/mate-desktop-utils.h>
  28. #include "gvc-level-bar.h"
  29. #include "gvc-utils.h"
  30. #define GVC_LEVEL_BAR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GVC_TYPE_LEVEL_BAR, GvcLevelBarPrivate))
  31. #define NUM_BOXES 15
  32. #define MIN_HORIZONTAL_BAR_WIDTH 150
  33. #define HORIZONTAL_BAR_HEIGHT 6
  34. #define VERTICAL_BAR_WIDTH 6
  35. #define MIN_VERTICAL_BAR_HEIGHT 400
  36. typedef struct {
  37. int peak_num;
  38. int max_peak_num;
  39. GdkRectangle area;
  40. int delta;
  41. int box_width;
  42. int box_height;
  43. int box_radius;
  44. #if GTK_CHECK_VERSION (3, 0, 0)
  45. GdkRGBA color_bg;
  46. GdkRGBA color_fg;
  47. GdkRGBA color_dark;
  48. #else
  49. GdkColor color_bg;
  50. GdkColor color_fg;
  51. GdkColor color_dark;
  52. #endif
  53. } LevelBarLayout;
  54. struct _GvcLevelBarPrivate
  55. {
  56. GtkOrientation orientation;
  57. GtkAdjustment *peak_adjustment;
  58. GtkAdjustment *rms_adjustment;
  59. GvcLevelScale scale;
  60. gdouble peak_fraction;
  61. gdouble rms_fraction;
  62. gdouble max_peak;
  63. guint max_peak_id;
  64. LevelBarLayout layout;
  65. };
  66. enum
  67. {
  68. PROP_0,
  69. PROP_PEAK_ADJUSTMENT,
  70. PROP_RMS_ADJUSTMENT,
  71. PROP_SCALE,
  72. PROP_ORIENTATION,
  73. N_PROPERTIES
  74. };
  75. static GParamSpec *properties[N_PROPERTIES] = { NULL, };
  76. static void gvc_level_bar_class_init (GvcLevelBarClass *klass);
  77. static void gvc_level_bar_init (GvcLevelBar *bar);
  78. static void gvc_level_bar_finalize (GObject *object);
  79. G_DEFINE_TYPE (GvcLevelBar, gvc_level_bar, GTK_TYPE_WIDGET)
  80. static gboolean
  81. layout_changed (LevelBarLayout *layout1, LevelBarLayout *layout2)
  82. {
  83. if (layout1->area.x != layout2->area.x)
  84. return TRUE;
  85. if (layout1->area.y != layout2->area.y)
  86. return TRUE;
  87. if (layout1->area.width != layout2->area.width)
  88. return TRUE;
  89. if (layout1->area.height != layout2->area.height)
  90. return TRUE;
  91. if (layout1->delta != layout2->delta)
  92. return TRUE;
  93. if (layout1->peak_num != layout2->peak_num)
  94. return TRUE;
  95. if (layout1->max_peak_num != layout2->max_peak_num)
  96. return TRUE;
  97. #if GTK_CHECK_VERSION (3, 0, 0)
  98. if (!gdk_rgba_equal (&layout1->color_fg, &layout2->color_fg))
  99. return TRUE;
  100. if (!gdk_rgba_equal (&layout1->color_bg, &layout2->color_bg))
  101. return TRUE;
  102. if (!gdk_rgba_equal (&layout1->color_dark, &layout2->color_dark))
  103. return TRUE;
  104. #else
  105. if (!gdk_color_equal (&layout1->color_fg, &layout2->color_fg))
  106. return TRUE;
  107. if (!gdk_color_equal (&layout1->color_bg, &layout2->color_bg))
  108. return TRUE;
  109. if (!gdk_color_equal (&layout1->color_dark, &layout2->color_dark))
  110. return TRUE;
  111. #endif
  112. return FALSE;
  113. }
  114. static gdouble
  115. fraction_from_adjustment (GvcLevelBar *bar,
  116. GtkAdjustment *adjustment)
  117. {
  118. gdouble level;
  119. gdouble fraction = 0.0;
  120. gdouble min;
  121. gdouble max;
  122. level = gtk_adjustment_get_value (adjustment);
  123. min = gtk_adjustment_get_lower (adjustment);
  124. max = gtk_adjustment_get_upper (adjustment);
  125. switch (bar->priv->scale) {
  126. case GVC_LEVEL_SCALE_LINEAR:
  127. fraction = (level - min) / (max - min);
  128. break;
  129. case GVC_LEVEL_SCALE_LOG:
  130. fraction = log10 ((level - min + 1) / (max - min + 1));
  131. break;
  132. }
  133. return fraction;
  134. }
  135. static gboolean
  136. reset_max_peak (GvcLevelBar *bar)
  137. {
  138. bar->priv->max_peak = gtk_adjustment_get_lower (bar->priv->peak_adjustment);
  139. bar->priv->layout.max_peak_num = 0;
  140. gtk_widget_queue_draw (GTK_WIDGET (bar));
  141. bar->priv->max_peak_id = 0;
  142. return FALSE;
  143. }
  144. static void
  145. bar_calc_layout (GvcLevelBar *bar)
  146. {
  147. int peak_level;
  148. int max_peak_level;
  149. GtkAllocation allocation;
  150. #if GTK_CHECK_VERSION (3, 0, 0)
  151. GtkStyleContext *context;
  152. context = gtk_widget_get_style_context (GTK_WIDGET (bar));
  153. gtk_style_context_save (context);
  154. gtk_style_context_set_state (context, GTK_STATE_FLAG_NORMAL);
  155. gtk_style_context_get_background_color (context,
  156. gtk_style_context_get_state (context),
  157. &bar->priv->layout.color_bg);
  158. mate_desktop_gtk_style_get_dark_color (context,
  159. gtk_style_context_get_state (context),
  160. &bar->priv->layout.color_dark);
  161. gtk_style_context_set_state (context, GTK_STATE_FLAG_SELECTED);
  162. gtk_style_context_get_background_color (context,
  163. gtk_style_context_get_state (context),
  164. &bar->priv->layout.color_fg);
  165. gtk_style_context_restore (context);
  166. #else
  167. GtkStyle *style;
  168. style = gtk_widget_get_style (GTK_WIDGET (bar));
  169. bar->priv->layout.color_bg = style->bg[GTK_STATE_NORMAL];
  170. bar->priv->layout.color_fg = style->bg[GTK_STATE_SELECTED];
  171. bar->priv->layout.color_dark = style->dark[GTK_STATE_NORMAL];
  172. #endif
  173. gtk_widget_get_allocation (GTK_WIDGET (bar), &allocation);
  174. bar->priv->layout.area.width = allocation.width - 2;
  175. bar->priv->layout.area.height = allocation.height - 2;
  176. if (bar->priv->orientation == GTK_ORIENTATION_VERTICAL) {
  177. peak_level = bar->priv->peak_fraction * bar->priv->layout.area.height;
  178. max_peak_level = bar->priv->max_peak * bar->priv->layout.area.height;
  179. bar->priv->layout.delta = bar->priv->layout.area.height / NUM_BOXES;
  180. bar->priv->layout.area.x = 0;
  181. bar->priv->layout.area.y = 0;
  182. bar->priv->layout.box_height = bar->priv->layout.delta / 2;
  183. bar->priv->layout.box_width = bar->priv->layout.area.width;
  184. bar->priv->layout.box_radius = bar->priv->layout.box_width / 2;
  185. } else {
  186. peak_level = bar->priv->peak_fraction * bar->priv->layout.area.width;
  187. max_peak_level = bar->priv->max_peak * bar->priv->layout.area.width;
  188. bar->priv->layout.delta = bar->priv->layout.area.width / NUM_BOXES;
  189. bar->priv->layout.area.x = 0;
  190. bar->priv->layout.area.y = 0;
  191. bar->priv->layout.box_width = bar->priv->layout.delta / 2;
  192. bar->priv->layout.box_height = bar->priv->layout.area.height;
  193. bar->priv->layout.box_radius = bar->priv->layout.box_height / 2;
  194. }
  195. bar->priv->layout.peak_num = peak_level / bar->priv->layout.delta;
  196. bar->priv->layout.max_peak_num = max_peak_level / bar->priv->layout.delta;
  197. }
  198. static void
  199. update_peak_value (GvcLevelBar *bar)
  200. {
  201. gdouble value;
  202. LevelBarLayout layout;
  203. value = fraction_from_adjustment (bar, bar->priv->peak_adjustment);
  204. bar->priv->peak_fraction = value;
  205. if (value > bar->priv->max_peak) {
  206. if (bar->priv->max_peak_id > 0)
  207. g_source_remove (bar->priv->max_peak_id);
  208. bar->priv->max_peak_id =
  209. g_timeout_add_seconds (1, (GSourceFunc) reset_max_peak, bar);
  210. bar->priv->max_peak = value;
  211. }
  212. layout = bar->priv->layout;
  213. bar_calc_layout (bar);
  214. if (layout_changed (&bar->priv->layout, &layout))
  215. gtk_widget_queue_draw (GTK_WIDGET (bar));
  216. }
  217. static void
  218. update_rms_value (GvcLevelBar *bar)
  219. {
  220. bar->priv->rms_fraction = fraction_from_adjustment (bar, bar->priv->rms_adjustment);
  221. }
  222. GtkOrientation
  223. gvc_level_bar_get_orientation (GvcLevelBar *bar)
  224. {
  225. g_return_val_if_fail (GVC_IS_LEVEL_BAR (bar), 0);
  226. return bar->priv->orientation;
  227. }
  228. void
  229. gvc_level_bar_set_orientation (GvcLevelBar *bar,
  230. GtkOrientation orientation)
  231. {
  232. g_return_if_fail (GVC_IS_LEVEL_BAR (bar));
  233. if (orientation != bar->priv->orientation) {
  234. if (G_UNLIKELY (orientation != GTK_ORIENTATION_VERTICAL &&
  235. orientation != GTK_ORIENTATION_HORIZONTAL)) {
  236. g_warn_if_reached ();
  237. return;
  238. }
  239. bar->priv->orientation = orientation;
  240. gtk_widget_queue_draw (GTK_WIDGET (bar));
  241. g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_ORIENTATION]);
  242. }
  243. }
  244. static void
  245. on_peak_adjustment_value_changed (GtkAdjustment *adjustment,
  246. GvcLevelBar *bar)
  247. {
  248. update_peak_value (bar);
  249. }
  250. static void
  251. on_rms_adjustment_value_changed (GtkAdjustment *adjustment,
  252. GvcLevelBar *bar)
  253. {
  254. update_rms_value (bar);
  255. }
  256. void
  257. gvc_level_bar_set_peak_adjustment (GvcLevelBar *bar,
  258. GtkAdjustment *adjustment)
  259. {
  260. g_return_if_fail (GVC_LEVEL_BAR (bar));
  261. g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
  262. if (bar->priv->peak_adjustment != NULL) {
  263. g_signal_handlers_disconnect_by_func (G_OBJECT (bar->priv->peak_adjustment),
  264. G_CALLBACK (on_peak_adjustment_value_changed),
  265. bar);
  266. g_object_unref (bar->priv->peak_adjustment);
  267. }
  268. bar->priv->peak_adjustment = g_object_ref_sink (adjustment);
  269. g_signal_connect (G_OBJECT (bar->priv->peak_adjustment),
  270. "value-changed",
  271. G_CALLBACK (on_peak_adjustment_value_changed),
  272. bar);
  273. update_peak_value (bar);
  274. g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_PEAK_ADJUSTMENT]);
  275. }
  276. void
  277. gvc_level_bar_set_rms_adjustment (GvcLevelBar *bar,
  278. GtkAdjustment *adjustment)
  279. {
  280. g_return_if_fail (GVC_LEVEL_BAR (bar));
  281. g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
  282. if (bar->priv->rms_adjustment != NULL) {
  283. g_signal_handlers_disconnect_by_func (G_OBJECT (bar->priv->rms_adjustment),
  284. G_CALLBACK (on_rms_adjustment_value_changed),
  285. bar);
  286. g_object_unref (bar->priv->rms_adjustment);
  287. }
  288. bar->priv->rms_adjustment = g_object_ref_sink (adjustment);
  289. g_signal_connect (G_OBJECT (bar->priv->rms_adjustment),
  290. "value-changed",
  291. G_CALLBACK (on_rms_adjustment_value_changed),
  292. bar);
  293. update_rms_value (bar);
  294. g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_RMS_ADJUSTMENT]);
  295. }
  296. GtkAdjustment *
  297. gvc_level_bar_get_peak_adjustment (GvcLevelBar *bar)
  298. {
  299. g_return_val_if_fail (GVC_IS_LEVEL_BAR (bar), NULL);
  300. return bar->priv->peak_adjustment;
  301. }
  302. GtkAdjustment *
  303. gvc_level_bar_get_rms_adjustment (GvcLevelBar *bar)
  304. {
  305. g_return_val_if_fail (GVC_IS_LEVEL_BAR (bar), NULL);
  306. return bar->priv->rms_adjustment;
  307. }
  308. void
  309. gvc_level_bar_set_scale (GvcLevelBar *bar, GvcLevelScale scale)
  310. {
  311. g_return_if_fail (GVC_IS_LEVEL_BAR (bar));
  312. if (scale != bar->priv->scale) {
  313. if (G_UNLIKELY (scale != GVC_LEVEL_SCALE_LINEAR &&
  314. scale != GVC_LEVEL_SCALE_LOG)) {
  315. g_warn_if_reached ();
  316. return;
  317. }
  318. bar->priv->scale = scale;
  319. update_peak_value (bar);
  320. update_rms_value (bar);
  321. g_object_notify_by_pspec (G_OBJECT (bar), properties[PROP_SCALE]);
  322. }
  323. }
  324. static void
  325. gvc_level_bar_set_property (GObject *object,
  326. guint prop_id,
  327. const GValue *value,
  328. GParamSpec *pspec)
  329. {
  330. GvcLevelBar *self = GVC_LEVEL_BAR (object);
  331. switch (prop_id) {
  332. case PROP_SCALE:
  333. gvc_level_bar_set_scale (self, g_value_get_int (value));
  334. break;
  335. case PROP_ORIENTATION:
  336. gvc_level_bar_set_orientation (self, g_value_get_enum (value));
  337. break;
  338. case PROP_PEAK_ADJUSTMENT:
  339. gvc_level_bar_set_peak_adjustment (self, g_value_get_object (value));
  340. break;
  341. case PROP_RMS_ADJUSTMENT:
  342. gvc_level_bar_set_rms_adjustment (self, g_value_get_object (value));
  343. break;
  344. default:
  345. G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  346. break;
  347. }
  348. }
  349. static void
  350. gvc_level_bar_get_property (GObject *object,
  351. guint prop_id,
  352. GValue *value,
  353. GParamSpec *pspec)
  354. {
  355. GvcLevelBar *self = GVC_LEVEL_BAR (object);
  356. switch (prop_id) {
  357. case PROP_SCALE:
  358. g_value_set_int (value, self->priv->scale);
  359. break;
  360. case PROP_ORIENTATION:
  361. g_value_set_enum (value, self->priv->orientation);
  362. break;
  363. case PROP_PEAK_ADJUSTMENT:
  364. g_value_set_object (value, self->priv->peak_adjustment);
  365. break;
  366. case PROP_RMS_ADJUSTMENT:
  367. g_value_set_object (value, self->priv->rms_adjustment);
  368. break;
  369. default:
  370. G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  371. break;
  372. }
  373. }
  374. static void
  375. gvc_level_bar_size_request (GtkWidget *widget,
  376. GtkRequisition *requisition)
  377. {
  378. GvcLevelBar *bar;
  379. g_return_if_fail (GVC_IS_LEVEL_BAR (widget));
  380. g_return_if_fail (requisition != NULL);
  381. bar = GVC_LEVEL_BAR (widget);
  382. switch (bar->priv->orientation) {
  383. case GTK_ORIENTATION_VERTICAL:
  384. requisition->width = VERTICAL_BAR_WIDTH;
  385. requisition->height = MIN_VERTICAL_BAR_HEIGHT;
  386. break;
  387. case GTK_ORIENTATION_HORIZONTAL:
  388. requisition->width = MIN_HORIZONTAL_BAR_WIDTH;
  389. requisition->height = HORIZONTAL_BAR_HEIGHT;
  390. break;
  391. }
  392. }
  393. #if GTK_CHECK_VERSION (3, 0, 0)
  394. static void
  395. gvc_level_bar_get_preferred_width (GtkWidget *widget,
  396. gint *minimum,
  397. gint *natural)
  398. {
  399. GtkRequisition requisition;
  400. gvc_level_bar_size_request (widget, &requisition);
  401. if (minimum != NULL)
  402. *minimum = requisition.width;
  403. if (natural != NULL)
  404. *natural = requisition.width;
  405. }
  406. static void
  407. gvc_level_bar_get_preferred_height (GtkWidget *widget,
  408. gint *minimum,
  409. gint *natural)
  410. {
  411. GtkRequisition requisition;
  412. gvc_level_bar_size_request (widget, &requisition);
  413. if (minimum != NULL)
  414. *minimum = requisition.height;
  415. if (natural != NULL)
  416. *natural = requisition.height;
  417. }
  418. #endif
  419. static void
  420. gvc_level_bar_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
  421. {
  422. GvcLevelBar *bar;
  423. bar = GVC_LEVEL_BAR (widget);
  424. /* FIXME: add height property, labels, etc */
  425. GTK_WIDGET_CLASS (gvc_level_bar_parent_class)->size_allocate (widget, allocation);
  426. gtk_widget_set_allocation (widget, allocation);
  427. gtk_widget_get_allocation (widget, allocation);
  428. if (bar->priv->orientation == GTK_ORIENTATION_VERTICAL) {
  429. allocation->height = MIN (allocation->height, MIN_VERTICAL_BAR_HEIGHT);
  430. allocation->width = MAX (allocation->width, VERTICAL_BAR_WIDTH);
  431. } else {
  432. allocation->width = MIN (allocation->width, MIN_HORIZONTAL_BAR_WIDTH);
  433. allocation->height = MAX (allocation->height, HORIZONTAL_BAR_HEIGHT);
  434. }
  435. bar_calc_layout (bar);
  436. }
  437. static void
  438. curved_rectangle (cairo_t *cr,
  439. double x0,
  440. double y0,
  441. double width,
  442. double height,
  443. double radius)
  444. {
  445. double x1;
  446. double y1;
  447. x1 = x0 + width;
  448. y1 = y0 + height;
  449. if (!width || !height)
  450. return;
  451. if (width / 2 < radius) {
  452. if (height / 2 < radius) {
  453. cairo_move_to (cr, x0, (y0 + y1) / 2);
  454. cairo_curve_to (cr, x0 ,y0, x0, y0, (x0 + x1) / 2, y0);
  455. cairo_curve_to (cr, x1, y0, x1, y0, x1, (y0 + y1) / 2);
  456. cairo_curve_to (cr, x1, y1, x1, y1, (x1 + x0) / 2, y1);
  457. cairo_curve_to (cr, x0, y1, x0, y1, x0, (y0 + y1) / 2);
  458. } else {
  459. cairo_move_to (cr, x0, y0 + radius);
  460. cairo_curve_to (cr, x0, y0, x0, y0, (x0 + x1) / 2, y0);
  461. cairo_curve_to (cr, x1, y0, x1, y0, x1, y0 + radius);
  462. cairo_line_to (cr, x1, y1 - radius);
  463. cairo_curve_to (cr, x1, y1, x1, y1, (x1 + x0) / 2, y1);
  464. cairo_curve_to (cr, x0, y1, x0, y1, x0, y1 - radius);
  465. }
  466. } else {
  467. if (height / 2 < radius) {
  468. cairo_move_to (cr, x0, (y0 + y1) / 2);
  469. cairo_curve_to (cr, x0, y0, x0 , y0, x0 + radius, y0);
  470. cairo_line_to (cr, x1 - radius, y0);
  471. cairo_curve_to (cr, x1, y0, x1, y0, x1, (y0 + y1) / 2);
  472. cairo_curve_to (cr, x1, y1, x1, y1, x1 - radius, y1);
  473. cairo_line_to (cr, x0 + radius, y1);
  474. cairo_curve_to (cr, x0, y1, x0, y1, x0, (y0 + y1) / 2);
  475. } else {
  476. cairo_move_to (cr, x0, y0 + radius);
  477. cairo_curve_to (cr, x0 , y0, x0 , y0, x0 + radius, y0);
  478. cairo_line_to (cr, x1 - radius, y0);
  479. cairo_curve_to (cr, x1, y0, x1, y0, x1, y0 + radius);
  480. cairo_line_to (cr, x1, y1 - radius);
  481. cairo_curve_to (cr, x1, y1, x1, y1, x1 - radius, y1);
  482. cairo_line_to (cr, x0 + radius, y1);
  483. cairo_curve_to (cr, x0, y1, x0, y1, x0, y1 - radius);
  484. }
  485. }
  486. cairo_close_path (cr);
  487. }
  488. static int
  489. gvc_level_bar_draw (GtkWidget *widget, cairo_t *cr)
  490. {
  491. GvcLevelBar *bar;
  492. bar = GVC_LEVEL_BAR (widget);
  493. cairo_save (cr);
  494. if (bar->priv->orientation == GTK_ORIENTATION_VERTICAL) {
  495. int i;
  496. int by;
  497. for (i = 0; i < NUM_BOXES; i++) {
  498. by = i * bar->priv->layout.delta;
  499. curved_rectangle (cr,
  500. bar->priv->layout.area.x + 0.5,
  501. by + 0.5,
  502. bar->priv->layout.box_width - 1,
  503. bar->priv->layout.box_height - 1,
  504. bar->priv->layout.box_radius);
  505. if ((bar->priv->layout.max_peak_num - 1) == i) {
  506. /* fill peak foreground */
  507. #if GTK_CHECK_VERSION (3, 0, 0)
  508. gdk_cairo_set_source_rgba (cr, &bar->priv->layout.color_fg);
  509. #else
  510. gdk_cairo_set_source_color (cr, &bar->priv->layout.color_fg);
  511. #endif
  512. cairo_fill_preserve (cr);
  513. } else if ((bar->priv->layout.peak_num - 1) >= i) {
  514. /* fill background */
  515. #if GTK_CHECK_VERSION (3, 0, 0)
  516. gdk_cairo_set_source_rgba (cr, &bar->priv->layout.color_bg);
  517. #else
  518. gdk_cairo_set_source_color (cr, &bar->priv->layout.color_bg);
  519. #endif
  520. cairo_fill_preserve (cr);
  521. /* fill foreground */
  522. #if GTK_CHECK_VERSION (3, 0, 0)
  523. cairo_set_source_rgba (cr,
  524. bar->priv->layout.color_fg.red,
  525. bar->priv->layout.color_fg.green,
  526. bar->priv->layout.color_fg.blue,
  527. 0.5);
  528. #else
  529. cairo_set_source_rgba (cr,
  530. bar->priv->layout.color_fg.red / 65535.0,
  531. bar->priv->layout.color_fg.green / 65535.0,
  532. bar->priv->layout.color_fg.blue / 65535.0,
  533. 0.5);
  534. #endif
  535. cairo_fill_preserve (cr);
  536. } else {
  537. /* fill background */
  538. #if GTK_CHECK_VERSION (3, 0, 0)
  539. gdk_cairo_set_source_rgba (cr, &bar->priv->layout.color_bg);
  540. #else
  541. gdk_cairo_set_source_color (cr, &bar->priv->layout.color_bg);
  542. #endif
  543. cairo_fill_preserve (cr);
  544. }
  545. /* stroke border */
  546. #if GTK_CHECK_VERSION (3, 0, 0)
  547. gdk_cairo_set_source_rgba (cr, &bar->priv->layout.color_dark);
  548. #else
  549. gdk_cairo_set_source_color (cr, &bar->priv->layout.color_dark);
  550. #endif
  551. cairo_set_line_width (cr, 1);
  552. cairo_stroke (cr);
  553. }
  554. } else {
  555. int i;
  556. int bx;
  557. if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) {
  558. GtkAllocation allocation;
  559. gtk_widget_get_allocation (widget, &allocation);
  560. cairo_scale (cr, -1, 1);
  561. cairo_translate (cr, -allocation.width, 0);
  562. }
  563. for (i = 0; i < NUM_BOXES; i++) {
  564. bx = i * bar->priv->layout.delta;
  565. curved_rectangle (cr,
  566. bx + 0.5,
  567. bar->priv->layout.area.y + 0.5,
  568. bar->priv->layout.box_width - 1,
  569. bar->priv->layout.box_height - 1,
  570. bar->priv->layout.box_radius);
  571. if ((bar->priv->layout.max_peak_num - 1) == i) {
  572. /* fill peak foreground */
  573. #if GTK_CHECK_VERSION (3, 0, 0)
  574. gdk_cairo_set_source_rgba (cr, &bar->priv->layout.color_fg);
  575. #else
  576. gdk_cairo_set_source_color (cr, &bar->priv->layout.color_fg);
  577. #endif
  578. cairo_fill_preserve (cr);
  579. } else if ((bar->priv->layout.peak_num - 1) >= i) {
  580. /* fill background */
  581. #if GTK_CHECK_VERSION (3, 0, 0)
  582. gdk_cairo_set_source_rgba (cr, &bar->priv->layout.color_bg);
  583. #else
  584. gdk_cairo_set_source_color (cr, &bar->priv->layout.color_bg);
  585. #endif
  586. cairo_fill_preserve (cr);
  587. /* fill foreground */
  588. #if GTK_CHECK_VERSION (3, 0, 0)
  589. cairo_set_source_rgba (cr,
  590. bar->priv->layout.color_fg.red,
  591. bar->priv->layout.color_fg.green,
  592. bar->priv->layout.color_fg.blue,
  593. 0.5);
  594. #else
  595. cairo_set_source_rgba (cr,
  596. bar->priv->layout.color_fg.red / 65535.0,
  597. bar->priv->layout.color_fg.green / 65535.0,
  598. bar->priv->layout.color_fg.blue / 65535.0,
  599. 0.5);
  600. #endif
  601. cairo_fill_preserve (cr);
  602. } else {
  603. /* fill background */
  604. #if GTK_CHECK_VERSION (3, 0, 0)
  605. gdk_cairo_set_source_rgba (cr, &bar->priv->layout.color_bg);
  606. #else
  607. gdk_cairo_set_source_color (cr, &bar->priv->layout.color_bg);
  608. #endif
  609. cairo_fill_preserve (cr);
  610. }
  611. /* stroke border */
  612. #if GTK_CHECK_VERSION (3, 0, 0)
  613. gdk_cairo_set_source_rgba (cr, &bar->priv->layout.color_dark);
  614. #else
  615. gdk_cairo_set_source_color (cr, &bar->priv->layout.color_dark);
  616. #endif
  617. cairo_set_line_width (cr, 1);
  618. cairo_stroke (cr);
  619. }
  620. }
  621. cairo_restore (cr);
  622. return FALSE;
  623. }
  624. #if !GTK_CHECK_VERSION (3, 0, 0)
  625. static int
  626. gvc_level_bar_expose (GtkWidget *widget, GdkEventExpose *event)
  627. {
  628. cairo_t *cr;
  629. GtkAllocation allocation;
  630. g_return_val_if_fail (event != NULL, FALSE);
  631. /* Event queue compression */
  632. if (event->count > 0)
  633. return FALSE;
  634. gtk_widget_get_allocation (widget, &allocation);
  635. cr = gdk_cairo_create (gtk_widget_get_window (widget));
  636. cairo_translate (cr,
  637. allocation.x,
  638. allocation.y);
  639. gvc_level_bar_draw (widget, cr);
  640. cairo_destroy (cr);
  641. return FALSE;
  642. }
  643. #endif
  644. static void
  645. gvc_level_bar_class_init (GvcLevelBarClass *klass)
  646. {
  647. GObjectClass *object_class = G_OBJECT_CLASS (klass);
  648. GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
  649. object_class->finalize = gvc_level_bar_finalize;
  650. object_class->set_property = gvc_level_bar_set_property;
  651. object_class->get_property = gvc_level_bar_get_property;
  652. #if GTK_CHECK_VERSION (3, 0, 0)
  653. widget_class->draw = gvc_level_bar_draw;
  654. widget_class->get_preferred_width = gvc_level_bar_get_preferred_width;
  655. widget_class->get_preferred_height = gvc_level_bar_get_preferred_height;
  656. #else
  657. widget_class->expose_event = gvc_level_bar_expose;
  658. widget_class->size_request = gvc_level_bar_size_request;
  659. #endif
  660. widget_class->size_allocate = gvc_level_bar_size_allocate;
  661. #if GTK_CHECK_VERSION (3, 20, 0)
  662. gtk_widget_class_set_css_name (widget_class, "gvc-level-bar");
  663. #endif
  664. properties[PROP_ORIENTATION] =
  665. g_param_spec_enum ("orientation",
  666. "Orientation",
  667. "The orientation of the bar",
  668. GTK_TYPE_ORIENTATION,
  669. GTK_ORIENTATION_HORIZONTAL,
  670. G_PARAM_READWRITE |
  671. G_PARAM_STATIC_STRINGS);
  672. properties[PROP_PEAK_ADJUSTMENT] =
  673. g_param_spec_object ("peak-adjustment",
  674. "Peak Adjustment",
  675. "The GtkAdjustment that contains the current peak value",
  676. GTK_TYPE_ADJUSTMENT,
  677. G_PARAM_READWRITE |
  678. G_PARAM_STATIC_STRINGS);
  679. properties[PROP_RMS_ADJUSTMENT] =
  680. g_param_spec_object ("rms-adjustment",
  681. "RMS Adjustment",
  682. "The GtkAdjustment that contains the current rms value",
  683. GTK_TYPE_ADJUSTMENT,
  684. G_PARAM_READWRITE |
  685. G_PARAM_STATIC_STRINGS);
  686. properties[PROP_SCALE] =
  687. g_param_spec_int ("scale",
  688. "Scale",
  689. "Scale",
  690. 0,
  691. G_MAXINT,
  692. GVC_LEVEL_SCALE_LINEAR,
  693. G_PARAM_READWRITE |
  694. G_PARAM_CONSTRUCT |
  695. G_PARAM_STATIC_STRINGS);
  696. g_object_class_install_properties (object_class, N_PROPERTIES, properties);
  697. g_type_class_add_private (klass, sizeof (GvcLevelBarPrivate));
  698. }
  699. static void
  700. gvc_level_bar_init (GvcLevelBar *bar)
  701. {
  702. #if GTK_CHECK_VERSION (3, 0, 0)
  703. GtkStyleContext *context = gtk_widget_get_style_context (GTK_WIDGET (bar));
  704. gtk_style_context_add_class (context, GTK_STYLE_CLASS_LIST_ROW);
  705. #endif
  706. bar->priv = GVC_LEVEL_BAR_GET_PRIVATE (bar);
  707. bar->priv->peak_adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0,
  708. 0.0,
  709. 1.0,
  710. 0.05,
  711. 0.1,
  712. 0.1));
  713. g_object_ref_sink (bar->priv->peak_adjustment);
  714. g_signal_connect (bar->priv->peak_adjustment,
  715. "value-changed",
  716. G_CALLBACK (on_peak_adjustment_value_changed),
  717. bar);
  718. bar->priv->rms_adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0.0,
  719. 0.0,
  720. 1.0,
  721. 0.05,
  722. 0.1,
  723. 0.1));
  724. g_object_ref_sink (bar->priv->rms_adjustment);
  725. g_signal_connect (bar->priv->rms_adjustment,
  726. "value-changed",
  727. G_CALLBACK (on_rms_adjustment_value_changed),
  728. bar);
  729. gtk_widget_set_has_window (GTK_WIDGET (bar), FALSE);
  730. }
  731. static void
  732. gvc_level_bar_finalize (GObject *object)
  733. {
  734. GvcLevelBar *bar;
  735. bar = GVC_LEVEL_BAR (object);
  736. if (bar->priv->max_peak_id > 0)
  737. g_source_remove (bar->priv->max_peak_id);
  738. G_OBJECT_CLASS (gvc_level_bar_parent_class)->finalize (object);
  739. }
  740. GtkWidget *
  741. gvc_level_bar_new (void)
  742. {
  743. return g_object_new (GVC_TYPE_LEVEL_BAR, NULL);
  744. }