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