]> git.lyx.org Git - lyx.git/blob - src/screen.C
c376a15a13469608f6fa8549990e8d2e81c3540d
[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 "font.h"
26
27 using std::max;
28 using std::min;
29
30 static
31 GC createGC()
32 {
33         XGCValues val;
34         val.foreground = BlackPixel(fl_display, 
35                                     DefaultScreen(fl_display));
36         
37         val.function=GXcopy;
38         val.graphics_exposures = false;
39         val.line_style = LineSolid;
40         val.line_width = 0;
41         return XCreateGC(fl_display, RootWindow(fl_display, 0), 
42                          GCForeground | GCFunction | GCGraphicsExposures
43                          | GCLineWidth | GCLineStyle , &val);
44 }
45
46
47 // Constructor
48 LyXScreen::LyXScreen(WorkArea & o, LyXText * text_ptr)
49         : owner(o), text(text_ptr)
50 {
51         first = 0;
52    
53         /* the cursor isnt yet visible */ 
54         cursor_visible = false;
55 #if 0
56         screen_refresh_y = -1;
57 #endif
58         cursor_pixmap = 0;
59         cursor_pixmap_x = 0;
60         cursor_pixmap_y = 0;
61         cursor_pixmap_w = 0;
62         cursor_pixmap_h = 0;
63
64         // We need this GC
65         gc_copy = createGC();
66 }
67
68
69 void LyXScreen::Redraw()
70 {
71         DrawFromTo(0, owner.height());
72 #if 0
73         screen_refresh_y = -1;
74 #endif
75         expose(0, 0, owner.workWidth(), owner.height());
76         if (cursor_visible) {
77                 cursor_visible = false;
78                 ShowCursor();
79         }
80 }
81
82
83 void LyXScreen::expose(int x, int y, int exp_width, int exp_height)
84 {
85         XCopyArea(fl_display,
86                   owner.getPixmap(),
87                   owner.getWin(),
88                   gc_copy,
89                   x, y,
90                   exp_width, exp_height,
91                   x + owner.xpos(),
92                   y + owner.ypos());
93 }
94
95
96 void LyXScreen::DrawFromTo(int y1, int y2)
97 {
98         long y_text = first + y1;
99    
100         /* get the first needed row */ 
101         Row * row = text->GetRowNearY(y_text);
102         /* y_text is now the real beginning of the row */
103    
104         long y = y_text - first;
105         /* y1 is now the real beginning of row on the screen */
106         
107         while (row != 0 && y < y2) {
108                 text->GetVisibleRow(y, row, y + first);
109                 y += row->height;
110                 row = row->next;
111         }
112    
113         /* maybe we have to clear the screen at the bottom */ 
114         if (y < y2) {
115                 owner.getPainter().fillRectangle(0, y,
116                                                  owner.workWidth(),
117                                                  y2 - y,
118                                                LColor::bottomarea);
119         }
120 }
121
122
123 #if 1
124 void LyXScreen::DrawOneRow(Row * row, long y_text)
125 {
126         long y = y_text - first;
127       
128         if (y + row->height > 0 && y - row->height <= long(owner.height())) {
129                 /* ok there is something visible */
130                 text->GetVisibleRow(y, row, y + first);
131         }
132 }
133 #else
134 void LyXScreen::DrawOneRow(Row * row, long & y_text)
135 {
136         long y = y_text - first;
137       
138         if (y + row->height > 0 && y - row->height <= long(owner.height())) {
139                 /* ok there is something visible */
140                 text->GetVisibleRow(y, row, y + first);
141         }
142         y_text += row->height;
143 }
144 #endif
145
146
147 /* draws the screen, starting with textposition y. uses as much already
148 * printed pixels as possible */
149 void LyXScreen::Draw(unsigned long y)
150 {
151         if (cursor_visible) HideCursor();
152
153         //if (y < 0) y = 0;
154         unsigned long old_first = first;
155         first = y;
156
157         /* is any optimiziation possible? */ 
158         if ((y - old_first) < owner.height()
159             && (old_first - y) < owner.height()) {
160                 if (first < old_first) {
161                         DrawFromTo(0, old_first - first);
162                         XCopyArea (fl_display,
163                                    owner.getWin(),
164                                    owner.getWin(),
165                                    gc_copy,
166                                    owner.xpos(),
167                                    owner.ypos(),
168                                    owner.workWidth(),
169                                    owner.height() - old_first + first,
170                                    owner.xpos(),
171                                    owner.ypos() + old_first - first
172                                 );
173                         // expose the area drawn
174                         expose(0, 0,
175                                owner.workWidth(),
176                                old_first - first);
177                 } else  {
178                         DrawFromTo(
179                                 owner.height() + old_first - first,
180                                    owner.height());
181                         XCopyArea (fl_display,
182                                    owner.getWin(),
183                                    owner.getWin(),
184                                    gc_copy,
185                                    owner.xpos(),
186                                    owner.ypos() + first - old_first,
187                                    owner.workWidth(),
188                                    owner.height() + old_first - first,
189                                    owner.xpos(),
190                                    owner.ypos());
191                         // expose the area drawn
192                         expose(0, owner.height() + old_first - first,
193                                owner.workWidth(), first - old_first);
194                 }
195         } else {
196                 /* make a dumb new-draw */ 
197                 DrawFromTo(0, owner.height());
198                 expose(0, 0, owner.workWidth(), owner.height());
199         }
200 }
201
202
203 void LyXScreen::ShowCursor()
204 {
205         if (!cursor_visible) {
206                 Cursor_Shape shape = BAR_SHAPE;
207                 if (text->real_current_font.language() !=
208                     text->buffer->params.language_info
209                     || text->real_current_font.isVisibleRightToLeft()
210                     != text->buffer->params.language_info->RightToLeft)
211                         shape = (text->real_current_font.isVisibleRightToLeft())
212                                 ? REVERSED_L_SHAPE : L_SHAPE;
213                 ShowManualCursor(text->cursor.x, text->cursor.y,
214                                  lyxfont::maxAscent(text->real_current_font),
215                                  lyxfont::maxDescent(text->real_current_font),
216                                  shape);
217         }
218 }
219
220
221 /* returns true if first has changed, otherwise false */ 
222 bool LyXScreen::FitManualCursor(long /*x*/, long y, int asc, int desc)
223 {
224         long newtop = first;
225   
226         if (y + desc - first >= owner.height())
227                 newtop = y - 3 * owner.height() / 4;   /* the scroll region must be so big!! */
228         else if (y - asc < long(first)
229                 && first > 0) {
230                 newtop = y - owner.height() / 4;
231         }
232         //if (newtop < 0)
233         newtop = max(newtop, 0L); // can newtop ever be < 0? (Lgb)
234   
235         if (newtop != long(first)) {
236                 Draw(newtop);
237                 first = newtop;
238                 return true;
239         }
240         return false;
241 }
242
243
244 void LyXScreen::ShowManualCursor(long x, long y, int asc, int desc,
245                                   Cursor_Shape shape)
246 {
247         unsigned long y1 = max(y - first - asc, 0UL);
248         typedef unsigned long ulong;
249         
250         unsigned long y2 = min(y - first + desc, ulong(owner.height()));
251
252         // Secure against very strange situations
253         //if (y2 < y1) y2 = y1;
254         y2 = max(y2, y1);
255         
256         if (cursor_pixmap){
257                 XFreePixmap(fl_display, cursor_pixmap);
258                 cursor_pixmap = 0;
259         }
260
261         if (y2 > 0 && y1 < owner.height()) {
262                 cursor_pixmap_h = y2 - y1 + 1;
263                 cursor_pixmap_y = y1;
264
265                 switch(shape) {
266                 case BAR_SHAPE:
267                         cursor_pixmap_w = 1;
268                         cursor_pixmap_x = x;
269                         break;
270                 case L_SHAPE:
271                         cursor_pixmap_w = cursor_pixmap_h/3;
272                         cursor_pixmap_x = x;
273                         break;
274                 case REVERSED_L_SHAPE:
275                         cursor_pixmap_w = cursor_pixmap_h/3;
276                         cursor_pixmap_x = x - cursor_pixmap_w + 1;
277                         break;
278                 }
279
280                 cursor_pixmap = 
281                         XCreatePixmap (fl_display,
282                                        fl_root,
283                                        cursor_pixmap_w,
284                                        cursor_pixmap_h,
285                                        fl_get_visual_depth());
286                 XCopyArea (fl_display,
287                            owner.getWin(),
288                            cursor_pixmap,
289                            gc_copy,
290                            owner.xpos() + cursor_pixmap_x,
291                            owner.ypos() + cursor_pixmap_y,
292                            cursor_pixmap_w,
293                            cursor_pixmap_h,
294                            0, 0);
295                 XDrawLine(fl_display,
296                           owner.getWin(),
297                           gc_copy,
298                           x + owner.xpos(),
299                           y1 + owner.ypos(),
300                           x + owner.xpos(),
301                           y2 + owner.ypos());
302                 switch(shape) {
303                 case BAR_SHAPE:
304                         break;
305                 case L_SHAPE:
306                 case REVERSED_L_SHAPE:
307                         int rectangle_h = (cursor_pixmap_h+10)/20;
308                         XFillRectangle(fl_display,
309                                        owner.getWin(),
310                                        gc_copy,
311                                        cursor_pixmap_x + owner.xpos(),
312                                        y2 - rectangle_h + 1 + owner.ypos(),
313                                        cursor_pixmap_w - 1, rectangle_h);
314                         break;
315                 }
316
317         }
318         cursor_visible = true;
319 }
320
321
322 void LyXScreen::HideCursor()
323 {
324         if (!cursor_visible) return;
325
326         if (cursor_pixmap){
327                 XCopyArea (fl_display, 
328                            cursor_pixmap,
329                            owner.getWin(),
330                            gc_copy,
331                            0, 0, 
332                            cursor_pixmap_w, cursor_pixmap_h,
333                            cursor_pixmap_x + owner.xpos(),
334                            cursor_pixmap_y + owner.ypos());
335         }
336         cursor_visible = false;
337 }
338
339
340 void LyXScreen::CursorToggle()
341 {
342         if (cursor_visible)
343                 HideCursor();
344         else
345                 ShowCursor();
346 }
347
348
349 /* returns a new top so that the cursor is visible */ 
350 unsigned long LyXScreen::TopCursorVisible()
351 {
352         long newtop = first;
353
354         if (text->cursor.y
355             - text->cursor.row->baseline
356             + text->cursor.row->height
357             - first >= owner.height()) {
358                 if (text->cursor.row->height < owner.height()
359                     && text->cursor.row->height > owner.height() / 4)
360                         newtop = text->cursor.y
361                                 + text->cursor.row->height
362                                 - text->cursor.row->baseline - owner.height();
363                 else
364                         newtop = text->cursor.y
365                                 - 3 * owner.height() / 4;   /* the scroll region must be so big!! */
366         } else if (text->cursor.y - text->cursor.row->baseline < first
367                    && first > 0) {
368                 if (text->cursor.row->height < owner.height()
369                     && text->cursor.row->height > owner.height() / 4)
370                         newtop = text->cursor.y - text->cursor.row->baseline;
371                 else {
372                         newtop = text->cursor.y - owner.height() / 4;
373                         //if (newtop > long(first))
374                         newtop = min(newtop, long(first));
375                 }
376         }
377         //if (newtop < 0)
378         //      newtop = 0;
379         newtop = max(newtop, 0L);
380
381         return newtop;
382 }
383
384
385 /* scrolls the screen so that the cursor is visible, if necessary.
386 * returns true if a change was made, otherwise false */ 
387 bool LyXScreen::FitCursor()
388 {
389         // Is a change necessary?
390         unsigned long newtop = TopCursorVisible();
391         bool result = (newtop != first);
392         if (result)
393                 Draw(newtop);
394         return result;
395 }
396
397    
398 void LyXScreen::Update()
399 {
400 #if 1
401         switch(text->status) {
402         case LyXText::NEED_MORE_REFRESH:
403         {
404                 long y = max(text->refresh_y - long(first), 0L);
405                 
406                 DrawFromTo(y, owner.height());
407                 text->refresh_y = 0;
408                 text->status = LyXText::UNCHANGED;
409                 expose(0, y,
410                        owner.workWidth(), owner.height() - y);
411         }
412         break;
413         case LyXText::NEED_VERY_LITTLE_REFRESH:
414         {
415                 /* ok I will update the current cursor row */
416                 DrawOneRow(text->refresh_row, text->refresh_y);
417                 text->status = LyXText::UNCHANGED;
418                 expose(0, text->refresh_y - first,
419                        owner.workWidth(), text->refresh_row->height);
420         }
421         break;
422         case LyXText::UNCHANGED:
423                 // Nothing needs done
424                 break;
425         }
426 #else
427         if (text->status == LyXText::NEED_MORE_REFRESH
428             || screen_refresh_y > -1 ) {
429                 long y = 0;
430                 if (screen_refresh_y > -1
431                     && screen_refresh_y < text->refresh_y)
432                         y = screen_refresh_y;
433                 else
434                         y = text->refresh_y;
435                 
436                 //if (y < first) y = first;
437                 y = max(y, long(first));
438                 
439                 DrawFromTo(y - first, owner.height());
440                 text->refresh_y = 0;
441                 text->status = LyXText::UNCHANGED;
442                 screen_refresh_y = -1;
443                 expose(0, y - first,
444                        owner.workWidth(), owner.height() - (y - first));
445         } else if (text->status == LyXText::NEED_VERY_LITTLE_REFRESH) {
446                 /* ok I will update the current cursor row */
447                 long y = text->refresh_y;
448                 DrawOneRow(text->refresh_row, y);
449                 text->status = LyXText::UNCHANGED;
450                 expose(0, text->refresh_y - first,
451                        owner.workWidth(), text->refresh_row->height);
452         }
453 #endif
454 }
455
456
457 #if 0
458 void LyXScreen::SmallUpdate()
459 {
460 #if 1
461         Update();
462 #else
463         if (text->status == LyXText::NEED_MORE_REFRESH) {
464                 /* ok I will update till the current cursor row */
465                 Row * row = text->refresh_row;
466                 long y = text->refresh_y;
467                 long y2 = y;
468       
469                 if (y > long(text->cursor.y)) {
470                         Update();
471                         return;
472                 }
473          
474                 while (row
475                        && row != text->cursor.row
476                        && y < long(first + owner.height())) {
477                         DrawOneRow(row, y);
478                         row = row->next;
479                 }
480       
481                 DrawOneRow(row, y);
482                 screen_refresh_y = y;
483                 screen_refresh_row = row->next;
484                 text->status = LyXText::UNCHANGED;
485                 // Is the right regin exposed?
486                 expose(0, y2 - first,
487                        owner.workWidth(), y - y2);
488         } else if (text->status == LyXText::NEED_VERY_LITTLE_REFRESH) {
489                 /* ok I will update the current cursor row */
490                 long y = text->refresh_y;
491                 DrawOneRow(text->refresh_row, y);
492                 text->status = LyXText::UNCHANGED;
493                 expose(0, text->refresh_y - first,
494                        owner.workWidth(), text->refresh_row->height);
495         }
496 #endif
497 }
498 #endif
499
500
501 void LyXScreen::ToggleSelection(bool kill_selection)
502 {
503         /* only if there is a selection */ 
504         if (!text->selection) return;
505
506         //long top = text->sel_start_cursor.y
507         //      - text->sel_start_cursor.row->baseline;
508         //long bottom = text->sel_end_cursor.y
509         //      - text->sel_end_cursor.row->baseline 
510         //      + text->sel_end_cursor.row->height;
511
512         //top = max(top, first);
513         //bottom = max(bottom, first);
514         
515         //bottom = min(max(bottom, first), first + owner.height());
516         //top = min(max(top, first), first + owner.height());
517         long bottom = min(max(text->sel_end_cursor.y
518                               - text->sel_end_cursor.row->baseline
519                               + text->sel_end_cursor.row->height, first),
520                           first + owner.height());
521         long top = min(max(text->sel_start_cursor.y
522                            - text->sel_start_cursor.row->baseline, first),
523                        first + owner.height());
524
525         if (kill_selection)
526                 text->selection = 0;
527         DrawFromTo(top - first, bottom - first);
528         expose(0, top - first,
529                owner.workWidth(),
530                bottom - first - (top - first));
531 }
532   
533    
534 void LyXScreen::ToggleToggle()
535 {
536         if (text->toggle_cursor.par == text->toggle_end_cursor.par
537             && text->toggle_cursor.pos == text->toggle_end_cursor.pos)
538                 return;
539         
540         long top = text->toggle_cursor.y
541                 - text->toggle_cursor.row->baseline;
542         long bottom = text->toggle_end_cursor.y
543                 - text->toggle_end_cursor.row->baseline 
544                 + text->toggle_end_cursor.row->height;
545         
546         //top = max(top, first);
547         //bottom = max(bottom, first);
548         typedef unsigned long ulong;
549         
550         bottom = min(max(ulong(bottom), first), first + owner.height());
551         top = min(max(ulong(top), first), first + owner.height());
552         
553         DrawFromTo(top - first, bottom - first);
554         expose(0, top - first, owner.workWidth(),
555                bottom - first - (top - first));
556 }