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