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