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