1 
2 /******************************************************************************
3 * MODULE     : qt_widget.cpp
4 * DESCRIPTION: QT widget class
5 * COPYRIGHT  : (C) 2008  Massimiliano Gubinelli
6 *******************************************************************************
7 * This software falls under the GNU general public license version 3 or later.
8 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
9 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
10 ******************************************************************************/
11 
12 #include "qt_utilities.hpp"
13 #include "qt_renderer.hpp"
14 
15 #include "qt_widget.hpp"
16 #include "qt_ui_element.hpp"
17 #include "qt_simple_widget.hpp"
18 #include "qt_tm_widget.hpp"
19 #include "qt_window_widget.hpp"
20 #include "qt_chooser_widget.hpp"
21 #include "qt_color_picker_widget.hpp"
22 #include "qt_printer_widget.hpp"
23 
24 #include "window.hpp"
25 
26 #include <QWidget>
27 #include <QWidgetItem>
28 #include "QTMMenuHelper.hpp"
29 #include "QTMWindow.hpp"
30 
31 
32 /******************************************************************************
33  * qt_widget_rep: the base widget for the Qt port.
34  ******************************************************************************/
35 
36 static long widget_counter = 0;
37 
qt_widget_rep(types _type,QWidget * _qwid)38 qt_widget_rep::qt_widget_rep(types _type, QWidget* _qwid)
39   : widget_rep (), id (widget_counter++), qwid (_qwid), type (_type)
40 {
41   if (DEBUG_QT_WIDGETS)
42     debug_widgets << "qt_widget_rep: created a " << type_as_string() << LF;
43 }
44 
~qt_widget_rep()45 qt_widget_rep::~qt_widget_rep() {
46   if (DEBUG_QT_WIDGETS)
47     debug_widgets << "~qt_widget_rep: deleted a " << type_as_string() << LF;
48 
49   // DON'T DO THIS! (several qt_widget_rep may have the same underlying QWidget)
50   // UPD: really? when? And it wouldn't matter since we are using a guarded
51   // pointer
52 //  delete qwid;
53 }
54 
55 void
add_child(widget w)56 qt_widget_rep::add_child (widget w) {
57   children << w;
58 }
59 
60 void
add_children(array<widget> a)61 qt_widget_rep::add_children (array<widget> a) {
62   children << a;
63 }
64 
65 void
send(slot s,blackbox val)66 qt_widget_rep::send (slot s, blackbox val) {
67   switch (s) {
68     case SLOT_KEYBOARD_FOCUS:
69     {
70       check_type<bool> (val, s);
71       bool focus = open_box<bool> (val);
72       if (focus && qwid && !qwid->hasFocus())
73         qwid->setFocus (Qt::OtherFocusReason);
74     }
75       break;
76     case SLOT_KEYBOARD_FOCUS_ON:
77     {
78       string field = open_box<string>(val);
79       if (qwid) {
80         QWidget* target = qwid->findChild<QWidget*> (to_qstring (field));
81         if (target == NULL)
82           target = qwid->findChild<QWidget*> ("default focus target");
83         if (target) target->setFocus(Qt::OtherFocusReason);
84       }
85       /* FIXME: This would be better than using QObject::findChild but it won't
86        work because the array of children is only sloppily used at best
87        (meaning some objects are not assigned as children of others...)
88        If this is ever made to work, we'll want to handle SLOT_KEYBOARD_FOCUS_ON
89        inside qt_input_text_widget_rep and possibly others
90 
91       for (int i = 0; i < N(children); ++i)
92         if (!is_nil(children[i])) children[i]->send (s, val);
93        */
94     }
95       break;
96     case SLOT_NAME:
97     {
98         // CHECK ME!
99       widget win = qt_window_widget_rep::widget_from_qwidget (qwid);
100       if (! is_nil (win)) win->send (SLOT_NAME, val);
101     }
102       break;
103     case SLOT_DESTROY:
104     {
105       if (DEBUG_QT_WIDGETS)
106         debug_widgets << "Resending to " << N(children) << " children" << LF;
107       for (int i = 0; i < N(children); ++i)
108         if (!is_nil(children[i])) children[i]->send (s, val);
109     }
110       break;
111     default:
112       if (DEBUG_QT_WIDGETS)
113         debug_widgets << "qt_widget_rep::send(), unhandled " << slot_name (s)
114                       << " for widget of type: " << type_as_string() << ".\n";
115   }
116 }
117 
118 /*! Returns the actual QWidget underlying this qt_widget_rep.
119 
120  Implementations of this method must comply with the following:
121 
122   * The policy is to give ownership of the object to the caller.
123   * The pointer qt_widget_rep::qwid must be set to the returned object.
124   * A new QWidget is built on each call.
125 
126  */
127 inline QWidget*
as_qwidget()128 qt_widget_rep::as_qwidget () {
129   if (DEBUG_QT_WIDGETS)
130     debug_widgets << "qt_widget_rep::as_qwidget() for "
131                   << type_as_string() << LF;
132   return qwid;
133 }
134 
135 /*! Returns the QAction associated to this qt_widget_rep.
136 
137  The default action is of type QTMAction, does nothing and is disabled.
138  See the remarks about memory management and menu instantiation and insertion
139  in the comments to class qt_menu_rep.
140  */
141 QAction*
as_qaction()142 qt_widget_rep::as_qaction() {
143   QAction* a = new QTMAction (NULL);
144   a->setEnabled (false);
145   return a;
146 }
147 
148 /*! Wraps the underlying QWidget into a QWidgetItem and returns it.
149 
150  The policy is to give ownership of the object to the caller.
151 */
152 inline QLayoutItem*
as_qlayoutitem()153 qt_widget_rep::as_qlayoutitem () {
154   return new QWidgetItem (as_qwidget ());
155 }
156 
157 
158 /*! Returns the QMenu associated if any.
159 
160  This method must not give ownership of the menu to the caller, thus
161  allowing menu caching at the TeXmacs level. See the implementations in
162  qt_ui_element_rep and qt_menu_rep.
163  */
164 QMenu*
get_qmenu()165 qt_widget_rep::get_qmenu () {
166   return NULL;
167 }
168 
169 
170 /*! Returns the widget as a window.
171 
172  Each TeXmacs widget can at some point be asked to present itself into a window.
173  The scheme-originating function window_create () expects this method in every
174  widget.
175 
176  This default implementation constructs a wrapper qt_window_widget for the
177  widget and returns it. This wrapper will hold a new QTMPlainWindow object
178  which will manage close events and take ownership of the original QWidget.
179  qt_window_widget owns the QTMPlainWindow and is responsible for its deletion.
180 
181  The default implementation should suffice in most cases.
182 
183  \param name A unique identifier for the window. This is *not* the window title.
184  \param quit A command to be executed when the window closes.
185  \return The new qt_window_widget.
186 */
187 widget
plain_window_widget(string name,command quit)188 qt_widget_rep::plain_window_widget (string name, command quit) {
189   if (DEBUG_QT_WIDGETS)
190     debug_widgets << "qt_widget_rep::plain_window_widget() around a "
191                   << type_as_string() << LF;
192 
193   QTMPlainWindow* win = new QTMPlainWindow (0);
194   QLayoutItem*     li = as_qlayoutitem();
195   if (li) {
196     QLayout* l = li->layout();
197     if (! l) {
198       l = new QVBoxLayout (win);
199       l->addItem (li); // Layout owns the QLayoutItem
200     }
201     win->setLayout (l);// Transfers ownership of QWidgets in QLayoutItems to win
202   } else {
203     QWidget* qw = as_qwidget();
204     if (qw) {
205       QLayout* l = new QVBoxLayout (win);
206       win->setLayout (l); // And the QLayout to the QTMPlainWindow.
207       l->addWidget (qw);  // qw now belongs to the QWidget with the layout (win)
208     } else {
209       FAILED ("attempt to create a window around a nil QWidget");
210     }
211   }
212 
213   int l,t,r,b;
214   win->layout()->getContentsMargins (&l, &t, &r, &b);
215   win->layout()->setContentsMargins (l+3, t+3, r+3, b+3);
216   win->setWindowTitle (to_qstring (name));  // HACK: remove me (see bug#40837)
217 
218   qt_window_widget_rep* wid = tm_new<qt_window_widget_rep> (win, name, quit);
219   wid->add_child (this);
220 
221   return widget (wid);
222 }
223 
224 /*! Instantiates and returns a new widget which will act as a popup widget.
225 
226  This is used by popup_window_widget: subclasses reimplement this method and
227  return the appropriate widget, and qt_widget_rep::popup_window_widget()
228  is the "interface".
229  */
230 widget
make_popup_widget()231 qt_widget_rep::make_popup_widget () {
232   return tm_new<qt_popup_widget_rep> (this, command());
233 }
234 
235 /*! Interface for the creation of popups.
236  FIXME: the check below should be unnecessary, but current design is ugly.
237  */
238 widget
popup_window_widget(string s)239 qt_widget_rep::popup_window_widget (string s) {
240   widget wid= make_popup_widget();
241   ASSERT(concrete(wid) != this, "Loop in call to popup_window_widget()");
242   return concrete(wid)->popup_window_widget(s);
243 }
244 
operator <<(tm_ostream & out,qt_widget w)245 tm_ostream& operator << (tm_ostream& out, qt_widget w) {
246   return out << "qt_widget of type: " << w.rep->type_as_string();
247 }
248 
249 
250 /******************************************************************************
251 * Global functions we export for the creation of windowed widgets by TeXmacs
252 ******************************************************************************/
253 
254 /*! Creates a decorated window using the given widget.
255 
256  Each widget type may choose how to present itself as a window, by
257  reimplementing qt_widget_rep::plain_window_widget(), although the base class
258  qt_widget_rep provides a default implementation which suffices in most cases.
259  See its documentation.
260 
261  \param w    The contents of the window.
262  \param name A unique identifier for the window. This is *not* the window title.
263  \param q    A command to be executed when the window closes.
264 */
265 widget
plain_window_widget(widget w,string name,command q)266 plain_window_widget (widget w, string name, command q) {
267   widget win= concrete(w)->plain_window_widget (name, q);
268   if (name != "popup") {
269     int xx, yy, ww, hh;
270     xx = yy = ww = hh = -1;
271     get_preferred_position (name, xx, yy);
272     get_preferred_size (name, ww, hh);
273     if (xx != -1)
274       set_position (win, xx, yy);
275     if (ww != -1)
276       set_size (win, ww, hh);
277   }
278   return win;
279 }
280 
281 /*! Creates an undecorated window with name s and contents w.
282  */
283 widget
popup_window_widget(widget w,string s)284 popup_window_widget (widget w, string s) {
285   return concrete(w)->popup_window_widget (s);
286 }
287 
288 /*! A factory for a popup widget container whose contents are to be unmapped as
289  soon as the mouse exits the widget.
290 
291  There are currently two kinds of popup widgets: those whose underlying QWidget
292  is a QMenu, and those that hold any sort of QWidget. The former are used in
293  edit_mouse.cpp to implement a contextual menu in the canvas and are implemented
294  using qt_menu_rep; the latter are used for help-balloons and are implemented
295  using qt_popup_widget_rep.
296 
297  \param w The widget to be placed in the popup. It will be deleted after the
298  mouse leaves the popup.
299  \return The popup widget.
300  */
301 widget
popup_widget(widget w)302 popup_widget (widget w) {
303   return concrete(w)->make_popup_widget();
304 }
305 
306 /*! Destroys a window as created via qt_window_widget.
307 
308  In the QT implementation explicitly destroying window widgets should
309  not be necessary since the widget itself destroys the QWidget as soon as
310  its destructor is called. No memory leak should be caused by this trivial
311  implementation.
312  */
313 void
destroy_window_widget(widget w)314 destroy_window_widget (widget w) {
315   if (DEBUG_QT_WIDGETS)
316     debug_widgets << "destroy_window_widget() on "
317                   << static_cast<qt_widget_rep*>(w.rep)->type_as_string() << LF;
318 }
319 
320 
321 /******************************************************************************
322  * TeXmacs interface for the creation of widgets.
323  * See Graphics/Gui/widget.hpp for comments.
324  ******************************************************************************/
325 
horizontal_menu(array<widget> a)326 widget horizontal_menu (array<widget> a) {
327   qt_widget wid = qt_ui_element_rep::create (qt_widget_rep::horizontal_menu, a);
328   wid->add_children (a);
329   return abstract (wid);
330 }
vertical_menu(array<widget> a)331 widget vertical_menu (array<widget> a)  {
332   qt_widget wid = qt_ui_element_rep::create (qt_widget_rep::vertical_menu, a);
333   wid->add_children (a);
334   return abstract (wid);
335 }
horizontal_list(array<widget> a)336 widget horizontal_list (array<widget> a) {
337   qt_widget wid = qt_ui_element_rep::create (qt_widget_rep::horizontal_list, a);
338   wid->add_children (a);
339   return abstract (wid);
340 }
vertical_list(array<widget> a)341 widget vertical_list (array<widget> a) {
342   qt_widget wid = qt_ui_element_rep::create (qt_widget_rep::vertical_list, a);
343   wid->add_children (a);
344   return abstract (wid);
345 }
aligned_widget(array<widget> lhs,array<widget> rhs,SI hsep,SI vsep,SI lpad,SI rpad)346 widget aligned_widget (array<widget> lhs, array<widget> rhs, SI hsep, SI vsep,
347                        SI lpad, SI rpad) {
348   qt_widget wid = qt_ui_element_rep::create (qt_widget_rep::aligned_widget,
349                                     lhs, rhs, coord4 (hsep, vsep, lpad, rpad));
350   wid->add_children (lhs);
351   wid->add_children (rhs);
352   return abstract (wid);
353 }
tabs_widget(array<widget> tabs,array<widget> bodies)354 widget tabs_widget (array<widget> tabs, array<widget> bodies) {
355   qt_widget wid = qt_ui_element_rep::create (qt_widget_rep::tabs_widget,
356                                              tabs, bodies);
357   wid->add_children (tabs);
358   wid->add_children (bodies);
359   return abstract (wid);
360 }
icon_tabs_widget(array<url> us,array<widget> ts,array<widget> bs)361 widget icon_tabs_widget (array<url> us, array<widget> ts, array<widget> bs) {
362   qt_widget wid = qt_ui_element_rep::create (qt_widget_rep::icon_tabs_widget,
363                                     us, ts, bs);
364   wid->add_children (ts);
365   wid->add_children (bs);
366   return abstract (wid);
367 }
wrapped_widget(widget w,command cmd)368 widget wrapped_widget (widget w, command cmd) {
369   return tm_new<qt_wrapped_widget_rep> (w, cmd);
370 }
tile_menu(array<widget> a,int cols)371 widget tile_menu (array<widget> a, int cols) {
372   qt_widget wid = qt_ui_element_rep::create (qt_widget_rep::tile_menu, a, cols);
373   wid->add_children (a);
374   return abstract (wid);
375 }
minibar_menu(array<widget> a)376 widget minibar_menu (array<widget> a) {
377   qt_widget wid = qt_ui_element_rep::create (qt_widget_rep::minibar_menu, a);
378   wid->add_children (a);
379   return abstract (wid);
380 }
menu_separator(bool vertical)381 widget menu_separator (bool vertical) {
382   qt_widget wid = qt_ui_element_rep::create (qt_widget_rep::menu_separator,
383                                              vertical);
384   return abstract (wid);
385 }
menu_group(string name,int style)386 widget menu_group (string name, int style) {
387   qt_widget wid = qt_ui_element_rep::create (qt_widget_rep::menu_group,
388                                              name, style);
389   return abstract (wid);
390 }
pulldown_button(widget w,promise<widget> pw)391 widget pulldown_button (widget w, promise<widget> pw) {
392   qt_widget wid = qt_ui_element_rep::create (qt_widget_rep::pulldown_button,
393                                              w, pw);
394     // FIXME: the promise widget isn't added to the children when it's evaluated
395     //  wid->add_child (??);
396   return abstract(wid);
397 }
pullright_button(widget w,promise<widget> pw)398 widget pullright_button (widget w, promise<widget> pw) {
399   qt_widget wid = qt_ui_element_rep::create (qt_widget_rep::pullright_button,
400                                              w, pw);
401     // FIXME: the promise widget isn't added to the children when it's evaluated
402     //  wid->add_child (??);
403   return abstract(wid);
404 }
menu_button(widget w,command cmd,string pre,string ks,int style)405 widget menu_button (widget w, command cmd, string pre, string ks, int style) {
406   qt_widget wid = qt_ui_element_rep::create (qt_widget_rep::menu_button,
407                                              w, cmd, pre, ks, style);
408   wid->add_child (w);
409   return abstract (wid);
410 }
balloon_widget(widget w,widget help)411 widget balloon_widget (widget w, widget help) {
412   qt_widget wid = qt_ui_element_rep::create (qt_widget_rep::balloon_widget,
413                                              w, help);
414   wid->add_child (w);
415   return abstract (wid);
416 }
text_widget(string s,int style,color col,bool tsp)417 widget text_widget (string s, int style, color col, bool tsp) {
418   qt_widget wid = qt_ui_element_rep::create (qt_widget_rep::text_widget,
419                                              s, style, col, tsp);
420   return abstract (wid);
421 }
xpm_widget(url file_name)422 widget xpm_widget (url file_name) {
423   qt_widget wid = qt_ui_element_rep::create (qt_widget_rep::xpm_widget,
424                                              file_name);
425   return abstract (wid);
426 }
toggle_widget(command cmd,bool on,int style)427 widget toggle_widget (command cmd, bool on, int style) {
428   qt_widget wid = qt_ui_element_rep::create (qt_widget_rep::toggle_widget,
429                                              cmd, on, style);
430   return abstract (wid);
431 }
enum_widget(command cmd,array<string> vals,string val,int style,string width)432 widget enum_widget (command cmd, array<string> vals, string val, int style,
433                     string width) {
434   qt_widget wid = qt_ui_element_rep::create (qt_widget_rep::enum_widget,
435                                              cmd, vals, val, style, width);
436   return abstract (wid);
437 }
choice_widget(command cmd,array<string> vals,array<string> chosen)438 widget choice_widget (command cmd, array<string> vals, array<string> chosen) {
439   qt_widget wid = qt_ui_element_rep::create (qt_widget_rep::choice_widget,
440                                              cmd, vals, chosen, true);
441   return abstract (wid);
442 }
choice_widget(command cmd,array<string> vals,string cur)443 widget choice_widget (command cmd, array<string> vals, string cur) {
444   array<string> chosen (1);
445   chosen[0]= cur;
446   qt_widget wid = qt_ui_element_rep::create (qt_widget_rep::choice_widget,
447                                              cmd, vals, chosen, false);
448   return abstract (wid);
449 }
choice_widget(command cmd,array<string> vals,string cur,string filter)450 widget choice_widget (command cmd, array<string> vals, string cur, string filter) {
451   qt_widget wid = qt_ui_element_rep::create (qt_widget_rep::filtered_choice_widget,
452                                              cmd, vals, cur, filter);
453   return abstract (wid);
454 }
user_canvas_widget(widget w,int style)455 widget user_canvas_widget (widget w, int style) {
456   qt_widget wid = qt_ui_element_rep::create (qt_widget_rep::scrollable_widget,
457                                              w, style);
458   wid->add_child (w);
459   return abstract (wid);
460 }
resize_widget(widget w,int style,string w1,string h1,string w2,string h2,string w3,string h3,string hpos,string vpos)461 widget resize_widget (widget w, int style, string w1, string h1,
462                       string w2, string h2, string w3, string h3,
463                       string hpos, string vpos) {
464   typedef triple<string, string, string> T1;
465   (void) hpos; (void) vpos;
466   qt_widget wid = qt_ui_element_rep::create (qt_widget_rep::resize_widget,
467                                              w, style, T1(w1, w2, w3),
468                                                        T1(h1, h2, h3));
469   wid->add_child (w);
470   return abstract (wid);
471 }
hsplit_widget(widget l,widget r)472 widget hsplit_widget (widget l, widget r) {
473   qt_widget wid = qt_ui_element_rep::create (qt_widget_rep::hsplit_widget, l, r);
474   wid->add_children (array<widget> (l, r));
475   return abstract (wid);
476 }
vsplit_widget(widget t,widget b)477 widget vsplit_widget (widget t, widget b) {
478   qt_widget wid = qt_ui_element_rep::create (qt_widget_rep::vsplit_widget, t, b);
479   wid->add_children (array<widget> (t, b));
480   return abstract (wid);
481 }
refresh_widget(string tmwid,string kind)482 widget refresh_widget (string tmwid, string kind) {
483   qt_widget wid = qt_ui_element_rep::create (qt_widget_rep::refresh_widget,
484                                              tmwid, kind);
485     // FIXME: decide what to do with children in QTMRefresh::recompute()
486   return abstract (wid);
487 }
refreshable_widget(object promise,string kind)488 widget refreshable_widget (object promise, string kind) {
489   qt_widget wid = qt_ui_element_rep::create (qt_widget_rep::refreshable_widget,
490                                              promise, kind);
491     // FIXME: decide what to do with children in QTMRefreshable::recompute()
492   return abstract (wid);
493 }
glue_widget(bool hx,bool vx,SI w,SI h)494 widget glue_widget (bool hx, bool vx, SI w, SI h) {
495   qt_widget wid = qt_ui_element_rep::create (qt_ui_element_rep::glue_widget,
496                                              hx, vx, w/PIXEL, h/PIXEL);
497   return abstract (wid);
498 }
glue_widget(tree col,bool hx,bool vx,SI w,SI h)499 widget glue_widget (tree col, bool hx, bool vx, SI w, SI h) {
500   return tm_new<qt_glue_widget_rep> (col, hx, vx, w, h);
501 }
inputs_list_widget(command call_back,array<string> prompts)502 widget inputs_list_widget (command call_back, array<string> prompts) {
503   return tm_new<qt_inputs_list_widget_rep> (call_back, prompts);
504 }
input_text_widget(command call_back,string type,array<string> def,int style,string width)505 widget input_text_widget (command call_back, string type, array<string> def,
506                           int style, string width) {
507   return tm_new<qt_input_text_widget_rep> (call_back, type, def, style, width);
508 }
color_picker_widget(command call_back,bool bg,array<tree> proposals)509 widget color_picker_widget (command call_back, bool bg, array<tree> proposals) {
510   return tm_new<qt_color_picker_widget_rep> (call_back, bg, proposals);
511 }
file_chooser_widget(command cmd,string type,string prompt)512 widget file_chooser_widget (command cmd, string type, string prompt) {
513   return tm_new<qt_chooser_widget_rep> (cmd, type, prompt);
514 }
printer_widget(command cmd,url ps_pdf_file)515 widget printer_widget (command cmd, url ps_pdf_file) {
516   return tm_new<qt_printer_widget_rep> (cmd, ps_pdf_file);
517 }
texmacs_widget(int mask,command quit)518 widget texmacs_widget (int mask, command quit) {
519   if (mask) return tm_new<qt_tm_widget_rep> (mask, quit);
520   else      return tm_new<qt_tm_embedded_widget_rep> (quit);
521 }
ink_widget(command cb)522 widget ink_widget (command cb) {
523   NOT_IMPLEMENTED("Ink widget");
524   (void) cb; return widget();
525 }
tree_view_widget(command cmd,tree data,tree actions)526 widget tree_view_widget (command cmd, tree data, tree actions) {
527   qt_widget wid = qt_ui_element_rep::create (qt_widget_rep::tree_view_widget,
528                                              cmd, data, actions);
529   return abstract (wid);
530 
531 }
532   //// Widgets which are not strictly required by TeXmacs have void implementations
533 
empty_widget()534 widget empty_widget () { NOT_IMPLEMENTED("empty_widget"); return widget(); }
extend(widget w,array<widget> a)535 widget extend (widget w, array<widget> a) { (void) a; return w; }
wait_widget(SI width,SI height,string message)536 widget wait_widget (SI width, SI height, string message) {
537   (void) width; (void) height; (void) message; return widget();
538 }
539