Back to index

texmacs  1.0.7.15
qt_ui_element.cpp
Go to the documentation of this file.
00001 /******************************************************************************
00002  * MODULE     : qt_ui_element.cpp
00003  * DESCRIPTION: User interface proxies
00004  * COPYRIGHT  : (C) 2010  Massimiliano Gubinelli
00005  *******************************************************************************
00006  * This software falls under the GNU general public license version 3 or later.
00007  * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
00008  * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
00009  ******************************************************************************/
00010 
00011 #include "qt_ui_element.hpp"
00012 #include "qt_utilities.hpp"
00013 #include "qt_renderer.hpp"
00014 #include "qt_simple_widget.hpp"
00015 #include "qt_basic_widgets.hpp"
00016 #include "QTMMenuHelper.hpp"
00017 #include "QTMStyle.hpp"
00018 #include "analyze.hpp"
00019 #include "widget.hpp"
00020 #include "message.hpp"
00021 #include "promise.hpp"
00022 #include "scheme.hpp"
00023 //#import "TMView.h"
00024 #include <QtGui>
00025 
00026 #include "QTMWindow.hpp"
00027 #include "QTMGuiHelper.hpp"
00028 #include "qt_gui.hpp"
00029 #include "qt_window_widget.hpp"
00030 #include "qt_menu.hpp"
00031 
00032 #include "../../Style/Evaluate/evaluate_main.hpp" // required for as_length(string)
00033 
00034 
00035 const char *ui_type_string[]= {
00036   "horizontal_menu", "vertical_menu", "horizontal_list", "vertical_list",
00037   "tile_menu", "minibar_menu", "menu_separator", "menu_group", 
00038   "pulldown_button", "pullright_button", "menu_button",
00039   "balloon_widget", "text_widget", "xpm_widget", "toggle_widget",
00040   "enum_widget", "choice_widget", "scrollable_widget",
00041   "hsplit_widget", "vsplit_widget", 
00042   "aligned_widget", "tabs_widget", "wrapped_widget"
00043 };
00044 
00045 
00046 /******************************************************************************
00047  * Auxiliary classes
00048  ******************************************************************************/
00049 
00050 // we use this class to properly initialize style options
00051 // for our QWidgets which have to blend into QMenus
00052 // see #QTBUG-1993.
00053 // see #QTBUG-7707.
00054 
00055 class QTMAuxMenu: public QMenu {
00056 public:
00057   QTMAuxMenu (): QMenu() {}
00058   
00059   void myInitStyleOption (QStyleOptionMenuItem *option) const {
00060     QAction action (NULL);
00061     initStyleOption(option,&action);
00062   }
00063 };
00064 
00065 
00066 // we need to subclass QToolButton for two reasons
00067 // 1) custom appearence
00068 // 2) if used in QWidgetAction the menu do not disappear upon triggering the
00069 //    button. See QTBUG-10427.
00070 
00071 // QTMMenuButton is a custom button appropriate for menus
00072 
00073 class QTMMenuButton: public QToolButton {
00074   QStyleOptionMenuItem option;
00075 public:
00076   QTMMenuButton (QWidget* parent = 0): QToolButton(parent) {
00077     QTMAuxMenu m;
00078     m.myInitStyleOption (&option);
00079     setAttribute (Qt::WA_Hover);
00080   }  
00081   void mouseReleaseEvent (QMouseEvent *event);
00082   void mousePressEvent (QMouseEvent *event);
00083   void paintEvent (QPaintEvent *event);
00084 };
00085 
00086 void
00087 QTMMenuButton::mousePressEvent (QMouseEvent* event) {
00088   // this one triggers the action and toggles the button
00089   QToolButton::mousePressEvent (event);
00090   // this one forwards the event to the parent
00091   // (which eventually is the menu)
00092   QWidget::mousePressEvent (event);
00093 }
00094 
00095 void
00096 QTMMenuButton::mouseReleaseEvent (QMouseEvent* event) {
00097   // this one triggers the action and untoggles the button
00098   QToolButton::mouseReleaseEvent (event);
00099   // this one forwards the event to the parent
00100   // (which eventually is the menu which then close itself)
00101   QWidget::mouseReleaseEvent (event);
00102 }
00103 
00104 void
00105 QTMMenuButton::paintEvent (QPaintEvent* event) {
00106   (void) event;
00107   QPainter p (this);
00108   
00109   // initialize the options
00110   QStyleOptionToolButton buttonOpt;
00111   initStyleOption (&buttonOpt);
00112   QRect r = rect ();
00113   option.rect = r;
00114   option.state = QStyle::State_Enabled |
00115   ( buttonOpt.state & QStyle::State_MouseOver ? 
00116    QStyle::State_Selected : QStyle::State_None ); 
00117   // draw the control background as a menu item
00118   style () -> drawControl (QStyle::CE_MenuItem, &option, &p, this); 
00119   // draw the icon with a bit of inset.
00120   r.adjust (2,2,-2,-2);
00121   defaultAction ()-> icon ().paint (&p, r);
00122 }
00123 
00124 
00125 class QTMMenuWidget: public QWidget {
00126   QStyleOptionMenuItem option;
00127 public:
00128   QTMMenuWidget (QWidget* parent = 0): QWidget(parent) {
00129     QTMAuxMenu m;
00130     m.myInitStyleOption (&option);
00131   }
00132   void paintEvent(QPaintEvent *event);
00133 };
00134 
00135 void
00136 QTMMenuWidget::paintEvent(QPaintEvent* event) {
00137   (void) event;
00138   QPainter p (this);
00139   option.rect = rect ();
00140   //QRect r = rect ();
00141   style()->drawControl (QStyle::CE_MenuEmptyArea, &option, &p, this);
00142   QWidget::paintEvent(event);
00143 }
00144 
00145 
00146 
00147 
00148 QTMWidgetAction::QTMWidgetAction (widget _wid, QObject *parent)
00149 : QWidgetAction (parent), wid (_wid) { 
00150   QObject::connect (the_gui->gui_helper, SIGNAL(refresh()), this, SLOT(doRefresh()));
00151 }
00152 
00153 QTMWidgetAction::~QTMWidgetAction() {
00154 }
00155 
00156 
00157 void 
00158 QTMWidgetAction::doRefresh() {
00159 #if 0
00160   if (N(str)) {
00161     string t= tm_var_encode (str);
00162     if (t == "Help") t= "Help ";
00163     setText (to_qstring (t));
00164   }
00165 #endif
00166 }
00167 
00168 QWidget * 
00169 QTMWidgetAction::createWidget ( QWidget * parent ) {
00170   QWidget *qw = concrete(wid)->as_qwidget();
00171   qw->setParent(parent);
00172   return qw;
00173 }
00174 
00175 
00176 class QTMTileAction: public QWidgetAction {
00177   QVector <QAction*> actions;
00178   int cols;
00179 public:
00180   QTMTileAction (QWidget* parent, array<widget>& arr, int _cols)
00181   : QWidgetAction (parent), cols (_cols)
00182   {
00183     actions.reserve(N(arr));
00184     for(int i = 0; i < N(arr); i++) {
00185       if (is_nil(arr[i])) break;
00186       QAction *act = concrete(arr[i])->as_qaction();
00187       act->setParent(this);
00188       actions.append(act);
00189     };
00190   }
00191   QWidget* createWidget(QWidget* parent);
00192   // virtual void activate (ActionEvent event) {
00193   //   cout << "TRIG\n"; QWidgetAction::activate (event); }
00194 };
00195 
00196 
00197 // FIXME: QTMTileAction::createWidget is called twice:
00198 // the first time when the action is added to the menu,
00199 // the second when from the menu it is transferred to the toolbar.
00200 // This is weird since the first widget does not ever use
00201 // the widget so it results in a waste of time.
00202 
00203 QWidget*
00204 QTMTileAction::createWidget(QWidget* parent) {
00205   if (DEBUG_QT) 
00206     cout << "QTMTileAction::createWidget\n";
00207   QWidget* wid= new QTMMenuWidget (parent);
00208   QGridLayout* l= new QGridLayout (wid);
00209   // wid->setAutoFillBackground(true);
00210   // wid->setBackgroundRole(QPalette::Base);
00211   wid->setLayout (l);
00212   l->setSizeConstraint (QLayout::SetFixedSize);
00213   l->setHorizontalSpacing (2);
00214   l->setVerticalSpacing (2);
00215   l->setContentsMargins (4, 0, 4, 0);
00216   int row= 0, col= 0;
00217   for (int i=0; i < actions.count(); i++) {
00218     QAction* sa= actions[i];
00219     QToolButton* tb= new QTMMenuButton (wid);
00220     tb->setDefaultAction (sa);
00221     QObject::connect(tb, SIGNAL(released()), this, SLOT(trigger()));
00222     //  tb->setStyle (qtmstyle ());
00223     l->addWidget (tb, row, col);
00224     col++;
00225     if (col >= cols) { col = 0; row++; }
00226   }
00227   return wid;
00228 }
00229 
00230 class QTMMinibarAction : public QWidgetAction {
00231   QVector <QAction*> actions;
00232 public:
00233   QTMMinibarAction (QWidget* parent, array<widget>& arr)
00234   : QWidgetAction (parent)
00235   {
00236     actions.reserve(N(arr));
00237     for(int i = 0; i < N(arr); i++) {
00238       if (is_nil(arr[i])) break;
00239       QAction *act = concrete(arr[i])->as_qaction();
00240       act->setParent(this);
00241       actions.append(act);
00242     };
00243   }
00244   QWidget* createWidget(QWidget* parent);
00245   // virtual void activate (ActionEvent event) {
00246   //   cout << "TRIG\n"; QWidgetAction::activate (event); }
00247 };
00248 
00249 // FIXME: QTMMinibarAction::createWidget is called twice:
00250 // the first time when the action is added to the menu,
00251 // the second when from the menu it is transferred to the toolbar.
00252 // This is weird since the first widget does not ever use
00253 // the widget so it results in a waste of time.
00254 
00255 QWidget*
00256 QTMMinibarAction::createWidget(QWidget* parent) {
00257   if (DEBUG_QT) cout << "QTMMinibarAction::createWidget\n";
00258   QWidget* wid= new QWidget (parent);
00259   QBoxLayout* l= new QBoxLayout (QBoxLayout::LeftToRight, wid);
00260   wid->setLayout (l);
00261   //  l->setSizeConstraint (QLayout::SetFixedSize);
00262   l->setContentsMargins (0, 0, 0, 0);
00263   l->setSpacing(0);
00264   for (int i=0; i < actions.count(); i++) {
00265     QAction* sa= actions[i];
00266     if (QWidgetAction * wa = qobject_cast<QWidgetAction*>(sa)) {
00267       QWidget *w = wa->requestWidget(wid);
00268       l->addWidget(w);
00269     } else if ((sa->text().isNull())&&(sa->icon().isNull())) {
00270       l->addSpacing(8);
00271     } else {
00272       QToolButton *tb = new QToolButton(wid);
00273       
00274       //HACK: texmacs does not use the checked state of the action
00275       // if the action is checkable then it means that it should be
00276       // checked
00277       sa->setChecked(sa->isCheckable());
00278       
00279       tb->setDefaultAction(sa);
00280       tb->setAutoRaise(true);
00281       tb->setPopupMode (QToolButton::InstantPopup);
00282       tb->setStyle(qtmstyle());
00283       //  tb->setIconSize(QSize(12,12));
00284       QFont f = tb->font();
00285       f.setPixelSize(10);
00286       tb->setFont(f);
00287       l->addWidget(tb);
00288     }
00289   }
00290   return wid;
00291 }
00292 
00293 
00294 
00295 static string
00296 conv_sub (string ks) {
00297   string r(ks);
00298 #ifdef Q_WS_MAC
00299   r = replace (r, "S-", "Shift+");
00300   r = replace (r, "C-", "Meta+");
00301   r = replace (r, "A-", "Alt+");
00302   r = replace (r, "M-", "Ctrl+");
00303   //r = replace (r, "K-", "");
00304   r = replace (r, " ", ",");
00305 #else
00306   r = replace (r, "S-", "Shift+");
00307   r = replace (r, "C-", "Ctrl+");
00308   r = replace (r, "A-", "Alt+");
00309   r = replace (r, "M-", "Meta+");
00310   //r = replace (r, "K-", "");
00311   r = replace (r, " ", ",");
00312 #endif
00313   if (N(r) == 1 || (N(r) > 2 && r[N(r)-2] == '+')) {
00314     if (is_locase (r[N(r)-1]))
00315       r= r (0, N(r)-1) * upcase_all (r (N(r)-1, N(r)));
00316     else if (is_upcase (r[N(r)-1]))
00317       r= r (0, N(r)-1) * "Shift+" * upcase_all (r (N(r)-1, N(r)));
00318   }
00319   return r;
00320 }
00321 
00322 static string
00323 conv (string s) {
00324   int i=0, k;
00325   string r;
00326   for (k=0; k<=N(s); k++)
00327     if (k == N(s) || s[k] == ' ') {
00328       r << conv_sub (s (i, k));
00329       i= k;
00330     }
00331   return r;
00332 }
00333 
00334 
00335 qt_ui_element_rep::~qt_ui_element_rep()
00336 {
00337   if (cachedAction) delete cachedAction;
00338 }
00339 
00340 widget 
00341 qt_ui_element_rep::make_popup_widget () {
00342   return tm_new<qt_menu_rep>(as_qaction());
00343 }
00344 
00345 widget 
00346 qt_ui_element_rep::popup_window_widget (string s)  {
00347   return concrete(make_popup_widget())->popup_window_widget(s);
00348 }
00349 
00350 
00351 
00360 class qt_plain_window_widget_rep: public qt_view_widget_rep {
00361   // QWidget* view;   // inherited
00362 public:
00363 
00364   qt_plain_window_widget_rep (QWidget* w) : qt_view_widget_rep(w) {
00365     ASSERT(w != NULL, "Null QWidgets cannot be promoted to windows");
00366     w->setProperty ("texmacs_window_widget", QVariant::fromValue ((void*) this));
00367   };
00368   ~qt_plain_window_widget_rep () {};
00369   
00370   virtual void send (slot s, blackbox val);
00371 };
00372 
00373 
00374 void
00375 qt_plain_window_widget_rep::send (slot s, blackbox val) {
00376   if (DEBUG_QT)
00377     cout << "qt_plain_widget_rep::send " << slot_name(s) << LF;
00378   switch (s) {
00379     case SLOT_POSITION:
00380     {
00381       TYPE_CHECK (type_box (val) == type_helper<coord2>::id);
00382       coord2 p= open_box<coord2> (val);
00383       if (view) {
00384         QPoint pt = to_qpoint (p);
00385         pt.ry() += 40;
00386         // to avoid window under menu bar on MAC when moving at (0,0)
00387         if (DEBUG_QT) 
00388           cout << "Moving to (" << pt.x() << "," 
00389           << pt.y() << ")" << LF;
00390         view->move (pt);
00391       }
00392     }
00393       break;
00394     case SLOT_VISIBILITY:
00395     {   
00396       check_type<bool> (val, "SLOT_VISIBILITY");
00397       bool flag = open_box<bool> (val);
00398       if (flag)
00399         view->show();
00400       else 
00401         view->hide();
00402     }   
00403       break;
00404     default:
00405       qt_view_widget_rep::send (s,val);
00406   }
00407 }
00408 
00409 
00410 widget 
00411 qt_ui_element_rep::plain_window_widget (string s, command quit)  {
00412   QLayoutItem *li     = as_qlayoutitem();
00413   QTMPlainWindow* win = new QTMPlainWindow();
00414   win->setWindowTitle (to_qstring (s));  
00415   
00416   if (li->widget()) 
00417     win->layout()->addItem(li);
00418   else if (li->layout())
00419     win->setLayout(li->layout());
00420   
00421 
00422   QTMCommand* qtmcmd = new QTMCommand(quit);
00423   qtmcmd->setParent(win);
00424   QObject::connect(win, SIGNAL(closed()), qtmcmd, SLOT(apply()));
00425 
00426   // FIXME: using qt_plain_window_widget causes a crash when closing "secondary"
00427   // windows. (example widget6 in menu-test.scm)
00428   //return tm_new<qt_plain_window_widget_rep>(win);
00429   return tm_new<qt_window_widget_rep>(win, quit);
00430 }
00431 
00432 QMenu *
00433 qt_ui_element_rep::get_qmenu() {
00434   if (!cachedAction) {
00435     cachedAction = as_qaction();
00436   }
00437   return (cachedAction ? cachedAction->menu() : NULL);
00438 }
00439 
00440 
00446 class qt_key_command_rep: public command_rep {
00447   string ks; 
00448     
00449 public:
00450   qt_key_command_rep(string ks_) : ks(ks_) { }
00451   
00452   void apply () {
00453   if (N(ks)) { 
00454     QTMWidget *w = qobject_cast<QTMWidget*>(qApp->focusWidget());
00455       if (w && w->tm_widget()) {
00456         if (DEBUG_QT) cout << "shortcut: " << ks << LF;
00457         the_gui -> process_keypress (w->tm_widget(), ks, texmacs_time());
00458       }
00459     }
00460   }
00461   
00462   tm_ostream& print (tm_ostream& out) { return out << "qt_key_command_rep"; }
00463 };
00464 
00465 
00466 QAction* 
00467 qt_ui_element_rep::as_qaction () {
00468   switch (type) {
00469     case horizontal_menu:
00470     case vertical_menu:
00471     case horizontal_list:
00472     case vertical_list:
00473     {
00474       typedef array<widget> T;
00475       array<widget> arr = open_box<T> (load);
00476       
00477       // a horizontal menu made up of the widgets in a
00478       QAction* act= new QTMAction (NULL);
00479       act->setText("Menu");
00480       QMenu* m= new QMenu ();
00481       for (int i = 0; i < N(arr); i++) {
00482         if (is_nil (arr[i])) break;
00483         QAction* a= concrete (arr[i]) -> as_qaction ();
00484         m->addAction (a);
00485         a->setParent (m);
00486       }
00487       act->setMenu (m);
00488       return act;
00489     }
00490       break;
00491       
00492     case tile_menu: 
00493     {
00494       typedef pair<array<widget>, int> T;
00495       T x = open_box<T>(load);
00496       array<widget> a = x.x1;
00497       int cols = x.x2;
00498       
00499       // a menu rendered as a table of cols columns wide & made up of widgets in a
00500       QWidgetAction* act= new QTMTileAction (NULL, a, cols);
00501       return act;
00502     }
00503       break;
00504       
00505     case minibar_menu: 
00506     {
00507       typedef array<widget> T;
00508       array<widget> arr = open_box<T> (load);
00509 
00510       QWidgetAction* act= new QTMMinibarAction (NULL, arr);
00511       return act;
00512     }
00513       break;
00514       
00515     case menu_separator: 
00516     {
00517       typedef bool T;
00518       bool vertical = open_box<T> (load);
00519       // a horizontal or vertical menu separator
00520       (void) vertical;
00521       QAction* a= new QTMAction (NULL);
00522       a->setSeparator (true);
00523       return a;
00524     }
00525       break;
00526     case glue_widget:
00527     {
00528       QTMAction* a = new QTMAction();
00529       a->setEnabled(false);
00530       return a;
00531     }
00532       break;
00533     case menu_group: 
00534     {
00535       typedef pair<string, int> T;
00536       T x = open_box<T>(load);
00537       string name = x.x1;
00538       int style = x.x2;
00539 
00540       // a menu group; the name should be greyed and centered
00541       QAction* a= new QTMAction (NULL);
00542       a->setText(to_qstring(tm_var_encode ((name))));
00543       a->setEnabled (false);
00544       if (style == WIDGET_STYLE_MINI) {
00545         QFont f = a->font();
00546         f.setPointSize(10);
00547         a->setFont(f);
00548       }  
00549       return a;
00550     }
00551       break;
00552       
00553     case pulldown_button:
00554     case pullright_button:
00555     {
00556       typedef pair<widget, promise<widget> > T;
00557       T x = open_box<T>(load);
00558       widget w = x.x1;
00559       promise<widget> pw = x.x2;
00560       
00561       // a button w with a lazy pulldown menu pw
00562       QAction* a= concrete (w) -> as_qaction ();
00563       QTMLazyMenu* lm= new QTMLazyMenu (pw);
00564       QMenu *old_menu = a->menu();
00565       a->setMenu (lm);
00566       a->setEnabled(true);
00567       if (old_menu) {
00568         cout << "this should not happen\n";
00569         delete old_menu;
00570       }
00571       return a;
00572     }
00573       break;
00574       
00575     case menu_button:
00576     {
00577       typedef quintuple<widget, command, string, string, int> T;
00578       T x = open_box<T>(load);
00579       widget w = x.x1;
00580       command cmd = x.x2;
00581       string pre = x.x3;
00582       string ks = x.x4;
00583       int style = x.x5;
00584       
00585       // a command button with an optional prefix (o, * or v) and
00586       // keyboard shortcut; if ok does not hold, then the button is greyed
00587       bool ok= (style & WIDGET_STYLE_INERT) == 0;
00588       QAction* a= NULL;
00589       a= concrete(w)->as_qaction();
00590 #ifdef Q_WS_MAC
00591       if (search_forwards (" ", ks) != -1) ks= "";
00592 #endif
00593       QTMCommand* c;
00594       if (N(ks) > 0) {
00595         string qtks = conv (ks);
00596         QKeySequence qks (to_qstring (qtks));
00597         if (DEBUG_QT)
00598           cout << "ks: " << ks << " " << qks.toString().toAscii().data() << "\n";
00599         a->setShortcut (qks);
00600         command key_cmd = tm_new<qt_key_command_rep>(ks);
00601         c= new QTMCommand (key_cmd);
00602       } else {
00603         c= new QTMCommand (cmd);
00604       }
00605       c->setParent (a);
00606         
00607       // NOTE: this used to be a Qt::QueuedConnection, but the slot would not
00608       // be called if in a contextual menu
00609       QObject::connect (a, SIGNAL (triggered ()), c, SLOT (apply ()));    
00610   
00611       // FIXME: implement complete prefix handling
00612       a->setEnabled (ok? true: false);
00613       
00614       bool check = (pre != "") || (style & WIDGET_STYLE_PRESSED);
00615       
00616       a->setCheckable (check? true: false);
00617       a->setChecked (check? true: false);
00618       if (pre == "v") {}
00619       else if (pre == "*") {}
00620       // [mi setOnStateImage:[NSImage imageNamed:@"TMStarMenuBullet"]];
00621       else if (pre == "o") {}
00622       return a;
00623     }
00624       break;
00625       
00626     case balloon_widget:
00627     {
00628       typedef pair<widget, widget> T;
00629       T x = open_box<T>(load);
00630       widget text = x.x1;
00631       widget help = x.x2;
00632       
00633       // given a button widget w, specify a help balloon which should be displayed
00634       // when the user leaves the mouse pointer on the button for a small while
00635       QAction* a= concrete(text)->as_qaction();
00636       {
00637         typedef quartet<string, int, color, bool> T1;
00638         T1 x = open_box<T1>(static_cast<qt_ui_element_rep*>(help.rep)->load);
00639         string str = x.x1;
00640         a->setToolTip (to_qstring (str));
00641         // HACK: force displaying of the tooltip (needed for items in the QMenuBar)
00642         QObject::connect(a, SIGNAL(hovered()), a, SLOT(showToolTip()));
00643       }
00644       return a;
00645     }
00646       break;
00647       
00648     case text_widget:
00649     {
00650       typedef quartet<string, int, color, bool> T;
00651       T x = open_box<T>(load);
00652       string str = x.x1;
00653       int style = x.x2;
00654       //color col = x.x3;
00655       //bool tsp = x.x4;
00656       
00657       // a text widget with a given color and transparency
00658 
00659       QTMAction* a= new QTMAction (NULL);
00660       string t= tm_var_encode (str);
00661       if (t == "Help") t= "Help "; // HACK to avoid MacOS autodetection of the Help menu?
00662       a->setText(to_qstring (t));
00663       a->str = str;
00664       if (style == WIDGET_STYLE_MINI) {
00665         QFont f = a->font();
00666         f.setPointSize(10);
00667         a->setFont(f);
00668       }
00669       return a;
00670     }
00671       break;
00672       
00673     case xpm_widget:
00674     {
00675       url image = open_box<url>(load);
00676 
00677       // return widget ();
00678       // a widget with an X pixmap icon
00679       QAction* a= new QTMAction (NULL);
00680       QPixmap* img= the_qt_renderer () -> xpm_image (image);
00681       QIcon icon (*img);
00682       a->setIcon (icon);
00683       return a;
00684     }
00685       break;
00686       
00687     default:
00688       ;
00689   }
00690   
00691   return NULL;
00692 }
00693 
00694 
00695 class QTMUIButton: public QToolButton {
00696 public:
00697   QTMUIButton (QWidget* parent = 0): QToolButton(parent) {}
00698   void paintEvent(QPaintEvent *event);
00699 };
00700 
00701 
00702 void
00703 QTMUIButton::paintEvent(QPaintEvent* event) {
00704   (void) event;
00705   QPainter p (this);
00706   defaultAction()->icon().paint (&p, rect ());
00707 }
00708 
00709 QLayoutItem *
00710 qt_ui_element_rep::as_qlayoutitem () {
00711   //cout << "as_qlayoutitem " << ui_type_string[type] << LF;
00712 
00713   switch (type) {
00714     case horizontal_menu:
00715     case vertical_menu:
00716     case horizontal_list:
00717     case vertical_list:
00718     {
00719       typedef array<widget> T;
00720       array<widget> arr = open_box<T> (load);
00721       
00722       // a horizontal/vertical menu made up of the widgets in a
00723       QLayout *l;
00724       if ((type == horizontal_list) || (type==horizontal_menu))
00725         l =  new QHBoxLayout();
00726       else
00727         l =  new QVBoxLayout();
00728 
00729       for (int i = 0; i < N(arr); i++) {
00730         if (is_nil (arr[i])) break;
00731         QLayoutItem* li= concrete (arr[i]) -> as_qlayoutitem ();
00732         if (li) l->addItem(li); // ownership transferred
00733       }
00734       return l;
00735     }
00736       break;
00737 
00738     // a menu rendered as a table of 'cols' columns wide & made up of widgets in
00739     // the array 'a'
00740     case tile_menu: 
00741     {
00742       typedef pair<array<widget>, int> T;
00743       T x = open_box<T>(load);
00744       array<widget> a = x.x1;
00745       int cols = x.x2;
00746             
00747       QGridLayout* l= new QGridLayout ();
00748       l->setSizeConstraint (QLayout::SetFixedSize);
00749       l->setHorizontalSpacing (2);
00750       l->setVerticalSpacing (2);
00751       l->setContentsMargins (4, 0, 4, 0);
00752       int row= 0, col= 0;
00753       for (int i=0; i < N(a); i++) {
00754         QLayoutItem *li = concrete(a[i])->as_qlayoutitem();
00755         l->addItem(li, row, col);
00756         col++;
00757         if (col >= cols) { col = 0; row++; }
00758       }
00759       return l;
00760     }
00761       break;
00762 
00763     //  a table with two columns
00764     case aligned_widget:       
00765     {
00766       typedef quartet<SI, SI, SI, SI> T1;
00767       typedef triple<array<widget>, array<widget>, T1 > T;
00768       T x = open_box<T>(load);
00769       array<widget> lhs = x.x1;
00770       array<widget> rhs = x.x2;
00771       T1 y = x.x3;
00772 
00773       // FIXME: lpad and rpad ignored.
00774       SI hsep = y.x1; SI vsep = y.x2; SI lpad = y.x3; SI rpad = y.x4;
00775       
00776       if (N(lhs) != N(rhs)) FAILED("aligned_widget: N(lhs) != N(rhs) ");
00777 
00778       QGridLayout* l= new QGridLayout ();
00779       l->setSizeConstraint (QLayout::SetMinimumSize);
00780       l->setHorizontalSpacing (6+hsep/PIXEL);
00781       l->setVerticalSpacing (2+vsep/PIXEL);
00782       for (int i=0; i < N(lhs); i++) {
00783         QLayoutItem* lli = concrete(lhs[i])->as_qlayoutitem();
00784         QLayoutItem* rli = concrete(rhs[i])->as_qlayoutitem();
00785         if (lli) l->addItem(lli, i, 0, 1, 1, Qt::AlignRight);
00786         if (rli) l->addItem(rli, i, 1, 1, 1, Qt::AlignLeft);
00787       }
00788       return l;
00789     }
00790       break;
00791       
00792       
00793     case minibar_menu: 
00794     {
00795       typedef array<widget> T;
00796       array<widget> arr = open_box<T> (load);
00797       QBoxLayout* l= new QBoxLayout (QBoxLayout::LeftToRight);
00798       l->setContentsMargins (0, 0, 0, 0);
00799       l->setSpacing(0);
00800       for (int i=0; i < N(arr); i++) {
00801         QLayoutItem *li = concrete(arr[i])->as_qlayoutitem();
00802         l->addItem(li);
00803       }
00804       return l;
00805     }
00806       break;
00807       
00808     case menu_separator: 
00809     {
00810       typedef bool T;
00811       bool vertical = open_box<T> (load);
00812       QSizePolicy::Policy hpolicy = vertical ? QSizePolicy::Fixed 
00813                                              : QSizePolicy::Preferred;
00814       QSizePolicy::Policy vpolicy = vertical ? QSizePolicy::Preferred 
00815                                              : QSizePolicy::Fixed;
00816       return new QSpacerItem (1, 1, hpolicy, vpolicy);
00817     }
00818       break;
00819       
00820     case menu_group: 
00821     {
00822       typedef pair<string, int> T;
00823       T x = open_box<T>(load);
00824       string name = x.x1;
00825       int style = x.x2;
00826       
00827       (void) style;
00828       // a menu group; the name should be greyed and centered
00829       return NULL;
00830     }
00831       break;
00832       
00833     case pulldown_button:
00834     case pullright_button:
00835     case menu_button:
00836     case text_widget:
00837     case xpm_widget:
00838     case toggle_widget:
00839     case enum_widget:
00840     case choice_widget:
00841     case scrollable_widget:
00842     case hsplit_widget:
00843     case vsplit_widget:
00844     case tabs_widget:
00845     case wrapped_widget:
00846     {
00847       QWidget *w = this->as_qwidget();
00848       return new QWidgetItem(w);
00849     }
00850       break;
00851       
00852     case balloon_widget:
00853     {
00854       typedef pair<widget, widget> T;
00855       T x = open_box<T>(load);
00856       widget text = x.x1;
00857       widget help = x.x2;
00858       
00859       // given a button widget w, specify a help balloon which should be displayed
00860       // when the user leaves the mouse pointer on the button for a small while
00861       QLayoutItem* li= concrete(text)->as_qlayoutitem();
00862       if (li->widget())
00863       {
00864         typedef quartet<string, int, color, bool> T;
00865         T x = open_box<T>(static_cast<qt_ui_element_rep*>(help.rep)->load);
00866         string str = x.x1;
00867         li->widget()->setToolTip (to_qstring (str));
00868       }
00869       return li;
00870     }
00871       break;
00872 
00873     case glue_widget:
00874     {
00875       typedef quartet<bool, bool, SI, SI> T;
00876       T x = open_box<T>(load);
00877       QSizePolicy::Policy hpolicy = x.x1 ? QSizePolicy::Preferred 
00878                                          : QSizePolicy::Fixed;
00879       QSizePolicy::Policy vpolicy = x.x2 ? QSizePolicy::Preferred
00880                                          : QSizePolicy::Fixed;
00881       return new QSpacerItem (x.x3, x.x4, hpolicy, vpolicy);
00882     }
00883       break;
00884     default:
00885       ;
00886   }
00887   
00888   return NULL;
00889 }
00890 
00899 class qt_toggle_command_rep: public command_rep {
00900   QPointer<QCheckBox> qwid;
00901   command cmd; 
00902 
00903 public:
00904   qt_toggle_command_rep(QCheckBox* w, command c) : qwid(w), cmd(c) { }
00905   void apply () { if(qwid) cmd (list_object (object (qwid->isChecked()))); }
00906   tm_ostream& print (tm_ostream& out) {
00907     return out << "Toggle"; }
00908 };
00909 
00915 class qt_enum_command_rep: public command_rep {
00916   QPointer<QComboBox> qwid;
00917   command cmd; 
00918   
00919 public:
00920   qt_enum_command_rep(QComboBox* w, command c) : qwid(w), cmd(c) {}
00921   void apply () { 
00922     if (qwid)
00923       cmd (list_object (object (from_qstring(qwid->currentText()))));
00924   }
00925   tm_ostream& print (tm_ostream& out) {
00926     return out << "Enum"; }
00927 };
00928 
00935 class qt_choice_command_rep: public command_rep {
00936   QPointer<QListWidget> qwid;
00937   command cmd;
00938   bool multiple;  //<! Whether multiple choices are allowed in the widget.
00939   
00940 public:
00941   qt_choice_command_rep(QListWidget* w, command c, bool m) : qwid(w), cmd(c), multiple(m) {}
00942   void apply () { 
00943     if (qwid) {
00944       QList<QListWidgetItem*> items = qwid->selectedItems();
00945       array<string> selected;
00946       for(int i = 0; i < items.size(); ++i)
00947         selected << from_qstring (items[i]->text());
00948       object l= null_object ();
00949       if(multiple)
00950         for (int i = N(selected)-1; i >= 0; --i)
00951           l= cons (selected[i], l);
00952       else if(N(selected)>0)  //Do not return a list with the item if only one
00953         l= selected[0];
00954       cmd (list_object (l));
00955     }
00956   }
00957   tm_ostream& print (tm_ostream& out) { return out << "Choice"; }
00958 };
00959 
00960 
00961 QWidget *
00962 qt_ui_element_rep::as_qwidget () {
00963   //cout << "as_qwidget " << ui_type_string[type] << LF;
00964 
00965   switch (type) {
00966     case horizontal_menu:
00967     case vertical_menu:
00968     case horizontal_list:
00969     case vertical_list:
00970     case tile_menu: 
00971     case minibar_menu: 
00972     case aligned_widget: 
00973     {
00974       QLayoutItem *li = this->as_qlayoutitem();
00975       QWidget *w = new QWidget();
00976       if (QLayout *l = li->layout()) {
00977         // note that the QLayout is the same object as the QLayoutItem 
00978         // so no need to free li
00979         w->setLayout(l);
00980       } else {
00981         cout << "qt_ui_element_rep::as_qwidget : invalid situation" << LF;
00982       }
00983       return w;
00984     }
00985       break;
00986       
00987     case menu_separator: 
00988     case menu_group:
00989     case glue_widget:
00990     {
00991       return NULL;
00992     }
00993       break;
00994       
00995     case pulldown_button:
00996     case pullright_button:
00997     {
00998       typedef pair<widget, promise<widget> > T;
00999       T x = open_box<T>(load);
01000       widget w = x.x1;
01001       promise<widget> pw = x.x2;
01002       
01003       // a button w with a lazy pulldown menu pw
01004       
01005       QAction* a= concrete (this) -> as_qaction ();
01006       QToolButton *b = new QTMUIButton();
01007       a->setParent(b);
01008       b->setDefaultAction(a);
01009       return b;
01010     }
01011       break;
01012       
01013     case menu_button:
01014     {
01015       typedef quintuple<widget, command, string, string, int> T;
01016       T x = open_box<T>(load);
01017       widget w = x.x1;
01018       command cmd = x.x2;
01019       string pre = x.x3;
01020       string ks = x.x4;
01021       int style = x.x5;
01022       
01023       
01024       // a command button with an optional prefix (o, * or v) and
01025       // keyboard shortcut; if ok does not hold, then the button is greyed
01026       QAction* a= concrete(this)->as_qaction();
01027       QToolButton *b = (style & WIDGET_STYLE_BUTTON) ? new QToolButton() : new QTMUIButton();
01028       b->setDefaultAction(a);
01029       a->setParent(b);
01030       return b;
01031     }
01032       break;
01033       
01034     case balloon_widget:
01035     {
01036       typedef pair<widget, widget> T;
01037       T x = open_box<T>(load);
01038       widget text = x.x1;
01039       widget help = x.x2;
01040       
01041       // given a button widget w, specify a help balloon which should be displayed
01042       // when the user leaves the mouse pointer on the button for a small while
01043       QWidget* w= concrete(text)->as_qwidget();
01044       if (w)
01045       {
01046         typedef quartet<string, int, color, bool> T1;
01047         T1 x = open_box<T1>(static_cast<qt_ui_element_rep*>(help.rep)->load);
01048         string str = x.x1;
01049         w->setToolTip (to_qstring (str));
01050       }
01051       return w;
01052     }
01053       break;
01054       
01055     case text_widget:
01056     {
01057       typedef quartet<string, int, color, bool> T;
01058       T x = open_box<T>(load);
01059       string str    = x.x1;
01060       QString style = to_qstylesheet(x.x2, x.x3);
01061       
01062       // a text widget with a given color and transparency
01063       QLabel *w = new QLabel();
01064 #if 0
01065       //FIXME: implement refresh when changing language
01066       QTMAction* a= new QTMAction (NULL);
01067       //a->str = str;
01068 #endif
01069       string t= tm_var_encode (str);
01070       if (t == "Help") t= "Help ";
01071       //w->setTextFormat(Qt::RichText);
01072       w->setText(to_qstring (t));
01073       w->setStyleSheet(style);
01074       return w;
01075     }
01076       break;
01077       
01078     case xpm_widget:
01079     {
01080       url image = open_box<url>(load);
01081       
01082       // return widget ();
01083       // a widget with an X pixmap icon
01084       QLabel* l= new QLabel (NULL);
01085       QPixmap* img= the_qt_renderer () -> xpm_image (image);
01086       QIcon icon (*img);
01087       l->setPixmap (*img);
01088       return l;
01089     }
01090       break;
01091 
01092     case toggle_widget:
01093     { 
01094       typedef triple<command, bool, int > T;
01095       T x = open_box<T>(load);
01096       command cmd = x.x1;
01097       bool check  = x.x2;
01098       QString style = to_qstylesheet(x.x3);
01099       
01100       QCheckBox* w  = new QCheckBox (NULL);  
01101       w->setCheckState(check ? Qt::Checked : Qt::Unchecked);
01102       w->setStyleSheet(style);
01103       command tcmd = tm_new<qt_toggle_command_rep> (w, cmd);
01104       QTMCommand* c = new QTMCommand (tcmd);
01105       c->setParent (w);
01106       QObject::connect (w, SIGNAL (stateChanged(int)), c, SLOT (apply()));
01107 
01108       return w;
01109     }
01110       break;
01111       
01112     case enum_widget:
01113     {
01114       typedef quintuple<command, array<string>, string, int, string> T;
01115       T x = open_box<T>(load);
01116       command cmd        = x.x1;
01117       QStringList values = to_qstringlist(x.x2);
01118       QString value      = to_qstring(x.x3);
01119       QString style      = to_qstylesheet(x.x4);
01120             
01121       QComboBox* w = new QComboBox(NULL);
01122       w->setEditable(value.isEmpty() || values.last().isEmpty());  // weird convention?!
01123       if (values.last().isEmpty())
01124         values.removeLast();
01125       
01126       w->addItems(values);
01127       int index = w->findText(value, Qt::MatchFixedString | Qt::MatchCaseSensitive);
01128       if (index != -1)
01129         w->setCurrentIndex(index);
01130    
01131       // The QComboBox must be already filled to calculate relative widths
01132       QSize size= qt_decode_length(x.x5, w);
01133       w->setMinimumSize(size);
01134       // QComboBox::AdjustToContentsOnFirstShow would fix the size. Better?
01135       w->setSizeAdjustPolicy(QComboBox::AdjustToContents);
01136       w->setStyleSheet(style);
01137       
01138       command ecmd = tm_new<qt_enum_command_rep> (w, cmd);
01139       QTMCommand* c = new QTMCommand (ecmd);
01140       c->setParent (w);
01141       // NOTE: with QueuedConnections, the slots are sometimes not invoked.
01142       QObject::connect (w, SIGNAL (currentIndexChanged(int)), c, SLOT (apply()));
01143       
01144       return w;
01145     }
01146       break;
01147       
01148     case choice_widget:
01149     {
01150       typedef quartet<command, array<string>, array<string>, bool > T;
01151       T x = open_box<T>(load);
01152       command cmd = x.x1;
01153       QStringList items  = to_qstringlist(x.x2);
01154       QStringList chosen = to_qstringlist(x.x3);
01155       bool multiple_sel  = x.x4;
01156       
01157       QListWidget* w = new QListWidget();
01158       w->addItems(items);
01159 
01160       if (multiple_sel)
01161         w->setSelectionMode(QAbstractItemView::ExtendedSelection);  // Support CTRL and SHIFT multiple selections.
01162       else
01163         w->setSelectionMode(QAbstractItemView::SingleSelection);
01164       
01165       for (int i = 0; i < chosen.size(); ++i) {
01166         QListWidgetItem* item = w->item(i);
01167         item->setSelected(chosen.contains(item->text(), Qt::CaseSensitive));  // Qt::CaseSensitive is the default anyway
01168       }
01169       
01170       command ecmd = tm_new<qt_choice_command_rep> (w, cmd, multiple_sel);
01171       QTMCommand* qcmd = new QTMCommand (ecmd);
01172       qcmd->setParent (w);
01173       QObject::connect (w, SIGNAL (itemSelectionChanged()), qcmd, SLOT (apply()));//, Qt::QueuedConnection);
01174       
01175       return w;      
01176     }
01177       break;
01178       
01179     case scrollable_widget:
01180     {
01181       typedef pair<widget, int> T;
01182       T x = open_box<T>(load);
01183       widget wid    = x.x1;
01184       QString style = to_qstylesheet(x.x2);
01185       
01186       QScrollArea* scroll = new QScrollArea();
01187       scroll->setBackgroundRole(QPalette::NoRole);
01188       scroll->setStyleSheet(style);
01189       QWidget* w = concrete(wid)->as_qwidget();
01190       scroll->setWidget(w);
01191     
01192       // FIXME????
01193       // "Note that You must add the layout of widget before you call this function; 
01194       //  if you add it later, the widget will not be visible - regardless of when you show() the scroll area.
01195       //  In this case, you can also not show() the widget later."
01196       return scroll;
01197     }
01198       break;
01199       
01200     case hsplit_widget:
01201     case vsplit_widget:
01202     {
01203       typedef pair<widget, widget> T;
01204       T x = open_box<T>(load);
01205       widget w1 = x.x1;
01206       widget w2 = x.x2;
01207       
01208       QWidget* qw1 = concrete(w1)->as_qwidget();
01209       QWidget* qw2 = concrete(w2)->as_qwidget();
01210       QSplitter* split = new QSplitter();
01211       split->setOrientation(type == hsplit_widget ? Qt::Horizontal : Qt::Vertical);
01212       split->addWidget(qw1);
01213       split->addWidget(qw2);
01214       
01215       return split;
01216     }
01217       break;
01218 
01219     case tabs_widget:
01220     {
01221       typedef pair<array<widget>, array<widget> > T;
01222       T x = open_box<T>(load);
01223       array<widget> tabs = x.x1;
01224       array<widget> bodies = x.x2;
01225       
01226       QTMTabWidget* tw = new QTMTabWidget ();
01227       
01228       int i;
01229       for (i = 0; i < N(tabs); i++) {
01230         if (is_nil (tabs[i])) break;
01231         QWidget* prelabel = concrete (tabs[i]) -> as_qwidget();
01232         QLabel* label = qobject_cast<QLabel*> (prelabel);
01233         QWidget* body = concrete (bodies[i]) -> as_qwidget();
01234         tw->addTab(body, label ? label->text() : "");
01235         delete prelabel;
01236       }
01237       if (i>0) tw->resizeOthers(0);   // Force the automatic resizing
01238 
01239       return tw;
01240     }
01241 
01242     case wrapped_widget:
01243     {
01244       typedef pair<widget, command> T;
01245       T x = open_box<T>(load);
01246       widget w = x.x1;
01247       command cmd = x.x2;
01248       
01249       QWidget* qw = concrete(w)->as_qwidget();
01250       QTMCommand* c = new QTMCommand (cmd);
01251       c->setParent (qw);
01252       QObject::connect (qw, SIGNAL (destroyed()), c, SLOT (apply()));//, Qt::QueuedConnection);
01253       
01254       return qw;
01255     }
01256       break;
01257       
01258     default:
01259       ;
01260   }
01261   
01262   return NULL;
01263 }
01264 
01265 
01266 /******************************************************************************
01267 * Widgets for the construction of menus and dialogs
01268 ******************************************************************************/
01269 
01270 // TeXmacs interface
01271 
01272 
01273 
01274 widget horizontal_menu (array<widget> arr) { return qt_ui_element_rep::create (qt_ui_element_rep::horizontal_menu, arr); }
01275 widget vertical_menu (array<widget> arr)  { return qt_ui_element_rep::create (qt_ui_element_rep::vertical_menu, arr); }
01276 widget horizontal_list (array<widget> arr) { return qt_ui_element_rep::create (qt_ui_element_rep::horizontal_list, arr); }
01277 widget vertical_list (array<widget> arr) { return qt_ui_element_rep::create (qt_ui_element_rep::vertical_list, arr); }
01278 widget aligned_widget (array<widget> lhs, array<widget> rhs, SI hsep, SI vsep, SI lpad, SI rpad) { 
01279   typedef quartet<SI, SI, SI, SI> T1;
01280   typedef triple<array<widget>, array<widget>, T1> T;
01281   return tm_new <qt_ui_element_rep> (qt_ui_element_rep::aligned_widget, 
01282                                      close_box (T (lhs,rhs, T1 (hsep, vsep, lpad, rpad)))); 
01283 }
01284 widget tabs_widget (array<widget> tabs, array<widget> bodies) { return qt_ui_element_rep::create (qt_ui_element_rep::tabs_widget, tabs, bodies); }
01285 widget wrapped_widget (widget w, command cmd) { return qt_ui_element_rep::create (qt_ui_element_rep::wrapped_widget, w, cmd); }
01286 widget tile_menu (array<widget> a, int cols) { return qt_ui_element_rep::create (qt_ui_element_rep::tile_menu, a, cols); }
01287 widget minibar_menu (array<widget> arr) { return qt_ui_element_rep::create (qt_ui_element_rep::minibar_menu, arr); }
01288 widget menu_separator (bool vertical) { return qt_ui_element_rep::create (qt_ui_element_rep::menu_separator, vertical); }
01289 widget menu_group (string name, int style) { return qt_ui_element_rep::create (qt_ui_element_rep::menu_group , name, style); }
01290 widget pulldown_button (widget w, promise<widget> pw) { return qt_ui_element_rep::create (qt_ui_element_rep::pulldown_button, w, pw); }
01291 widget pullright_button (widget w, promise<widget> pw) { return qt_ui_element_rep::create (qt_ui_element_rep::pullright_button, w, pw); }
01292 widget menu_button (widget w, command cmd, string pre, string ks, int style) { return qt_ui_element_rep::create (qt_ui_element_rep::menu_button, w, cmd, pre, ks, style); }
01293 widget balloon_widget (widget w, widget help) { return qt_ui_element_rep::create (qt_ui_element_rep::balloon_widget, w, help); }
01294 widget text_widget (string s, int style, color col, bool tsp) { return qt_ui_element_rep::create (qt_ui_element_rep::text_widget, s, style, col, tsp); }
01295 widget xpm_widget (url file_name) { return qt_ui_element_rep::create (qt_ui_element_rep::xpm_widget, file_name); }
01296 widget toggle_widget (command cmd, bool on, int style) { return qt_ui_element_rep::create (qt_ui_element_rep::toggle_widget, cmd, on, style); }
01297 widget enum_widget (command cmd, array<string> vals, string val, int style, string width) { return qt_ui_element_rep::create (qt_ui_element_rep::enum_widget, cmd, vals, val, style, width); }
01298 widget choice_widget (command cmd, array<string> vals, array<string> chosen) { return qt_ui_element_rep::create(qt_ui_element_rep::choice_widget, cmd, vals, chosen, true); }
01299 widget choice_widget (command cmd, array<string> vals, string cur) {
01300   array<string> chosen (1);
01301   chosen[0]= cur;
01302   return qt_ui_element_rep::create(qt_ui_element_rep::choice_widget, cmd, vals, chosen, false); }
01303 widget user_canvas_widget (widget wid, int style) { return qt_ui_element_rep::create(qt_ui_element_rep::scrollable_widget, wid, style); }
01304 widget resize_widget (widget w, int style, string w1, string h1,
01305                       string w2, string h2, string w3, string h3) {
01306   (void) w; (void) style; (void) w1; (void) h1; (void) w2; (void) h2; (void) w3; (void) h3;
01307   //FIXME: add a meaningul semantics
01308   return w;
01309 }
01310 widget hsplit_widget (widget l, widget r) { return qt_ui_element_rep::create(qt_ui_element_rep::hsplit_widget, l, r); }
01311 widget vsplit_widget (widget t, widget b) { return qt_ui_element_rep::create(qt_ui_element_rep::vsplit_widget, t, b); }
01312 widget ink_widget (command cb) {
01313   (void) cb;
01314   FAILED ("not yet implemented"); }
01315 widget refresh_widget (string tmwid) {
01316   (void) tmwid;
01317   FAILED ("not yet implemented"); }