Back to index

texmacs  1.0.7.15
qt_dialogues.cpp
Go to the documentation of this file.
00001 
00002 /******************************************************************************
00003 * MODULE     : qt_dialogues.cpp
00004 * DESCRIPTION: QT dialogues widgets classes
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 "widget.hpp"
00013 #include "message.hpp"
00014 #include "qt_dialogues.hpp"
00015 #include "qt_utilities.hpp"
00016 #include "qt_tm_widget.hpp"
00017 #include "qt_basic_widgets.hpp"
00018 #include "qt_chooser_widget.hpp"
00019 #include "qt_printer_widget.hpp"
00020 #include "qt_color_picker_widget.hpp"
00021 #include "url.hpp"
00022 #include "analyze.hpp"
00023 #include "converter.hpp"
00024 #include "QTMMenuHelper.hpp"
00025 #include "QTMGuiHelper.hpp"
00026 #include "QTMStyle.hpp"
00027 
00028 #include <QtGui>
00029 #include "string.hpp"
00030 #include "scheme.hpp"
00031 
00032 
00033 class qt_field_widget;
00034 
00035 class qt_input_widget_rep: public qt_widget_rep {
00036 protected:
00037   command cmd;
00038   array<qt_field_widget> fields;
00039   coord2 size, position;
00040   string win_title;
00041   int style;
00042 public:
00043   qt_input_widget_rep (command, array<string>);
00044   ~qt_input_widget_rep ();
00045         
00046   virtual void send (slot s, blackbox val);
00047   virtual blackbox query (slot s, int type_id);
00048   virtual widget read (slot s, blackbox index);
00049   virtual void write (slot s, blackbox index, widget w);
00050   virtual void notify (slot s, blackbox new_val);
00051   // virtual void connect (slot s, widget w2, slot s2);
00052   // virtual void deconnect (slot s, widget w2, slot s2);
00053   virtual widget plain_window_widget (string s, command q);
00054   //virtual QLayoutItem* as_qlayoutitem () { return NULL; }
00055   void perform_dialog();
00056 };
00057 
00058 class qt_field_widget_rep: public widget_rep {
00059   string prompt;
00060   string input;
00061   string type;
00062   array<string> proposals;
00063   qt_input_widget_rep *parent;
00064 public:
00065   qt_field_widget_rep(qt_input_widget_rep *_parent):
00066     widget_rep(), prompt(""), input(""),  proposals(), parent(_parent) {}
00067   virtual void send (slot s, blackbox val);
00068   virtual blackbox query (slot s, int type_id);
00069   //virtual QLayoutItem* as_qlayoutitem () { return NULL; }
00070 
00071   friend class qt_input_widget_rep;
00072 };
00073 
00074 void
00075 qt_field_widget_rep::send (slot s, blackbox val) {
00076   if (DEBUG_QT)
00077     cout << "qt_field_widget_rep::send " << slot_name(s) << LF;
00078   switch (s) {
00079   case SLOT_STRING_INPUT:
00080     TYPE_CHECK (type_box (val) == type_helper<string>::id);
00081     input= open_box<string> (val);
00082     // send_string (THIS, "input", val);
00083     break;
00084   case SLOT_INPUT_TYPE:
00085     TYPE_CHECK (type_box (val) == type_helper<string>::id);
00086     type= open_box<string> (val);
00087     break;
00088   case SLOT_INPUT_PROPOSAL:
00089     TYPE_CHECK (type_box (val) == type_helper<string>::id);
00090     proposals << open_box<string> (val);
00091     // send_string (THIS, "default", val);
00092     break;
00093   case SLOT_KEYBOARD_FOCUS:
00094     parent->send(s,val);
00095     break;
00096   default:
00097     widget_rep::send (s, val);
00098   }
00099 }
00100 
00101 blackbox
00102 qt_field_widget_rep::query (slot s, int type_id) {
00103   if (DEBUG_QT)
00104     cout << "qt_field_widget_rep::query " << slot_name(s) << LF;
00105   switch (s) {
00106   case SLOT_STRING_INPUT:
00107     TYPE_CHECK (type_id == type_helper<string>::id);
00108     return close_box<string> (input);
00109   default:
00110     return widget_rep::query (s, type_id);
00111   }
00112 }
00113 
00114 
00115 class qt_field_widget {
00116 public:
00117 ABSTRACT_NULL(qt_field_widget);
00118 };
00119 ABSTRACT_NULL_CODE(qt_field_widget);
00120 
00121 qt_input_widget_rep::qt_input_widget_rep
00122   (command _cmd, array<string> _prompts):
00123     qt_widget_rep (), cmd (_cmd),
00124     fields (N (_prompts)),
00125     size (coord2 (100, 100)), position (coord2 (0, 0)),
00126     win_title (""), style (0)
00127 {
00128   for (int i=0; i < N(_prompts); i++) {
00129     fields[i] = tm_new<qt_field_widget_rep> (this);
00130     fields[i]->prompt = _prompts[i];
00131   }
00132 }
00133 
00134 qt_input_widget_rep::~qt_input_widget_rep()  {}
00135 
00136 void
00137 qt_input_widget_rep::send (slot s, blackbox val) {
00138   if (DEBUG_QT)
00139     cout << "qt_input_widget_rep::send " << slot_name(s) << LF;
00140 
00141   switch (s) {
00142   case SLOT_VISIBILITY:
00143     {   
00144       check_type<bool> (val, "SLOT_VISIBILITY");
00145       bool flag = open_box<bool> (val);
00146       (void) flag;
00147       NOT_IMPLEMENTED
00148     }   
00149     break;
00150   case SLOT_SIZE:
00151     TYPE_CHECK (type_box (val) == type_helper<coord2>::id);
00152     size = open_box<coord2> (val);
00153     break;
00154   case SLOT_POSITION:
00155     TYPE_CHECK (type_box (val) == type_helper<coord2>::id);
00156     position = open_box<coord2> (val);
00157     break;
00158   case SLOT_KEYBOARD_FOCUS:
00159     TYPE_CHECK (type_box (val) == type_helper<bool>::id);
00160     perform_dialog ();
00161     break;
00162   default:
00163     qt_widget_rep::send (s, val);
00164   }
00165 }
00166 
00167 
00168 blackbox
00169 qt_input_widget_rep::query (slot s, int type_id) {
00170   if (DEBUG_QT)
00171     cout << "qt_input_widget_rep::query " << slot_name(s) << LF;
00172   switch (s) {
00173   case SLOT_POSITION:
00174     {
00175       typedef pair<SI,SI> coord2;
00176       TYPE_CHECK (type_id == type_helper<coord2>::id);
00177       return close_box<coord2> (position);
00178     }
00179   case SLOT_SIZE:
00180     {
00181       typedef pair<SI,SI> coord2;
00182       TYPE_CHECK (type_id == type_helper<coord2>::id);
00183       return close_box<coord2> (size);
00184     }
00185   case SLOT_STRING_INPUT:
00186     return fields[0]->query (s, type_id);
00187   default:
00188     return qt_widget_rep::query (s, type_id);
00189   }
00190 }
00191 
00192 void
00193 qt_input_widget_rep::notify (slot s, blackbox new_val) {
00194   if (DEBUG_QT)
00195     cout << "[qt_input_widget_rep] ";
00196   switch (s) {
00197   default: ;
00198   }
00199   qt_widget_rep::notify (s, new_val);
00200 }
00201 
00202 widget
00203 qt_input_widget_rep::read (slot s, blackbox index) {
00204   if (DEBUG_QT)
00205     cout << "qt_input_widget_rep::read " << slot_name(s) << LF;
00206   switch (s) {
00207   case SLOT_WINDOW:
00208     check_type_void (index, "SLOT_WINDOW");
00209     return this;
00210   case SLOT_FORM_FIELD:
00211     check_type<int> (index, "SLOT_FORM_FIELD");
00212     return (widget_rep*) (fields [open_box<int> (index)].rep);
00213   default:
00214     return qt_widget_rep::read (s, index);
00215   }
00216 }
00217 
00218 void
00219 qt_input_widget_rep::write (slot s, blackbox index, widget w) {
00220   if (DEBUG_QT)
00221     cout << "[qt_input_widget_rep] ";
00222   switch (s) {
00223   default:
00224     qt_widget_rep::write (s, index, w);
00225   }
00226 }
00227 
00228 widget
00229 qt_input_widget_rep::plain_window_widget (string s, command q)
00230 {
00231   win_title = s;
00232   (void) q;  // FIXME? ignore ok?
00233   return this;
00234 }
00235 
00236 void
00237 qt_input_widget_rep::perform_dialog() {
00238   if ((N(fields)==1) && (fields[0]->type == "question")) {
00239    // then use Qt messagebox for smoother, more standard UI
00240     QWidget * mainwindow = QApplication::activeWindow ();
00241     // main texmacs window. There are probably better ways...
00242     // Presently not checking if the windows has the focus;
00243     // In case it has not, it should be brought into focus
00244     // before calling the dialog
00245     QMessageBox * msgBox=new QMessageBox(mainwindow);
00246     //sets parent widget, so that appears at proper location   
00247     msgBox->setText(to_qstring (qt_translate (fields[0]->prompt)));
00248     msgBox->setStandardButtons(QMessageBox::Cancel);
00249     int choices = N(fields[0]->proposals);
00250     QVector<QPushButton*> buttonlist (choices);
00251     //allowing for any number of choices
00252     for(int i=0; i<choices; i++) {
00253       string blabel= "&" * upcase_first (fields[0]->proposals[i]);
00254       //capitalize the first character?
00255       buttonlist[i] = msgBox->addButton (to_qstring (qt_translate (blabel)), QMessageBox::ActionRole);
00256     }
00257     msgBox->setDefaultButton (buttonlist[0]); //default is first choice
00258     msgBox->setWindowTitle (to_qstring (qt_translate ("Question")));
00259     msgBox->setIcon (QMessageBox::Question);
00260 
00261     msgBox->exec();
00262     bool buttonclicked=false;
00263     for(int i=0; i<choices; i++) {
00264       if (msgBox->clickedButton() == buttonlist[i]) {
00265         fields[0] -> input = scm_quote (fields[0]->proposals[i]);
00266         buttonclicked=true;
00267         break;
00268       }
00269     }
00270     if (!buttonclicked) {fields[0] -> input = "#f";} //cancelled
00271   }
00272 
00273   else {  //usual dialogue layout
00274     QDialog d (0, Qt::Sheet);
00275     QVBoxLayout* vl = new QVBoxLayout(&d);
00276 
00277     QVector<QComboBox*> cbs (N (fields));
00278 
00279     for(int i=0; i<N(fields); i++) {
00280       QHBoxLayout *hl = new QHBoxLayout();
00281 
00282       QLabel *lab = new QLabel (to_qstring (tm_var_encode( (fields[i]->prompt))),&d);
00283       cbs[i] = new QComboBox(&d);
00284       cbs[i] -> setSizeAdjustPolicy (QComboBox::AdjustToMinimumContentsLength);
00285       cbs[i] -> setEditText (to_qstring(fields[i]->input));
00286       int minlen = 0;
00287       for(int j=0; j < N(fields[i]->proposals); j++) {
00288         QString str = to_qstring (fields[i]->proposals[j]);
00289         cbs[i] -> addItem (str);
00290         int c = str.count();
00291         if (c > minlen) minlen = c;
00292       }
00293       cbs[i] -> setMinimumContentsLength (minlen>50 ? 50 : (minlen < 2 ? 10 : minlen));
00294       cbs[i] -> setEditable (true);
00295       // apparently the following flag prevents Qt from substituting
00296       // an history item for an input when they differ only from
00297       // the point of view of case (upper/lower)
00298       // eg. if the history contains aAAAAa and you type AAAAAA then
00299       // the combo box will retain the string aAAAAa    
00300       cbs[i]->setDuplicatesEnabled(true); 
00301       cbs[i]->completer()->setCaseSensitivity(Qt::CaseSensitive);
00302       lab -> setBuddy (cbs[i]);
00303       hl -> addWidget (lab);
00304       hl -> addWidget (cbs[i]);
00305       vl -> addLayout (hl);
00306       
00307       if (ends (fields[i]->type, "file") || fields[i]->type == "directory") {
00308         // autocompletion
00309         //QCompleter *completer = new QCompleter(cbs[i]);
00310         QCompleter *completer = cbs[i]->completer();
00311         QDirModel *dirModel = new QDirModel();
00312         completer->setModel(dirModel);
00313         //cbs[i]->setCompleter(completer);
00314       }
00315     }
00316     
00317     {
00318       QDialogButtonBox* buttonBox =
00319         new QDialogButtonBox (QDialogButtonBox::Ok | QDialogButtonBox::Cancel,
00320                               Qt::Horizontal, &d);
00321       QObject::connect (buttonBox, SIGNAL (accepted()), &d, SLOT (accept()));
00322       QObject::connect (buttonBox, SIGNAL (rejected()), &d, SLOT (reject()));
00323       vl -> addWidget (buttonBox);
00324     }
00325     //  d.setLayout (vl);
00326     d.setWindowTitle(to_qstring(win_title));
00327     QPoint pos = to_qpoint(position);
00328     //cout << "Size :" << size.x1 << "," << size.x2 << LF;
00329     //cout << "Position :" << pos.x() << "," << pos.y() << LF;
00330     
00331     d.updateGeometry();
00332     QSize sz = d.sizeHint();
00333     QRect r; r.setSize(sz);
00334     r.moveCenter(pos);
00335     d.setGeometry(r);
00336     
00337     int result = d.exec ();
00338     if (result == QDialog::Accepted) {
00339       for(int i=0; i<N(fields); i++) {
00340         QString item = cbs[i]->currentText();
00341         fields[i] -> input = scm_quote (from_qstring (item));
00342       }
00343     } else {
00344       for(int i=0; i<N(fields); i++) {
00345         fields[i] -> input = "#f";
00346       }
00347     }
00348   }
00349 
00350   cmd ();
00351 }
00352 
00353 
00354 /*******************************************************************************
00355 * Input text widget implementation
00356 *******************************************************************************/
00357 
00358 class QTMLineEdit : public QLineEdit {
00359   
00360 public:
00361   string ww; // width
00362   
00363   QTMLineEdit (QWidget *parent, string _ww) 
00364   : QLineEdit (parent), ww (_ww) {
00365     setStyle (qtmstyle());
00366     QPalette pal (palette());
00367     pal.setColor (QPalette::Base, QColor (252, 252, 248));
00368     setPalette (pal);
00369   };
00370   
00371   virtual QSize      sizeHint () const ;
00372 protected:
00373   void keyPressEvent (QKeyEvent *event);
00374   void focusInEvent (QFocusEvent *evenement);
00375 
00376 };
00377 
00378 void 
00379 QTMLineEdit::keyPressEvent(QKeyEvent *event)
00380 {
00381   QCompleter *c = completer();
00382   // reset completion
00383   if (c) c->setCompletionPrefix (QString ());
00384 
00385   if (event->key() == Qt::Key_Up){
00386     if (c) {
00387       int row = c->currentRow();
00388       c->setCurrentRow(row-1);
00389       setText(c->currentCompletion());
00390     }
00391     event->accept();
00392     // move back in history
00393   } else if (event->key() == Qt::Key_Down){
00394     if (c) {
00395       int row = c->currentRow();
00396       c->setCurrentRow(row+1);
00397       setText(c->currentCompletion());
00398     }
00399     event->accept();
00400     // move forward in history
00401   } else if (event->key() == Qt::Key_Escape){
00402     emit editingFinished();
00403     event->accept();
00404     // exit editing
00405   }
00406   else {
00407     // default handler for event
00408     QLineEdit::keyPressEvent(event);
00409   }
00410 }
00411 
00412 QSize
00413 QTMLineEdit::sizeHint () const {
00414   QSize sz(QLineEdit::sizeHint());
00415   string s = ww; // to avoid const casting
00416   if (ends (s, "w") && is_double (s (0, N(s) - 1)) && parentWidget()) {
00417     double x= as_double (s (0, N(s) - 1));
00418     sz.setWidth(parentWidget()->width() * x);
00419 //    ev->w= max (ev->w, (4 * fn->wquad + 2*dw) / SHRINK);
00420   } else if (ends (s, "em") && is_double (s (0, N(s) - 2))) {
00421     double x= as_double (s (0, N(s) - 2));
00422     QFontMetrics fm(fontMetrics());
00423     sz.setWidth(x*fm.width("m")); //FIXME: put real font width
00424   }  else if (ends (s, "px") && is_double (s (0, N(s) - 2))) {
00425     double x= as_double (s (0, N(s) - 2));
00426     sz.setWidth(x);
00427   }  
00428   return sz;
00429 }
00430 
00431 void 
00432 QTMLineEdit::focusInEvent (QFocusEvent *event)
00433 {
00434   setCursorPosition (text().size());
00435 //  selectAll ();
00436   QLineEdit::focusInEvent (event);
00437 }
00438 
00439 QAction *
00440 qt_input_text_widget_rep::as_qaction () {
00441   QTMWidgetAction *a = new QTMWidgetAction (this);
00442   return a;
00443 }
00444 
00445 QWidget *
00446 qt_input_text_widget_rep::as_qwidget () {
00447   if (!helper) {
00448     helper = new QTMInputTextWidgetHelper(this);
00449     // helper retains the current widget
00450     // in toolbars the widget is not referenced directly in texmacs code
00451     // so must be retained by Qt objects
00452   }
00453   QLineEdit *le;
00454   // FIXME: how is this check necessary (out of memory check seems unlikely...)
00455   if (helper) {
00456     le = new QTMLineEdit (NULL, helper->wid()->width);
00457     helper -> add (le);
00458     QObject::connect(le, SIGNAL(returnPressed ()), helper, SLOT(commit ()));
00459     QObject::connect(le, SIGNAL(editingFinished ()), helper, SLOT(leave ()));
00460     le -> setText (to_qstring (helper->wid()->text));
00461     
00462     le -> setStyleSheet (to_qstylesheet (style));
00463     le -> setMinimumSize(qt_decode_length(width, le));
00464     
00465     if (ends (type, "file") || type == "directory") {
00466       // autocompletion
00467       QCompleter *completer = new QCompleter(le);
00468       QDirModel *dirModel = new QDirModel(le);
00469       completer->setModel(dirModel);
00470       le->setCompleter(completer);
00471     } else if (N(def) > 0) {
00472       QStringList items;
00473       for (int j=0; j < N(def); j++) {
00474         items << to_qstring(def[j]);
00475       }
00476       QCompleter *completer = new QCompleter(items, le);
00477       completer->setCaseSensitivity(Qt::CaseSensitive);
00478       completer->setCompletionMode(QCompleter::InlineCompletion);
00479       le->setCompleter(completer);
00480     }
00481     
00482   } else {
00483     le = new QLineEdit(NULL);
00484   }
00485   return le;
00486 }
00487 
00488 
00489 QLayoutItem *
00490 qt_input_text_widget_rep::as_qlayoutitem () {
00491   return new QWidgetItem (as_qwidget ());
00492 }
00493 
00494 
00495 qt_input_text_widget_rep::qt_input_text_widget_rep 
00496   (command _cmd, string _type, array<string> _def, int _style, string _width)
00497 : cmd (_cmd), type (_type), def (_def), text (""), style(_style), width(_width),
00498   helper(NULL), ok(false) 
00499 {
00500   if (N(def) > 0) {
00501     text = def[0];
00502   }
00503 }
00504 
00505 
00506 qt_input_text_widget_rep::~qt_input_text_widget_rep() { 
00507 }
00508 
00509 
00510 /*******************************************************************************
00511 * Interface to texmacs. See src/Graphics/Gui/widget.hpp.
00512 *******************************************************************************/
00513 
00514 
00515 widget
00516 inputs_list_widget (command call_back, array<string> prompts) {
00517   // a dialogue widget with Ok and Cancel buttons and a series of textual
00518   // input widgets with specified prompts
00519   if (DEBUG_QT) cout << "inputs_list_widget\n";
00520   return tm_new<qt_input_widget_rep> (call_back, prompts);
00521 }
00522 
00523 widget
00524 input_text_widget (command call_back, string type, array<string> def,
00525                    int style, string width) {
00526   // a textual input widget for input of a given type and a list of suggested
00527   // default inputs (the first one should be displayed, if there is one)
00528   return tm_new<qt_input_text_widget_rep> (call_back, type, def, style, width);
00529 }
00530 
00531 widget
00532 color_picker_widget (command call_back, bool bg, array<tree> proposals) {
00533   // widgets for selecting a color, a pattern or a background image,
00534   // encoded by a tree. On input, we give a list of recently used proposals
00535   // on termination the command is called with the selected color as argument
00536   // the bg flag specifies whether we are picking a background color or fill
00537   
00538   return tm_new<qt_color_picker_widget_rep>(call_back, bg, proposals);  
00539 }
00540 
00541 widget
00542 file_chooser_widget (command cmd, string type, bool save)  {
00543   // file chooser widget for files of a given type; for files of type "image",
00544   // the widget includes a previsualizer and a default magnification
00545   // for importation can be specified
00546   return tm_new<qt_chooser_widget_rep> (cmd, type, save);
00547 }
00548 
00549 widget 
00550 printer_widget (command cmd, url ps_pdf_file) {
00551   // widget to print the document, offering a way for selecting a page range,
00552   // changing the paper type and orientation, previewing, etc.
00553   return tm_new<qt_printer_widget_rep>(cmd, ps_pdf_file);
00554 }