]> git.lyx.org Git - lyx.git/blob - src/screen.C
16b64314e270135cab42841a2db5ed0c20261fb8
[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(owner.owner(), y, 0, 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
122             && y - row->height() <= long(owner.height())) {
123                 // ok there is something visible
124                 text->GetVisibleRow(owner.owner(), y, 0, row, y + first);
125         }
126 }
127
128
129 /* draws the screen, starting with textposition y. uses as much already
130 * printed pixels as possible */
131 void LyXScreen::Draw(unsigned long y)
132 {
133         if (cursor_visible) HideCursor();
134
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                     owner.owner()->buffer()->params.language_info
190                     || text->real_current_font.isVisibleRightToLeft()
191                     != owner.owner()->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
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         y2 = max(y2, y1);
235         
236         if (cursor_pixmap){
237                 XFreePixmap(fl_display, cursor_pixmap);
238                 cursor_pixmap = 0;
239         }
240
241         if (y2 > 0 && y1 < 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 unsigned 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                         newtop = min(newtop, long(first));
354                 }
355         }
356
357         newtop = max(newtop, 0L);
358
359         return newtop;
360 }
361
362
363 /* scrolls the screen so that the cursor is visible, if necessary.
364 * returns true if a change was made, otherwise false */ 
365 bool LyXScreen::FitCursor()
366 {
367         // Is a change necessary?
368         unsigned long newtop = TopCursorVisible();
369         bool result = (newtop != first);
370         if (result)
371                 Draw(newtop);
372         return result;
373 }
374
375    
376 void LyXScreen::Update()
377 {
378         switch(text->status) {
379         case LyXText::NEED_MORE_REFRESH:
380         {
381                 long y = max(text->refresh_y - long(first), 0L);
382                 
383                 DrawFromTo(y, owner.height());
384                 text->refresh_y = 0;
385                 text->status = LyXText::UNCHANGED;
386                 expose(0, y,
387                        owner.workWidth(), owner.height() - y);
388         }
389         break;
390         case LyXText::NEED_VERY_LITTLE_REFRESH:
391         {
392                 // ok I will update the current cursor row
393                 DrawOneRow(text->refresh_row, text->refresh_y);
394                 text->status = LyXText::UNCHANGED;
395                 expose(0, text->refresh_y - first,
396                        owner.workWidth(), text->refresh_row->height());
397         }
398         break;
399         case LyXText::UNCHANGED:
400                 // Nothing needs done
401                 break;
402         }
403 }
404
405
406 void LyXScreen::ToggleSelection(bool kill_selection)
407 {
408         // only if there is a selection
409         if (!text->selection) return;
410
411         long bottom = min(max(text->sel_end_cursor.y()
412                               - text->sel_end_cursor.row()->baseline()
413                               + text->sel_end_cursor.row()->height(), first),
414                           first + owner.height());
415         long top = min(max(text->sel_start_cursor.y()
416                            - text->sel_start_cursor.row()->baseline(), first),
417                        first + owner.height());
418
419         if (kill_selection)
420                 text->selection = 0;
421         DrawFromTo(top - first, bottom - first);
422         expose(0, top - first,
423                owner.workWidth(),
424                bottom - first - (top - first));
425 }
426   
427    
428 void LyXScreen::ToggleToggle()
429 {
430         if (text->toggle_cursor.par() == text->toggle_end_cursor.par()
431             && text->toggle_cursor.pos() == text->toggle_end_cursor.pos())
432                 return;
433         
434         long top = text->toggle_cursor.y()
435                 - text->toggle_cursor.row()->baseline();
436         long bottom = text->toggle_end_cursor.y()
437                 - text->toggle_end_cursor.row()->baseline() 
438                 + text->toggle_end_cursor.row()->height();
439         
440         typedef unsigned long ulong;
441         
442         bottom = min(max(ulong(bottom), first), first + owner.height());
443         top = min(max(ulong(top), first), first + owner.height());
444         
445         DrawFromTo(top - first, bottom - first);
446         expose(0, top - first, owner.workWidth(),
447                bottom - first - (top - first));
448 }