Back to index

texmacs  1.0.7.15
qt_gui.cpp
Go to the documentation of this file.
00001 
00002 /******************************************************************************
00003 * MODULE     : qt_gui.cpp
00004 * DESCRIPTION: QT display class
00005 * COPYRIGHT  : (C) 2008  Massimiliano Gubinelli
00006 *******************************************************************************
00007 * This software falls under the GNU general public license version 3 or later.
00008 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
00009 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
00010 ******************************************************************************/
00011 
00012 #include "iterator.hpp"
00013 #include "dictionary.hpp"
00014 #include "qt_gui.hpp"
00015 #include "qt_utilities.hpp"
00016 #include "analyze.hpp"
00017 #include <locale.h>
00018 #include "language.hpp"
00019 #include "message.hpp"
00020 #include <QDesktopWidget>
00021 #include <QClipboard>
00022 #include <QFileOpenEvent>
00023 #include <QStackedLayout>
00024 #include <QLabel>
00025 #include <QSocketNotifier>
00026 #include <QSetIterator>
00027 #include "QTMGuiHelper.hpp"
00028 #include "QTMWidget.hpp"
00029 #include "QTMWindow.hpp"
00030 #include "qt_renderer.hpp" // for the_qt_renderer
00031 
00032 #ifdef MACOSX_EXTENSIONS
00033 #include "MacOS/mac_utilities.h"
00034 #endif
00035 
00036 
00037 #include "tm_link.hpp" // for number_of_servers
00038 
00039 #include "scheme.hpp"
00040 //#include "TeXmacs/server.hpp" // for get_server
00041 
00042 #include "qt_simple_widget.hpp"
00043 #include "qt_window_widget.hpp"
00044 
00045 extern window (*get_current_window) (void);
00046 
00047 qt_gui_rep* the_gui= NULL;
00048 int nr_windows = 0; // FIXME: fake variable, referenced in tm_server
00049 
00050 time_t time_credit = 100;  // interval to interrupt long redrawings
00051 time_t timeout_time; // new redraw interruption
00052 time_t lapse = 0; // optimization for delayed commands
00053 
00054 // marshalling flags between update, needs_update and check_event.
00055 bool disable_check_event = false;
00056 bool updating = false;
00057 bool needing_update = false;
00058 bool wait_for_delayed_commands = true;
00059 // this flag is used in update() to insert QP_DELAYED_COMMANDS events in TeXmacs 
00060 // event queue so to have delayed command handling properly interspersed with 
00061 // the other events
00062 
00063 /******************************************************************************
00064 * Constructor and geometry
00065 ******************************************************************************/
00066 
00067 qt_gui_rep::qt_gui_rep(int &argc, char **argv):
00068   interrupted (false), waitWindow (NULL)
00069 {
00070   (void) argc; (void) argv;
00071   // argc= argc2;
00072   // argv= argv2;
00073 
00074   interrupted   = false;
00075   time_credit = 100;
00076   timeout_time= texmacs_time () + time_credit;
00077 
00078   //waitDialog = NULL;
00079   
00080   set_output_language (get_locale_language ());
00081   gui_helper = new QTMGuiHelper (this);
00082   qApp -> installEventFilter (gui_helper);
00083   
00084 #ifdef QT_MAC_USE_COCOA
00085   //HACK: this filter is needed to overcome a bug in Qt/Cocoa
00086   extern void mac_install_filter(); // defined in src/Plugins/MacOS/mac_app.mm
00087   mac_install_filter();
00088 #endif
00089   
00090   
00091   
00092   qApp-> installTranslator(new QTMTranslator(qApp));
00093   
00094   updatetimer = new QTimer (gui_helper);
00095   updatetimer->setSingleShot (true);
00096   QObject::connect ( updatetimer, SIGNAL(timeout()), 
00097                      gui_helper, SLOT(doUpdate()));
00098 //  (void) default_font ();
00099 }
00100 
00101 /* important routines */
00102 void
00103 qt_gui_rep::get_extents (SI& width, SI& height) {
00104   QDesktopWidget* d= QApplication::desktop();
00105   int w = d->width();  // returns desktop width
00106   int h = d->height(); // returns desktop height        
00107   width = ((SI) w) * PIXEL;
00108   height= ((SI) h) * PIXEL;
00109 }
00110 
00111 void
00112 qt_gui_rep::get_max_size (SI& width, SI& height) {
00113   width = 8000 * PIXEL;
00114   height= 6000 * PIXEL;
00115 }
00116 
00117 
00118 qt_gui_rep::~qt_gui_rep()  {
00119   delete gui_helper;
00120   
00121   while (waitDialogs.count()) {
00122     waitDialogs.last()->deleteLater();
00123     waitDialogs.removeLast();
00124   }    
00125   if (waitWindow) delete waitWindow;
00126   
00127  // delete updatetimer; we do not need this given that gui_helper is the 
00128  // parent of updatetimer
00129 }
00130 
00131 /******************************************************************************
00132 * interclient communication
00133 ******************************************************************************/
00134 
00135 bool
00136 qt_gui_rep::get_selection (string key, tree& t, string& s, string format) {
00137   (void) format;
00138   QClipboard *cb= QApplication::clipboard ();
00139   QClipboard::Mode mode= QClipboard::Clipboard;
00140   bool owns= false;
00141   if (key == "primary" || (key == "mouse" && cb->supportsSelection ())) {
00142     if (key == "mouse") mode= QClipboard::Selection;
00143     const QMimeData *md= cb->mimeData (mode);
00144     if (md) owns= md->hasFormat ("application/x-texmacs-clipboard");  
00145   }
00146   else owns= true;
00147   
00148   s= "";
00149   t= "none";
00150   if (owns) {
00151     if (!selection_t->contains (key)) return false;
00152     t= copy (selection_t [key]);
00153     s= copy (selection_s [key]);
00154     return true;
00155   }
00156   else {
00157     QString originalText= cb->text (mode);
00158     QByteArray buf;
00159     if (format == "verbatim" &&
00160        get_preference ("verbatim->texmacs:encoding") == "utf-8")
00161       buf= originalText.toUtf8 ();
00162     else buf= originalText.toAscii ();
00163     if (!(buf.isEmpty())) s << string (buf.constData(), buf.size());
00164     t= tuple ("extern", s);
00165     return true;
00166   }
00167 }
00168 
00169 bool
00170 qt_gui_rep::set_selection (string key, tree t, string s, string format) {
00171   selection_t (key)= copy (t);
00172   selection_s (key)= copy (s);
00173         
00174   QClipboard *cb= QApplication::clipboard ();
00175   QClipboard::Mode mode= QClipboard::Clipboard;
00176   if (key == "primary");
00177   else if (key == "mouse" && cb->supportsSelection())
00178     mode= QClipboard::Selection;
00179   else return true;
00180   cb->clear (mode);
00181 
00182   char *selection = as_charp (s);
00183   cb->setText (selection, mode);
00184   QByteArray ba (selection);
00185   QMimeData *md= new QMimeData;
00186   md->setData ("application/x-texmacs-clipboard", ba);
00187   if (format == "verbatim" &&
00188       get_preference ("texmacs->verbatim:encoding") == "utf-8")
00189     md->setText (QString::fromUtf8 (selection));
00190   else md->setText (selection);
00191   cb->setMimeData (md, mode); 
00192   // according to the docs, ownership of mimedata is transferred to clipboard
00193   // so no memory leak here
00194   tm_delete_array (selection);
00195   return true;
00196 }
00197 
00198 void
00199 qt_gui_rep::clear_selection (string key) {
00200   selection_t->reset (key);
00201   selection_s->reset (key);
00202   
00203   QClipboard *cb= QApplication::clipboard();
00204   QClipboard::Mode mode= QClipboard::Clipboard;
00205   bool owns= false;
00206   if (key == "primary");
00207   else if (key == "mouse" && cb->supportsSelection())
00208     mode= QClipboard::Selection;
00209   else return;
00210 
00211   const QMimeData *md= cb->mimeData (mode);
00212   if (md) owns= md->hasFormat ("application/x-texmacs");
00213   if (owns) cb->clear (mode);
00214 }
00215 
00216 /******************************************************************************
00217 * Miscellaneous
00218 ******************************************************************************/
00219 
00220 void qt_gui_rep::image_gc (string name) { (void) name; }
00221 // FIXME: remove this unused function
00222 void qt_gui_rep::set_mouse_pointer (string name) { (void) name; }
00223 // FIXME: implement this function
00224 void qt_gui_rep::set_mouse_pointer (string curs_name, string mask_name) 
00225 { (void) curs_name; (void) mask_name; } ;
00226 
00227 /******************************************************************************
00228 * Main loop
00229 ******************************************************************************/
00230 
00231 void
00232 qt_gui_rep::show_wait_indicator (widget w, string message, string arg)  {
00233   (void) w; (void) message; (void) arg;
00234   
00235   if (DEBUG_QT)  cout << "show_wait_indicator \"" << message << "\"\"" << arg << "\"" << LF;
00236 
00237   qt_window_widget_rep *wid = static_cast<qt_window_widget_rep*> (w.rep);
00238 
00239   // we move the texmacs window during an operation. 
00240   // We need to disable updates of the window to avoid erasure of the canvas
00241   // area
00242   //  wid->wid->setUpdatesEnabled(false);
00243   
00244   //FIXME: we must center the wait widget wrt the current active window
00245   
00246   if (!waitWindow) {
00247     waitWindow = new QWidget (wid->wid->window());
00248     waitWindow->setWindowFlags (Qt::Window | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
00249     QStackedLayout *layout = new QStackedLayout();
00250     layout->setSizeConstraint(QLayout::SetFixedSize);
00251     waitWindow->setLayout(layout);
00252   }
00253   
00254   if (waitDialogs.count()) {
00255     waitWindow->layout()->removeWidget(waitDialogs.last());
00256   }
00257   
00258   if (N(message)) {
00259     // push a new wait message in the list
00260     
00261     if (arg != "") message= message * " " * arg * "...";
00262     
00263     QLabel* lab = new  QLabel();
00264     lab->setFocusPolicy(Qt::NoFocus);
00265     lab->setMargin(15);
00266     lab->setText (to_qstring (tm_var_encode(message)));
00267     waitDialogs << lab;
00268   } else {
00269     // pop the next wait message from the list
00270     if (waitDialogs.count()) {
00271       waitDialogs.last()->deleteLater();
00272       waitDialogs.removeLast();
00273     }    
00274   }
00275 
00276   if (waitDialogs.count()) {
00277     waitWindow->layout()->addWidget(waitDialogs.last());
00278     waitWindow->updateGeometry();
00279     {
00280       QSize sz = waitWindow->geometry().size();
00281       QRect rect = QRect(QPoint(0,0),sz);
00282       //HACK: 
00283       // processEvents is needed to let Qt update windows coordinates in the case
00284       qApp->processEvents();
00285       //ENDHACK
00286       QPoint pt = wid->wid->window()->geometry().center();
00287       rect.moveCenter(pt);
00288       waitWindow->move(rect.topLeft());
00289       
00290     }
00291     waitWindow->show();
00292     qApp->processEvents();
00293     waitWindow->repaint();
00294   } else {
00295     waitWindow->close();
00296   }
00297   qApp->processEvents();
00298   QApplication::flush();
00299   
00300   
00301   //    wid->wid->setUpdatesEnabled(true);
00302 
00303   // next time we do update the dialog will disappear
00304   needs_update();  
00305 }
00306 
00307 void (*the_interpose_handler) (void) = NULL;
00308 
00309 void gui_interpose (void (*r) (void)) { the_interpose_handler= r; }
00310 
00311 void
00312 qt_gui_rep::event_loop () {
00313   QApplication *app = (QApplication*) QApplication::instance();
00314   update();
00315   //needs_update();
00316   app->exec();
00317 }
00318 
00319 /******************************************************************************
00320  * Sockets notifications
00321  ******************************************************************************/
00322 
00323 static hashmap<socket_notifier,pointer> read_notifiers;
00324 static hashmap<socket_notifier,pointer> write_notifiers;
00325 
00326 void 
00327 qt_gui_rep::add_notifier (socket_notifier sn)
00328 {
00329   QSocketNotifier *qsn;
00330   
00331   // replace any already present notifier
00332 
00333   remove_notifier (sn);
00334 
00335   if (DEBUG_QT) cout << "ADD NOTIFIER " << sn->fd << LF;
00336 
00337   
00338   // installs both a read and a write notifier 
00339   // (the texmacs interface does not specify enough its needs)
00340   
00341   qsn = new QSocketNotifier(sn->fd, QSocketNotifier::Read, gui_helper);
00342   read_notifiers (sn) = (pointer) (qsn); 
00343   QObject::connect( qsn, SIGNAL(activated(int)), 
00344                     gui_helper, SLOT(doReadSocketNotification(int)) );
00345 
00346   qsn = new QSocketNotifier(sn->fd, QSocketNotifier::Write, gui_helper);
00347   write_notifiers (sn) = (pointer) (qsn);
00348   QObject::connect( qsn, SIGNAL(activated(int)), 
00349                     gui_helper, SLOT(doWriteSocketNotification(int)) );  
00350 }
00351 
00352 void 
00353 qt_gui_rep::remove_notifier (socket_notifier sn)
00354 {  
00355   QSocketNotifier *qsn;
00356 
00357   if (DEBUG_QT) cout << "REMOVE NOTIFIER" << LF;
00358 
00359   // disable the (r/w) notifiers to prevent them to fire past this point
00360   // and schedule them for deletion at the end of the current runloop
00361 
00362   qsn = (QSocketNotifier *)read_notifiers [sn];
00363   if (qsn) {
00364     qsn->setEnabled (false);
00365     qsn->deleteLater ();
00366   }
00367   read_notifiers->reset (sn);
00368 
00369   qsn = (QSocketNotifier *)write_notifiers (sn);
00370   if (qsn) {
00371     qsn->setEnabled (false);
00372     qsn->deleteLater ();
00373   }
00374   write_notifiers->reset (sn);
00375 }
00376 
00377 void 
00378 qt_gui_rep::enable_notifier (socket_notifier sn, bool flag)
00379 {
00380   QSocketNotifier *qsn;
00381   qsn = (QSocketNotifier *)read_notifiers (sn);
00382   if (qsn) qsn->setEnabled (flag);
00383   qsn = (QSocketNotifier *)write_notifiers (sn);
00384   if (qsn) qsn->setEnabled (flag);
00385 }
00386 
00387 
00388 
00389 /******************************************************************************
00390  * Delayed commands
00391  ******************************************************************************/
00392 
00393 //FIXME: the code below is almost a copy of the code in Guile/Scheme/object.cpp
00394 //       maybe some refactoring would be necessary
00395 
00396 static array<object> delayed_queue;
00397 static array<time_t> start_queue;
00398 
00399 void
00400 exec_delayed (object cmd) {
00401   delayed_queue << cmd;
00402   start_queue << (((time_t) texmacs_time ()) - 1000000000);
00403   lapse = texmacs_time();
00404   needs_update();
00405 }
00406 
00407 void
00408 exec_delayed_pause (object cmd) {
00409   delayed_queue << cmd;
00410   start_queue << ((time_t) texmacs_time ());
00411   lapse = texmacs_time();
00412   needs_update();
00413 }
00414 
00415 void
00416 exec_pending_commands () {
00417   array<object> a= delayed_queue;
00418   array<time_t> b= start_queue;
00419   delayed_queue= array<object> (0);
00420   start_queue= array<time_t> (0);
00421   int i, n= N(a);
00422   for (i=0; i<n; i++) {
00423     time_t now=  texmacs_time ();
00424     if ((now - b[i]) >= 0) {
00425       object obj= call (a[i]);
00426       if (is_int (obj) && (now - b[i] < 1000000000)) {
00427         time_t pause = as_int (obj);
00428         //cout << "pause= " << obj << "\n";
00429         delayed_queue << a[i];
00430         start_queue << (now + pause);
00431       }
00432     }
00433     else {
00434       delayed_queue << a[i];
00435       start_queue << b[i];
00436     }
00437   }
00438   if (N(delayed_queue)>0) {
00439     lapse = start_queue[0];
00440     int n = N(start_queue);
00441     for (i=1; i<n; i++) {
00442       if (lapse > start_queue[i]) lapse = start_queue[i];
00443     }
00444   }
00445 }
00446 
00447 void
00448 clear_pending_commands () {
00449   delayed_queue= array<object> (0);
00450   start_queue= array<time_t> (0);
00451 }
00452 
00453 /******************************************************************************
00454 * Main routines
00455 ******************************************************************************/
00456 
00457 
00458 void
00459 gui_open (int& argc, char** argv) {
00460   // start the gui
00461  // new QApplication (argc,argv); now in texmacs.cpp
00462   the_gui = tm_new<qt_gui_rep> (argc, argv);
00463   
00464 #ifdef MACOSX_EXTENSIONS
00465   mac_begin_remote();
00466 #endif
00467 }
00468 
00469 void
00470 gui_start_loop () {
00471   // start the main loop
00472   the_gui->event_loop ();
00473 }
00474 
00475 void
00476 gui_close () {
00477   // cleanly close the gui
00478   ASSERT (the_gui != NULL, "gui not yet open");
00479   tm_delete (the_gui);
00480   the_gui=NULL;
00481 
00482 #ifdef MACOSX_EXTENSIONS
00483   mac_end_remote();
00484 #endif
00485 }
00486 
00487 void
00488 gui_root_extents (SI& width, SI& height) {
00489   // get the screen size
00490   the_gui->get_extents (width, height);
00491   if (DEBUG_QT) 
00492     cout << "gui_root_extents (" << width << "," << height << ")" << LF;
00493 }
00494 
00495 void
00496 gui_maximal_extents (SI& width, SI& height) {
00497   // get the maximal size of a window (can be larger than the screen size)
00498   the_gui->get_max_size (width, height);
00499   if (DEBUG_QT) 
00500     cout << "gui_maximal_extents (" << width << "," << height << ")" << LF;
00501 }
00502 
00503 void
00504 gui_refresh () {
00505   // called upon change of output language
00506   // emit a signal which force every QTMAction to change his text
00507   // according to the new language
00508 
00509   the_gui->gui_helper->doRefresh();
00510 }
00511 
00512 
00513 /******************************************************************************
00514  * QTMGuiHelper methods
00515  ******************************************************************************/
00516 
00517 void
00518 QTMGuiHelper::doUpdate () {
00519 //  cout << "UPDATE " << texmacs_time () << LF;
00520   gui->update();
00521 }
00522 
00523 void
00524 QTMGuiHelper::doRefresh () {
00525   emit refresh();
00526 }
00527 
00528 bool
00529 QTMGuiHelper::eventFilter (QObject *obj, QEvent *event) {
00530    if (event->type() == QEvent::FileOpen) {
00531      QFileOpenEvent *openEvent = static_cast<QFileOpenEvent *>(event);
00532      const char *s = openEvent->file().toAscii().constData();
00533      //qDebug ("File Open Event %s", s);
00534      call ( "texmacs-load-buffer", object(url_system (s)), 
00535             object("generic"), object(1), object(false));
00536      return true;
00537    } else {
00538      // standard event processing
00539      return QObject::eventFilter(obj, event);
00540    }
00541 }
00542 
00543 void
00544 QTMGuiHelper::doWriteSocketNotification (int socket) {
00545   if (DEBUG_QT) 
00546     cout << "WRITE SOCKET NOTIFICATION " << socket << " "
00547          << texmacs_time () << LF;
00548   iterator<socket_notifier> it = iterate (write_notifiers);
00549   while (it->busy ()) {
00550     socket_notifier sn= it->next ();
00551     if (sn->fd == socket) {
00552       //sn->notify();
00553       the_gui->process_socket_notification (sn);
00554       the_gui->enable_notifier (sn, false);
00555     }
00556   }
00557 }
00558 
00559 void
00560 QTMGuiHelper::doReadSocketNotification (int socket) {
00561   if (DEBUG_QT) 
00562     cout << "READ SOCKET NOTIFICATION " << socket << " "
00563          << texmacs_time () << LF;
00564   iterator<socket_notifier> it = iterate (read_notifiers);
00565   while (it->busy ()) {
00566     socket_notifier sn= it->next ();
00567     if (sn->fd == socket) {
00568       //sn->notify();
00569       the_gui->process_socket_notification (sn);
00570       the_gui->enable_notifier (sn, false);
00571     }
00572   }
00573 }
00574 
00575 
00576 /******************************************************************************
00577  * Queued processing
00578  ******************************************************************************/
00579 
00580 enum qp_type_id {
00581   QP_NULL,
00582   QP_KEYPRESS,
00583   QP_KEYBOARD_FOCUS,
00584   QP_MOUSE,
00585   QP_RESIZE,
00586   QP_SOCKET_NOTIFICATION,
00587   QP_COMMAND,
00588   QP_COMMAND_ARGS,
00589   QP_DELAYED_COMMANDS
00590 };
00591 
00592 class qp_type {
00593 public:
00594   qp_type_id sid;
00595   inline qp_type (qp_type_id sid2 = QP_NULL): sid (sid2) {}
00596   inline qp_type (const qp_type& s): sid (s.sid) {}
00597   inline qp_type& operator = (qp_type s) { sid= s.sid; return *this; }
00598   inline operator qp_type_id () { return sid; }
00599   inline bool operator == (qp_type_id sid2) { return sid == sid2; }
00600   inline bool operator != (qp_type_id sid2) { return sid != sid2; }
00601   inline bool operator == (qp_type s) { return sid == s.sid; }
00602   inline bool operator != (qp_type s) { return sid != s.sid; }
00603   inline friend tm_ostream& operator << (tm_ostream& out, qp_type s) 
00604     { return out << s.sid; }
00605 };
00606 
00607 class queued_event : public pair<qp_type, blackbox> 
00608 {
00609 public:
00610   queued_event(qp_type _type = qp_type(), blackbox _bb = blackbox()) 
00611     : pair<qp_type, blackbox>(_type, _bb) {};
00612 };
00613 
00614 
00615 static array<queued_event> waiting_events;
00616 
00617 
00618 void
00619 process_event (queued_event ev) {
00620   //cout << "<" << (qp_type_id) ev.x1 << LF;
00621   switch ((qp_type_id) ev.x1) {
00622     case QP_NULL :
00623       break;
00624     case QP_KEYPRESS :
00625     {
00626       typedef triple<widget, string, time_t > T;
00627       T x = open_box <T> (ev.x2) ;
00628       concrete_simple_widget(x.x1) -> handle_keypress (x.x2, x.x3) ;
00629     }
00630       break;
00631     case QP_KEYBOARD_FOCUS :
00632     {
00633       typedef triple<widget, bool, time_t > T;
00634       T x = open_box <T> (ev.x2) ;
00635       concrete_simple_widget(x.x1) -> handle_keyboard_focus (x.x2, x.x3) ;
00636     }
00637       break;
00638     case QP_MOUSE :
00639     {
00640       typedef quintuple<string, SI, SI, int, time_t > T1;
00641       typedef pair<widget, T1> T;
00642       T x = open_box <T> (ev.x2) ;
00643       concrete_simple_widget(x.x1) -> handle_mouse (x.x2.x1, x.x2.x2, x.x2.x3, x.x2.x4, x.x2.x5) ;
00644     }
00645       break;
00646     case QP_RESIZE :
00647     {
00648       typedef triple<widget, SI, SI > T;
00649       T x = open_box <T> (ev.x2) ;
00650       concrete_simple_widget(x.x1) -> handle_notify_resize (x.x2, x.x3) ;
00651     }
00652       break;
00653     case QP_SOCKET_NOTIFICATION :
00654     {
00655       socket_notifier sn = open_box <socket_notifier> (ev.x2) ;
00656       // cout << "QP_SOCKET_NOTIFICATION " << sn->fd << LF;
00657       sn -> notify ();
00658       the_gui->enable_notifier (sn, true);
00659     }
00660       break;
00661     case QP_COMMAND :
00662     {
00663       command cmd = open_box <command> (ev.x2) ;
00664       // cout << "QP_COMMAND" << LF;
00665       cmd->apply();
00666     }
00667       break;
00668     case QP_COMMAND_ARGS :
00669     {
00670       typedef pair<command, object> T;
00671       T x = open_box <T> (ev.x2) ;
00672       // cout << "QP_COMMAND_ARGS" << LF;
00673       x.x1->apply(x.x2);
00674     }
00675       break;
00676     case QP_DELAYED_COMMANDS :
00677     {
00678       // cout << "QP_DELAYED_COMMANDS" << LF;
00679       exec_pending_commands();
00680       wait_for_delayed_commands = true;
00681     }
00682       break;
00683     default:   
00684       FAILED("Unexpected queued event");
00685   }
00686   //cout << ">" << (qp_type_id) ev.x1 << LF;
00687 }
00688 
00689 
00690 
00691 queued_event 
00692 next_event() {
00693   queued_event ev;
00694   if (N(waiting_events)>0) 
00695     ev = waiting_events[0];
00696   
00697   array<queued_event> a;
00698 
00699   for(int i=1; i<N(waiting_events); i++)
00700     a << waiting_events[i];
00701   waiting_events = a;
00702   
00703   return ev;
00704 }
00705 
00706 void 
00707 qt_gui_rep::process_queued_events (int max) {
00708   // we process a maximum of max events. There are two kind of events: those
00709   // which need a pass on interpose_handler just after and the others. We count
00710   // only the first kind of events. In update() we call this function with
00711   // max=1 so that only one of these "sensible" events is handled. Otherwise
00712   // updating of internal TeXmacs structure become very slow. This can be 
00713   // considerer a limitation of the current implementation of interpose_handler
00714   // Likewise this function is just an hack to get things working properly.
00715   
00716   int count = 0;
00717   //cout << "(" << n << " events)"
00718   while ((max < 0) || (count<max))  {
00719     queued_event ev = next_event();
00720     if (ev.x1 == QP_NULL) break;
00721     process_event(ev);
00722     switch (ev.x1) {
00723         case QP_COMMAND:
00724         case QP_COMMAND_ARGS:
00725         case QP_SOCKET_NOTIFICATION:
00726         case QP_RESIZE:
00727         case QP_DELAYED_COMMANDS:
00728           break;
00729         default:
00730           count++;
00731           break;
00732     }
00733   }
00734 }
00735 
00736 
00737 void
00738 add_event(const queued_event &ev)
00739 {
00740   if (updating) {
00741     waiting_events << ev;
00742     needing_update = true;
00743     //needs_update();
00744   } else {
00745     waiting_events << ev;
00746 //    process_event(ev);
00747     needs_update();
00748     // NOTE: we cannot update now since sometimes this seems to give problems
00749     // to the update of the window size after a resize. In that situation
00750     // sometimes when the window take again focus, update will be called for the
00751     // focus_in event and interpose_handler is run which send a slot_extent
00752     // message to the widget causing a wrong resize of the window.
00753     // this seems to cure the problem.
00754   }
00755 }
00756 
00757 void 
00758 qt_gui_rep::process_keypress (simple_widget_rep *wid, string key, time_t t) {
00759   typedef triple<widget, string, time_t > T;
00760   add_event( queued_event ( QP_KEYPRESS, close_box<T> (T(wid, key, t)))); 
00761  // wid -> handle_keypress (key, t);
00762  // needs_update ();
00763 }
00764 
00765 void 
00766 qt_gui_rep::process_keyboard_focus ( simple_widget_rep *wid, bool has_focus, 
00767                                      time_t t ) {
00768   typedef triple<widget, bool, time_t > T;
00769   add_event( 
00770     queued_event ( QP_KEYBOARD_FOCUS, close_box<T> (T(wid, has_focus, t)))); 
00771   //wid -> handle_keyboard_focus (has_focus, t);
00772   //needs_update ();
00773 }
00774 
00775 void 
00776 qt_gui_rep::process_mouse ( simple_widget_rep *wid, string kind, SI x, SI y, 
00777                             int mods, time_t t ) {
00778   typedef quintuple<string, SI, SI, int, time_t > T1;
00779   typedef pair<widget, T1> T;
00780   add_event ( 
00781     queued_event ( QP_MOUSE, close_box<T> ( T (wid, T1 (kind, x, y, mods, t))))); 
00782 //  wid -> handle_mouse (kind, x, y, mods, t);
00783 //  needs_update ();
00784 }
00785 
00786 void 
00787 qt_gui_rep::process_resize ( simple_widget_rep *wid, SI x, SI y ) {
00788   typedef triple<widget, SI, SI > T;
00789   add_event(  queued_event ( QP_RESIZE, close_box<T> (T(wid, x, y)))); 
00790 //  wid -> handle_notify_resize (x, y);
00791 //  needs_update ();
00792 }
00793 
00794 void 
00795 qt_gui_rep::process_socket_notification ( socket_notifier sn ) {
00796   add_event ( 
00797     queued_event (QP_SOCKET_NOTIFICATION, close_box< socket_notifier > (sn)));
00798 //  sn -> notify ();
00799 //  enable_notifier (sn, true);
00800 // needs_update ();
00801 }
00802 
00803 void 
00804 qt_gui_rep::process_command (command _cmd) {
00805   add_event ( 
00806              queued_event (QP_COMMAND, close_box< command > (_cmd)));
00807 }
00808 
00809 void 
00810 qt_gui_rep::process_command (command _cmd, object _args) {
00811   typedef pair<command, object > T;
00812   add_event ( 
00813              queued_event (QP_COMMAND_ARGS, close_box< T > (T(_cmd,_args))));
00814 }
00815 
00816 void 
00817 qt_gui_rep::process_delayed_commands () {
00818   add_event ( 
00819              queued_event (QP_DELAYED_COMMANDS, blackbox()));
00820 }
00821 
00822 
00823 void
00824 fill_event_queue() {
00825   if (N(waiting_events) == 0) {
00826     qApp->processEvents(QEventLoop::ExcludeSocketNotifiers, 0); 
00827     // if (N(waiting_events) > 0) cout << "addins some events" << LF;
00828   }  
00829 }
00830 
00831 bool
00832 qt_gui_rep::check_event (int type) {
00833 //FIXME: add more types and refine, compare with X11 version.
00834   
00835 // cout << "."; cout.flush();
00836 //  return false;
00837   
00838   // do not interrupt while not in update
00839   // (for example while painting the icons in the menus)
00840   if (!updating || disable_check_event) return false;
00841   
00842   switch (type) {
00843     case INTERRUPT_EVENT:
00844       if (interrupted) return true;
00845       else {
00846         time_t now= texmacs_time ();
00847         if (now - timeout_time < 0) return false;
00848         timeout_time= now + time_credit;
00849         interrupted= (N(waiting_events) > 0);
00850         //if (interrupted) cout << "INTERRUPT " 
00851         //  << now << "------------------" << LF;
00852         return interrupted;
00853       }
00854     case INTERRUPTED_EVENT:
00855       return interrupted;
00856     default:
00857       return false;
00858   }
00859 }
00860 
00861 
00862 void
00863 qt_gui_rep::update () {
00864   // this is called by doUpdate, which in turns is fired by a timer activated in 
00865   // needs_update, and ensuring that interpose_handler is run during a pass in 
00866   // the eventloop afterwards we reactivate the timer with a pause 
00867   // (see FIXME below) 
00868 
00869   if (updating) {
00870     cout << "NESTED UPDATING: This should not happen" << LF;
00871     needs_update();
00872     return;
00873   }
00874 
00875   // cout << "<" << texmacs_time() << " " << N(delayed_queue) << " ";
00876   
00877   updatetimer->stop();
00878   updating = true;
00879   
00880   static int count_events = 0;
00881   static int max_proc_events = 10;
00882 
00883   time_t now = texmacs_time();
00884   needing_update = false;
00885 
00886   time_credit = 100 / (N(waiting_events)+1);
00887 
00888   
00889   // 1.
00890   // check if a wait dialog is active and in the case remove it.
00891   // if we are here then the long operation is finished.
00892   
00893   if (waitDialogs.count()) {
00894     waitWindow->layout()->removeWidget(waitDialogs.last());
00895     waitWindow->close();
00896     while (waitDialogs.count()) {
00897       waitDialogs.last()->deleteLater();
00898       waitDialogs.removeLast();
00899     }
00900   }
00901   
00902   
00903   // 2.
00904   // manage delayed commands
00905   
00906   if (wait_for_delayed_commands && (lapse <= now)) process_delayed_commands();
00907   
00908   // 3.
00909   // if there are pending events in the private queue process them up to some
00910   // limit in processed events is reached. 
00911   // if there are no events or the limit is reached then proceed to a redraw.
00912 
00913   if (N(waiting_events) == 0) {
00914     // if there are not waiting events call at least once
00915     // the interpose handler
00916     if (the_interpose_handler) the_interpose_handler();
00917   } else while ((N(waiting_events) > 0) && (count_events < max_proc_events)) {
00918     // cout << "e";
00919     //cout << "PROCESS QUEUED EVENTS START..."; cout.flush();
00920     process_queued_events (1);
00921     //cout << "AND END"  << LF;
00922     count_events++;
00923     
00924     //cout << "TYPESET START..."; cout.flush();
00925     if (the_interpose_handler) the_interpose_handler();
00926     //cout << "AND END" << LF;
00927   }
00928   
00929   {
00930     // redrawing
00931     // cout << "r";
00932     count_events = 0;
00933     
00934     // repaint invalid regions  
00935     interrupted = false;
00936     timeout_time = texmacs_time() + time_credit;
00937     
00938     //cout << "REPAINT START..."; cout.flush();
00939     
00940     QSetIterator<QTMWidget*> i(QTMWidget::all_widgets);
00941     while (i.hasNext()) {
00942       QTMWidget *w = i.next();
00943       w->repaint_invalid_regions();
00944     }
00945     //cout << "AND END" << LF;
00946   }
00947   
00948   
00949   if (N(waiting_events) > 0) needing_update = true;
00950   if (interrupted) needing_update = true;
00951  // if (interrupted)     cout << "*";
00952 
00953   if (nr_windows == 0) {
00954     qApp->quit();
00955   }
00956   
00957   time_t delay = lapse - texmacs_time();
00958   if (delay > 1000/6) delay = 1000/6;
00959   if (delay < 0) delay = 0;
00960   if (needing_update) delay = 0;
00961   //cout << delay << ">" <<  LF;
00962   updatetimer->start (delay);
00963   
00964   updating = false;
00965   
00966 
00967   // FIXME: we need to ensure that the interpose_handler is run at regular 
00968   //        intervals (1/6th of sec) so that informations on the footbar are 
00969   //        updated. (this should be better handled by promoting code in 
00970   //        tm_editor::apply_changes (which is activated only after idle 
00971   //        periods) at the level of delayed commands in the gui.
00972   //        The interval cannot be too small to keep CPU usage low in idle state
00973 } 
00974 
00975 
00976 
00977 /******************************************************************************
00978 * Font support
00979 ******************************************************************************/
00980 
00981 void
00982 set_default_font (string name) {
00983   (void) name;
00984   // set the name of the default font
00985   // this is ignored since Qt handles fonts for the widgets
00986 }
00987 
00988 font
00989 get_default_font (bool tt, bool mini, bool bold) {
00990   (void) tt; (void) mini; (void) bold;
00991   // get the default font or monospaced font (if tt is true)
00992         
00993   // return a null font since this function is not called in the Qt port.
00994   if (DEBUG_QT) cout << "get_default_font(): SHOULD NOT BE CALLED\n";
00995   return NULL;
00996   //return tex_font (this, "ecrm", 10, 300, 0);
00997 }
00998 
00999 // load the metric and glyphs of a system font
01000 // you are not obliged to provide any system fonts
01001 
01002 void
01003 load_system_font (string family, int size, int dpi,
01004                   font_metric& fnm, font_glyphs& fng)
01005 {
01006   (void) family; (void) size; (void) dpi; (void) fnm; (void) fng;
01007   if (DEBUG_QT) cout << "load_system_font(): SHOULD NOT BE CALLED\n";
01008 }
01009 
01010 /******************************************************************************
01011 * Clipboard support
01012 ******************************************************************************/
01013 
01014 bool
01015 set_selection (string key, tree t, string s, string format) {
01016   // Copy a selection 't' with string equivalent 's' to the clipboard 'cb'
01017   // Returns true on success
01018   return the_gui->set_selection (key, t, s, format);
01019 }
01020 
01021 bool
01022 get_selection (string key, tree& t, string& s, string format) {
01023   // Retrieve the selection 't' with string equivalent 's' from clipboard 'cb'
01024   // Returns true on success; sets t to (extern s) for external selections
01025   return the_gui->get_selection (key, t, s, format);
01026 }
01027 
01028 void
01029 clear_selection (string key) {
01030   // Clear the selection on clipboard 'cb'
01031   the_gui->clear_selection (key);
01032 }
01033 
01034 /******************************************************************************
01035 * Miscellaneous
01036 ******************************************************************************/
01037 int char_clip=0;
01038 
01039 void
01040 beep () {
01041   // Issue a beep
01042   QApplication::beep();
01043 }
01044 
01045 void
01046 force_update() {
01047   if (updating) {
01048     needing_update = true;
01049   }
01050   else {
01051     the_gui->update();
01052   }
01053 }
01054 
01055 void
01056 needs_update () {
01057   // 0 ms - call immediately when all other events have 
01058   // been processed
01059   //cout << "needs_update" << LF;
01060   if (updating) {
01061     needing_update = true;
01062   }
01063   else {
01064     the_gui->updatetimer->start (0);
01065   }
01066 }
01067 
01068 bool
01069 check_event (int type) {
01070   // Check whether an event of one of the above types has occurred;
01071   // we check for keyboard events while repainting windows
01072   return the_gui->check_event(type);
01073 }
01074 
01075 void image_gc (string name) {
01076 // Garbage collect images of a given name (may use wildcards)
01077 // This routine only needs to be implemented if you use your own image cache
01078  the_qt_renderer()->image_gc(name);
01079 }
01080 
01081 void
01082 show_help_balloon (widget balloon, SI x, SI y) {
01083   // Display a help balloon at position (x, y); the help balloon should
01084   // disappear as soon as the user presses a key or moves the mouse
01085   (void) balloon; (void) x; (void) y;
01086 }
01087 
01088 void
01089 show_wait_indicator (widget base, string message, string argument) {
01090   // Display a wait indicator with a message and an optional argument
01091   // The indicator might for instance be displayed at the center of
01092   // the base widget which triggered the lengthy operation;
01093   // the indicator should be removed if the message is empty
01094   the_gui->show_wait_indicator(base,message,argument);
01095 }
01096 
01097 void
01098 external_event (string type, time_t t) {
01099   // External events, such as pushing a button of a remote infrared commander
01100   QTMWidget *tm_focus = qobject_cast<QTMWidget*>(qApp->focusWidget());
01101   if (tm_focus) {
01102     simple_widget_rep *wid = tm_focus->tm_widget();
01103     if (wid) the_gui -> process_keypress (wid, type, t);
01104   }
01105 }
01106 
01107 font x_font (string family, int size, int dpi)
01108 {
01109   (void) family; (void) size; (void) dpi;
01110   if (DEBUG_QT) cout << "x_font(): SHOULD NOT BE CALLED\n";
01111   return NULL;
01112 }
01113 
01114 
01115 QString 
01116 QTMTranslator::translate ( const char * context, const char * sourceText, 
01117                            const char * disambiguation ) const 
01118 {
01119   (void) disambiguation;  (void) context;
01120   if (DEBUG_QT) {
01121     cout << "Translating: " << sourceText << LF;
01122     cout << "Translation: " << qt_translate (sourceText) << LF;
01123   }
01124   return QString (to_qstring (qt_translate (sourceText)));
01125 }