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 }