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