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