]> git.lyx.org Git - lyx.git/blob - src/WorkArea.C
8483620d142be81c6be8a719347d19d3e27e8352
[lyx.git] / src / WorkArea.C
1 /* This file is part of
2  * ======================================================
3  * 
4  *           LyX, The Document Processor
5  *        
6  *           Copyright 1995 Matthias Ettrich
7  *           Copyright 1995-2000 The LyX Team.
8  *
9  * ====================================================== */
10
11 #include <config.h>
12 #include <cmath>
13 #include <cctype>
14 #ifdef __GNUG__
15 #pragma implementation
16 #endif
17
18 #include "WorkArea.h"
19 #include "debug.h"
20 #include "support/lstrings.h"
21 #include "LyXView.h"
22 #include "lyxfunc.h"
23
24 #if FL_REVISION < 89 || (FL_REVISION == 89 && FL_FIXLEVEL < 5)
25 #include "lyxlookup.h"
26 #endif
27
28 using std::endl;
29
30 FL_OBJECT * figinset_canvas;
31
32 // needed to make the c++ compiler find the correct version of abs.
33 // This is at least true for g++.
34 //using std::abs;
35
36 namespace {
37
38 //static
39 inline
40 void waitForX()
41 {
42         XSync(fl_get_display(), 0);
43 }
44
45 } // anon namespace
46
47
48 extern "C" {
49 // Just a bunch of C wrappers around static members of WorkArea
50         void C_WorkArea_scroll_cb(FL_OBJECT * ob, long buf)
51         {
52                 WorkArea::scroll_cb(ob, buf);
53         }
54
55         int C_WorkArea_work_area_handler(FL_OBJECT * ob, int event,
56                                            FL_Coord, FL_Coord, 
57                                            int key, void * xev)
58         {
59                 return WorkArea::work_area_handler(ob, event,
60                                                    0, 0, key, xev);
61         }
62 }
63
64
65 WorkArea::WorkArea(int xpos, int ypos, int width, int height)
66         : workareapixmap(0), painter_(*this)
67 {
68         fl_freeze_all_forms();
69
70         figinset_canvas = 0;
71
72         if (lyxerr.debugging(Debug::GUI))
73                 lyxerr << "Creating work area: +"
74                        << xpos << '+' << ypos << ' '
75                        << width << 'x' << height << endl;
76         //
77         FL_OBJECT * obj;
78         int const bw = int(std::abs(float(fl_get_border_width())));
79
80         // We really want to get rid of figinset_canvas.
81         ::figinset_canvas = figinset_canvas = obj =
82                   fl_add_canvas(FL_NORMAL_CANVAS,
83                                 xpos + 1, ypos + 1, 1, 1, "");
84         fl_set_object_boxtype(obj, FL_NO_BOX);
85         fl_set_object_resize(obj, FL_RESIZE_ALL);
86         fl_set_object_gravity(obj, NorthWestGravity, NorthWestGravity);
87         
88         // a box
89         if (lyxerr.debugging(Debug::GUI))
90                 lyxerr << "\tbackground box: +"
91                        << xpos << '+' << ypos << ' '
92                        << width - 15 << 'x' << height << endl;
93         backgroundbox = obj = fl_add_box(FL_BORDER_BOX,
94                                          xpos, ypos,
95                                          width - 15,
96                                          height,"");
97         fl_set_object_resize(obj, FL_RESIZE_ALL);
98         fl_set_object_gravity(obj, NorthWestGravity, SouthEastGravity);
99
100         //
101         // THE SCROLLBAR
102         //
103
104         scrollbar = obj = fl_add_scrollbar(FL_VERT_SCROLLBAR,
105                                            xpos + width - 15,
106                                            ypos, 17, height, "");
107         fl_set_object_boxtype(obj, FL_UP_BOX);
108         fl_set_object_resize(obj, FL_RESIZE_ALL);
109         fl_set_object_gravity(obj, NorthEastGravity, SouthEastGravity);
110         obj->u_vdata = this;
111         fl_set_object_callback(obj, C_WorkArea_scroll_cb, 0);
112
113         ///
114         /// The free object
115
116         // Create the workarea pixmap
117         createPixmap(width - 15 - 2 * bw, height - 2 * bw);
118
119         // We add this object as late as possible to avoit problems
120         // with drawing.
121         if (lyxerr.debugging(Debug::GUI))
122                 lyxerr << "\tfree object: +"
123                        << xpos + bw << '+' << ypos + bw << ' '
124                        << width - 15 - 2 * bw << 'x'
125                        << height - 2 * bw << endl;
126         
127         work_area = obj = fl_add_free(FL_ALL_FREE,
128                                       xpos + bw, ypos + bw,
129                                       width - 15 - 2 * bw, // scrollbarwidth
130                                       height - 2 * bw, "",
131                                       C_WorkArea_work_area_handler);
132         obj->wantkey = FL_KEY_ALL;
133         obj->u_vdata = this; /* This is how we pass the WorkArea
134                                 to the work_area_handler. */
135         fl_set_object_boxtype(obj,FL_DOWN_BOX);
136         fl_set_object_resize(obj, FL_RESIZE_ALL);
137         fl_set_object_gravity(obj, NorthWestGravity, SouthEastGravity);
138
139         fl_unfreeze_all_forms();
140 }
141
142
143 WorkArea::~WorkArea()
144 {
145         if (workareapixmap)
146                 XFreePixmap(fl_get_display(), workareapixmap);
147 }
148
149
150 bool WorkArea::belowMouse() const
151 {
152         FL_Coord x, y;
153         unsigned int button;
154         fl_get_mouse(&x, &y, &button);
155         FL_Coord ulx = work_area->form->x + work_area->x;
156         FL_Coord uly = work_area->form->y + work_area->y;
157         FL_Coord w = work_area->w;
158         FL_Coord h = work_area->h;
159         if (x > ulx && y > uly && x < ulx + h && y < uly + w)
160                 return true;
161         return false;
162         
163         
164         //lyxerr << "Mouse: (" << x << ", " << y <<") button = " << button << endl;
165         //lyxerr << "Workarea: (" << work_area->x + work_area->form->x << ", " << work_area->y + work_area->form->y << ", " << work_area->w << ", " << work_area->h << ")" << endl;
166         //lyxerr << "Below mouse: " << work_area->belowmouse << endl;
167         //return work_area->belowmouse;
168 }
169
170
171 void WorkArea::resize(int xpos, int ypos, int width, int height)
172 {
173         fl_freeze_all_forms();
174         
175         int const bw = int(std::abs(float(fl_get_border_width())));
176
177         // a box
178         fl_set_object_geometry(backgroundbox, xpos, ypos, width - 15, height);
179         
180         //
181         // THE SCROLLBAR
182         //
183         fl_set_object_geometry(scrollbar, xpos + width - 15,
184                                ypos, 17, height);
185
186         // Create the workarea pixmap
187         createPixmap(width - 15 - 2 * bw, height - 2 * bw);
188
189         // the free object
190         fl_set_object_geometry(work_area, xpos + bw, ypos + bw,
191                                width - 15 - 2 * bw,
192                                height - 2 * bw);
193
194         fl_unfreeze_all_forms();
195
196 }
197
198
199 void WorkArea::createPixmap(int width, int height)
200 {
201         static int cur_width = -1;
202         static int cur_height = -1;
203
204         if (cur_width == width && cur_height == height && workareapixmap)
205                 return;
206         
207         cur_width = width;
208         cur_height = height;
209
210         if (workareapixmap)
211                 XFreePixmap(fl_get_display(), workareapixmap);
212
213         if (lyxerr.debugging(Debug::GUI))
214                 lyxerr << "Creating pixmap ("
215                        << width << 'x' << height << ")" << endl;
216         
217         workareapixmap = XCreatePixmap(fl_get_display(),
218                                        RootWindow(fl_get_display(), 0),
219                                        width,
220                                        height, 
221                                        fl_get_visual_depth());
222         if (lyxerr.debugging(Debug::GUI))
223                 lyxerr << "\tpixmap=" << workareapixmap << endl;
224 }
225
226
227 void WorkArea::greyOut() const
228 {
229         fl_winset(FL_ObjWin(work_area));
230         fl_rectangle(1, work_area->x, work_area->y,
231                      work_area->w, work_area->h, FL_GRAY63);
232 }
233
234
235 void WorkArea::setFocus() const
236 {
237         fl_set_focus_object(work_area->form, work_area);
238 }
239
240
241 void WorkArea::setScrollbar(double pos, double length_fraction) const
242 {
243         fl_set_scrollbar_value(scrollbar, pos);
244         fl_set_scrollbar_size(scrollbar, scrollbar->h * length_fraction);
245 }
246
247
248 void WorkArea::setScrollbarBounds(double l1, double l2) const
249 {
250         fl_set_scrollbar_bounds(scrollbar, l1, l2);
251 }
252
253
254 void WorkArea::setScrollbarIncrements(double inc) const
255 {
256         fl_set_scrollbar_increment(scrollbar, work_area->h - inc, inc);
257 }
258
259
260 // Callback for scrollbar slider
261 void WorkArea::scroll_cb(FL_OBJECT * ob, long)
262 {
263         WorkArea * area = static_cast<WorkArea*>(ob->u_vdata);
264         // If we really want the accellerating scroll we can do that
265         // from here. IMHO that is a waste of effort since we already
266         // have other ways to move fast around in the document. (Lgb)
267         area->scrollCB(fl_get_scrollbar_value(ob));
268         waitForX();
269 }
270
271
272 bool Lgb_bug_find_hack = false;
273
274 int WorkArea::work_area_handler(FL_OBJECT * ob, int event,
275                                 FL_Coord, FL_Coord ,
276                                 int key, void * xev)
277 {
278         static int x_old = -1;
279         static int y_old = -1;
280         static long scrollbar_value_old = -1;
281         
282         XEvent * ev = static_cast<XEvent*>(xev);
283         WorkArea * area = static_cast<WorkArea*>(ob->u_vdata);
284
285         if (!area) return 1;
286         
287         switch (event){
288         case FL_DRAW:
289                 if (!area->work_area ||
290                     !area->work_area->form->visible)
291                         return 1;
292                 lyxerr[Debug::GUI] << "Workarea event: DRAW" << endl;
293                 area->createPixmap(area->workWidth(), area->height());
294                 Lgb_bug_find_hack = true;
295                 area->workAreaExpose();
296                 Lgb_bug_find_hack = false;
297                 break;
298         case FL_PUSH:
299                 if (!ev || ev->xbutton.button == 0) break;
300                 // Should really have used xbutton.state
301                 lyxerr[Debug::GUI] << "Workarea event: PUSH" << endl;
302                 area->workAreaButtonPress(ev->xbutton.x - ob->x,
303                                           ev->xbutton.y - ob->y,
304                                           ev->xbutton.button);
305                 //area->workAreaKeyPress(XK_Pointer_Button1, ev->xbutton.state);
306                 break; 
307         case FL_RELEASE:
308                 if (!ev || ev->xbutton.button == 0) break;
309                 // Should really have used xbutton.state
310                 lyxerr[Debug::GUI] << "Workarea event: RELEASE" << endl;
311                 area->workAreaButtonRelease(ev->xbutton.x - ob->x,
312                                       ev->xbutton.y - ob->y,
313                                       ev->xbutton.button);
314                 break;
315 #if FL_REVISION < 89
316         case FL_MOUSE:
317 #else
318         case FL_DRAG:
319 #endif
320                 if (!ev || ! area->scrollbar) break;
321                 if (ev->xmotion.x != x_old ||
322                     ev->xmotion.y != y_old ||
323                     fl_get_scrollbar_value(area->scrollbar) != scrollbar_value_old
324                         ) {
325                         lyxerr[Debug::GUI] << "Workarea event: MOUSE" << endl;
326                         area->workAreaMotionNotify(ev->xmotion.x - ob->x,
327                                              ev->xmotion.y - ob->y,
328                                              ev->xbutton.state);
329                 }
330                 break;
331 #if FL_REVISION < 89
332         case FL_KEYBOARD:
333 #else
334         case FL_KEYPRESS:
335 #endif
336         {
337                 lyxerr[Debug::KEY] << "Workarea event: KEYBOARD" << endl;
338                 
339                 KeySym keysym = 0;
340                 char dummy[1];
341                 XKeyEvent * xke = reinterpret_cast<XKeyEvent *>(ev);
342 #if FL_REVISION < 89 || (FL_REVISION == 89 && FL_FIXLEVEL < 5)
343                 // XForms < 0.89.5 does not have compose support
344                 // so we are using our own compose support
345                 LyXLookupString(ev, dummy, 1, &keysym);
346 #else
347                 XLookupString(xke, dummy, 1, &keysym, 0);
348 #endif
349                 if (lyxerr.debugging(Debug::KEY)) {
350                         char const * tmp = XKeysymToString(key);
351                         char const * tmp2 = XKeysymToString(keysym);
352                         string const stm = (tmp ? tmp : "");
353                         string const stm2 = (tmp2 ? tmp2 : "");
354                         
355                         lyxerr << "WorkArea: Key is `" << stm << "' ["
356                                << key << "]" << endl;
357                         lyxerr << "WorkArea: Keysym is `" << stm2 << "' ["
358                                << keysym << "]" << endl;
359                 }
360
361 #if FL_REVISION < 89
362                 if (keysym == NoSymbol) {
363                         lyxerr[Debug::KEY]
364                                 << "Empty kdb action (probably composing)"
365                                 << endl;
366                         break;
367                 }
368                 KeySym ret_key = keysym;
369 #else
370                 // Note that we need this handling because of a bug
371                 // in XForms 0.89, if this bug is resolved in the way I hope
372                 // we can just use the keysym directly with out looking
373                 // at key at all. (Lgb)
374                 KeySym ret_key = 0;
375                 if (!key) {
376                         // We migth have to add more keysyms here also,
377                         // we will do that as the issues arise. (Lgb)
378                         if (keysym == XK_space) {
379                                 ret_key = keysym;
380                                 lyxerr[Debug::KEY] << "Using keysym [A]"
381                                                    << endl;
382                         } else
383                                 break;
384                 } else {
385                         // It seems that this was a bit optimistic...
386                         // With this hacking things seems to be better (Lgb)
387                         //if (!iscntrl(key)) {
388                         //      ret_key = key;
389                         //      lyxerr[Debug::KEY]
390                         //              << "Using key [B]\n"
391                         //              << "Uchar["
392                         //              << static_cast<unsigned char>(key)
393                         //              << endl;
394                         //} else {
395                                 ret_key = (keysym ? keysym : key);
396                                 lyxerr[Debug::KEY] << "Using keysym [B]"
397                                                    << endl;
398                                 //}
399                 }
400                 
401 #endif  
402                 unsigned int const ret_state = xke->state;
403
404                 // If you have a better way to handle "wild-output" of
405                 // characters after the key has been released than the one
406                 // below, please contact me. (Lgb)
407                 static Time last_time_pressed = 0;
408                 static unsigned int last_key_pressed = 0;
409                 static unsigned int last_state_pressed = 0;
410                 lyxerr[Debug::KEY] << "Workarea Diff: "
411                                    << xke->time - last_time_pressed
412                                    << endl;
413                 if (xke->time - last_time_pressed < 25 // should perhaps be tunable
414                     && ret_state == last_state_pressed
415                     && xke->keycode == last_key_pressed) {
416                         lyxerr[Debug::KEY]
417                                 << "Workarea: Purging X events." << endl;
418                         //lyxerr << "Workarea Events: "
419                         //       << XEventsQueued(fl_get_display(), QueuedAlready)
420                         //       << endl;
421                         if (XEventsQueued(fl_get_display(), QueuedAlready) > 0)
422                                 XSync(fl_get_display(), 1);
423                         // This purge make f.ex. scrolling stop immidiatly when
424                         // releasing the PageDown button. The question is if
425                         // this purging of XEvents can cause any harm...
426                         // after some testing I can see no problems, but
427                         // I'd like other reports too.
428                         break;
429                 }
430                 last_time_pressed = xke->time;
431                 last_key_pressed = xke->keycode;
432                 last_state_pressed = ret_state;
433                 
434                 area->workAreaKeyPress(ret_key, ret_state);
435         }
436         break;
437
438 #if FL_REVISION >= 89
439         case FL_KEYRELEASE:
440                 lyxerr << "Workarea event: KEYRELEASE" << endl;
441                 break;
442 #endif
443
444         case FL_FOCUS:
445                 lyxerr[Debug::GUI] << "Workarea event: FOCUS" << endl;
446                 area->workAreaFocus();
447                 break;
448         case FL_UNFOCUS:
449                 lyxerr[Debug::GUI] << "Workarea event: UNFOCUS" << endl;
450                 area->workAreaUnfocus();
451                 break;
452         case FL_ENTER:
453                 lyxerr[Debug::GUI] << "Workarea event: ENTER" << endl;
454                 area->workAreaEnter();
455                 break;
456         case FL_LEAVE:
457                 lyxerr[Debug::GUI] << "Workarea event: LEAVE" << endl;
458                 area->workAreaLeave();
459                 break;
460         case FL_DBLCLICK:
461                 if (!ev) break;
462                 lyxerr[Debug::GUI] << "Workarea event: DBLCLICK" << endl;
463                 area->workAreaDoubleClick(ev->xbutton.x - ob->x,
464                                           ev->xbutton.y - ob->y,
465                                           ev->xbutton.button);
466                 break;
467         case FL_TRPLCLICK:
468                 if (!ev) break;
469                 lyxerr[Debug::GUI] << "Workarea event: TRPLCLICK" << endl;
470                 area->workAreaTripleClick(ev->xbutton.x - ob->x,
471                                           ev->xbutton.y - ob->y,
472                                           ev->xbutton.button);
473                 break;
474         case FL_OTHER:
475                 if (!ev) break;
476                         lyxerr[Debug::GUI] << "Workarea event: OTHER" << endl;
477
478                 break;
479         }
480   
481         return 1;
482 }
483
484
485 static string clipboard_selection;
486 static bool clipboard_read = false;
487
488 extern "C" {
489         static
490 int request_clipboard_cb(FL_OBJECT * /*ob*/, long /*type*/,
491                         void const * data, long size) 
492 {
493         clipboard_selection.erase();
494
495         if (size > 0)
496                 clipboard_selection.reserve(size);
497         for (int i = 0; i < size; ++i)
498                 clipboard_selection += static_cast<char const *>(data)[i];
499         clipboard_read = true;
500         return 0;
501 }
502 } // extern "C"
503
504
505 string const WorkArea::getClipboard() const 
506 {
507         clipboard_read = false;
508         
509         if (fl_request_clipboard(work_area, 0, request_clipboard_cb) == -1)
510                 return string();
511
512         XEvent ev;
513         
514         while (!clipboard_read) {
515                 if (fl_check_forms() == FL_EVENT) {
516                         lyxerr << "LyX: This shouldn't happen..." << endl;
517                         fl_XNextEvent(&ev);
518                 }
519         }
520         return clipboard_selection;
521 }
522
523         
524 void WorkArea::putClipboard(string const & s) const
525 {
526         static string hold;
527         hold = s;
528         
529         fl_stuff_clipboard(work_area, 0, hold.c_str(), hold.size(), 0);
530 }