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