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
14 #include "ColorHandler.h"
19 #include <boost/scoped_array.hpp>
20 #include "support/tostr.h"
21 #include "support/lstrings.h"
25 using namespace lyx::support;
27 #ifndef CXX_GLOBAL_CSTD
33 #include "lyx_forms.h"
37 string tostr(XColor const & col)
39 return bformat("(%1$s,%2$s,%3$s)",
40 ::tostr(col.red), ::tostr(col.green), ::tostr(col.blue));
46 LyXColorHandler::LyXColorHandler()
48 display = fl_get_display();
49 drawable = XCreatePixmap(display,
50 RootWindow(display, fl_screen),
51 10, 10, fl_get_visual_depth());
53 colormap = fl_state[fl_get_vclass()].colormap;
55 for (int i = 0; i <= LColor::ignore; ++i) {
61 LyXColorHandler::~LyXColorHandler()
63 // Release all the registered GCs
64 for (int i = 0; i <= LColor::ignore; ++i) {
65 if (colorGCcache[i] != 0) {
66 XFreeGC(display, colorGCcache[i]);
69 // Iterate over the line cache and Free the GCs
70 for (LineGCCache::iterator lit = lineGCcache.begin();
71 lit != lineGCcache.end(); ++lit) {
72 XFreeGC(display, lit->second);
77 unsigned long LyXColorHandler::colorPixel(LColor::color c)
80 XGetGCValues(display, getGCForeground(c), GCForeground, &val);
81 return val.foreground;
85 // Gets GC according to color
87 GC LyXColorHandler::getGCForeground(LColor::color c)
89 if (colorGCcache[c] != 0)
90 return colorGCcache[c];
94 string const s = lcolor.getX11Name(c);
97 // Look up the RGB values for the color, and an approximate
98 // color that we can hope to get on this display.
99 if (XLookupColor(display, colormap, s.c_str(), &xcol, &ccol) == 0) {
101 _("LyX: Unknown X11 color %1$s for %2$s\n"
102 " Using black instead, sorry!"),
103 s, lcolor.getGUIName(c)) << endl;
104 unsigned long bla = BlackPixel(display,
105 DefaultScreen(display));
106 val.foreground = bla;
107 // Try the exact RGB values first, then the approximate.
108 } else if (XAllocColor(display, colormap, &xcol) != 0) {
109 if (lyxerr.debugging(Debug::GUI)) {
110 lyxerr << bformat(_("LyX: X11 color %1$s allocated for %2$s"),
111 s, lcolor.getGUIName(c)) << endl;
113 val.foreground = xcol.pixel;
114 } else if (XAllocColor(display, colormap, &ccol)) {
116 _("LyX: Using approximated X11 color %1$s allocated for %2$s"),
117 s, lcolor.getGUIName(c)) << endl;
118 val.foreground = xcol.pixel;
120 // Here we are traversing the current colormap to find
121 // the color closest to the one we want.
122 Visual * vi = DefaultVisual(display, DefaultScreen(display));
124 boost::scoped_array<XColor> cmap(new XColor[vi->map_entries]);
126 for (int i = 0; i < vi->map_entries; ++i) {
129 XQueryColors(display, colormap, cmap.get(), vi->map_entries);
131 // Walk through the cmap and look for close colors.
132 int closest_pixel = 0;
133 double closest_distance = 1e20; // we want to minimize this
135 for (int t = 0; t < vi->map_entries; ++t) {
136 // The Euclidean distance between two points in
137 // a three-dimensional space, the RGB color-cube,
138 // is used as the distance measurement between two
141 // Since square-root is monotonous, we don't have to
142 // take the square-root to find the minimum, and thus
143 // we use the squared distance instead to be faster.
145 // If we want to get fancy, we could convert the RGB
146 // coordinates to a different color-cube, maybe HSV,
147 // but the RGB cube seems to work great. (Asger)
148 distance = pow(cmap[t].red - xcol.red, 2.0) +
149 pow(cmap[t].green - xcol.green, 2.0) +
150 pow(cmap[t].blue - xcol.blue, 2.0);
151 if (distance < closest_distance) {
152 closest_distance = distance;
158 _("LyX: Couldn't allocate '%1$s' for %2$s with (r,g,b)=%3$s.\n"),
159 s, lcolor.getGUIName(c), tostr(xcol));
162 _(" Using closest allocated color with (r,g,b)=%1$s instead.\n"
163 "Pixel [%2$s] is used."),
164 tostr(cmap[closest_pixel]), tostr(closest_pixel)) << endl;
166 val.foreground = cmap[closest_pixel].pixel;
169 val.function = GXcopy;
170 return colorGCcache[c] = XCreateGC(display, drawable,
171 GCForeground | GCFunction, &val);
176 GC LyXColorHandler::getGCLinepars(Painter::line_style ls,
177 Painter::line_width lw, LColor::color c)
179 //if (lyxerr.debugging()) {
180 // lyxerr << "Painter drawable: " << drawable() << endl;
183 int index = lw + (ls << 1) + (c << 6);
185 LineGCCache::iterator it = lineGCcache.find(index);
186 if (it != lineGCcache.end())
190 XGetGCValues(display, getGCForeground(c), GCForeground, &val);
193 case Painter::line_thin:
196 case Painter::line_thick:
202 case Painter::line_solid:
203 val.line_style = LineSolid;
205 case Painter::line_onoffdash:
206 val.line_style = LineOnOffDash;
211 val.cap_style = CapRound;
212 val.join_style = JoinRound;
213 val.function = GXcopy;
215 return lineGCcache[index] =
216 XCreateGC(display, drawable,
217 GCForeground | GCLineStyle | GCLineWidth |
218 GCCapStyle | GCJoinStyle | GCFunction, &val);
222 // update GC cache after color redefinition
223 void LyXColorHandler::updateColor (LColor::color c)
226 GC gc = colorGCcache[c];
228 XFreeGC(display, gc);
229 colorGCcache[c] = NULL;
235 for (int ls = 0; ls < 3; ++ls)
236 for (int lw = 0; lw < 2; ++lw) {
237 int const index = lw + (ls << 1) + (c << 6);
238 LineGCCache::iterator it = lineGCcache.find(index);
239 if (it != lineGCcache.end()) {
241 XFreeGC(display, gc);
242 lineGCcache.erase(it);
243 getGCLinepars(Painter::line_style(ls),
244 Painter::line_width(lw), c);
251 boost::scoped_ptr<LyXColorHandler> lyxColorHandler;