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