]> git.lyx.org Git - lyx.git/blob - src/Painter.C
b46b1cef75dc2a7a3583763e44dc458baf0cde75
[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, 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         int w = 0, h = 0;
253         XCopyArea(display, bitmap, drawable, gc,
254                   0, 0, w, h, x, y);
255         XFreeGC(display, gc);
256         return *this;
257 }
258
259
260 PainterBase & Painter::text(int x, int y, string const & s, LyXFont const & f)
261 {
262         if (lyxerr.debugging()) {
263                 if (!Lgb_bug_find_hack)
264                         lyxerr << "text not called from "
265                                 "workarea::workhandler\n";
266                 lyxerr << "Painter drawable: " << drawable << endl;
267         }
268         
269         GC gc = getGCForeground(f.realColor());
270         XSetFont(display, gc, f.getFontID());
271         XDrawString(display, drawable, gc, x, y, s.c_str(), s.length());
272         underline(f, x, y, this->width(s, f));
273         return *this;
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 PainterBase & Painter::text(int x, int y, char c, LyXFont const & f)
296 {
297         if (lyxerr.debugging()) {
298                 if (!Lgb_bug_find_hack)
299                         lyxerr << "text not called from "
300                                 "workarea::workhandler\n";
301                 lyxerr << "Painter drawable: " << drawable << endl;
302         }
303         
304         GC gc = getGCForeground(f.realColor());
305         XSetFont(display, gc, f.getFontID());
306         char s[2];
307         s[0] = c; s[1] = '\0';
308         XDrawString(display, drawable, gc, x, y, s, 1);
309         underline(f, x, y, this->width(c, f));
310         return *this;
311 }
312
313
314 void Painter::underline(LyXFont const & f, int x, int y, int width) {
315         // What about underbars?
316         if (f.underbar() == LyXFont::ON && f.latex() != LyXFont::ON) {
317                 int below = f.maxDescent() / 2;
318                 if (below < 2) below = 2;
319                 int height = (f.maxDescent() / 4) - 1;
320                 if (height < 0) height = 0;
321                 fillRectangle(x, y + below, width, below + height, f.color());
322         }
323 }
324
325
326 // Gets GC according to color
327 // Uses caching
328 GC Painter::getGCForeground(LColor::color c)
329 {
330         if (lyxerr.debugging()) {
331                 lyxerr << "Painter drawable: " << drawable << endl;
332         }
333         
334         if (colorGCcache[c] != 0) return colorGCcache[c];
335
336         XColor xcol, ccol;
337         string s = lcolor.getX11Name(c);
338         XGCValues val;
339
340         // Look up the RGB values for the color, and an approximate
341         // color that we can hope to get on this display.
342         if (XLookupColor(display, colormap, s.c_str(), &xcol, &ccol) == 0) {
343                 lyxerr << _("LyX: Unknown X11 color ") << s
344                        << _(" for ") << lcolor.getGUIName(c) << '\n'
345                        << _("     Using black instead, sorry!.") << endl;
346                 unsigned long bla = BlackPixel(display,
347                                                DefaultScreen(display));
348                 val.foreground = bla;
349         // Try the exact RGB values first, then the approximate.
350         } else if (XAllocColor(display, colormap, &xcol) != 0) {
351                 lyxerr << _("LyX: X11 color ") << s
352                        << _(" allocated for ") 
353                        << lcolor.getGUIName(c) << endl;
354                 val.foreground = xcol.pixel;
355         } else if (XAllocColor(display, colormap, &ccol)) {
356                 lyxerr << _("LyX: Using approximated X11 color ") << s
357                        << _(" allocated for ")
358                        << lcolor.getGUIName(c) << endl;
359                 val.foreground = xcol.pixel;
360         } else {
361                 // Here we are traversing the current colormap to find
362                 // the color closest to the one we want.
363                 Visual * vi = DefaultVisual(display, DefaultScreen(display));
364
365                 XColor * cmap = new XColor[vi->map_entries];
366
367                 for(int i = 0; i < vi->map_entries; ++i) {
368                         cmap[i].pixel = i;
369                 }
370                 XQueryColors(display, colormap, cmap, vi->map_entries);
371
372                 // Walk through the cmap and look for close colors.
373                 int closest_pixel = 0;
374                 double closest_distance = 1e20; // we want to minimize this
375                 double distance = 0;
376                 for(int t = 0; t < vi->map_entries; ++t) {
377                         // The Euclidean distance between two points in 
378                         // a three-dimensional space, the RGB color-cube,
379                         // is used as the distance measurement between two
380                         // colors.
381
382                         // Since square-root is monotonous, we don't have to
383                         // take the square-root to find the minimum, and thus 
384                         // we use the squared distance instead to be faster.
385
386                         // If we want to get fancy, we could convert the RGB
387                         // coordinates to a different color-cube, maybe HSV,
388                         // but the RGB cube seems to work great.  (Asger)
389                         distance = pow(cmap[t].red   - xcol.red,   2.0) +
390                                    pow(cmap[t].green - xcol.green, 2.0) +
391                                    pow(cmap[t].blue  - xcol.blue,  2.0);
392                         if (distance < closest_distance) {
393                                 closest_distance = distance;
394                                 closest_pixel = t;
395                         }
396                 }
397                 lyxerr << _("LyX: Couldn't allocate '") << s 
398                        << _("' for ") << lcolor.getGUIName(c)
399                        << _(" with (r,g,b)=(") 
400                        << xcol.red << "," << xcol.green << ","
401                        << xcol.blue << ").\n"
402                        << _("     Using closest allocated "
403                             "color with (r,g,b)=(") 
404                        << cmap[closest_pixel].red << ","
405                        << cmap[closest_pixel].green << ","
406                        << cmap[closest_pixel].blue << ") instead.\n"
407                        << "Pixel [" << closest_pixel << "] is used." << endl;
408                 val.foreground = cmap[closest_pixel].pixel;
409                 delete[] cmap;
410         }
411
412         val.function = GXcopy;
413         return colorGCcache[c] = XCreateGC(display, drawable,
414                                     GCForeground | GCFunction, &val);
415 }
416
417
418 // Gets GC for line
419 GC Painter::getGCLinepars(enum line_style ls,
420                           enum line_width lw, LColor::color c)
421 {
422         if (lyxerr.debugging()) {
423                 lyxerr << "Painter drawable: " << drawable << endl;
424         }
425         
426         int index = lw + (ls << 1) + (c << 3);
427
428         if (lineGCcache.find(index) != lineGCcache.end())
429                 return lineGCcache[index];
430
431         XGCValues val;
432         XGetGCValues(display, getGCForeground(c), GCForeground, &val);
433         
434         switch (lw) {
435         case line_thin:         val.line_width = 0; break;
436         case line_thick:        val.line_width = 2; break;
437         }
438         
439         switch (ls) {
440         case line_solid:        val.line_style = LineSolid; break;
441         case line_onoffdash:    val.line_style = LineOnOffDash; break;
442         case line_doubledash:   val.line_style = LineDoubleDash; break;
443         }
444
445
446         val.cap_style = CapRound;
447         val.join_style = JoinRound;
448         val.function = GXcopy;
449
450         return lineGCcache[index] =
451                 XCreateGC(display, drawable, 
452                           GCForeground | GCLineStyle | GCLineWidth | 
453                           GCCapStyle | GCJoinStyle | GCFunction, &val);
454 }
455
456 #endif