]> git.lyx.org Git - lyx.git/blob - src/screen.C
Fix crash when running lyx -dbg insets -e ...
[lyx.git] / src / screen.C
1 /* This file is part of
2 * ====================================================== 
3
4 *           LyX, The Document Processor
5 *        
6 *           Copyright 1995 Matthias Ettrich
7 *           Copyright 1995-1998 The LyX Team
8 *
9 * ====================================================== */
10
11 #include <config.h>
12
13 #ifdef __GNUG__
14 #pragma implementation "lyxscreen.h"
15 #endif
16
17 #include <algorithm>
18
19 #include "lyxscreen.h"
20 #include "lyxtext.h"
21 #include "lyxrow.h"
22 #include "Painter.h"
23 #include "WorkArea.h"
24 #include "buffer.h"
25 #include "BufferView.h"
26 #include "font.h"
27 #include "insets/insettext.h"
28 #include "ColorHandler.h"
29 #include "language.h"
30
31 using std::max;
32 using std::min;
33
34 namespace {
35
36 GC createGC()
37 {
38         XGCValues val;
39         val.foreground = BlackPixel(fl_get_display(), 
40                                     DefaultScreen(fl_get_display()));
41         
42         val.function = GXcopy;
43         val.graphics_exposures = false;
44         val.line_style = LineSolid;
45         val.line_width = 0;
46         return XCreateGC(fl_get_display(), RootWindow(fl_get_display(), 0), 
47                          GCForeground | GCFunction | GCGraphicsExposures
48                          | GCLineWidth | GCLineStyle , &val);
49 }
50
51 } // namespace anon
52
53
54 // Constructor
55 LyXScreen::LyXScreen(WorkArea & o)
56         : owner(o), force_clear(true)
57 {
58         // the cursor isnt yet visible
59         cursor_visible = false;
60         cursor_pixmap = 0;
61         cursor_pixmap_x = 0;
62         cursor_pixmap_y = 0;
63         cursor_pixmap_w = 0;
64         cursor_pixmap_h = 0;
65
66         // We need this GC
67         gc_copy = createGC();
68 }
69
70
71 LyXScreen::~LyXScreen()
72 {
73         XFreeGC(fl_get_display(), gc_copy);
74 }
75
76  
77 void LyXScreen::setCursorColor() 
78 {
79         if (!lyxColorHandler.get()) return;
80
81         GC gc = lyxColorHandler->getGCForeground(LColor::cursor);
82         
83         XGCValues val;
84         XGetGCValues(fl_get_display(),
85                      gc, GCForeground, &val);
86         XChangeGC(fl_get_display(), gc_copy, GCForeground, &val);
87 }
88
89
90 void LyXScreen::redraw(LyXText * text, BufferView * bv)
91 {
92         drawFromTo(text, bv, 0, owner.height());
93         expose(0, 0, owner.workWidth(), owner.height());
94         if (cursor_visible) {
95                 cursor_visible = false;
96                 showCursor(text, bv);
97         }
98 }
99
100
101 void LyXScreen::expose(int x, int y, int exp_width, int exp_height)
102 {
103         XCopyArea(fl_get_display(),
104                   owner.getPixmap(),
105                   owner.getWin(),
106                   gc_copy,
107                   x, y,
108                   exp_width, exp_height,
109                   x + owner.xpos(),
110                   y + owner.ypos());
111 }
112
113
114 void LyXScreen::drawFromTo(LyXText * text, BufferView * bv,
115                            int y1, int y2, int y_offset, int x_offset)
116 {
117         int y_text = text->first + y1;
118    
119         // get the first needed row 
120         Row * row = text->getRowNearY(y_text);
121         // y_text is now the real beginning of the row
122    
123         int y = y_text - text->first;
124         // y1 is now the real beginning of row on the screen
125         
126         while (row != 0 && y < y2) {
127                 LyXText::text_status st = bv->text->status();
128                 do {
129                         bv->text->status(bv, st);
130                         text->getVisibleRow(bv, y + y_offset,
131                                             x_offset, row, y + text->first);
132                 } while (bv->text->status() == LyXText::CHANGED_IN_DRAW);
133                 bv->text->status(bv, st);
134                 y += row->height();
135                 row = row->next();
136         }
137         force_clear = false;
138
139         // maybe we have to clear the screen at the bottom
140         if ((y < y2) && text->bv_owner) {
141                 owner.getPainter().fillRectangle(0, y,
142                                                  owner.workWidth(),
143                                                  y2 - y,
144                                                LColor::bottomarea);
145         }
146 }
147
148
149 void LyXScreen::drawOneRow(LyXText * text, BufferView * bv, Row * row,
150                            int y_text, int y_offset, int x_offset)
151 {
152         int const y = y_text - text->first + y_offset;
153
154         if (((y + row->height()) > 0) &&
155             ((y - row->height()) <= static_cast<int>(owner.height()))) {
156                 // ok there is something visible
157                 LyXText::text_status st = bv->text->status();
158                 do {
159                         bv->text->status(bv, st);
160                         text->getVisibleRow(bv, y, x_offset, row,
161                                             y + text->first);
162                 } while (bv->text->status() == LyXText::CHANGED_IN_DRAW);
163                 bv->text->status(bv, st);
164         }
165         force_clear = false;
166 }
167
168
169 /* draws the screen, starting with textposition y. uses as much already
170 * printed pixels as possible */
171 void LyXScreen::draw(LyXText * text, BufferView * bv, unsigned int y)
172 {
173         if (cursor_visible) hideCursor();
174
175         int const old_first = text->first;
176         text->first = y;
177
178         // is any optimiziation possible?
179         if ((y - old_first) < owner.height()
180             && (old_first - y) < owner.height()) {
181                 if (text->first < old_first) {
182                         drawFromTo(text, bv, 0, old_first - text->first);
183                         XCopyArea (fl_get_display(),
184                                    owner.getWin(),
185                                    owner.getWin(),
186                                    gc_copy,
187                                    owner.xpos(),
188                                    owner.ypos(),
189                                    owner.workWidth(),
190                                    owner.height() - old_first + text->first,
191                                    owner.xpos(),
192                                    owner.ypos() + old_first - text->first
193                                 );
194                         // expose the area drawn
195                         expose(0, 0,
196                                owner.workWidth(),
197                                old_first - text->first);
198                 } else  {
199                         drawFromTo(text, bv,
200                                    owner.height() + old_first - text->first,
201                                    owner.height());
202                         XCopyArea (fl_get_display(),
203                                    owner.getWin(),
204                                    owner.getWin(),
205                                    gc_copy,
206                                    owner.xpos(),
207                                    owner.ypos() + text->first - old_first,
208                                    owner.workWidth(),
209                                    owner.height() + old_first - text->first,
210                                    owner.xpos(),
211                                    owner.ypos());
212                         // expose the area drawn
213                         expose(0, owner.height() + old_first - text->first,
214                                owner.workWidth(), text->first - old_first);
215                 }
216         } else {
217                 // make a dumb new-draw 
218                 drawFromTo(text, bv, 0, owner.height());
219                 expose(0, 0, owner.workWidth(), owner.height());
220         }
221 }
222
223
224 void LyXScreen::showCursor(LyXText const * text, BufferView const * bv)
225 {
226         if (!cursor_visible) {
227                 Cursor_Shape shape = BAR_SHAPE;
228                 if (text->real_current_font.language() !=
229                     bv->buffer()->params.language
230                     || text->real_current_font.isVisibleRightToLeft()
231                     != bv->buffer()->params.language->RightToLeft())
232                         shape = (text->real_current_font.isVisibleRightToLeft())
233                                 ? REVERSED_L_SHAPE : L_SHAPE;
234                 showManualCursor(text, text->cursor.x(), text->cursor.y(),
235                                  lyxfont::maxAscent(text->real_current_font),
236                                  lyxfont::maxDescent(text->real_current_font),
237                                  shape);
238         }
239 }
240
241
242 /* returns true if first has changed, otherwise false */ 
243 bool LyXScreen::fitManualCursor(LyXText * text, BufferView * bv,
244                                 int /*x*/, int y, int asc, int desc)
245 {
246         int newtop = text->first;
247   
248         if (y + desc - text->first >= static_cast<int>(owner.height()))
249                 newtop = y - 3 * owner.height() / 4;  // the scroll region must be so big!!
250         else if (y - asc < static_cast<int>(text->first)
251                 && text->first > 0) {
252                 newtop = y - owner.height() / 4;
253         }
254
255         newtop = max(newtop, 0); // can newtop ever be < 0? (Lgb)
256   
257         if (newtop != static_cast<int>(text->first)) {
258                 draw(text, bv, newtop);
259                 text->first = newtop;
260                 return true;
261         }
262         return false;
263 }
264
265
266 void LyXScreen::showManualCursor(LyXText const * text, int x, int y,
267                                  int asc, int desc, Cursor_Shape shape)
268 {
269         // Update the cursor color.
270         setCursorColor();
271         
272         int const y1 = max(y - text->first - asc, 0);
273         int const y_tmp = min(y - text->first + desc,
274                               static_cast<int>(owner.height()));
275
276         // Secure against very strange situations
277         int const y2 = max(y_tmp, y1);
278         
279         if (cursor_pixmap){
280                 XFreePixmap(fl_get_display(), cursor_pixmap);
281                 cursor_pixmap = 0;
282         }
283
284         if (y2 > 0 && y1 < int(owner.height())) {
285                 cursor_pixmap_h = y2 - y1 + 1;
286                 cursor_pixmap_y = y1;
287
288                 switch (shape) {
289                 case BAR_SHAPE:
290                         cursor_pixmap_w = 1;
291                         cursor_pixmap_x = x;
292                         break;
293                 case L_SHAPE:
294                         cursor_pixmap_w = cursor_pixmap_h/3;
295                         cursor_pixmap_x = x;
296                         break;
297                 case REVERSED_L_SHAPE:
298                         cursor_pixmap_w = cursor_pixmap_h/3;
299                         cursor_pixmap_x = x - cursor_pixmap_w + 1;
300                         break;
301                 }
302
303                 cursor_pixmap = 
304                         XCreatePixmap (fl_get_display(),
305                                        fl_root,
306                                        cursor_pixmap_w,
307                                        cursor_pixmap_h,
308                                        fl_get_visual_depth());
309                 XCopyArea (fl_get_display(),
310                            owner.getWin(),
311                            cursor_pixmap,
312                            gc_copy,
313                            owner.xpos() + cursor_pixmap_x,
314                            owner.ypos() + cursor_pixmap_y,
315                            cursor_pixmap_w,
316                            cursor_pixmap_h,
317                            0, 0);
318                 XDrawLine(fl_get_display(),
319                           owner.getWin(),
320                           gc_copy,
321                           x + owner.xpos(),
322                           y1 + owner.ypos(),
323                           x + owner.xpos(),
324                           y2 + owner.ypos());
325                 switch (shape) {
326                 case BAR_SHAPE:
327                         break;
328                 case L_SHAPE:
329                 case REVERSED_L_SHAPE:
330                         int const rectangle_h = (cursor_pixmap_h + 10) / 20;
331                         XFillRectangle(fl_get_display(),
332                                        owner.getWin(),
333                                        gc_copy,
334                                        cursor_pixmap_x + owner.xpos(),
335                                        y2 - rectangle_h + 1 + owner.ypos(),
336                                        cursor_pixmap_w - 1, rectangle_h);
337                         break;
338                 }
339
340         }
341         cursor_visible = true;
342 }
343
344
345 void LyXScreen::hideCursor()
346 {
347         if (!cursor_visible) return;
348
349         if (cursor_pixmap){
350                 XCopyArea (fl_get_display(), 
351                            cursor_pixmap,
352                            owner.getWin(),
353                            gc_copy,
354                            0, 0, 
355                            cursor_pixmap_w, cursor_pixmap_h,
356                            cursor_pixmap_x + owner.xpos(),
357                            cursor_pixmap_y + owner.ypos());
358         }
359         cursor_visible = false;
360 }
361
362
363 void LyXScreen::cursorToggle(LyXText const * text, BufferView const * bv)
364 {
365         if (cursor_visible)
366                 hideCursor();
367         else
368                 showCursor(text, bv);
369 }
370
371
372 /* returns a new top so that the cursor is visible */ 
373 unsigned int LyXScreen::topCursorVisible(LyXText const * text)
374 {
375         int newtop = text->first;
376
377         if (text->cursor.y()
378             - text->cursor.row()->baseline()
379             + text->cursor.row()->height()
380             - text->first >= owner.height()) {
381                 if (text->cursor.row()->height() < owner.height()
382                     && text->cursor.row()->height() > owner.height() / 4)
383                         newtop = text->cursor.y()
384                                 + text->cursor.row()->height()
385                                 - text->cursor.row()->baseline() - owner.height();
386                 else
387                         newtop = text->cursor.y()
388                                 - 3 * owner.height() / 4;   /* the scroll region must be so big!! */
389         } else if (static_cast<int>((text->cursor.y()) - text->cursor.row()->baseline()) <
390                    text->first && text->first > 0)
391         {
392                 if (text->cursor.row()->height() < owner.height()
393                     && text->cursor.row()->height() > owner.height() / 4)
394                         newtop = text->cursor.y() - text->cursor.row()->baseline();
395                 else {
396                         newtop = text->cursor.y() - owner.height() / 4;
397                         newtop = min(newtop, int(text->first));
398                 }
399         }
400
401         newtop = max(newtop, 0);
402
403         return newtop;
404 }
405
406
407 /* scrolls the screen so that the cursor is visible, if necessary.
408 * returns true if a change was made, otherwise false */ 
409 bool LyXScreen::fitCursor(LyXText * text, BufferView * bv)
410 {
411         // Is a change necessary?
412         int const newtop = topCursorVisible(text);
413         bool const result = (newtop != text->first);
414         if (result)
415                 draw(text, bv, newtop);
416         return result;
417 }
418
419    
420 void LyXScreen::update(LyXText * text, BufferView * bv,
421                        int y_offset, int x_offset)
422 {
423         switch (text->status()) {
424         case LyXText::NEED_MORE_REFRESH:
425         {
426                 int const y = max(int(text->refresh_y - text->first), 0);
427                 drawFromTo(text, bv, y, owner.height(), y_offset, x_offset);
428                 text->refresh_y = 0;
429                 text->status(bv, LyXText::UNCHANGED);
430                 expose(0, y, owner.workWidth(), owner.height() - y);
431         }
432         break;
433         case LyXText::NEED_VERY_LITTLE_REFRESH:
434         {
435                 // ok I will update the current cursor row
436                 drawOneRow(text, bv, text->refresh_row, text->refresh_y,
437                            y_offset, x_offset);
438                 text->status(bv, LyXText::UNCHANGED);
439                 expose(0, text->refresh_y - text->first + y_offset,
440                        owner.workWidth(), text->refresh_row->height());
441         }
442         break;
443         case LyXText::CHANGED_IN_DRAW: // just to remove the warning
444         case LyXText::UNCHANGED:
445                 // Nothing needs done
446                 break;
447         }
448 }
449
450
451 void LyXScreen::toggleSelection(LyXText * text, BufferView * bv,
452                                 bool kill_selection,
453                                 int y_offset, int x_offset)
454 {
455         // only if there is a selection
456         if (!text->selection.set()) return;
457
458         int const bottom = min(
459                 max(static_cast<int>(text->selection.end.y()
460                                      - text->selection.end.row()->baseline()
461                                      + text->selection.end.row()->height()),
462                     text->first),
463                 static_cast<int>(text->first + owner.height()));
464         int const top = min(
465                 max(static_cast<int>(text->selection.start.y() -
466                                      text->selection.start.row()->baseline()),
467                     text->first),
468                 static_cast<int>(text->first + owner.height()));
469
470         if (kill_selection)
471                 text->selection.set(false);
472         drawFromTo(text, bv, top - text->first, bottom - text->first,
473                    y_offset, x_offset);
474         expose(0, top - text->first,
475                owner.workWidth(),
476                bottom - text->first - (top - text->first));
477 }
478   
479    
480 void LyXScreen::toggleToggle(LyXText * text, BufferView * bv,
481                              int y_offset, int x_offset)
482 {
483         if (text->toggle_cursor.par() == text->toggle_end_cursor.par()
484             && text->toggle_cursor.pos() == text->toggle_end_cursor.pos())
485                 return;
486         
487         int const top_tmp = text->toggle_cursor.y()
488                 - text->toggle_cursor.row()->baseline();
489         int const bottom_tmp = text->toggle_end_cursor.y()
490                 - text->toggle_end_cursor.row()->baseline() 
491                 + text->toggle_end_cursor.row()->height();
492         
493         int const bottom = min(max(bottom_tmp, text->first),
494                      static_cast<int>(text->first + owner.height()));
495         int const top = min(max(top_tmp, text->first),
496                   static_cast<int>(text->first + owner.height()));
497
498         drawFromTo(text, bv, top - text->first, bottom - text->first, y_offset,
499                    x_offset);
500         expose(0, top - text->first, owner.workWidth(),
501                bottom - text->first - (top - text->first));
502 }