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