3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
8 * Full author contact details are available in file CREDITS.
13 #include "ColorHandler.h"
19 #include "support/lstrings.h"
20 #include "support/tostr.h"
22 #include "lyx_forms.h"
24 #include <boost/scoped_array.hpp>
28 using lyx::support::bformat;
30 #ifndef CXX_GLOBAL_CSTD
39 string tostr(XColor const & col)
41 return bformat("(%1$s,%2$s,%3$s)",
42 ::tostr(col.red), ::tostr(col.green), ::tostr(col.blue));
48 LyXColorHandler::LyXColorHandler()
49 : colorGCcache(LColor::ignore + 1)
51 display = fl_get_display();
52 drawable = XCreatePixmap(display,
53 RootWindow(display, fl_screen),
54 10, 10, fl_get_visual_depth());
56 colormap = fl_state[fl_get_vclass()].colormap;
58 for (int i = 0; i < colorGCcache.size(); ++i) {
64 LyXColorHandler::~LyXColorHandler()
66 // Release all the registered GCs
67 for (unsigned i = 0; i < colorGCcache.size(); ++i) {
68 if (colorGCcache[i] != 0) {
69 XFreeGC(display, colorGCcache[i]);
72 // Iterate over the line cache and Free the GCs
73 for (LineGCCache::iterator lit = lineGCcache.begin();
74 lit != lineGCcache.end(); ++lit) {
75 XFreeGC(display, lit->second);
80 unsigned long LyXColorHandler::colorPixel(LColor_color c)
83 XGetGCValues(display, getGCForeground(c), GCForeground, &val);
84 return val.foreground;
88 GC LyXColorHandler::getGCForeground(string const & s)
93 // Look up the RGB values for the color, and an approximate
94 // color that we can hope to get on this display.
95 if (XLookupColor(display, colormap, s.c_str(), &xcol, &ccol) == 0) {
97 _("LyX: Unknown X11 color %1$s\n"
98 " Using black instead, sorry!"),
100 unsigned long bla = BlackPixel(display,
101 DefaultScreen(display));
102 val.foreground = bla;
103 // Try the exact RGB values first, then the approximate.
104 } else if (XAllocColor(display, colormap, &xcol) != 0) {
105 if (lyxerr.debugging(Debug::GUI)) {
106 lyxerr << bformat(_("LyX: X11 color %1$s allocated"),
109 val.foreground = xcol.pixel;
111 // Here we are traversing the current colormap to find
112 // the color closest to the one we want.
113 Visual * vi = DefaultVisual(display, DefaultScreen(display));
115 boost::scoped_array<XColor> cmap(new XColor[vi->map_entries]);
117 for (int i = 0; i < vi->map_entries; ++i) {
120 XQueryColors(display, colormap, cmap.get(), vi->map_entries);
122 // Walk through the cmap and look for close colors.
123 int closest_pixel = 0;
124 double closest_distance = 1e20; // we want to minimize this
126 for (int t = 0; t < vi->map_entries; ++t) {
127 // The Euclidean distance between two points in
128 // a three-dimensional space, the RGB color-cube,
129 // is used as the distance measurement between two
132 // Since square-root is monotonous, we don't have to
133 // take the square-root to find the minimum, and thus
134 // we use the squared distance instead to be faster.
136 // If we want to get fancy, we could convert the RGB
137 // coordinates to a different color-cube, maybe HSV,
138 // but the RGB cube seems to work great. (Asger)
139 distance = pow(cmap[t].red - xcol.red, 2.0) +
140 pow(cmap[t].green - xcol.green, 2.0) +
141 pow(cmap[t].blue - xcol.blue, 2.0);
142 if (distance < closest_distance) {
143 closest_distance = distance;
149 _("LyX: Couldn't allocate '%1$s' with (r,g,b)=%3$s.\n"),
153 _(" Using closest allocated color with (r,g,b)=%1$s instead.\n"
154 "Pixel [%2$s] is used."),
155 tostr(cmap[closest_pixel]), tostr(closest_pixel)) << endl;
157 val.foreground = cmap[closest_pixel].pixel;
159 val.function = GXcopy;
160 return XCreateGC(display, drawable,
161 GCForeground | GCFunction, &val);
164 // Gets GC according to color
166 GC LyXColorHandler::getGCForeground(LColor_color c)
168 if (static_cast<unsigned>(c) >= colorGCcache.size()) {
169 colorGCcache.resize(c + 1, 0);
172 if (colorGCcache[c] != 0) {
173 return colorGCcache[c];
177 string const s = lcolor.getX11Name(c);
178 // Look up the RGB values for the color, and an approximate
179 // color that we can hope to get on this display.
180 if (XLookupColor(display, colormap, s.c_str(), &xcol, &ccol) == 0) {
182 _("LyX: Unknown X11 color %1$s for %2$s\n"),
183 s, lcolor.getGUIName(c)) << endl;
185 return colorGCcache[c] = getGCForeground(s);
190 GC LyXColorHandler::getGCLinepars(Painter::line_style ls,
191 Painter::line_width lw, LColor_color c)
193 //if (lyxerr.debugging()) {
194 // lyxerr << "Painter drawable: " << drawable() << endl;
197 int index = lw + (ls << 1) + (c << 6);
199 LineGCCache::iterator it = lineGCcache.find(index);
200 if (it != lineGCcache.end())
204 XGetGCValues(display, getGCForeground(c), GCForeground, &val);
207 case Painter::line_thin:
210 case Painter::line_thick:
216 case Painter::line_solid:
217 val.line_style = LineSolid;
219 case Painter::line_onoffdash:
220 val.line_style = LineOnOffDash;
225 val.cap_style = CapRound;
226 val.join_style = JoinRound;
227 val.function = GXcopy;
229 return lineGCcache[index] =
230 XCreateGC(display, drawable,
231 GCForeground | GCLineStyle | GCLineWidth |
232 GCCapStyle | GCJoinStyle | GCFunction, &val);
236 // update GC cache after color redefinition
237 void LyXColorHandler::updateColor (LColor_color c)
240 GC gc = colorGCcache[c];
242 XFreeGC(display, gc);
243 colorGCcache[c] = NULL;
249 for (int ls = 0; ls < 3; ++ls)
250 for (int lw = 0; lw < 2; ++lw) {
251 int const index = lw + (ls << 1) + (c << 6);
252 LineGCCache::iterator it = lineGCcache.find(index);
253 if (it != lineGCcache.end()) {
255 XFreeGC(display, gc);
256 lineGCcache.erase(it);
257 getGCLinepars(Painter::line_style(ls),
258 Painter::line_width(lw), c);
265 boost::scoped_ptr<LyXColorHandler> lyxColorHandler;