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