]> git.lyx.org Git - features.git/blob - src/frontends/xforms/ColorHandler.C
96600a1816c1b85dcbbc143716ec102d830649a7
[features.git] / src / frontends / xforms / ColorHandler.C
1 /**
2  * \file ColorHandler.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author unknown
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "ColorHandler.h"
14
15 #include "debug.h"
16 #include "gettext.h"
17
18 #include "support/lstrings.h"
19 #include "support/tostr.h"
20
21 #include "lyx_forms.h"
22
23 #include <boost/scoped_array.hpp>
24
25 #include <cmath>
26
27 using lyx::support::bformat;
28
29 #ifndef CXX_GLOBAL_CSTD
30 using std::pow;
31 #endif
32
33 using std::endl;
34
35
36 namespace {
37
38         string tostr(XColor const & col)
39         {
40                 return bformat("(%1$s,%2$s,%3$s)",
41                         ::tostr(col.red), ::tostr(col.green), ::tostr(col.blue));
42         }
43
44 }
45
46
47 LyXColorHandler::LyXColorHandler()
48                 : colorGCcache(LColor::ignore + 1)
49 {
50         display = fl_get_display();
51         drawable = XCreatePixmap(display,
52                                  RootWindow(display, fl_screen),
53                                  10, 10, fl_get_visual_depth());
54
55         colormap = fl_state[fl_get_vclass()].colormap;
56         // Clear the GC cache
57         for (int i = 0; i < colorGCcache.size(); ++i) {
58                 colorGCcache[i] = 0;
59         }
60 }
61
62
63 LyXColorHandler::~LyXColorHandler()
64 {
65         // Release all the registered GCs
66         for (unsigned i = 0; i < colorGCcache.size(); ++i) {
67                 if (colorGCcache[i] != 0) {
68                         XFreeGC(display, colorGCcache[i]);
69                 }
70         }
71         // Iterate over the line cache and Free the GCs
72         for (LineGCCache::iterator lit = lineGCcache.begin();
73              lit != lineGCcache.end(); ++lit) {
74                 XFreeGC(display, lit->second);
75         }
76 }
77
78
79 unsigned long LyXColorHandler::colorPixel(LColor::color c)
80 {
81         XGCValues val;
82         XGetGCValues(display, getGCForeground(c), GCForeground, &val);
83         return val.foreground;
84 }
85
86
87 GC LyXColorHandler::getGCForeground(string const & s)
88 {
89         XColor xcol;
90         XColor ccol;
91         XGCValues val;
92         // Look up the RGB values for the color, and an approximate
93         // color that we can hope to get on this display.
94         if (XLookupColor(display, colormap, s.c_str(), &xcol, &ccol) == 0) {
95                 lyxerr << bformat(
96                         _("LyX: Unknown X11 color %1$s\n"
97                           "     Using black instead, sorry!"),
98                         s) << endl;
99                 unsigned long bla = BlackPixel(display,
100                                                DefaultScreen(display));
101                 val.foreground = bla;
102         // Try the exact RGB values first, then the approximate.
103         } else if (XAllocColor(display, colormap, &xcol) != 0) {
104                 if (lyxerr.debugging(Debug::GUI)) {
105                         lyxerr << bformat(_("LyX: X11 color %1$s allocated"),
106                                 s) << endl;
107                 }
108                 val.foreground = xcol.pixel;
109         } else {
110                 // Here we are traversing the current colormap to find
111                 // the color closest to the one we want.
112                 Visual * vi = DefaultVisual(display, DefaultScreen(display));
113
114                 boost::scoped_array<XColor> cmap(new XColor[vi->map_entries]);
115
116                 for (int i = 0; i < vi->map_entries; ++i) {
117                         cmap[i].pixel = i;
118                 }
119                 XQueryColors(display, colormap, cmap.get(), vi->map_entries);
120
121                 // Walk through the cmap and look for close colors.
122                 int closest_pixel = 0;
123                 double closest_distance = 1e20; // we want to minimize this
124                 double distance = 0;
125                 for (int t = 0; t < vi->map_entries; ++t) {
126                         // The Euclidean distance between two points in
127                         // a three-dimensional space, the RGB color-cube,
128                         // is used as the distance measurement between two
129                         // colors.
130
131                         // Since square-root is monotonous, we don't have to
132                         // take the square-root to find the minimum, and thus
133                         // we use the squared distance instead to be faster.
134
135                         // If we want to get fancy, we could convert the RGB
136                         // coordinates to a different color-cube, maybe HSV,
137                         // but the RGB cube seems to work great.  (Asger)
138                         distance = pow(cmap[t].red   - xcol.red,   2.0) +
139                                    pow(cmap[t].green - xcol.green, 2.0) +
140                                    pow(cmap[t].blue  - xcol.blue,  2.0);
141                         if (distance < closest_distance) {
142                                 closest_distance = distance;
143                                 closest_pixel = t;
144                         }
145                 }
146
147                 lyxerr << bformat(
148                         _("LyX: Couldn't allocate '%1$s' with (r,g,b)=%3$s.\n"),
149                         s, tostr(xcol));
150
151                 lyxerr << bformat(
152                                 _("     Using closest allocated color with (r,g,b)=%1$s instead.\n"
153                           "Pixel [%2$s] is used."),
154                         tostr(cmap[closest_pixel]), tostr(closest_pixel)) << endl;
155
156                 val.foreground = cmap[closest_pixel].pixel;
157         }
158         val.function = GXcopy;
159         return XCreateGC(display, drawable,
160                                            GCForeground | GCFunction, &val);
161 }
162
163 // Gets GC according to color
164 // Uses caching
165 GC LyXColorHandler::getGCForeground(LColor::color c)
166 {
167         if (static_cast<unsigned>(c) >= colorGCcache.size()) {
168                 colorGCcache.resize(c + 1, 0);
169         }
170
171         if (colorGCcache[c] != 0) {
172                 return colorGCcache[c];
173         }
174         XColor xcol;
175         XColor ccol;
176         string const s = lcolor.getX11Name(c);
177         // Look up the RGB values for the color, and an approximate
178         // color that we can hope to get on this display.
179         if (XLookupColor(display, colormap, s.c_str(), &xcol, &ccol) == 0) {
180                 lyxerr << bformat(
181                         _("LyX: Unknown X11 color %1$s for %2$s\n"),
182                         s, lcolor.getGUIName(c)) << endl;
183         }
184         return colorGCcache[c] = getGCForeground(s);
185 }
186
187
188 // Gets GC for line
189 GC LyXColorHandler::getGCLinepars(Painter::line_style ls,
190                                   Painter::line_width lw, LColor::color c)
191 {
192         //if (lyxerr.debugging()) {
193         //      lyxerr << "Painter drawable: " << drawable() << endl;
194         //}
195
196         int index = lw + (ls << 1) + (c << 6);
197
198         LineGCCache::iterator it = lineGCcache.find(index);
199         if (it != lineGCcache.end())
200                 return it->second;
201
202         XGCValues val;
203         XGetGCValues(display, getGCForeground(c), GCForeground, &val);
204
205         switch (lw) {
206         case Painter::line_thin:
207                 val.line_width = 0;
208                 break;
209         case Painter::line_thick:
210                 val.line_width = 2;
211                 break;
212         }
213
214         switch (ls) {
215         case Painter::line_solid:
216                 val.line_style = LineSolid;
217                 break;
218         case Painter::line_onoffdash:
219                 val.line_style = LineOnOffDash;
220                 break;
221         }
222
223
224         val.cap_style = CapRound;
225         val.join_style = JoinRound;
226         val.function = GXcopy;
227
228         return lineGCcache[index] =
229                 XCreateGC(display, drawable,
230                           GCForeground | GCLineStyle | GCLineWidth |
231                           GCCapStyle | GCJoinStyle | GCFunction, &val);
232 }
233
234
235 // update GC cache after color redefinition
236 void LyXColorHandler::updateColor (LColor::color c)
237 {
238         // color GC cache
239         GC gc = colorGCcache[c];
240         if (gc != 0) {
241                 XFreeGC(display, gc);
242                 colorGCcache[c] = NULL;
243                 getGCForeground(c);
244         }
245
246         // line GC cache
247
248         for (int ls = 0; ls < 3; ++ls)
249                 for (int lw = 0; lw < 2; ++lw) {
250                         int const index = lw + (ls << 1) + (c << 6);
251                         LineGCCache::iterator it = lineGCcache.find(index);
252                         if (it != lineGCcache.end()) {
253                                 gc = it->second;
254                                 XFreeGC(display, gc);
255                                 lineGCcache.erase(it);
256                                 getGCLinepars(Painter::line_style(ls),
257                                               Painter::line_width(lw), c);
258                         }
259                 }
260 }
261
262
263 //
264 boost::scoped_ptr<LyXColorHandler> lyxColorHandler;