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