]> git.lyx.org Git - lyx.git/blob - src/frontends/xforms/XFormsToolbar.C
* Positionable and dynamically visible toolbars for the XForms frontend.
[lyx.git] / src / frontends / xforms / XFormsToolbar.C
1 /**
2  * \file XFormsToolbar.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author Jean-Marc Lasgouttes
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 //  Added pseudo-action handling, asierra 180296
13
14 #include <config.h>
15
16 #include "XFormsToolbar.h"
17
18 #include "Tooltips.h"
19 #include "xforms_helpers.h"
20
21 #include "buffer.h"
22 #include "bufferparams.h"
23 #include "debug.h"
24 #include "funcrequest.h"
25 #include "FuncStatus.h"
26 #include "gettext.h"
27 #include "lyxfunc.h"
28
29 #include "lyx_forms.h"
30 #include "combox.h"
31
32 #include <boost/bind.hpp>
33
34 using lyx::frontend::Box;
35 using lyx::frontend::BoxList;
36
37 using std::distance;
38 using std::endl;
39 using std::string;
40
41
42 // some constants
43 const int standardspacing = 2; // the usual space between items
44 const int sepspace = 6; // extra space
45 const int buttonwidth = 30; // the standard button width
46 const int height = 30; // the height of all items in the toolbar
47
48 namespace {
49
50 XFormsView::Position getPosition(ToolbarBackend::Flags const & flags)
51 {
52         if (flags & ToolbarBackend::TOP)
53                 return XFormsView::Top;
54         if (flags & ToolbarBackend::BOTTOM)
55                 return XFormsView::Bottom;
56         if (flags & ToolbarBackend::LEFT)
57                 return XFormsView::Left;
58         if (flags & ToolbarBackend::RIGHT)
59                 return XFormsView::Right;
60         return XFormsView::Top;
61 }
62
63
64 LyXTextClass const & getTextClass(LyXView const & lv)
65 {
66         return lv.buffer()->params().getLyXTextClass();
67 }
68
69 } // namespace anon
70
71
72 XFormsToolbar::toolbarItem::toolbarItem()
73         : icon(0)
74 {}
75
76
77 XFormsToolbar::toolbarItem::~toolbarItem()
78 {
79         // Lars said here that ~XFormsView() dealt with the icons.
80         // This is not true. But enabling this causes crashes,
81         // because somehow we kill the same icon twice :(
82         // FIXME
83         //kill_icon();
84 }
85
86
87 void XFormsToolbar::toolbarItem::kill_icon()
88 {
89         if (icon) {
90                 fl_delete_object(icon);
91                 fl_free_object(icon);
92                 icon = 0;
93         }
94 }
95
96
97 XFormsToolbar::toolbarItem &
98 XFormsToolbar::toolbarItem::operator=(toolbarItem const & ti)
99 {
100         if (this == &ti)
101                 return *this;
102
103         // If we already have an icon, release it.
104         // But we don't copy the icon from ti
105         kill_icon();
106
107         func = ti.func;
108
109         return *this;
110 }
111
112
113
114 Toolbars::ToolbarPtr make_toolbar(ToolbarBackend::Toolbar const & tbb,
115                                   LyXView & owner)
116 {
117         return Toolbars::ToolbarPtr(new XFormsToolbar(tbb, owner));
118 }
119
120
121 XFormsToolbar::XFormsToolbar(ToolbarBackend::Toolbar const & tbb,
122                              LyXView & o)
123         : toolbar_(0),
124           toolbar_buttons_(0),
125           owner_(static_cast<XFormsView &>(o)),
126           tooltip_(new Tooltips)
127 {
128         position_ = getPosition(tbb.flags);
129         BoxList & boxlist = owner_.getBox(position_).children();
130         toolbar_ = &boxlist.push_back(Box(0,0));
131
132         // If the toolbar is horizontal, then it contains three
133         // vertically-aligned Boxes,the center one of which is to
134         // contain the buttons, aligned horizontally.
135         // The other two provide some visual padding.
136
137         // If it is vertical, then this is swapped around.
138
139         Box::Orientation const toolbar_orientation =
140                 (position_ == XFormsView::Left ||
141                  position_ == XFormsView::Right)
142                 ? Box::Vertical : Box::Horizontal;
143
144         Box::Orientation const padding_orientation =
145                 (toolbar_orientation == Box::Vertical)
146                 ? Box::Horizontal : Box::Vertical;
147
148         toolbar_->set(padding_orientation);
149
150         // A bit of a hack, but prevents 'M-x' causing the addition of
151         // visible borders.
152         int const padding =
153                 (tbb.name == "minibuffer") ?
154                 0 : 2 + abs(fl_get_border_width());
155
156         toolbar_->children().push_back(Box(padding, padding));
157
158         Box & toolbar_center = toolbar_->children().push_back(Box(0,0));
159         toolbar_center.set(toolbar_orientation);
160         toolbar_buttons_ = &toolbar_center.children();
161
162         toolbar_->children().push_back(Box(padding, padding));
163
164         using lyx::frontend::WidgetMap;
165         owner_.metricsUpdated.connect(boost::bind(&WidgetMap::updateMetrics,
166                                                   &widgets_));
167
168         // Populate the toolbar.
169         ToolbarBackend::item_iterator it = tbb.items.begin();
170         ToolbarBackend::item_iterator end = tbb.items.end();
171         for (; it != end; ++it)
172                 add(it->first, it->second);
173
174 }
175
176
177 XFormsToolbar::~XFormsToolbar()
178 {
179         fl_freeze_form(owner_.getForm());
180
181         // G++ vector does not have clear defined
182         //toollist.clear();
183         toollist_.erase(toollist_.begin(), toollist_.end());
184
185         fl_unfreeze_form(owner_.getForm());
186 }
187
188
189 namespace {
190
191 extern "C" {
192
193 void C_ToolbarCB(FL_OBJECT * ob, long ac)
194 {
195         if (!ob || !ob->u_vdata)
196                 return;
197
198         XFormsToolbar * ptr = static_cast<XFormsToolbar *>(ob->u_vdata);
199         XFormsView & owner = ptr->owner_;
200         owner.getLyXFunc().dispatch(ptr->funcs[ac], true);
201 }
202
203 } // extern "C"
204
205 } // namespace anon
206
207
208 void XFormsToolbar::hide(bool update_metrics)
209 {
210         toolbar_->set(Box::Invisible);
211         if (update_metrics)
212                 owner_.updateMetrics();
213 }
214
215
216 void XFormsToolbar::show(bool update_metrics)
217 {
218         toolbar_->set(Box::Visible);
219         toolbar_->show();
220         if (update_metrics)
221                 owner_.updateMetrics();
222 }
223
224
225 void XFormsToolbar::add(FuncRequest const & func, string const & tooltip)
226 {
227         toolbarItem item;
228         item.func = func;
229
230         switch (func.action) {
231         case ToolbarBackend::SEPARATOR:
232                 toolbar_buttons_->push_back(Box(sepspace, sepspace));
233                 break;
234
235         case ToolbarBackend::MINIBUFFER:
236                 // Not implemented.
237                 // XForms uses the same widget to display the buffer messages
238                 // and to input commands.
239                 break;
240
241         case ToolbarBackend::LAYOUTS:
242                 layout_.reset(new XLayoutBox(owner_, *this));
243                 break;
244
245         default: {
246                 FL_OBJECT * obj;
247
248                 toolbar_buttons_->push_back(Box(standardspacing,
249                                                 standardspacing));
250
251                 item.icon = obj =
252                         fl_add_pixmapbutton(FL_NORMAL_BUTTON,
253                                             0, 0, 0, 0, "");
254
255                 widgets_.add(obj, *toolbar_buttons_, buttonwidth, height);
256
257                 fl_set_object_resize(obj, FL_RESIZE_ALL);
258
259                 int gravity = 0;
260                 if (position_ == XFormsView::Top ||
261                     position_ == XFormsView::Left)
262                         gravity = NorthWestGravity;
263                 else if (position_ == XFormsView::Right)
264                         gravity = NorthEastGravity;
265                 else if (position_ == XFormsView::Bottom)
266                         gravity = SouthWestGravity;
267                 
268                 fl_set_object_gravity(obj, gravity, gravity);
269
270                 Funcs::iterator fit = funcs.insert(funcs.end(), func);
271                 int const index = distance(funcs.begin(), fit);
272                 fl_set_object_callback(obj, C_ToolbarCB, index);
273                 // Remove the blue feedback rectangle
274                 fl_set_pixmapbutton_focus_outline(obj, 0);
275
276                 tooltip_->init(obj, tooltip);
277
278                 // The view that this object belongs to.
279                 obj->u_vdata = this;
280
281                 string const xpm = toolbarbackend.getIcon(func);
282                 fl_set_pixmapbutton_file(obj, xpm.c_str());
283                 break;
284         }
285         }
286
287         toollist_.push_back(item);
288 }
289
290
291 void XFormsToolbar::update()
292 {
293         ToolbarList::const_iterator p = toollist_.begin();
294         ToolbarList::const_iterator end = toollist_.end();
295         for (; p != end; ++p) {
296                 if (!p->icon)
297                         continue;
298
299                 FuncStatus const status =
300                         owner_.getLyXFunc().getStatus(p->func);
301
302                 if (status.onoff(true)) {
303                         // I'd like to use a different color
304                         // here, but then the problem is to
305                         // know how to use transparency with
306                         // Xpm library. It seems pretty
307                         // complicated to me (JMarc)
308                         fl_set_object_color(p->icon, FL_LEFT_BCOL, FL_BLUE);
309                         fl_set_object_boxtype(p->icon, FL_DOWN_BOX);
310                 } else {
311                         fl_set_object_color(p->icon, FL_MCOL, FL_BLUE);
312                         fl_set_object_boxtype(p->icon, FL_UP_BOX);
313                 }
314                 if (status.enabled()) {
315                         fl_activate_object(p->icon);
316                 } else {
317                         // Is there a way here to specify a
318                         // mask in order to show that the
319                         // button is disabled? (JMarc)
320                         fl_deactivate_object(p->icon);
321                 }
322         }
323
324         bool const enable = owner_.getLyXFunc().
325                 getStatus(FuncRequest(LFUN_LAYOUT)).enabled();
326
327         if (layout_.get())
328                 layout_->setEnabled(enable);
329 }
330
331
332 namespace {
333
334 extern "C"
335 void C_LayoutBoxSelectedCB(FL_OBJECT * ob, long)
336 {
337         if (!ob || !ob->u_vdata)
338                 return;
339         XLayoutBox * ptr = static_cast<XLayoutBox *>(ob->u_vdata);
340         ptr->selected();
341 }
342
343 } // namespace anon
344
345
346 XLayoutBox::XLayoutBox(LyXView & owner, XFormsToolbar & toolbar)
347         : owner_(owner)
348 {
349         toolbar.toolbar_buttons_->push_back(Box(standardspacing, 0));
350
351         combox_ = fl_add_combox(FL_DROPLIST_COMBOX,
352                                 0, 0, 135, height, "");
353
354         toolbar.widgets_.add(combox_, *toolbar.toolbar_buttons_, 135, height);
355
356         fl_set_combox_browser_height(combox_, 400);
357         fl_set_object_boxtype(combox_, FL_DOWN_BOX);
358         fl_set_object_color(combox_, FL_MCOL, FL_MCOL);
359         fl_set_object_gravity(combox_, FL_NorthWest, FL_NorthWest);
360         fl_set_object_resize(combox_, FL_RESIZE_ALL);
361
362         combox_->u_vdata = this;
363         fl_set_object_callback(combox_, C_LayoutBoxSelectedCB, 0);
364 }
365
366
367 void XLayoutBox::set(string const & layout)
368 {
369         LyXTextClass const & tc = getTextClass(owner_);
370
371         string const layoutname = _(tc[layout]->name());
372
373         int const nnames = fl_get_combox_maxitems(combox_);
374         for (int i = 1; i <= nnames; ++i) {
375                 string const name = fl_get_combox_line(combox_, i);
376                 if (name == layoutname) {
377                         fl_set_combox(combox_, i);
378                         break;
379                 }
380         }
381 }
382
383
384 void XLayoutBox::update()
385 {
386         LyXTextClass const & tc = getTextClass(owner_);
387
388         fl_clear_combox(combox_);
389
390         LyXTextClass::const_iterator it = tc.begin();
391         LyXTextClass::const_iterator const end = tc.end();
392         for (; it != end; ++it) {
393                 // ignore obsolete entries
394                 if ((*it)->obsoleted_by().empty()) {
395                         string const & name = _((*it)->name());
396                         fl_addto_combox(combox_, name.c_str());
397                 }
398         }
399
400         // we need to do this.
401         fl_redraw_object(combox_);
402 }
403
404
405 void XLayoutBox::clear()
406 {
407         fl_clear_combox(combox_);
408         fl_redraw_object(combox_);
409 }
410
411
412 void XLayoutBox::open()
413 {
414         fl_show_combox_browser(combox_);
415 }
416
417
418 void XLayoutBox::setEnabled(bool enable)
419 {
420         ::setEnabled(combox_, enable);
421 }
422
423
424 void XLayoutBox::selected()
425 {
426         string const layoutguiname = getString(combox_);
427
428         LyXTextClass const & tc = getTextClass(owner_);
429
430         LyXTextClass::const_iterator it  = tc.begin();
431         LyXTextClass::const_iterator const end = tc.end();
432         for (; it != end; ++it) {
433                 string const & name = (*it)->name();
434                 if (_(name) == layoutguiname) {
435                         owner_.getLyXFunc()
436                                 .dispatch(FuncRequest(LFUN_LAYOUT, name),
437                                           true);
438                         return;
439                 }
440         }
441         lyxerr << "ERROR (XLayoutBox::selected): layout not found!" << endl;
442 }