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