fixes for event handling & repaint
[texmacs.git] / src / src / Plugins / Qt / qt_gui.cpp
blob83c869ffe44d2b9eb77ab4ed15c322a6438ef1eb
2 /******************************************************************************
3 * MODULE : qt_gui.cpp
4 * DESCRIPTION: QT display class
5 * COPYRIGHT : (C) 2008 Massimiliano Gubinelli
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 ******************************************************************************/
12 #include "iterator.hpp"
13 #include "dictionary.hpp"
14 #include "qt_gui.hpp"
15 #include "analyze.hpp"
16 #include <locale.h>
17 #include "language.hpp"
18 #include "message.hpp"
19 #include <QDesktopWidget>
20 #include <QClipboard>
21 #include <QFileOpenEvent>
22 #include <QSocketNotifier>
23 #include <QSetIterator>
24 #include "QTMGuiHelper.hpp"
25 #include "QTMWidget.hpp"
26 #include "qt_renderer.hpp" // for the_qt_renderer
28 #include "tm_link.hpp" // for number_of_servers
30 #include "Scheme/object.hpp"
31 //#include "TeXmacs/server.hpp" // for get_server
33 #include "qt_simple_widget.hpp"
35 extern window (*get_current_window) (void);
37 qt_gui_rep* the_gui= NULL;
38 int nr_windows = 0; // FIXME: fake variable, referenced in tm_server
40 time_t time_credit; // interval to interrupt long redrawings
41 time_t timeout_time; // new redraw interruption
42 time_t lapse = 0; // optimization for delayed commands
44 // marshalling flags between update, needs_update and check_event.
45 bool updating = false;
46 bool needing_update = false;
48 /******************************************************************************
49 * Constructor and geometry
50 ******************************************************************************/
52 qt_gui_rep::qt_gui_rep(int &argc, char **argv):
53 interrupted (false)
55 (void) argc; (void) argv;
56 // argc= argc2;
57 // argv= argv2;
59 interrupted = false;
60 time_credit = 25;
61 timeout_time= texmacs_time () + time_credit;
63 set_output_language (get_locale_language ());
64 gui_helper = new QTMGuiHelper (this);
65 qApp -> installEventFilter (gui_helper);
66 updatetimer = new QTimer (gui_helper);
67 updatetimer->setSingleShot (true);
68 QObject::connect ( updatetimer, SIGNAL(timeout()),
69 gui_helper, SLOT(doUpdate()));
70 // (void) default_font ();
73 /* important routines */
74 void
75 qt_gui_rep::get_extents (SI& width, SI& height) {
76 QDesktopWidget* d= QApplication::desktop();
77 int w = d->width(); // returns desktop width
78 int h = d->height(); // returns desktop height
79 width = ((SI) w) * PIXEL;
80 height= ((SI) h) * PIXEL;
83 void
84 qt_gui_rep::get_max_size (SI& width, SI& height) {
85 width = 8000 * PIXEL;
86 height= 6000 * PIXEL;
90 qt_gui_rep::~qt_gui_rep() {
91 delete gui_helper;
92 // delete updatetimer; we do not need this given that gui_helper is the
93 // parent of updatetimer
96 /******************************************************************************
97 * interclient communication
98 ******************************************************************************/
100 bool
101 qt_gui_rep::get_selection (string key, tree& t, string& s) {
102 QClipboard *cb= QApplication::clipboard();
103 bool owns= true;
104 QClipboard::Mode mode;
105 if (key == "primary") {
106 owns= cb->ownsClipboard();
107 mode= QClipboard::Clipboard;
108 } else if (key == "mouse" && cb->supportsSelection()) {
109 owns= cb->ownsSelection();
110 mode= QClipboard::Selection;
112 s= "";
113 t= "none";
115 if (owns) {
116 if (selection_t->contains (key)) {
117 t= copy (selection_t [key]);
118 s= copy (selection_s [key]);
119 return true;
121 return false;
124 QString originalText = cb->text(mode);
125 QByteArray buf = originalText.toAscii();
126 if (!(buf.isEmpty())) {
127 s << string(buf.constData(), buf.size());
130 t= tuple ("extern", s);
131 return true;
134 bool
135 qt_gui_rep::set_selection (string key, tree t, string s) {
136 selection_t (key)= copy (t);
137 selection_s (key)= copy (s);
140 QClipboard *cb = QApplication::clipboard();
141 QClipboard::Mode mode;
142 if (key == "primary") {
143 mode= QClipboard::Clipboard;
144 } else if (key == "mouse" && cb->supportsSelection()) {
145 mode= QClipboard::Selection;
146 } else {
147 return true;
150 char *selection = as_charp (s);
151 cb->setText(selection,mode);
152 tm_delete_array (selection);
153 return true;
156 void
157 qt_gui_rep::clear_selection (string key) {
158 selection_t->reset (key);
159 selection_s->reset (key);
162 /******************************************************************************
163 * Miscellaneous
164 ******************************************************************************/
166 void qt_gui_rep::image_gc (string name) { (void) name; }
167 // FIXME: remove this unused function
168 void qt_gui_rep::set_mouse_pointer (string name) { (void) name; }
169 // FIXME: implement this function
170 void qt_gui_rep::set_mouse_pointer (string curs_name, string mask_name)
171 { (void) curs_name; (void) mask_name; } ;
173 /******************************************************************************
174 * Main loop
175 ******************************************************************************/
177 void
178 qt_gui_rep::show_wait_indicator (widget w, string message, string arg) {
179 (void) w; (void) message; (void) arg;
182 void (*the_interpose_handler) (void) = NULL;
184 void gui_interpose (void (*r) (void)) { the_interpose_handler= r; }
186 void
187 qt_gui_rep::event_loop () {
188 QApplication *app = (QApplication*) QApplication::instance();
190 update();
191 //needs_update();
192 app->exec();
195 /******************************************************************************
196 * Sockets notifications
197 ******************************************************************************/
199 static hashmap<socket_notifier,pointer> read_notifiers;
200 static hashmap<socket_notifier,pointer> write_notifiers;
202 void
203 qt_gui_rep::add_notifier (socket_notifier sn)
205 QSocketNotifier *qsn;
207 if (DEBUG_QT) cout << "ADD NOTIFIER " << sn->fd << LF;
209 // replace any already present notifier
211 remove_notifier (sn);
213 // installs both a read and a write notifier
214 // (the texmacs interface does not specify enough its needs)
216 qsn = new QSocketNotifier(sn->fd, QSocketNotifier::Read, gui_helper);
217 read_notifiers (sn) = (pointer) (qsn);
218 QObject::connect( qsn, SIGNAL(activated(int)),
219 gui_helper, SLOT(doReadSocketNotification(int)) );
221 qsn = new QSocketNotifier(sn->fd, QSocketNotifier::Write, gui_helper);
222 write_notifiers (sn) = (pointer) (qsn);
223 QObject::connect( qsn, SIGNAL(activated(int)),
224 gui_helper, SLOT(doWriteSocketNotification(int)) );
227 void
228 qt_gui_rep::remove_notifier (socket_notifier sn)
230 QSocketNotifier *qsn;
232 if (DEBUG_QT) cout << "REMOVE NOTIFIER" << LF;
234 // disable the (r/w) notifiers to prevent them to fire past this point
235 // and schedule them for deletion at the end of the current runloop
237 qsn = (QSocketNotifier *)read_notifiers [sn];
238 if (qsn) {
239 qsn->setEnabled (false);
240 qsn->deleteLater ();
242 read_notifiers->reset (sn);
244 qsn = (QSocketNotifier *)write_notifiers (sn);
245 if (qsn) {
246 qsn->setEnabled (false);
247 qsn->deleteLater ();
249 write_notifiers->reset (sn);
252 void
253 qt_gui_rep::enable_notifier (socket_notifier sn, bool flag)
255 QSocketNotifier *qsn;
256 qsn = (QSocketNotifier *)read_notifiers (sn);
257 if (qsn) qsn->setEnabled (flag);
258 qsn = (QSocketNotifier *)write_notifiers (sn);
259 if (qsn) qsn->setEnabled (flag);
264 /******************************************************************************
265 * Delayed commands
266 ******************************************************************************/
268 //FIXME: the code below is almost a copy of the code in Guile/Scheme/object.cpp
269 // maybe some refactoring would be necessary
271 static array<object> delayed_queue;
272 static array<time_t> start_queue;
274 void
275 exec_delayed (object cmd) {
276 delayed_queue << cmd;
277 start_queue << (((time_t) texmacs_time ()) - 1000000000);
278 lapse = texmacs_time();
279 needs_update();
282 void
283 exec_delayed_pause (object cmd) {
284 delayed_queue << cmd;
285 start_queue << ((time_t) texmacs_time ());
286 lapse = texmacs_time();
287 needs_update();
290 void
291 exec_pending_commands () {
292 array<object> a= delayed_queue;
293 array<time_t> b= start_queue;
294 delayed_queue= array<object> (0);
295 start_queue= array<time_t> (0);
296 int i, n= N(a);
297 for (i=0; i<n; i++) {
298 time_t now= texmacs_time ();
299 if ((now - b[i]) >= 0) {
300 object obj= call (a[i]);
301 if (is_int (obj) && (now - b[i] < 1000000000)) {
302 time_t pause = as_int (obj);
303 //cout << "pause= " << obj << "\n";
304 delayed_queue << a[i];
305 start_queue << (now + pause);
308 else {
309 delayed_queue << a[i];
310 start_queue << b[i];
313 if (N(delayed_queue)>0) {
314 lapse = start_queue[0];
315 int n = N(start_queue);
316 for (i=1; i<n; i++) {
317 if (lapse > start_queue[i]) lapse = start_queue[i];
322 void
323 clear_pending_commands () {
324 delayed_queue= array<object> (0);
325 start_queue= array<time_t> (0);
328 /******************************************************************************
329 * Main routines
330 ******************************************************************************/
333 void
334 gui_open (int& argc, char** argv) {
335 // start the gui
336 // new QApplication (argc,argv); now in texmacs.cpp
337 the_gui = tm_new<qt_gui_rep> (argc, argv);
340 void
341 gui_start_loop () {
342 // start the main loop
343 the_gui->event_loop ();
346 void
347 gui_close () {
348 // cleanly close the gui
349 ASSERT (the_gui != NULL, "gui not yet open");
350 tm_delete (the_gui);
351 the_gui=NULL;
354 void
355 gui_root_extents (SI& width, SI& height) {
356 // get the screen size
357 the_gui->get_extents (width, height);
358 if (DEBUG_QT)
359 cout << "gui_root_extents (" << width << "," << height << ")" << LF;
362 void
363 gui_maximal_extents (SI& width, SI& height) {
364 // get the maximal size of a window (can be larger than the screen size)
365 the_gui->get_max_size (width, height);
366 if (DEBUG_QT)
367 cout << "gui_maximal_extents (" << width << "," << height << ")" << LF;
370 void
371 gui_refresh () {
372 // update and redraw all windows (e.g. on change of output language)
373 // FIXME: add suitable code
377 /******************************************************************************
378 * QTMGuiHelper methods
379 ******************************************************************************/
381 void
382 QTMGuiHelper::doUpdate () {
383 // cout << "UPDATE " << texmacs_time () << LF;
384 gui->update();
387 bool
388 QTMGuiHelper::eventFilter (QObject *obj, QEvent *event) {
389 if (event->type() == QEvent::FileOpen) {
390 QFileOpenEvent *openEvent = static_cast<QFileOpenEvent *>(event);
391 const char *s = openEvent->file().toAscii().constData();
392 //qDebug ("File Open Event %s", s);
393 call ( "texmacs-load-buffer", object(url_system (s)),
394 object("generic"), object(1), object(false));
395 return true;
396 } else {
397 // standard event processing
398 return QObject::eventFilter(obj, event);
402 void
403 QTMGuiHelper::doWriteSocketNotification (int socket) {
404 //cout << "WRITE SOCKET NOTIFICATION " << socket << " "<< texmacs_time () << LF;
405 iterator<socket_notifier> it = iterate (write_notifiers);
406 while (it->busy ()) {
407 socket_notifier sn= it->next ();
408 if (sn->fd == socket) {
409 //sn->notify();
410 the_gui->process_socket_notification (sn);
411 the_gui->enable_notifier (sn, false);
416 void
417 QTMGuiHelper::doReadSocketNotification (int socket) {
418 //cout << "READ SOCKET NOTIFICATION " << socket << " "<< texmacs_time () << LF;
419 iterator<socket_notifier> it = iterate (read_notifiers);
420 while (it->busy ()) {
421 socket_notifier sn= it->next ();
422 if (sn->fd == socket) {
423 //sn->notify();
424 the_gui->process_socket_notification (sn);
425 the_gui->enable_notifier (sn, false);
431 /******************************************************************************
432 * Queued processing
433 ******************************************************************************/
435 enum qp_type_id {
436 QP_NULL,
437 QP_KEYPRESS,
438 QP_KEYBOARD_FOCUS,
439 QP_MOUSE,
440 QP_RESIZE,
441 QP_SOCKET_NOTIFICATION
444 class qp_type {
445 public:
446 qp_type_id sid;
447 inline qp_type (qp_type_id sid2 = QP_NULL): sid (sid2) {}
448 inline qp_type (const qp_type& s): sid (s.sid) {}
449 inline qp_type& operator = (qp_type s) { sid= s.sid; return *this; }
450 inline operator qp_type_id () { return sid; }
451 inline bool operator == (qp_type_id sid2) { return sid == sid2; }
452 inline bool operator != (qp_type_id sid2) { return sid != sid2; }
453 inline bool operator == (qp_type s) { return sid == s.sid; }
454 inline bool operator != (qp_type s) { return sid != s.sid; }
455 inline friend ostream& operator << (ostream& out, qp_type s)
456 { return out << s.sid; }
459 class queued_event : public pair<qp_type, blackbox>
461 public:
462 queued_event(qp_type _type = qp_type(), blackbox _bb = blackbox())
463 : pair<qp_type, blackbox>(_type, _bb) {};
467 static array<queued_event> waiting_events;
469 void
470 process_event (queued_event ev) {
471 switch ((qp_type_id) ev.x1) {
472 case QP_NULL :
473 break;
474 case QP_KEYPRESS :
476 typedef triple<simple_widget_rep*, string, time_t > T;
477 T x = open_box <T> (ev.x2) ;
478 x.x1 -> handle_keypress (x.x2, x.x3) ;
480 break;
481 case QP_KEYBOARD_FOCUS :
483 typedef triple<simple_widget_rep*, bool, time_t > T;
484 T x = open_box <T> (ev.x2) ;
485 x.x1 -> handle_keyboard_focus (x.x2, x.x3) ;
486 send_invalidate_all(x.x1);
487 //FIXME: the above invalidate is due to incorrect repainting when
488 // the widget loose the focus. I should investigate better
489 // the problem.
491 break;
492 case QP_MOUSE :
494 typedef quintuple<string, SI, SI, int, time_t > T1;
495 typedef pair<simple_widget_rep*, T1> T;
496 T x = open_box <T> (ev.x2) ;
497 x.x1 -> handle_mouse (x.x2.x1, x.x2.x2, x.x2.x3, x.x2.x4, x.x2.x5) ;
499 break;
500 case QP_RESIZE :
502 typedef triple<simple_widget_rep*, SI, SI > T;
503 T x = open_box <T> (ev.x2) ;
504 x.x1 -> handle_notify_resize (x.x2, x.x3) ;
506 break;
507 case QP_SOCKET_NOTIFICATION :
509 socket_notifier sn = open_box <socket_notifier> (ev.x2) ;
510 // cout << "QP_SOCKET_NOTIFICATION " << sn->fd << LF;
511 sn -> notify ();
512 the_gui->enable_notifier (sn, true);
514 break;
515 default:
516 FAILED("Unexpected queued event");
521 void
522 qt_gui_rep::process_queued_events (int max) {
524 array<queued_event> a = waiting_events, b;
525 waiting_events = array<queued_event> ();
527 int i, n = N(a);
529 //cout << "(" << n << " events)";
530 for (i=0; i<n; i++) {
531 //cout << (int)a[i].x1 << ",";
532 if ((max < 0) || (i<max))
533 process_event(a[i]);
534 else
535 b << a[i];
537 b << waiting_events;
538 waiting_events = b;
542 void
543 add_event(const queued_event &ev)
545 if (updating) {
546 waiting_events << ev;
547 needing_update = true;
548 //needs_update();
549 } else {
550 process_event(ev);
551 the_gui->update();
555 void
556 qt_gui_rep::process_keypress (simple_widget_rep *wid, string key, time_t t) {
557 typedef triple<simple_widget_rep*, string, time_t > T;
558 add_event( queued_event ( QP_KEYPRESS, close_box<T> (T(wid, key, t))));
559 // wid -> handle_keypress (key, t);
560 // needs_update ();
563 void
564 qt_gui_rep::process_keyboard_focus ( simple_widget_rep *wid, bool has_focus,
565 time_t t ) {
566 typedef triple<simple_widget_rep*, bool, time_t > T;
567 add_event(
568 queued_event ( QP_KEYBOARD_FOCUS, close_box<T> (T(wid, has_focus, t))));
569 // wid -> handle_keyboard_focus (has_focus, t);
570 // needs_update ();
573 void
574 qt_gui_rep::process_mouse ( simple_widget_rep *wid, string kind, SI x, SI y,
575 int mods, time_t t ) {
576 typedef quintuple<string, SI, SI, int, time_t > T1;
577 typedef pair<simple_widget_rep*, T1> T;
578 add_event (
579 queued_event ( QP_MOUSE, close_box<T> ( T (wid, T1 (kind, x, y, mods, t)))));
580 // wid -> handle_mouse (kind, x, y, mods, t);
581 // needs_update ();
584 void
585 qt_gui_rep::process_resize ( simple_widget_rep *wid, SI x, SI y ) {
586 typedef triple<simple_widget_rep*, SI, SI > T;
587 add_event( queued_event ( QP_RESIZE, close_box<T> (T(wid, x, y))));
588 // wid -> handle_notify_resize (x, y);
589 // needs_update ();
592 void
593 qt_gui_rep::process_socket_notification ( socket_notifier sn ) {
594 add_event (
595 queued_event (QP_SOCKET_NOTIFICATION, close_box< socket_notifier > (sn)));
596 // sn -> notify ();
597 // enable_notifier (sn, true);
598 // needs_update ();
602 void
603 fill_event_queue() {
604 if (N(waiting_events) == 0) {
605 qApp->processEvents(QEventLoop::ExcludeSocketNotifiers, 0);
606 // if (N(waiting_events) > 0) cout << "addins some events" << LF;
610 bool
611 qt_gui_rep::check_event (int type) {
612 //FIXME: add more types and refine, compare with X11 version.
614 // cout << "."; cout.flush();
615 // return false;
616 switch (type) {
617 case INTERRUPT_EVENT:
618 if (interrupted) return true;
619 else {
620 time_t now= texmacs_time ();
621 if (now - timeout_time < 0) return false;
622 fill_event_queue();
623 timeout_time= now + (100 / (N(waiting_events) + 1));
624 interrupted= (N(waiting_events) > 0);
625 //if (interrupted) cout << "INTERRUPT "
626 // << now << "------------------" << LF;
627 return interrupted;
629 case INTERRUPTED_EVENT:
630 return interrupted;
631 default:
632 return false;
637 void
638 qt_gui_rep::update () {
639 // this is called by doUpdate, which in turns is fired by a timer activated in
640 // needs_update, and ensuring that interpose_handler is run during a pass in
641 // the eventloop afterwards we reactivate the timer with a pause
642 // (see FIXME below)
644 time_credit = 100;
645 updating = true;
646 updatetimer->stop();
648 int count_events = 0;
649 int max_proc_events = 2;
651 do {
652 time_t now = texmacs_time();
653 needing_update = false;
655 count_events++;
657 if (N(waiting_events) > 0) {
658 //cout << "PROCESS QUEUED EVENTS START..."; cout.flush();
659 process_queued_events (1);
660 //cout << "AND END" << LF;
663 //cout << "TYPESET START..."; cout.flush();
664 if (lapse <= now) exec_pending_commands();
665 if (the_interpose_handler) the_interpose_handler();
666 //cout << "AND END" << LF;
668 fill_event_queue();
670 if ((count_events > max_proc_events) || (N(waiting_events) == 0)) {
671 //if (count_events > max_proc_events) cout << "PARTIAL REDRAW" << LF;
672 count_events = 0;
674 // repaint invalid regions
675 interrupted = false;
676 timeout_time = texmacs_time() + time_credit/(N(waiting_events)+1);
678 //cout << "REPAINT START..."; cout.flush();
680 QSetIterator<QTMWidget*> i(QTMWidget::all_widgets);
681 while (i.hasNext()) {
682 QTMWidget *w = i.next();
683 w->repaint_invalid_regions();
686 if (interrupted) needing_update = true;
687 //cout << "AND END" << LF;
690 } while ((N(waiting_events)>0) || needing_update);
693 updating = false;
696 if (nr_windows == 0) {
697 qApp->quit();
701 time_t delay = lapse - texmacs_time();
702 if (delay > 1000/6) delay = 1000/6;
703 if (delay < 0) delay = 0;
704 if (needing_update || interrupted) delay = 0;
705 updatetimer->start (delay);
707 // FIXME: we need to ensure that the interpose_handler is run at regular
708 // intervals (1/6th of sec) so that informations on the footbar are
709 // updated. (this should be better handled by promoting code in
710 // tm_editor::apply_changes (which is activated only after idle
711 // periods) at the level of delayed commands in the gui.
712 // The interval cannot be too small to keep CPU usage low in idle state
717 /******************************************************************************
718 * Font support
719 ******************************************************************************/
721 void
722 set_default_font (string name) {
723 (void) name;
724 // set the name of the default font
725 // this is ignored since Qt handles fonts for the widgets
728 font
729 get_default_font (bool tt) {
730 (void) tt;
731 // get the default font or monospaced font (if tt is true)
733 // return a null font since this function is not called in the Qt port.
734 if (DEBUG_QT) cout << "get_default_font(): SHOULD NOT BE CALLED\n";
735 return NULL;
736 //return tex_font (this, "ecrm", 10, 300, 0);
739 // load the metric and glyphs of a system font
740 // you are not obliged to provide any system fonts
742 void
743 load_system_font (string family, int size, int dpi,
744 font_metric& fnm, font_glyphs& fng)
746 (void) family; (void) size; (void) dpi; (void) fnm; (void) fng;
747 if (DEBUG_QT) cout << "load_system_font(): SHOULD NOT BE CALLED\n";
750 /******************************************************************************
751 * Clipboard support
752 ******************************************************************************/
754 bool
755 set_selection (string key, tree t, string s) {
756 // Copy a selection 't' with string equivalent 's' to the clipboard 'cb'
757 // Returns true on success
758 return the_gui->set_selection (key, t, s);
761 bool
762 get_selection (string key, tree& t, string& s) {
763 // Retrieve the selection 't' with string equivalent 's' from clipboard 'cb'
764 // Returns true on success; sets t to (extern s) for external selections
765 return the_gui->get_selection (key, t, s);
768 void
769 clear_selection (string key) {
770 // Clear the selection on clipboard 'cb'
771 the_gui->clear_selection (key);
774 /******************************************************************************
775 * Miscellaneous
776 ******************************************************************************/
777 int char_clip=0;
779 void
780 beep () {
781 // Issue a beep
782 QApplication::beep();
785 void
786 needs_update () {
787 // 0 ms - call immediately when all other events have
788 // been processed
789 //cout << "needs_update" << LF;
790 if (updating) {
791 needing_update = true;
793 else {
794 the_gui->updatetimer->start (0);
798 bool
799 check_event (int type) {
800 // Check whether an event of one of the above types has occurred;
801 // we check for keyboard events while repainting windows
802 return the_gui->check_event(type);
805 void image_gc (string name) {
806 // Garbage collect images of a given name (may use wildcards)
807 // This routine only needs to be implemented if you use your own image cache
808 the_qt_renderer()->image_gc(name);
811 void
812 show_help_balloon (widget balloon, SI x, SI y) {
813 // Display a help balloon at position (x, y); the help balloon should
814 // disappear as soon as the user presses a key or moves the mouse
815 (void) balloon; (void) x; (void) y;
818 void
819 show_wait_indicator (widget base, string message, string argument) {
820 // Display a wait indicator with a message and an optional argument
821 // The indicator might for instance be displayed at the center of
822 // the base widget which triggered the lengthy operation;
823 // the indicator should be removed if the message is empty
824 the_gui->show_wait_indicator(base,message,argument);
827 font x_font (string family, int size, int dpi)
829 (void) family; (void) size; (void) dpi;
830 if (DEBUG_QT) cout << "x_font(): SHOULD NOT BE CALLED\n";
831 return NULL;