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