src/mn-non-linear-range.c (4011B) - raw
1 /*
2 * Mail Notification
3 * Copyright (C) 2003-2008 Jean-Yves Lefort <jylefort@brutele.be>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include <math.h>
21 #include "mn-non-linear-range.h"
22 #include "mn-util.h"
23
24 typedef struct
25 {
26 const MNNonLinearRangeBlock *blocks;
27 int num_blocks;
28 } RangeInfo;
29
30 static GQuark info_quark = 0;
31
32 static void
33 global_init (void)
34 {
35 if (! info_quark)
36 info_quark = g_quark_from_static_string("mn-non-linear-range-info");
37 }
38
39 static RangeInfo *
40 get_info (GtkRange *range)
41 {
42 RangeInfo *info;
43
44 g_return_val_if_fail(GTK_IS_RANGE(range), NULL);
45 g_return_val_if_fail(info_quark != 0, NULL);
46
47 info = g_object_get_qdata(G_OBJECT(range), info_quark);
48 g_return_val_if_fail(info != NULL, NULL);
49
50 return info;
51 }
52
53 static int
54 get_block_len (const MNNonLinearRangeBlock *block)
55 {
56 g_return_val_if_fail(block != NULL, -1);
57
58 return ((block->max - block->min) / block->step) + 1;
59 }
60
61 void
62 mn_non_linear_range_setup_static (GtkRange *range,
63 const MNNonLinearRangeBlock *blocks,
64 int num_blocks)
65 {
66 RangeInfo *info;
67 int i;
68 int num_values = 0;
69
70 g_return_if_fail(GTK_IS_RANGE(range));
71 g_return_if_fail(blocks != NULL);
72 g_return_if_fail(num_blocks > 0);
73
74 global_init();
75
76 info = g_new0(RangeInfo, 1);
77 info->blocks = blocks;
78 info->num_blocks = num_blocks;
79
80 g_object_set_qdata_full(G_OBJECT(range), info_quark, info, g_free);
81
82 for (i = 0; i < num_blocks; i++)
83 num_values += get_block_len(&blocks[i]);
84
85 gtk_range_set_range(range, 0, num_values - 1);
86 }
87
88 gboolean
89 mn_is_non_linear_range (gpointer object)
90 {
91 return object != NULL && info_quark != 0 && g_object_get_qdata(object, info_quark) != NULL;
92 }
93
94 int
95 mn_non_linear_range_get_value (GtkRange *range)
96 {
97 RangeInfo *info;
98 int raw;
99 int offset = 0;
100 int i;
101
102 g_return_val_if_fail(GTK_IS_RANGE(range), -1);
103
104 info = get_info(range);
105
106 raw = (int) gtk_range_get_value(range);
107
108 for (i = 0; i < info->num_blocks; i++)
109 {
110 const MNNonLinearRangeBlock *block = &info->blocks[i];
111 int next_offset;
112
113 next_offset = offset + get_block_len(block);
114
115 if (raw >= offset && raw < next_offset)
116 return block->min + (raw - offset) * block->step;
117
118 offset = next_offset;
119 }
120
121 g_assert_not_reached();
122 return -1;
123 }
124
125 static int
126 value_to_index (RangeInfo *info, int value)
127 {
128 int offset = 0;
129 int i;
130
131 /* if smaller than the first value, use the first value */
132 if (value < info->blocks[0].min)
133 return 0;
134
135 for (i = 0; i < info->num_blocks; i++)
136 {
137 const MNNonLinearRangeBlock *block = &info->blocks[i];
138
139 if (value >= block->min && value <= block->max)
140 {
141 int rounded;
142 int index;
143 int j;
144
145 /* round the value to the nearest step */
146 rounded = lround((double) value / block->step) * block->step;
147
148 for (j = block->min, index = 0; j <= block->max; j += block->step, index++)
149 if (j == rounded)
150 return offset + index;
151
152 g_assert_not_reached();
153 }
154
155 offset += get_block_len(block);
156 }
157
158 /* block not found: fallback to the maximum value */
159 return offset - 1;
160 }
161
162 /*
163 * If the value is not found in the blocks, the nearest existing value
164 * will be used.
165 */
166 void
167 mn_non_linear_range_set_value (GtkRange *range, int value)
168 {
169 g_return_if_fail(GTK_IS_RANGE(range));
170
171 gtk_range_set_value(range, value_to_index(get_info(range), value));
172 }