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