1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
16  */
17 
18 #include "config.h"
19 
20 #include <string.h>
21 
22 #include <gegl.h>
23 #include <gtk/gtk.h>
24 
25 #include "libgimpmath/gimpmath.h"
26 #include "libgimpwidgets/gimpwidgets.h"
27 
28 #include "widgets-types.h"
29 
30 #include "core/gimphistogram.h"
31 
32 #include "gimpcolorbar.h"
33 #include "gimphandlebar.h"
34 #include "gimphistogrambox.h"
35 #include "gimphistogramview.h"
36 
37 #include "gimp-intl.h"
38 
39 
40 /*  #define DEBUG_VIEW  */
41 
42 #define GRADIENT_HEIGHT  12
43 #define CONTROL_HEIGHT   10
44 
45 
46 /*  local function prototypes  */
47 
48 static void   gimp_histogram_box_low_adj_update  (GtkAdjustment     *adj,
49                                                   GimpHistogramBox  *box);
50 static void   gimp_histogram_box_high_adj_update (GtkAdjustment     *adj,
51                                                   GimpHistogramBox  *box);
52 static void   gimp_histogram_box_histogram_range (GimpHistogramView *view,
53                                                   gint               start,
54                                                   gint               end,
55                                                   GimpHistogramBox  *box);
56 static void   gimp_histogram_box_channel_notify  (GimpHistogramView *view,
57                                                   GParamSpec        *pspec,
58                                                   GimpHistogramBox  *box);
59 static void   gimp_histogram_box_border_notify   (GimpHistogramView *view,
60                                                   GParamSpec        *pspec,
61                                                   GimpHistogramBox  *box);
62 
63 
G_DEFINE_TYPE(GimpHistogramBox,gimp_histogram_box,GTK_TYPE_BOX)64 G_DEFINE_TYPE (GimpHistogramBox, gimp_histogram_box, GTK_TYPE_BOX)
65 
66 
67 static void
68 gimp_histogram_box_class_init (GimpHistogramBoxClass *klass)
69 {
70 }
71 
72 static void
gimp_histogram_box_init(GimpHistogramBox * box)73 gimp_histogram_box_init (GimpHistogramBox *box)
74 {
75   GtkWidget *hbox;
76   GtkWidget *vbox;
77   GtkWidget *vbox2;
78   GtkWidget *frame;
79   GtkWidget *view;
80   GtkWidget *bar;
81 
82   box->n_bins = 256;
83 
84   gtk_orientable_set_orientation (GTK_ORIENTABLE (box),
85                                   GTK_ORIENTATION_VERTICAL);
86 
87   gtk_box_set_spacing (GTK_BOX (box), 2);
88 
89   frame = gtk_frame_new (NULL);
90   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
91   gtk_box_pack_start (GTK_BOX (box), frame, TRUE, TRUE, 0);
92   gtk_widget_show (frame);
93 
94   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
95   gtk_container_add (GTK_CONTAINER (frame), vbox);
96   gtk_widget_show (vbox);
97 
98   /*  The histogram  */
99   view = gimp_histogram_view_new (TRUE);
100   gtk_box_pack_start (GTK_BOX (vbox), view, TRUE, TRUE, 0);
101   gtk_widget_show (view);
102 
103   g_signal_connect (view, "range-changed",
104                     G_CALLBACK (gimp_histogram_box_histogram_range),
105                     box);
106 
107   box->view = GIMP_HISTOGRAM_VIEW (view);
108 
109   /*  The gradient below the histogram */
110   vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
111   gtk_container_set_border_width (GTK_CONTAINER (vbox2),
112                                   GIMP_HISTOGRAM_VIEW (view)->border_width);
113   gtk_box_pack_start (GTK_BOX (vbox), vbox2, FALSE, FALSE, 0);
114   gtk_widget_show (vbox2);
115 
116   box->color_bar = bar = g_object_new (GIMP_TYPE_COLOR_BAR,
117                                        "histogram-channel", box->view->channel,
118                                        NULL);
119   gtk_widget_set_size_request (bar, -1, GRADIENT_HEIGHT);
120   gtk_box_pack_start (GTK_BOX (vbox2), bar, FALSE, FALSE, 0);
121   gtk_widget_show (bar);
122 
123   g_signal_connect (view, "notify::histogram-channel",
124                     G_CALLBACK (gimp_histogram_box_channel_notify),
125                     box);
126   g_signal_connect (view, "notify::border-width",
127                     G_CALLBACK (gimp_histogram_box_border_notify),
128                     box);
129 
130   box->slider_bar = bar = g_object_new (GIMP_TYPE_HANDLE_BAR, NULL);
131   gtk_widget_set_size_request (bar, -1, CONTROL_HEIGHT);
132   gtk_box_pack_start (GTK_BOX (vbox2), bar, FALSE, FALSE, 0);
133   gtk_widget_show (bar);
134 
135   gimp_handle_bar_connect_events (GIMP_HANDLE_BAR (box->slider_bar),
136                                   box->color_bar);
137 
138   /*  The range selection */
139   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
140   gtk_box_pack_start (GTK_BOX (box), hbox, FALSE, FALSE, 0);
141   gtk_widget_show (hbox);
142 
143   /*  low spinbutton  */
144   box->low_adj = (GtkAdjustment *)
145     gtk_adjustment_new (0.0, 0.0, 255.0, 1.0, 16.0, 0.0);
146   box->low_spinbutton = gimp_spin_button_new (box->low_adj, 1.0, 0);
147   gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (box->low_spinbutton), TRUE);
148   gtk_box_pack_start (GTK_BOX (hbox), box->low_spinbutton, FALSE, FALSE, 0);
149   gtk_widget_show (box->low_spinbutton);
150 
151   g_signal_connect (box->low_adj, "value-changed",
152                     G_CALLBACK (gimp_histogram_box_low_adj_update),
153                     box);
154 
155   gimp_handle_bar_set_adjustment (GIMP_HANDLE_BAR (bar), 0,
156                                   GTK_ADJUSTMENT (box->low_adj));
157 
158   /*  high spinbutton  */
159   box->high_adj = (GtkAdjustment *)
160     gtk_adjustment_new (255.0, 0.0, 255.0, 1.0, 16.0, 0.0);
161   box->high_spinbutton = gimp_spin_button_new (box->high_adj, 1.0, 0);
162   gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (box->high_spinbutton), TRUE);
163   gtk_box_pack_end (GTK_BOX (hbox), box->high_spinbutton, FALSE, FALSE, 0);
164   gtk_widget_show (box->high_spinbutton);
165 
166   g_signal_connect (box->high_adj, "value-changed",
167                     G_CALLBACK (gimp_histogram_box_high_adj_update),
168                     box);
169 
170   gimp_handle_bar_set_adjustment (GIMP_HANDLE_BAR (bar), 2,
171                                   GTK_ADJUSTMENT (box->high_adj));
172 
173 #ifdef DEBUG_VIEW
174   spinbutton = gimp_prop_spin_button_new (G_OBJECT (box->view), "border-width",
175                                           1, 5, 0);
176   gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
177   gtk_widget_show (spinbutton);
178 
179   spinbutton = gimp_prop_spin_button_new (G_OBJECT (box->view), "subdivisions",
180                                           1, 5, 0);
181   gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
182   gtk_widget_show (spinbutton);
183 #endif
184 }
185 
186 static void
gimp_histogram_box_low_adj_update(GtkAdjustment * adjustment,GimpHistogramBox * box)187 gimp_histogram_box_low_adj_update (GtkAdjustment    *adjustment,
188                                    GimpHistogramBox *box)
189 {
190   gdouble value = gtk_adjustment_get_value (adjustment);
191 
192   gtk_adjustment_set_lower (box->high_adj, value);
193 
194   if (box->n_bins != 256)
195     value *= box->n_bins - 1;
196 
197   value = ROUND (value);
198 
199   if (box->view->start != value)
200     gimp_histogram_view_set_range (box->view, value, box->view->end);
201 }
202 
203 static void
gimp_histogram_box_high_adj_update(GtkAdjustment * adjustment,GimpHistogramBox * box)204 gimp_histogram_box_high_adj_update (GtkAdjustment    *adjustment,
205                                     GimpHistogramBox *box)
206 {
207   gdouble value = gtk_adjustment_get_value (adjustment);
208 
209   gtk_adjustment_set_upper (box->low_adj, value);
210 
211   if (box->n_bins != 256)
212     value *= box->n_bins - 1;
213 
214   value = ROUND (value);
215 
216   if (box->view->end != value)
217     gimp_histogram_view_set_range (box->view, box->view->start, value);
218 }
219 
220 static void
gimp_histogram_box_histogram_range(GimpHistogramView * view,gint start,gint end,GimpHistogramBox * box)221 gimp_histogram_box_histogram_range (GimpHistogramView *view,
222                                     gint               start,
223                                     gint               end,
224                                     GimpHistogramBox  *box)
225 {
226   gdouble s = start;
227   gdouble e = end;
228 
229   if (box->n_bins != view->n_bins)
230     {
231       gdouble upper;
232       gdouble page_increment;
233       gdouble step_increment;
234       guint   digits;
235 
236       box->n_bins = view->n_bins;
237 
238       if (box->n_bins == 256)
239         {
240           digits         = 0;
241           upper          = 255.0;
242           step_increment = 1.0;
243           page_increment = 16.0;
244         }
245       else
246         {
247           digits         = 3;
248           upper          = 1.0;
249           step_increment = 0.01;
250           page_increment = 0.1;
251         }
252 
253       g_object_set (G_OBJECT (box->high_adj),
254                     "upper", upper,
255                     "step-increment", step_increment,
256                     "page-increment", page_increment,
257                     NULL);
258 
259       gtk_spin_button_set_digits (GTK_SPIN_BUTTON (box->high_spinbutton), digits);
260 
261       g_object_set (G_OBJECT (box->low_adj),
262                     "step-increment", step_increment,
263                     "page-increment", page_increment,
264                     NULL);
265 
266       gtk_spin_button_set_digits (GTK_SPIN_BUTTON (box->low_spinbutton), digits);
267     }
268 
269   if (box->n_bins != 256)
270     {
271       s /= box->n_bins - 1;
272       e /= box->n_bins - 1;
273     }
274 
275   gtk_adjustment_set_lower (box->high_adj, s);
276   gtk_adjustment_set_upper (box->low_adj,  e);
277 
278   gtk_adjustment_set_value (box->low_adj,  s);
279   gtk_adjustment_set_value (box->high_adj, e);
280 }
281 
282 static void
gimp_histogram_box_channel_notify(GimpHistogramView * view,GParamSpec * pspec,GimpHistogramBox * box)283 gimp_histogram_box_channel_notify (GimpHistogramView *view,
284                                    GParamSpec        *pspec,
285                                    GimpHistogramBox  *box)
286 {
287   gimp_color_bar_set_channel (GIMP_COLOR_BAR (box->color_bar), view->channel);
288 }
289 
290 static void
gimp_histogram_box_border_notify(GimpHistogramView * view,GParamSpec * pspec,GimpHistogramBox * box)291 gimp_histogram_box_border_notify (GimpHistogramView *view,
292                                   GParamSpec        *pspec,
293                                   GimpHistogramBox  *box)
294 {
295   GtkWidget *vbox = gtk_widget_get_parent (box->color_bar);
296 
297   gtk_container_set_border_width (GTK_CONTAINER (vbox), view->border_width);
298 }
299 
300 
301 /*  public functions  */
302 
303 GtkWidget *
gimp_histogram_box_new(void)304 gimp_histogram_box_new (void)
305 {
306   return g_object_new (GIMP_TYPE_HISTOGRAM_BOX, NULL);
307 }
308 
309 void
gimp_histogram_box_set_channel(GimpHistogramBox * box,GimpHistogramChannel channel)310 gimp_histogram_box_set_channel (GimpHistogramBox     *box,
311                                 GimpHistogramChannel  channel)
312 {
313   g_return_if_fail (GIMP_IS_HISTOGRAM_BOX (box));
314 
315   if (box->view)
316     gimp_histogram_view_set_channel (box->view, channel);
317 }
318