]> git.lyx.org Git - lyx.git/blob - src/screen.C
(Herbert): small read graphics inset bug fix.
[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(), 0, 0, text == bv->text);
93         expose(0, 0, owner.workWidth(), owner.height());
94         if (cursor_visible) {
95                 cursor_visible = false;
96                 bv->showCursor();
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                            bool internal)
117 {
118         int y_text = text->first_y + y1;
119
120         // get the first needed row
121         Row * row = text->getRowNearY(y_text);
122         // y_text is now the real beginning of the row
123
124         int y = y_text - text->first_y;
125         // y1 is now the real beginning of row on the screen
126
127         while (row != 0 && y < y2) {
128                 LyXText::text_status st = text->status();
129                 text->getVisibleRow(bv, y + y_offset,
130                                     x_offset, row, y + text->first_y);
131                 internal = internal && (st != LyXText::CHANGED_IN_DRAW);
132                 while (internal && text->status() == LyXText::CHANGED_IN_DRAW) {
133                         text->fullRebreak(bv);
134                         st = LyXText::NEED_MORE_REFRESH;
135                         text->setCursor(bv, text->cursor.par(),
136                                         text->cursor.pos());
137                         text->status(bv, st);
138                         // we should be sure our row-pointer is still valid, so it's
139                         // better to recompute it.
140                         y_text = y + text->first_y;
141                         row = text->getRowNearY(y_text);
142                         y = y_text - text->first_y;
143                         text->getVisibleRow(bv, y + y_offset,
144                                             x_offset, row, y + text->first_y);
145                 }
146                 y += row->height();
147                 row = row->next();
148         }
149         force_clear = false;
150
151         // maybe we have to clear the screen at the bottom
152         if ((y < y2) && text->bv_owner) {
153                 owner.getPainter().fillRectangle(0, y,
154                                                  owner.workWidth(),
155                                                  y2 - y,
156                                                LColor::bottomarea);
157         }
158 }
159
160
161 void LyXScreen::drawOneRow(LyXText * text, BufferView * bv, Row * row,
162                            int y_text, int y_offset, int x_offset)
163 {
164         int const y = y_text - text->first_y + y_offset;
165
166         if (((y + row->height()) > 0) &&
167             ((y - row->height()) <= static_cast<int>(owner.height()))) {
168                 // ok there is something visible
169                 text->getVisibleRow(bv, y, x_offset, row, y + text->first_y);
170         }
171         force_clear = false;
172 }
173
174
175 /* draws the screen, starting with textposition y. uses as much already
176  * printed pixels as possible */
177 void LyXScreen::draw(LyXText * text, BufferView * bv, unsigned int y)
178 {
179         if (cursor_visible) hideCursor();
180
181         int const old_first = text->first_y;
182         bool internal = (text == bv->text);
183         text->first_y = y;
184
185         // is any optimiziation possible?
186         if ((y - old_first) < owner.height()
187             && (old_first - y) < owner.height())
188         {
189                 if (text->first_y < old_first) {
190                         drawFromTo(text, bv, 0,
191                                    old_first - text->first_y, 0, 0, internal);
192                         XCopyArea (fl_get_display(),
193                                    owner.getWin(),
194                                    owner.getWin(),
195                                    gc_copy,
196                                    owner.xpos(),
197                                    owner.ypos(),
198                                    owner.workWidth(),
199                                    owner.height() - old_first + text->first_y,
200                                    owner.xpos(),
201                                    owner.ypos() + old_first - text->first_y
202                                 );
203                         // expose the area drawn
204                         expose(0, 0,
205                                owner.workWidth(),
206                                old_first - text->first_y);
207                 } else  {
208                         drawFromTo(text, bv,
209                                    owner.height() + old_first - text->first_y,
210                                    owner.height(), 0, 0, internal);
211                         XCopyArea (fl_get_display(),
212                                    owner.getWin(),
213                                    owner.getWin(),
214                                    gc_copy,
215                                    owner.xpos(),
216                                    owner.ypos() + text->first_y - old_first,
217                                    owner.workWidth(),
218                                    owner.height() + old_first - text->first_y,
219                                    owner.xpos(),
220                                    owner.ypos());
221                         // expose the area drawn
222                         expose(0, owner.height() + old_first - text->first_y,
223                                owner.workWidth(), text->first_y - old_first);
224                 }
225         } else {
226                 // make a dumb new-draw
227                 drawFromTo(text, bv, 0, owner.height(), 0, 0, internal);
228                 expose(0, 0, owner.workWidth(), owner.height());
229         }
230 }
231
232
233 void LyXScreen::showCursor(LyXText const * text, BufferView const * bv)
234 {
235         if (!cursor_visible) {
236                 Cursor_Shape shape = BAR_SHAPE;
237                 if (text->real_current_font.language() !=
238                     bv->buffer()->params.language
239                     || text->real_current_font.isVisibleRightToLeft()
240                     != bv->buffer()->params.language->RightToLeft())
241                         shape = (text->real_current_font.isVisibleRightToLeft())
242                                 ? REVERSED_L_SHAPE : L_SHAPE;
243                 showManualCursor(text, text->cursor.x(), text->cursor.y(),
244                                  lyxfont::maxAscent(text->real_current_font),
245                                  lyxfont::maxDescent(text->real_current_font),
246                                  shape);
247         }
248 }
249
250
251 /* returns true if first has changed, otherwise false */
252 bool LyXScreen::fitManualCursor(LyXText * text, BufferView * bv,
253                                 int /*x*/, int y, int asc, int desc)
254 {
255         int newtop = text->first_y;
256
257         if (y + desc - text->first_y >= static_cast<int>(owner.height()))
258                 newtop = y - 3 * owner.height() / 4;  // the scroll region must be so big!!
259         else if (y - asc < text->first_y
260                 && text->first_y > 0) {
261                 newtop = y - owner.height() / 4;
262         }
263
264         newtop = max(newtop, 0); // can newtop ever be < 0? (Lgb)
265
266         if (newtop != text->first_y) {
267                 draw(text, bv, newtop);
268                 text->first_y = newtop;
269                 return true;
270         }
271         return false;
272 }
273
274
275 void LyXScreen::showManualCursor(LyXText const * text, int x, int y,
276                                  int asc, int desc, Cursor_Shape shape)
277 {
278         // Update the cursor color.
279         setCursorColor();
280
281         int const y1 = max(y - text->first_y - asc, 0);
282         int const y_tmp = min(y - text->first_y + desc,
283                               static_cast<int>(owner.height()));
284
285         // Secure against very strange situations
286         int const y2 = max(y_tmp, y1);
287
288         if (cursor_pixmap) {
289                 XFreePixmap(fl_get_display(), cursor_pixmap);
290                 cursor_pixmap = 0;
291         }
292
293         if (y2 > 0 && y1 < int(owner.height())) {
294                 cursor_pixmap_h = y2 - y1 + 1;
295                 cursor_pixmap_y = y1;
296
297                 switch (shape) {
298                 case BAR_SHAPE:
299                         cursor_pixmap_w = 1;
300                         cursor_pixmap_x = x;
301                         break;
302                 case L_SHAPE:
303                         cursor_pixmap_w = cursor_pixmap_h/3;
304                         cursor_pixmap_x = x;
305                         break;
306                 case REVERSED_L_SHAPE:
307                         cursor_pixmap_w = cursor_pixmap_h/3;
308                         cursor_pixmap_x = x - cursor_pixmap_w + 1;
309                         break;
310                 }
311
312                 cursor_pixmap =
313                         XCreatePixmap (fl_get_display(),
314                                        fl_root,
315                                        cursor_pixmap_w,
316                                        cursor_pixmap_h,
317                                        fl_get_visual_depth());
318                 XCopyArea (fl_get_display(),
319                            owner.getWin(),
320                            cursor_pixmap,
321                            gc_copy,
322                            owner.xpos() + cursor_pixmap_x,
323                            owner.ypos() + cursor_pixmap_y,
324                            cursor_pixmap_w,
325                            cursor_pixmap_h,
326                            0, 0);
327                 XDrawLine(fl_get_display(),
328                           owner.getWin(),
329                           gc_copy,
330                           x + owner.xpos(),
331                           y1 + owner.ypos(),
332                           x + owner.xpos(),
333                           y2 + owner.ypos());
334                 switch (shape) {
335                 case BAR_SHAPE:
336                         break;
337                 case L_SHAPE:
338                 case REVERSED_L_SHAPE:
339                         int const rectangle_h = (cursor_pixmap_h + 10) / 20;
340                         XFillRectangle(fl_get_display(),
341                                        owner.getWin(),
342                                        gc_copy,
343                                        cursor_pixmap_x + owner.xpos(),
344                                        y2 - rectangle_h + 1 + owner.ypos(),
345                                        cursor_pixmap_w - 1, rectangle_h);
346                         break;
347                 }
348
349         }
350         cursor_visible = true;
351 }
352
353
354 void LyXScreen::hideCursor()
355 {
356         if (!cursor_visible) return;
357
358         if (cursor_pixmap) {
359                 XCopyArea (fl_get_display(),
360                            cursor_pixmap,
361                            owner.getWin(),
362                            gc_copy,
363                            0, 0,
364                            cursor_pixmap_w, cursor_pixmap_h,
365                            cursor_pixmap_x + owner.xpos(),
366                            cursor_pixmap_y + owner.ypos());
367         }
368         cursor_visible = false;
369 }
370
371
372 void LyXScreen::cursorToggle(BufferView * bv) const
373 {
374         if (cursor_visible)
375                 bv->hideCursor();
376         else
377                 bv->showCursor();
378 }
379
380
381 /* returns a new top so that the cursor is visible */
382 unsigned int LyXScreen::topCursorVisible(LyXText const * text)
383 {
384         int newtop = text->first_y;
385
386         Row * row = text->cursor.row();
387
388         // Is this a hack? Yes, probably... (Lgb)
389         if (!row)
390                 return max(newtop, 0);
391
392         if (text->cursor.y() - row->baseline() + row->height()
393             - text->first_y >= owner.height()) {
394                 if (row->height() < owner.height()
395                     && row->height() > owner.height() / 4) {
396                         newtop = text->cursor.y()
397                                 + row->height()
398                                 - row->baseline() - owner.height();
399                 } else {
400                         // scroll down
401                         newtop = text->cursor.y()
402                                 - owner.height() / 2;   /* the scroll region must be so big!! */
403                 }
404
405         } else if (static_cast<int>((text->cursor.y()) - row->baseline()) <
406                    text->first_y && text->first_y > 0) {
407                 if (row->height() < owner.height()
408                     && row->height() > owner.height() / 4) {
409                         newtop = text->cursor.y() - row->baseline();
410                 } else {
411                         // scroll up
412                         newtop = text->cursor.y() - owner.height() / 2;
413                         newtop = min(newtop, text->first_y);
414                 }
415         }
416
417         newtop = max(newtop, 0);
418
419         return newtop;
420 }
421
422
423 /* scrolls the screen so that the cursor is visible, if necessary.
424 * returns true if a change was made, otherwise false */
425 bool LyXScreen::fitCursor(LyXText * text, BufferView * bv)
426 {
427         // Is a change necessary?
428         int const newtop = topCursorVisible(text);
429         bool const result = (newtop != text->first_y);
430         if (result)
431                 draw(text, bv, newtop);
432         return result;
433 }
434
435
436 void LyXScreen::update(LyXText * text, BufferView * bv,
437                        int y_offset, int x_offset)
438 {
439         switch (text->status()) {
440         case LyXText::NEED_MORE_REFRESH:
441         {
442                 int const y = max(int(text->refresh_y - text->first_y), 0);
443                 drawFromTo(text, bv, y, owner.height(), y_offset, x_offset);
444                 text->refresh_y = 0;
445                 // otherwise this is called ONLY from BufferView_pimpl(update)
446                 // or we should see to set this flag accordingly
447                 if (text != bv->text)
448                         text->status(bv, LyXText::UNCHANGED);
449                 expose(0, y, owner.workWidth(), owner.height() - y);
450         }
451         break;
452         case LyXText::NEED_VERY_LITTLE_REFRESH:
453         {
454                 // ok I will update the current cursor row
455                 drawOneRow(text, bv, text->refresh_row, text->refresh_y,
456                            y_offset, x_offset);
457                 // this because if we had a major update the refresh_row could
458                 // have been set to 0!
459                 if (text->refresh_row) {
460                         // otherwise this is called ONLY from BufferView_pimpl(update)
461                         // or we should see to set this flag accordingly
462                         if (text != bv->text)
463                                 text->status(bv, LyXText::UNCHANGED);
464                         expose(0, text->refresh_y - text->first_y + y_offset,
465                                    owner.workWidth(), text->refresh_row->height());
466                 }
467         }
468         break;
469         case LyXText::CHANGED_IN_DRAW: // just to remove the warning
470         case LyXText::UNCHANGED:
471                 // Nothing needs done
472                 break;
473         }
474 }
475
476
477 void LyXScreen::toggleSelection(LyXText * text, BufferView * bv,
478                                 bool kill_selection,
479                                 int y_offset, int x_offset)
480 {
481         // only if there is a selection
482         if (!text->selection.set()) return;
483
484         int const bottom = min(
485                 max(static_cast<int>(text->selection.end.y()
486                                      - text->selection.end.row()->baseline()
487                                      + text->selection.end.row()->height()),
488                     text->first_y),
489                 static_cast<int>(text->first_y + owner.height()));
490         int const top = min(
491                 max(static_cast<int>(text->selection.start.y() -
492                                      text->selection.start.row()->baseline()),
493                     text->first_y),
494                 static_cast<int>(text->first_y + owner.height()));
495
496         if (kill_selection)
497                 text->selection.set(false);
498         drawFromTo(text, bv, top - text->first_y, bottom - text->first_y,
499                    y_offset, x_offset);
500         expose(0, top - text->first_y,
501                owner.workWidth(),
502                bottom - text->first_y - (top - text->first_y));
503 }
504
505
506 void LyXScreen::toggleToggle(LyXText * text, BufferView * bv,
507                              int y_offset, int x_offset)
508 {
509         if (text->toggle_cursor.par() == text->toggle_end_cursor.par()
510             && text->toggle_cursor.pos() == text->toggle_end_cursor.pos())
511                 return;
512
513         int const top_tmp = text->toggle_cursor.y()
514                 - text->toggle_cursor.row()->baseline();
515         int const bottom_tmp = text->toggle_end_cursor.y()
516                 - text->toggle_end_cursor.row()->baseline()
517                 + text->toggle_end_cursor.row()->height();
518
519         int const offset = y_offset < 0 ? y_offset : 0;
520         int const bottom = min(max(bottom_tmp, text->first_y),
521                      static_cast<int>(text->first_y + owner.height()))-offset;
522         int const top = min(max(top_tmp, text->first_y),
523                   static_cast<int>(text->first_y + owner.height()))-offset;
524
525         drawFromTo(text, bv, top - text->first_y,
526                    bottom - text->first_y, y_offset,
527                    x_offset);
528         expose(0, top - text->first_y, owner.workWidth(),
529                bottom - text->first_y - (top - text->first_y));
530 }