]> git.lyx.org Git - lyx.git/blob - src/screen.C
6b61b9228e9f08331b7128b240aeb51f6f89747a
[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 void LyXScreen::DrawOneRow(Row * row, long y_text)
118 {
119         long y = y_text - first;
120       
121         if (y + row->height > 0 && y - row->height <= long(owner.height())) {
122                 // ok there is something visible
123                 text->GetVisibleRow(y, row, y + first);
124         }
125 }
126
127
128 /* draws the screen, starting with textposition y. uses as much already
129 * printed pixels as possible */
130 void LyXScreen::Draw(unsigned long y)
131 {
132         if (cursor_visible) HideCursor();
133
134         //if (y < 0) y = 0;
135         unsigned long old_first = first;
136         first = y;
137
138         /* is any optimiziation possible? */ 
139         if ((y - old_first) < owner.height()
140             && (old_first - y) < owner.height()) {
141                 if (first < old_first) {
142                         DrawFromTo(0, old_first - first);
143                         XCopyArea (fl_display,
144                                    owner.getWin(),
145                                    owner.getWin(),
146                                    gc_copy,
147                                    owner.xpos(),
148                                    owner.ypos(),
149                                    owner.workWidth(),
150                                    owner.height() - old_first + first,
151                                    owner.xpos(),
152                                    owner.ypos() + old_first - first
153                                 );
154                         // expose the area drawn
155                         expose(0, 0,
156                                owner.workWidth(),
157                                old_first - first);
158                 } else  {
159                         DrawFromTo(
160                                 owner.height() + old_first - first,
161                                    owner.height());
162                         XCopyArea (fl_display,
163                                    owner.getWin(),
164                                    owner.getWin(),
165                                    gc_copy,
166                                    owner.xpos(),
167                                    owner.ypos() + first - old_first,
168                                    owner.workWidth(),
169                                    owner.height() + old_first - first,
170                                    owner.xpos(),
171                                    owner.ypos());
172                         // expose the area drawn
173                         expose(0, owner.height() + old_first - first,
174                                owner.workWidth(), first - old_first);
175                 }
176         } else {
177                 /* make a dumb new-draw */ 
178                 DrawFromTo(0, owner.height());
179                 expose(0, 0, owner.workWidth(), owner.height());
180         }
181 }
182
183
184 void LyXScreen::ShowCursor()
185 {
186         if (!cursor_visible) {
187                 Cursor_Shape shape = BAR_SHAPE;
188                 if (text->real_current_font.language() !=
189                     text->buffer->params.language_info
190                     || text->real_current_font.isVisibleRightToLeft()
191                     != text->buffer->params.language_info->RightToLeft)
192                         shape = (text->real_current_font.isVisibleRightToLeft())
193                                 ? REVERSED_L_SHAPE : L_SHAPE;
194                 ShowManualCursor(text->cursor.x, text->cursor.y,
195                                  lyxfont::maxAscent(text->real_current_font),
196                                  lyxfont::maxDescent(text->real_current_font),
197                                  shape);
198         }
199 }
200
201
202 /* returns true if first has changed, otherwise false */ 
203 bool LyXScreen::FitManualCursor(long /*x*/, long y, int asc, int desc)
204 {
205         long newtop = first;
206   
207         if (y + desc - first >= owner.height())
208                 newtop = y - 3 * owner.height() / 4;   /* the scroll region must be so big!! */
209         else if (y - asc < long(first)
210                 && first > 0) {
211                 newtop = y - owner.height() / 4;
212         }
213         //if (newtop < 0)
214         newtop = max(newtop, 0L); // can newtop ever be < 0? (Lgb)
215   
216         if (newtop != long(first)) {
217                 Draw(newtop);
218                 first = newtop;
219                 return true;
220         }
221         return false;
222 }
223
224
225 void LyXScreen::ShowManualCursor(long x, long y, int asc, int desc,
226                                   Cursor_Shape shape)
227 {
228         unsigned long y1 = max(y - first - asc, 0UL);
229         typedef unsigned long ulong;
230         
231         unsigned long y2 = min(y - first + desc, ulong(owner.height()));
232
233         // Secure against very strange situations
234         //if (y2 < y1) y2 = y1;
235         y2 = max(y2, y1);
236         
237         if (cursor_pixmap){
238                 XFreePixmap(fl_display, cursor_pixmap);
239                 cursor_pixmap = 0;
240         }
241
242         if (y2 > 0 && y1 < owner.height()) {
243                 cursor_pixmap_h = y2 - y1 + 1;
244                 cursor_pixmap_y = y1;
245
246                 switch(shape) {
247                 case BAR_SHAPE:
248                         cursor_pixmap_w = 1;
249                         cursor_pixmap_x = x;
250                         break;
251                 case L_SHAPE:
252                         cursor_pixmap_w = cursor_pixmap_h/3;
253                         cursor_pixmap_x = x;
254                         break;
255                 case REVERSED_L_SHAPE:
256                         cursor_pixmap_w = cursor_pixmap_h/3;
257                         cursor_pixmap_x = x - cursor_pixmap_w + 1;
258                         break;
259                 }
260
261                 cursor_pixmap = 
262                         XCreatePixmap (fl_display,
263                                        fl_root,
264                                        cursor_pixmap_w,
265                                        cursor_pixmap_h,
266                                        fl_get_visual_depth());
267                 XCopyArea (fl_display,
268                            owner.getWin(),
269                            cursor_pixmap,
270                            gc_copy,
271                            owner.xpos() + cursor_pixmap_x,
272                            owner.ypos() + cursor_pixmap_y,
273                            cursor_pixmap_w,
274                            cursor_pixmap_h,
275                            0, 0);
276                 XDrawLine(fl_display,
277                           owner.getWin(),
278                           gc_copy,
279                           x + owner.xpos(),
280                           y1 + owner.ypos(),
281                           x + owner.xpos(),
282                           y2 + owner.ypos());
283                 switch(shape) {
284                 case BAR_SHAPE:
285                         break;
286                 case L_SHAPE:
287                 case REVERSED_L_SHAPE:
288                         int rectangle_h = (cursor_pixmap_h+10)/20;
289                         XFillRectangle(fl_display,
290                                        owner.getWin(),
291                                        gc_copy,
292                                        cursor_pixmap_x + owner.xpos(),
293                                        y2 - rectangle_h + 1 + owner.ypos(),
294                                        cursor_pixmap_w - 1, rectangle_h);
295                         break;
296                 }
297
298         }
299         cursor_visible = true;
300 }
301
302
303 void LyXScreen::HideCursor()
304 {
305         if (!cursor_visible) return;
306
307         if (cursor_pixmap){
308                 XCopyArea (fl_display, 
309                            cursor_pixmap,
310                            owner.getWin(),
311                            gc_copy,
312                            0, 0, 
313                            cursor_pixmap_w, cursor_pixmap_h,
314                            cursor_pixmap_x + owner.xpos(),
315                            cursor_pixmap_y + owner.ypos());
316         }
317         cursor_visible = false;
318 }
319
320
321 void LyXScreen::CursorToggle()
322 {
323         if (cursor_visible)
324                 HideCursor();
325         else
326                 ShowCursor();
327 }
328
329
330 /* returns a new top so that the cursor is visible */ 
331 unsigned long LyXScreen::TopCursorVisible()
332 {
333         long newtop = first;
334
335         if (text->cursor.y
336             - text->cursor.row->baseline
337             + text->cursor.row->height
338             - first >= owner.height()) {
339                 if (text->cursor.row->height < owner.height()
340                     && text->cursor.row->height > owner.height() / 4)
341                         newtop = text->cursor.y
342                                 + text->cursor.row->height
343                                 - text->cursor.row->baseline - owner.height();
344                 else
345                         newtop = text->cursor.y
346                                 - 3 * owner.height() / 4;   /* the scroll region must be so big!! */
347         } else if (text->cursor.y - text->cursor.row->baseline < first
348                    && first > 0) {
349                 if (text->cursor.row->height < owner.height()
350                     && text->cursor.row->height > owner.height() / 4)
351                         newtop = text->cursor.y - text->cursor.row->baseline;
352                 else {
353                         newtop = text->cursor.y - owner.height() / 4;
354                         //if (newtop > long(first))
355                         newtop = min(newtop, long(first));
356                 }
357         }
358         //if (newtop < 0)
359         //      newtop = 0;
360         newtop = max(newtop, 0L);
361
362         return newtop;
363 }
364
365
366 /* scrolls the screen so that the cursor is visible, if necessary.
367 * returns true if a change was made, otherwise false */ 
368 bool LyXScreen::FitCursor()
369 {
370         // Is a change necessary?
371         unsigned long newtop = TopCursorVisible();
372         bool result = (newtop != first);
373         if (result)
374                 Draw(newtop);
375         return result;
376 }
377
378    
379 void LyXScreen::Update()
380 {
381         switch(text->status) {
382         case LyXText::NEED_MORE_REFRESH:
383         {
384                 long y = max(text->refresh_y - long(first), 0L);
385                 
386                 DrawFromTo(y, owner.height());
387                 text->refresh_y = 0;
388                 text->status = LyXText::UNCHANGED;
389                 expose(0, y,
390                        owner.workWidth(), owner.height() - y);
391         }
392         break;
393         case LyXText::NEED_VERY_LITTLE_REFRESH:
394         {
395                 /* ok I will update the current cursor row */
396                 DrawOneRow(text->refresh_row, text->refresh_y);
397                 text->status = LyXText::UNCHANGED;
398                 expose(0, text->refresh_y - first,
399                        owner.workWidth(), text->refresh_row->height);
400         }
401         break;
402         case LyXText::UNCHANGED:
403                 // Nothing needs done
404                 break;
405         }
406 }
407
408
409 void LyXScreen::ToggleSelection(bool kill_selection)
410 {
411         /* only if there is a selection */ 
412         if (!text->selection) return;
413
414         //long top = text->sel_start_cursor.y
415         //      - text->sel_start_cursor.row->baseline;
416         //long bottom = text->sel_end_cursor.y
417         //      - text->sel_end_cursor.row->baseline 
418         //      + text->sel_end_cursor.row->height;
419
420         //top = max(top, first);
421         //bottom = max(bottom, first);
422         
423         //bottom = min(max(bottom, first), first + owner.height());
424         //top = min(max(top, first), first + owner.height());
425         long bottom = min(max(text->sel_end_cursor.y
426                               - text->sel_end_cursor.row->baseline
427                               + text->sel_end_cursor.row->height, first),
428                           first + owner.height());
429         long top = min(max(text->sel_start_cursor.y
430                            - text->sel_start_cursor.row->baseline, first),
431                        first + owner.height());
432
433         if (kill_selection)
434                 text->selection = 0;
435         DrawFromTo(top - first, bottom - first);
436         expose(0, top - first,
437                owner.workWidth(),
438                bottom - first - (top - first));
439 }
440   
441    
442 void LyXScreen::ToggleToggle()
443 {
444         if (text->toggle_cursor.par == text->toggle_end_cursor.par
445             && text->toggle_cursor.pos == text->toggle_end_cursor.pos)
446                 return;
447         
448         long top = text->toggle_cursor.y
449                 - text->toggle_cursor.row->baseline;
450         long bottom = text->toggle_end_cursor.y
451                 - text->toggle_end_cursor.row->baseline 
452                 + text->toggle_end_cursor.row->height;
453         
454         //top = max(top, first);
455         //bottom = max(bottom, first);
456         typedef unsigned long ulong;
457         
458         bottom = min(max(ulong(bottom), first), first + owner.height());
459         top = min(max(ulong(top), first), first + owner.height());
460         
461         DrawFromTo(top - first, bottom - first);
462         expose(0, top - first, owner.workWidth(),
463                bottom - first - (top - first));
464 }