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