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