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