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