]> git.lyx.org Git - lyx.git/blob - src/Painter.C
4396ab1408173227ae00195ccac8c8c79db245bd
[lyx.git] / src / Painter.C
1 // -*- C++ -*-
2 /* This file is part of
3  * ======================================================
4  * 
5  *           LyX, The Document Processor
6  *       
7  *          Copyright 1998-2000 The LyX Team
8  *
9  *======================================================*/
10
11 #include <config.h>
12
13 #ifdef __GNUG__
14 #pragma implementation
15 #endif
16
17 #include <cmath>
18
19 #ifdef USE_STL_MEMORY
20 #include <memory>
21 #endif
22
23 #include FORMS_H_LOCATION
24 #include "Painter.h"
25 #include "LString.h"
26 #include "debug.h"
27 #include "lyxfont.h"
28 #include "support/LAssert.h"
29 #include "support/lstrings.h"
30 #include "WorkArea.h"
31
32 Painter::Painter(WorkArea & wa)
33         : PainterBase(wa)
34 {
35         colormap = fl_state[fl_get_vclass()].colormap;
36         // Clear the GC cache
37         for (int i = 0; i <= LColor::ignore; ++i) {
38                 colorGCcache[i] = 0;
39         }
40 }
41
42
43 Painter::~Painter() {
44         // Release all the registered GCs
45         for (int i = 0; i <= LColor::ignore; ++i) {
46                 if (colorGCcache[i] != 0) {
47                         XFreeGC(display, colorGCcache[i]);
48                 }
49         }
50         // Iterate over the line cache and Free the GCs
51         for (LineGCCache::iterator lit = lineGCcache.begin();
52              lit != lineGCcache.end(); ++lit) {
53                 XFreeGC(display, (*lit).second);
54         }
55 }
56
57
58 Drawable Painter::drawable() const 
59 {
60         return owner.getPixmap();
61 }
62
63
64 /* Basic drawing routines */
65
66 extern bool Lgb_bug_find_hack;
67
68 PainterBase & Painter::point(int x, int y, LColor::color c)
69 {
70         if (lyxerr.debugging()) {
71                 if (!Lgb_bug_find_hack)
72                         lyxerr << "point not called from "
73                                 "workarea::workhandler\n";
74                 lyxerr.debug() << "Painter drawable: " << drawable() << endl;
75         }
76         
77         XDrawPoint(display, drawable(), getGCForeground(c), x, y);
78         return *this;
79 }
80
81
82 PainterBase & Painter::line(int x1, int y1, int x2, int y2,
83                         LColor::color col,
84                         enum line_style ls,
85                         enum line_width lw)
86 {
87         if (lyxerr.debugging()) {
88                 if (!Lgb_bug_find_hack)
89                         lyxerr << "line not called from "
90                                 "workarea::workhandler\n";
91                 lyxerr.debug() << "Painter drawable: " << drawable() << endl;
92         }
93         
94         XDrawLine(display, drawable(), 
95                   getGCLinepars(ls, lw, col), x1, y1, x2, y2);
96         return *this;
97 }
98
99
100 PainterBase & Painter::lines(int const * xp, int const * yp, int np,
101                         LColor::color col,
102                         enum line_style ls,
103                         enum line_width lw)
104 {
105         if (lyxerr.debugging()) {
106                 if (!Lgb_bug_find_hack)
107                         lyxerr << "lines not called from "
108                                 "workarea::workhandler\n";
109                 lyxerr.debug() << "Painter drawable: " << drawable() << endl;
110         }
111         
112 #ifndef HAVE_AUTO_PTR
113         XPoint * points = new XPoint[np];
114 #else
115         auto_ptr<XPoint> points(new Xpoint[np]);
116 #endif
117         for (int i=0; i<np; ++i) {
118                 points[i].x = xp[i];
119                 points[i].y = yp[i];
120         }
121
122         XDrawLines(display, drawable(), getGCLinepars(ls, lw, col), 
123                    points, np, CoordModeOrigin);
124
125 #ifndef HAVE_AUTO_PTR
126         delete[] points;
127 #endif  
128         return *this;
129 }      
130
131
132 PainterBase & Painter::rectangle(int x, int y, int w, int h,
133                         LColor::color col,
134                         enum line_style ls,
135                         enum line_width lw)
136 {
137         if (lyxerr.debugging()) {
138                 if (!Lgb_bug_find_hack)
139                         lyxerr << "rectangle not called from "
140                                 "workarea::workhandler\n";
141                 lyxerr << "Painter drawable: " << drawable() << endl;
142         }
143         
144         XDrawRectangle(display, drawable(), getGCLinepars(ls, lw, col), 
145                        x, y, w, h);
146         return *this;
147 }
148
149
150 PainterBase & Painter::fillRectangle(int x, int y, int w, int h,
151                                  LColor::color col)
152 {
153         if (lyxerr.debugging()) {
154                 if (!Lgb_bug_find_hack)
155                         lyxerr << "fillrectangle not called from "
156                                 "workarea::workhandler\n";
157                 lyxerr << "Painter drawable: " << drawable() << endl;
158         }
159         
160         XFillRectangle(display, drawable(), getGCForeground(col), x, y, w, h);
161         return *this;
162 }
163
164
165 PainterBase & Painter::fillPolygon(int const * xp, int const * yp, int np,
166                                LColor::color col)
167 {
168         if (lyxerr.debugging()) {
169                 if (!Lgb_bug_find_hack)
170                         lyxerr <<"fillpolygon not called from "
171                                 "workarea::workhandler\n";
172                 lyxerr << "Painter drawable: " << drawable() << endl;
173         }
174         
175 #ifndef HAVE_AUTO_PTR
176         XPoint * points = new XPoint[np];
177 #else
178         auto_ptr<XPoint> points(new XPoint[np]);
179 #endif
180         for (int i=0; i<np; ++i) {
181                 points[i].x = xp[i];
182                 points[i].y = yp[i];
183         }
184
185         XFillPolygon(display, drawable(), getGCForeground(col), points, np, 
186                      Nonconvex, CoordModeOrigin);
187 #ifndef HAVE_AUTO_PTR
188         delete[] points;
189 #endif  
190         return *this;
191 }      
192
193
194 PainterBase & Painter::arc(int x, int y,
195                   unsigned int w, unsigned int h,
196                   int a1, int a2, LColor::color col)
197 {
198         if (lyxerr.debugging()) {
199                 if (!Lgb_bug_find_hack)
200                         lyxerr << "arc not called from "
201                                 "workarea::workhandler\n";
202                 lyxerr << "Painter drawable: " << drawable() << endl;
203         }
204         
205         XDrawArc(display, drawable(), getGCForeground(col),
206                  x, y, w, h, a1, a2);
207         return *this;
208 }     
209
210
211 /// Draw lines from x1,y1 to x2,y2. They are arrays
212 PainterBase & Painter::segments(int const * x1, int const * y1, 
213                             int const * x2, int const * y2, int ns,
214                             LColor::color col,
215                             enum line_style ls, enum line_width lw)
216 {
217         if (lyxerr.debugging()) {
218                 if (!Lgb_bug_find_hack)
219                         lyxerr << "segments not called from "
220                                 "workarea::workhandler\n";
221                 lyxerr << "Painter drawable: " << drawable() << endl;
222         }
223         
224 #ifndef HAVE_AUTO_PTR
225         XSegment * s= new XSegment[ns];
226 #else
227         auto_ptr<XSegment> s(new XSegment[ns]);
228 #endif
229         for (int i=0; i<ns; ++i) {
230                 s[i].x1 = x1[i];
231                 s[i].y1 = y1[i];
232                 s[i].x2 = x2[i];
233                 s[i].y2 = y2[i];
234         }
235         XDrawSegments(display, drawable(), getGCLinepars(ls, lw, col), s, ns);
236
237 #ifndef HAVE_AUTO_PTR
238         delete [] s;
239 #endif
240         return *this;
241 }
242
243
244 PainterBase & Painter::pixmap(int x, int y, int w, int h, Pixmap bitmap)
245 {
246         if (lyxerr.debugging()) {
247                 if (!Lgb_bug_find_hack)
248                         lyxerr << "workAreaExpose not called from "
249                                 "workarea::workhandler\n";
250                 lyxerr << "Painter drawable: " << drawable() << endl;
251         }
252         
253         XGCValues val;
254         val.function = GXcopy;
255         GC gc = XCreateGC(display, drawable(),
256                           GCFunction, &val);
257         XCopyArea(display, bitmap, drawable(), gc,
258                   0, 0, w, h, x, y);
259         XFreeGC(display, gc);
260         return *this;
261 }
262
263
264 PainterBase & Painter::text(int x, int y, string const & s, LyXFont const & f)
265 {
266         return text(x, y, s.c_str(), s.length(), f);
267 }
268
269
270 PainterBase & Painter::text(int x, int y, char c, LyXFont const & f)
271 {
272         char s[2] = { c, '\0' };
273         return text(x, y, s, 1, f);
274 }
275
276
277 PainterBase & Painter::text(int x, int y, char const * s, int ls,
278                         LyXFont const & f)
279 {
280         if (lyxerr.debugging()) {
281                 if (!Lgb_bug_find_hack)
282                         lyxerr << "text not called from "
283                                 "workarea::workhandler\n";
284                 lyxerr << "Painter drawable: " << drawable() << endl;
285         }
286         
287         GC gc = getGCForeground(f.realColor());
288         XSetFont(display, gc, f.getFontID());
289         XDrawString(display, drawable(), gc, x, y, s, ls);
290         underline(f, x, y, this->width(s, ls, f));
291         return *this;
292 }
293
294
295 void Painter::underline(LyXFont const & f, int x, int y, int width)
296 {
297         // What about underbars?
298         if (f.underbar() == LyXFont::ON && f.latex() != LyXFont::ON) {
299                 int below = f.maxDescent() / 2;
300                 if (below < 2) below = 2;
301                 int height = (f.maxDescent() / 4) - 1;
302                 if (height < 0) height = 0;
303                 fillRectangle(x, y + below, width, below + height, f.color());
304         }
305 }
306
307
308 // Gets GC according to color
309 // Uses caching
310 GC Painter::getGCForeground(LColor::color c)
311 {
312         if (lyxerr.debugging()) {
313                 lyxerr << "Painter drawable: " << drawable() << endl;
314         }
315         
316         if (colorGCcache[c] != 0) return colorGCcache[c];
317
318         XColor xcol, ccol;
319         string s = lcolor.getX11Name(c);
320         XGCValues val;
321
322         // Look up the RGB values for the color, and an approximate
323         // color that we can hope to get on this display.
324         if (XLookupColor(display, colormap, s.c_str(), &xcol, &ccol) == 0) {
325                 lyxerr << _("LyX: Unknown X11 color ") << s
326                        << _(" for ") << lcolor.getGUIName(c) << '\n'
327                        << _("     Using black instead, sorry!.") << endl;
328                 unsigned long bla = BlackPixel(display,
329                                                DefaultScreen(display));
330                 val.foreground = bla;
331         // Try the exact RGB values first, then the approximate.
332         } else if (XAllocColor(display, colormap, &xcol) != 0) {
333                 if (lyxerr.debugging()) {
334                         lyxerr << _("LyX: X11 color ") << s
335                                << _(" allocated for ") 
336                                << lcolor.getGUIName(c) << endl;
337                 }
338                 val.foreground = xcol.pixel;
339         } else if (XAllocColor(display, colormap, &ccol)) {
340                 lyxerr << _("LyX: Using approximated X11 color ") << s
341                        << _(" allocated for ")
342                        << lcolor.getGUIName(c) << endl;
343                 val.foreground = xcol.pixel;
344         } else {
345                 // Here we are traversing the current colormap to find
346                 // the color closest to the one we want.
347                 Visual * vi = DefaultVisual(display, DefaultScreen(display));
348
349                 XColor * cmap = new XColor[vi->map_entries];
350
351                 for(int i = 0; i < vi->map_entries; ++i) {
352                         cmap[i].pixel = i;
353                 }
354                 XQueryColors(display, colormap, cmap, vi->map_entries);
355
356                 // Walk through the cmap and look for close colors.
357                 int closest_pixel = 0;
358                 double closest_distance = 1e20; // we want to minimize this
359                 double distance = 0;
360                 for(int t = 0; t < vi->map_entries; ++t) {
361                         // The Euclidean distance between two points in 
362                         // a three-dimensional space, the RGB color-cube,
363                         // is used as the distance measurement between two
364                         // colors.
365
366                         // Since square-root is monotonous, we don't have to
367                         // take the square-root to find the minimum, and thus 
368                         // we use the squared distance instead to be faster.
369
370                         // If we want to get fancy, we could convert the RGB
371                         // coordinates to a different color-cube, maybe HSV,
372                         // but the RGB cube seems to work great.  (Asger)
373                         distance = pow(cmap[t].red   - xcol.red,   2.0) +
374                                    pow(cmap[t].green - xcol.green, 2.0) +
375                                    pow(cmap[t].blue  - xcol.blue,  2.0);
376                         if (distance < closest_distance) {
377                                 closest_distance = distance;
378                                 closest_pixel = t;
379                         }
380                 }
381                 lyxerr << _("LyX: Couldn't allocate '") << s 
382                        << _("' for ") << lcolor.getGUIName(c)
383                        << _(" with (r,g,b)=(") 
384                        << xcol.red << "," << xcol.green << ","
385                        << xcol.blue << ").\n"
386                        << _("     Using closest allocated "
387                             "color with (r,g,b)=(") 
388                        << cmap[closest_pixel].red << ","
389                        << cmap[closest_pixel].green << ","
390                        << cmap[closest_pixel].blue << ") instead.\n"
391                        << "Pixel [" << closest_pixel << "] is used." << endl;
392                 val.foreground = cmap[closest_pixel].pixel;
393                 delete[] cmap;
394         }
395
396         val.function = GXcopy;
397         return colorGCcache[c] = XCreateGC(display, drawable(),
398                                     GCForeground | GCFunction, &val);
399 }
400
401
402 // Gets GC for line
403 GC Painter::getGCLinepars(enum line_style ls,
404                           enum line_width lw, LColor::color c)
405 {
406         if (lyxerr.debugging()) {
407                 lyxerr << "Painter drawable: " << drawable() << endl;
408         }
409         
410         int index = lw + (ls << 1) + (c << 3);
411
412         if (lineGCcache.find(index) != lineGCcache.end())
413                 return lineGCcache[index];
414
415         XGCValues val;
416         XGetGCValues(display, getGCForeground(c), GCForeground, &val);
417         
418         switch (lw) {
419         case line_thin:         val.line_width = 0; break;
420         case line_thick:        val.line_width = 2; break;
421         }
422         
423         switch (ls) {
424         case line_solid:        val.line_style = LineSolid; break;
425         case line_onoffdash:    val.line_style = LineOnOffDash; break;
426         case line_doubledash:   val.line_style = LineDoubleDash; break;
427         }
428
429
430         val.cap_style = CapRound;
431         val.join_style = JoinRound;
432         val.function = GXcopy;
433
434         return lineGCcache[index] =
435                 XCreateGC(display, drawable(), 
436                           GCForeground | GCLineStyle | GCLineWidth | 
437                           GCCapStyle | GCJoinStyle | GCFunction, &val);
438 }