2 /******************************************************************************
3 * MODULE : QTMWidget.cpp
4 * DESCRIPTION: QT Texmacs widget class
5 * COPYRIGHT : (C) 2008 Massimiliano Gubinelli and Joris van der Hoeven
6 *******************************************************************************
7 * This software falls under the GNU general public license version 3 or later.
8 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
9 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
10 ******************************************************************************/
14 #include "QTMWidget.hpp"
15 #include "qt_renderer.hpp"
17 #include "qt_utilities.hpp"
18 #include "qt_simple_widget.hpp"
19 #include "converter.hpp"
22 #include "message.hpp"
25 #include "Cairo/cairo_renderer.hpp"
26 #include "Cairo/tm_cairo.hpp"
30 extern Drawable
qt_x11Handle(const QPaintDevice
*pd
);
31 extern const QX11Info
*qt_x11Info(const QPaintDevice
*pd
);
32 #undef KeyPress // conflict between QEvent::KeyPrees and X11 defintion
41 QSet
<QTMWidget
*> QTMWidget::all_widgets
;
44 hashmap
<int,string
> qtkeymap (0);
45 hashmap
<int,string
> qtdeadmap (0);
48 scale (QPoint
& point
) {
49 point
.rx() *= PIXEL
; point
.ry() *= -PIXEL
;
53 map (int code
, string name
) {
54 qtkeymap(code
) = name
;
57 deadmap (int code
, string name
) {
58 qtdeadmap(code
) = name
;
63 map (Qt::Key_Space
, "space");
64 map (Qt::Key_Return
, "return");
65 map (Qt::Key_Tab
, "tab");
66 map (Qt::Key_Backspace
, "backspace");
67 map (Qt::Key_Enter
, "enter");
68 map (Qt::Key_Escape
, "escape");
69 map (Qt::Key_Backspace
, "backspace");
70 map (Qt::Key_Up
, "up" );
71 map (Qt::Key_Down
, "down" );
72 map (Qt::Key_Left
, "left" );
73 map (Qt::Key_Right
, "right" );
74 map (Qt::Key_F1
, "F1" );
75 map (Qt::Key_F2
, "F2" );
76 map (Qt::Key_F3
, "F3" );
77 map (Qt::Key_F4
, "F4" );
78 map (Qt::Key_F5
, "F5" );
79 map (Qt::Key_F6
, "F6" );
80 map (Qt::Key_F7
, "F7" );
81 map (Qt::Key_F8
, "F8" );
82 map (Qt::Key_F9
, "F9" );
83 map (Qt::Key_F10
, "F10" );
84 map (Qt::Key_F11
, "F11" );
85 map (Qt::Key_F12
, "F12" );
86 map (Qt::Key_F13
, "F13" );
87 map (Qt::Key_F14
, "F14" );
88 map (Qt::Key_F15
, "F15" );
89 map (Qt::Key_F16
, "F16" );
90 map (Qt::Key_F17
, "F17" );
91 map (Qt::Key_F18
, "F18" );
92 map (Qt::Key_F19
, "F19" );
93 map (Qt::Key_F20
, "F20" );
94 map (Qt::Key_F21
, "F21" );
95 map (Qt::Key_F22
, "F22" );
96 map (Qt::Key_F23
, "F23" );
97 map (Qt::Key_F24
, "F24" );
98 map (Qt::Key_F25
, "F25" );
99 map (Qt::Key_F26
, "F26" );
100 map (Qt::Key_F27
, "F27" );
101 map (Qt::Key_F28
, "F28" );
102 map (Qt::Key_F29
, "F29" );
103 map (Qt::Key_F30
, "F30" );
104 map (Qt::Key_F31
, "F31" );
105 map (Qt::Key_F32
, "F32" );
106 map (Qt::Key_F33
, "F33" );
107 map (Qt::Key_F34
, "F34" );
108 map (Qt::Key_F35
, "F35" );
109 map (Qt::Key_Insert
, "insert" );
110 map (Qt::Key_Delete
, "delete" );
111 map (Qt::Key_Home
, "home" );
112 map (Qt::Key_End
, "end" );
113 map (Qt::Key_PageUp
, "pageup" );
114 map (Qt::Key_PageDown
, "pagedown" );
115 map (Qt::Key_ScrollLock
, "scrolllock" );
116 map (Qt::Key_Pause
, "pause" );
117 map (Qt::Key_SysReq
, "sysreq" );
118 map (Qt::Key_Stop
, "stop" );
119 map (Qt::Key_Menu
, "menu" );
120 map (Qt::Key_Print
, "print" );
121 map (Qt::Key_Select
, "select" );
122 map (Qt::Key_Execute
, "execute" );
123 map (Qt::Key_Help
, "help" );
125 deadmap (Qt::Key_Dead_Acute
, "acute");
126 deadmap (Qt::Key_Dead_Grave
, "grave");
127 deadmap (Qt::Key_Dead_Diaeresis
, "umlaut");
128 deadmap (Qt::Key_Dead_Circumflex
, "hat");
129 deadmap (Qt::Key_Dead_Tilde
, "tilde");
131 // map (0x0003 , "K-enter");
132 // map (Qt::Key_Begin , "begin" );
133 // map (Qt::Key_PrintScreen , "printscreen" );
134 // map (Qt::Key_Break , "break" );
135 // map (Qt::Key_User , "user" );
136 // map (Qt::Key_System , "system" );
137 // map (Qt::Key_Reset , "reset" );
138 // map (Qt::Key_ClearLine , "clear" );
139 // map (Qt::Key_ClearDisplay, "cleardisplay" );
140 // map (Qt::Key_InsertLine , "insertline" );
141 // map (Qt::Key_DeleteLine , "deleteline" );
142 // map (Qt::Key_InsertChar , "insert" );
143 // map (Qt::Key_DeleteChar , "delete" );
144 // map (Qt::Key_Prev , "prev" );
145 // map (Qt::Key_Next , "next" );
146 // map (Qt::Key_Undo , "undo" );
147 // map (Qt::Key_Redo , "redo" );
148 // map (Qt::Key_Find , "find" );
149 // map (Qt::Key_ModeSwitchFunctionKey, "modeswitch" );
154 QTMWidget::QTMWidget (simple_widget_rep
*_wid
)
155 : QTMScrollView (), backingPixmap() {
156 setObjectName("A QTMWidget");
157 setProperty ("texmacs_widget", QVariant::fromValue ((void*) _wid
));
158 QAbstractScrollArea::viewport()->setMouseTracking (true);
159 QAbstractScrollArea::viewport()->setFocusPolicy (Qt::StrongFocus
);
160 backing_pos
= origin
;
164 QTMWidget::~QTMWidget () {
168 QTMWidget::invalidate_rect (int x1
, int y1
, int x2
, int y2
) {
169 rectangle r
= rectangle (x1
, y1
, x2
, y2
);
170 // cout << "invalidating " << r << LF;
171 invalid_regions
= invalid_regions
| rectangles (r
);
175 QTMWidget::invalidate_all () {
176 QSize sz
= QAbstractScrollArea::viewport()->size();
177 //cout << "invalidate all " << LF;
178 invalid_regions
= rectangles();
179 invalidate_rect (0, 0, sz
.width(), sz
.height());
184 QTMWidget::getRenderer() {
186 cairo_renderer_rep
*ren
= the_cairo_renderer ();
187 cairo_surface_t
*surf
;
189 //const QX11Info & info = x11Info();//qt_x11Info(this);
190 // Display *dpy = x11Info().display();
191 //backingPixmap = QPixmap(width(),height());
192 //cout << backingPixmap.width() << LF;
193 Display
*dpy
= QX11Info::display();
194 Drawable drawable
= backingPixmap
.handle();
195 Visual
*visual
= (Visual
*)(backingPixmap
.x11Info().visual());
196 surf
= tm_cairo_xlib_surface_create (dpy
, drawable
, visual
,
197 backingPixmap
.width (), backingPixmap
.height ());
198 #elif defined(Q_WS_MAC)
199 surf
= tm_cairo_quartz_surface_create_for_cg_context (
200 (CGContextRef
)(this->macCGHandle()), width(), height());
202 cairo_t
*ct
= tm_cairo_create (surf
);
204 tm_cairo_surface_destroy (surf
);
205 tm_cairo_destroy (ct
);
207 qt_renderer_rep
* ren
= the_qt_renderer();
208 ren
->begin(&backingPixmap
);
214 QTMWidget::repaint_invalid_regions () {
216 // this function is called by the qt_gui::update method to keep the backing
217 // store in sync and propagate the changes to the surface on screen.
218 // first we check that the backing store geometry is right and then we
219 // request to the texmacs canvas widget to repaint the regions which were
220 // marked invalid. Subsequently, for each succesfully repainted region, we
221 // propagate its contents from the backing store to the onscreen surface.
222 // If repaint has been interrupted we do not propagate the changes and proceed
223 // to mark the region invalid again.
226 // qrgn is to keep track of the area on the sceen which needs to be updated
228 // update backing store origin wrt. TeXmacs document
229 if ( backing_pos
!= origin
) {
231 int dx
= origin
.x() - backing_pos
.x();
232 int dy
= origin
.y() - backing_pos
.y();
233 backing_pos
= origin
;
235 QPixmap
newBackingPixmap (backingPixmap
.size());
236 QPainter
p (&newBackingPixmap
);
237 //newBackingPixmap.fill(Qt::black);
238 p
.drawPixmap(-dx
,-dy
,backingPixmap
);
240 backingPixmap
= newBackingPixmap
;
241 //cout << "SCROLL CONTENTS BY " << dx << " " << dy << LF;
243 QSize sz
= backingPixmap
.size();
246 while (!is_nil(invalid_regions
)) {
247 rectangle r
= invalid_regions
->item
;
248 // rectangle q = rectangle(r->x1+dx,r->y1-dy,r->x2+dx,r->y2-dy);
249 rectangle q
= rectangle(r
->x1
-dx
,r
->y1
-dy
,r
->x2
-dx
,r
->y2
-dy
);
250 invalid
= rectangles (q
, invalid
);
251 //cout << r << " ---> " << q << LF;
252 invalid_regions
= invalid_regions
->next
;
254 invalid_regions
= invalid
&
255 rectangles(rectangle(0,0,
256 sz
.width(),sz
.height())) ;
259 invalidate_rect(0,0,sz
.width(),min(sz
.height(),-dy
));
261 invalidate_rect(0,max(0,sz
.height()-dy
),sz
.width(),sz
.height());
264 invalidate_rect(0,0,min(-dx
,sz
.width()),sz
.height());
266 invalidate_rect(max(0,sz
.width()-dx
),0,sz
.width(),sz
.height());
268 // we cal update now to allow repainint of invalid regions
269 // this cannot be done directly since interpose handler needs
270 // to be run at least once in some situations
271 // (for example when scrolling is initiated by TeXmacs itself)
273 // QAbstractScrollArea::viewport()->scroll(-dx,-dy);
274 // QAbstractScrollArea::viewport()->update();
275 qrgn
+= QRect(QPoint(0,0),sz
);
278 // update backing store size
280 QSize _oldSize
= backingPixmap
.size();
281 QSize _newSize
= QAbstractScrollArea::viewport()->size();
282 if (_newSize
!= _oldSize
) {
283 // cout << "RESIZING BITMAP"<< LF;
284 QPixmap
newBackingPixmap (_newSize
);
285 //QPainter p (&newBackingPixmap);
286 //p.drawPixmap(0,0,backingPixmap);
288 backingPixmap
= newBackingPixmap
;
290 the_gui
-> process_resize(tm_widget(), 0, 0); // FIXME
294 // repaint invalid rectangles
296 rectangles new_regions
;
297 if (!is_nil (invalid_regions
)) {
298 rectangle lub
= least_upper_bound (invalid_regions
);
299 if (area (lub
) < 1.2 * area (invalid_regions
))
300 invalid_regions
= rectangles (lub
);
302 basic_renderer_rep
* ren
= getRenderer();
303 tm_widget()->set_current_renderer(ren
);
305 SI ox
= -backing_pos
.x()*PIXEL
;
306 SI oy
= backing_pos
.y()*PIXEL
;
308 rectangles rects
= invalid_regions
;
309 invalid_regions
= rectangles();
311 while (!is_nil (rects
)) {
312 rectangle r
= copy (rects
->item
);
313 rectangle r0
= rects
->item
;
314 QRect qr
= QRect(r0
->x1
, r0
->y1
, r0
->x2
- r0
->x1
, r0
->y2
- r0
->y1
);
315 //cout << "repainting " << r0 << "\n";
316 ren
->set_origin(ox
,oy
);
317 ren
->encode (r
->x1
, r
->y1
);
318 ren
->encode (r
->x2
, r
->y2
);
319 ren
->set_clipping (r
->x1
, r
->y2
, r
->x2
, r
->y1
);
320 tm_widget()->handle_repaint (r
->x1
, r
->y2
, r
->x2
, r
->y1
);
321 if (ren
->interrupted ()) {
322 //cout << "interrupted repainting of " << r0 << "\n";
323 //ren->set_color(green);
324 //ren->line(r->x1, r->y1, r->x2, r->y2);
325 //ren->line(r->x1, r->y2, r->x2, r->y1);
326 invalidate_rect (r0
->x1
, r0
->y1
, r0
->x2
, r0
->y2
);
332 tm_widget()->set_current_renderer(NULL
);
334 } // !is_nil(invalid_regions)
338 // propagate immediatly the changes to the screen
339 QAbstractScrollArea::viewport()->repaint(qrgn
);
344 QTMWidget::scrollContentsBy ( int dx
, int dy
) {
345 QTMScrollView::scrollContentsBy (dx
,dy
);
346 // the_gui::update needs to be run as soon as possible to refresh the status
352 QTMWidget::resizeEvent( QResizeEvent
* event
) {
353 // cout << "QTMWidget::resizeEvent (" << event->size().width()
354 // << "," << event->size().height() << ")" << LF;
355 QTMScrollView::resizeEvent (event
);
356 // the_gui::update needs to be run as soon as possible to refresh the status
362 QTMWidget::paintEvent (QPaintEvent
* event
) {
363 // In the current implementation repainting take place during the call to
364 // the widget's repaint_invalid_regions method in the_gui::update. All
365 // we have to do is to take the backing store and put it on screen according
366 // to the QRegion marked invalid.
367 // CHECK: Maybe just put onscreen all the region bounding rectangle could not
373 QRect rect
= event
->rect ();
374 cout
<< "paintEvent ("<< rect
.x() << "," << rect
.y()
375 << "," << rect
.width() << "," << rect
.height()
376 << ") regions:" << event
->region().numRects() << LF
;
380 QPainter
p (QAbstractScrollArea::viewport());
381 QVector
<QRect
> rects
= event
->region().rects();
382 for (int i
=0; i
< rects
.count(); i
++) {
383 QRect qr
= rects
.at(i
);
384 p
.drawPixmap(qr
,backingPixmap
,qr
);
392 QTMWidget::keyPressEvent (QKeyEvent
* event
) {
393 static bool fInit
= false;
396 cout
<< "Initializing keymap\n";
402 cout
<< "keypressed\n";
403 simple_widget_rep
*wid
= tm_widget();
407 int key
= event
->key();
408 Qt::KeyboardModifiers mods
= event
->modifiers();
411 cout
<< "key : " << key
<< LF
;
412 cout
<< "text : " << event
->text().toAscii().data() << LF
;
413 cout
<< "count: " << event
->text().count() << LF
;
414 if (mods
& Qt::ShiftModifier
) cout
<< "shift\n";
415 if (mods
& Qt::MetaModifier
) cout
<< "meta\n";
416 if (mods
& Qt::ControlModifier
) cout
<< "control\n";
417 if (mods
& Qt::KeypadModifier
) cout
<< "keypad\n";
418 if (mods
& Qt::AltModifier
) cout
<< "alt\n";
422 if (qtkeymap
->contains (key
)) {
424 } else if (qtdeadmap
->contains (key
)) {
425 mods
&=~ Qt::ShiftModifier
;
428 QString nss
= event
->text();
429 unsigned short unic
= nss
.data()[0].unicode();
430 if (unic
< 32 && key
< 128) {
431 if (((char) key
) >= 'A' && ((char) key
) <= 'Z') {
432 if ((mods
& Qt::ShiftModifier
) == 0)
433 key
= (int) (key
+ ((int) 'a') - ((int) 'A'));
435 mods
&=~ Qt::ShiftModifier
;
436 r
= string ((char) key
);
440 // unicode to cork conversion not appropriate for this case...
442 // CHECKME: are these two MAC exceptions really needed?
443 if (mods
& Qt::AltModifier
) r
= "grave";
446 case 168: r
= "umlaut"; break;
447 case 180: r
= "acute"; break;
448 // the following combining characters should be caught by qtdeadmap
449 case 0x300: r
= "grave"; break;
450 case 0x301: r
= "acute"; break;
451 case 0x302: r
= "hat"; break;
452 case 0x308: r
= "umlaut"; break;
453 case 0x33e: r
= "tilde"; break;
455 QByteArray buf
= nss
.toUtf8();
456 string
rr (buf
.constData(), buf
.count());
457 r
= utf8_to_cork (rr
);
458 if (r
== "<less>") r
= "<";
459 if (r
== "<gtr>") r
= ">";
462 // CHECKME: are these two MAC exceptions really needed?
463 mods
&=~ Qt::AltModifier
;
465 mods
&=~ Qt::ShiftModifier
;
472 if (mods
& Qt::ShiftModifier
) r
= "S-" * r
;
473 if (mods
& Qt::MetaModifier
) r
= "C-" * r
; // The "Control" key
474 if (mods
& Qt::ControlModifier
) r
= "Mod1-" * r
; // The "Command" key
475 //if (mods & Qt::KeypadModifier) r= "Mod3-" * r;
476 if (mods
& Qt::AltModifier
) r
= "Mod4-" * r
;
478 if (mods
& Qt::ShiftModifier
) r
= "S-" * r
;
479 if (mods
& Qt::ControlModifier
) r
= "C-" * r
;
480 if (mods
& Qt::AltModifier
) r
= "Mod1-" * r
;
481 //if (mods & Qt::KeypadModifier) r= "Mod3-" * r;
482 if (mods
& Qt::MetaModifier
) r
= "Mod4-" * r
; // The "Windows" key
486 cout
<< "key press: " << r
<< LF
;
487 //int start= texmacs_time ();
488 //wid -> handle_keypress (r, texmacs_time());
489 the_gui
-> process_keypress (wid
, r
, texmacs_time());
490 //int end= texmacs_time ();
491 //if (end > start) cout << "Keypress " << end - start << "\n";
492 // the_gui->update (); // FIXME: remove this line when
493 // edit_typeset_rep::get_env_value will be faster
500 mouse_state (QMouseEvent
* event
, bool flag
) {
502 Qt::MouseButtons bstate
= event
->buttons ();
503 Qt::MouseButton tstate
= event
->button ();
504 Qt::KeyboardModifiers kstate
= event
->modifiers ();
505 if (flag
) bstate
= bstate
| tstate
;
506 if ((bstate
& Qt::LeftButton
) != 0) i
+= 1;
507 if ((bstate
& Qt::MidButton
) != 0) i
+= 2;
508 if ((bstate
& Qt::RightButton
) != 0) i
+= 4;
509 if ((bstate
& Qt::XButton1
) != 0) i
+= 8;
510 if ((bstate
& Qt::XButton2
) != 0) i
+= 16;
512 if ((kstate
& Qt::AltModifier
) != 0) i
= 2;
513 if ((kstate
& Qt::MetaModifier
) != 0) i
= 4;
514 if ((kstate
& Qt::ShiftModifier
) != 0) i
+= 256;
515 if ((kstate
& Qt::ControlModifier
) != 0) i
+= 2048;
517 if ((kstate
& Qt::ShiftModifier
) != 0) i
+= 256;
518 if ((kstate
& Qt::ControlModifier
) != 0) i
+= 512;
519 if ((kstate
& Qt::AltModifier
) != 0) i
+= 2048;
520 if ((kstate
& Qt::MetaModifier
) != 0) i
+= 16384;
526 mouse_decode (unsigned int mstate
) {
527 if (mstate
& 1 ) return "left";
528 else if (mstate
& 2 ) return "middle";
529 else if (mstate
& 4 ) return "right";
530 else if (mstate
& 8 ) return "up";
531 else if (mstate
& 16) return "down";
536 QTMWidget::mousePressEvent (QMouseEvent
* event
) {
537 simple_widget_rep
*wid
= tm_widget ();
539 QPoint point
= event
->pos() + origin
;
541 unsigned int mstate
= mouse_state (event
, false);
542 string s
= "press-" * mouse_decode (mstate
);
543 the_gui
-> process_mouse (wid
, s
, point
.x (), point
.y (),
544 mstate
, texmacs_time ());
545 // wid -> handle_mouse (s, point.x (), point.y (), mstate, texmacs_time ());
547 cout
<< "mouse event: " << s
<< " at "
548 << point
.x () << ", " << point
.y () << LF
;
552 QTMWidget::mouseReleaseEvent (QMouseEvent
* event
) {
553 simple_widget_rep
*wid
= tm_widget();
555 QPoint point
= event
->pos() + origin
;;
557 unsigned int mstate
= mouse_state (event
, true);
558 string s
= "release-" * mouse_decode (mstate
);
559 the_gui
-> process_mouse (wid
, s
, point
.x (), point
.y (),
560 mstate
, texmacs_time ());
561 // wid -> handle_mouse (s, point.x (), point.y (), mstate, texmacs_time ());
563 cout
<< "mouse event: " << s
<< " at "
564 << point
.x () << ", " << point
.y () << LF
;
568 QTMWidget::mouseMoveEvent (QMouseEvent
* event
) {
569 simple_widget_rep
*wid
= tm_widget();
571 QPoint point
= event
->pos() + origin
;
573 unsigned int mstate
= mouse_state (event
, false);
575 the_gui
-> process_mouse (wid
, s
, point
.x (), point
.y (),
576 mstate
, texmacs_time ());
577 // wid -> handle_mouse (s, point.x (), point.y (), mstate, texmacs_time ());
579 cout
<< "mouse event: " << s
<< " at "
580 << point
.x () << ", " << point
.y () << LF
;
585 QTMWidget::event (QEvent
* event
) {
586 if (event
->type() == QEvent::KeyPress
) {
587 QKeyEvent
*ke
= static_cast<QKeyEvent
*> (event
);
591 return QTMScrollView::event (event
);
596 QTMWidget::focusInEvent ( QFocusEvent
* event
) {
597 if (DEBUG_QT
) cout
<< "FOCUSIN" << LF
;
598 simple_widget_rep
*wid
= tm_widget ();
600 the_gui
-> process_keyboard_focus (wid
, true, texmacs_time());
601 //wid -> handle_keyboard_focus (true, texmacs_time ());
603 QTMScrollView::focusInEvent (event
);
607 QTMWidget::focusOutEvent ( QFocusEvent
* event
) {
608 if (DEBUG_QT
) cout
<< "FOCUSOUT" << LF
;
609 simple_widget_rep
*wid
= tm_widget ();
611 the_gui
-> process_keyboard_focus (wid
, false, texmacs_time());
612 // wid -> handle_keyboard_focus (false, texmacs_time ());
614 QTMScrollView::focusOutEvent (event
);