]> git.lyx.org Git - lyx.git/blob - src/frontends/xforms/XFormsMenubar.C
Change glob() API to accept a dir parameter.
[lyx.git] / src / frontends / xforms / XFormsMenubar.C
1 /**
2  * \file XFormsMenubar.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 #include <config.h>
13
14 #include "XFormsMenubar.h"
15
16 #include "XFormsView.h"
17
18 #include "debug.h"
19 #include "gettext.h"
20 #include "lyxfunc.h"
21 #include "MenuBackend.h"
22
23 #include "support/lstrings.h"
24 #include "support/tostr.h"
25
26 #include <boost/bind.hpp>
27
28 #include "lyx_forms.h"
29
30 using boost::shared_ptr;
31
32 using std::distance;
33 using std::endl;
34 using std::for_each;
35 using std::string;
36 using std::vector;
37
38 namespace lyx {
39
40 using support::lowercase;
41 using support::subst;
42
43 namespace frontend {
44
45 typedef vector<int>::size_type size_type;
46
47 namespace {
48
49 // Some constants
50 int const MENU_LABEL_SIZE = FL_NORMAL_SIZE;
51 int const MENU_LABEL_STYLE = FL_NORMAL_STYLE;
52 int const mheight = 30;
53 int const mbheight= 22;
54 // where to place the menubar?
55 int const yloc = (mheight - mbheight)/2; //air + bw;
56 int const mbadd = 20; // menu button add (to width)
57 // Some space between buttons on the menubar
58 int const air = 2;
59 char const * menu_tabstop = "aa";
60 char const * default_tabstop = "aaaaaaaa";
61 // We do not want to mix position values in a menu (like the index of
62 // a submenu) with the action numbers which convey actual information.
63 // Therefore we offset all the action values by an arbitrary large
64 // constant.
65 int const action_offset = 1000;
66
67 // This is used a few times below.
68 inline
69 int string_width(string const & str)
70 {
71         return fl_get_string_widthTAB(MENU_LABEL_STYLE, MENU_LABEL_SIZE,
72                                       str.c_str(),
73                                       static_cast<int>(str.length()));
74 }
75
76 } // namespace anon
77
78
79 extern "C" {
80
81         //Defined later, used in makeMenubar().
82         static
83         void C_XFormsMenubar_MenuCallback(FL_OBJECT * ob, long button)
84         {
85                 XFormsMenubar::MenuCallback(ob, button);
86         }
87
88 }
89
90
91 XFormsMenubar::XFormsMenubar(LyXView * view, MenuBackend const & mb)
92         : owner_(static_cast<XFormsView*>(view)),
93           menubackend_(&mb)
94 {
95         owner_->metricsUpdated.connect(boost::bind(&WidgetMap::updateMetrics,
96                                                    &widgets_));
97
98         makeMenubar(menubackend_->getMenubar());
99 }
100
101
102 XFormsMenubar::~XFormsMenubar()
103 {}
104
105
106 void XFormsMenubar::makeMenubar(Menu const & menu)
107 {
108         // Draw a frame around the whole.
109         BoxList & boxlist = owner_->getBox(XFormsView::Top)->children();
110
111         FL_OBJECT * frame = fl_add_frame(FL_UP_FRAME, 0, 0, 0, 0, "");
112         fl_set_object_resize(frame, FL_RESIZE_ALL);
113         fl_set_object_gravity(frame, NorthWestGravity, NorthEastGravity);
114
115         menubar_ = widgets_.add(frame, boxlist, 0, mheight);
116
117         // The menubar contains three vertically-aligned Boxes,
118         // the center one of which is to contain the buttons,
119         // aligned horizontally.
120         // The other two provide some visual padding.
121         menubar_->children().push_back(Box(0, yloc));
122         shared_ptr<Box> menubar_center = menubar_->children().push_back(Box(0,0));
123         menubar_center->set(Box::Horizontal);
124         menubar_->children().push_back(Box(0, yloc));
125
126         BoxList & menubar_buttons = menubar_center->children();
127
128         // Add the buttons.
129         Menu::const_iterator i = menu.begin();
130         Menu::const_iterator end = menu.end();
131         for (; i != end; ++i) {
132                 FL_OBJECT * obj;
133                 if (i->kind() != MenuItem::Submenu) {
134                         lyxerr << "ERROR: XFormsMenubar::createMenubar:"
135                                 " only submenus can appear in a menubar"
136                                << endl;
137                         continue;
138                 }
139                 string const label = i->label();
140                 string const shortcut = '#' + i->shortcut();
141                 int const width = string_width(label);
142
143                 obj = fl_add_button(FL_MENU_BUTTON, 0, 0, 0, 0, label.c_str());
144
145                 menubar_buttons.push_back(Box(air, 0));
146                 widgets_.add(obj, menubar_buttons, width + mbadd, mbheight);
147
148                 fl_set_object_boxtype(obj, FL_FLAT_BOX);
149                 fl_set_object_color(obj, FL_MCOL, FL_MCOL);
150                 fl_set_object_lsize(obj, MENU_LABEL_SIZE);
151                 fl_set_object_lstyle(obj, MENU_LABEL_STYLE);
152                 fl_set_object_resize(obj, FL_RESIZE_ALL);
153                 fl_set_object_gravity(obj, NorthWestGravity,
154                                       NorthWestGravity);
155                 fl_set_object_shortcut(obj, shortcut.c_str(), 1);
156                 fl_set_object_callback(obj, C_XFormsMenubar_MenuCallback, 1);
157
158                 boost::shared_ptr<ItemInfo>
159                         iteminfo(new ItemInfo(this, new MenuItem(*i), obj));
160                 buttonlist_.push_back(iteminfo);
161                 obj->u_vdata = iteminfo.get();
162         }
163 }
164
165
166 void XFormsMenubar::update()
167 {
168         // nothing yet
169 }
170
171
172 void XFormsMenubar::openByName(string const & name)
173 {
174         for (ButtonList::const_iterator cit = buttonlist_.begin();
175              cit != buttonlist_.end(); ++cit) {
176                 if ((*cit)->item_->submenuname() == name) {
177                         MenuCallback((*cit)->obj_, 1);
178                         return;
179                 }
180         }
181
182         lyxerr << "XFormsMenubar::openByName: menu "
183                << name << " not found" << endl;
184 }
185
186
187 namespace {
188
189 Menu::size_type const max_number_of_items = 25;
190
191 int get_new_submenu(vector<int> & smn, Window win)
192 {
193         static size_type max_number_of_menus = 32;
194         if (smn.size() >= max_number_of_menus)
195                 max_number_of_menus =
196                     fl_setpup_maxpup(static_cast<int>(2*smn.size()));
197         int menu = fl_newpup(win);
198         fl_setpup_softedge(menu, true);
199         fl_setpup_bw(menu, -1);
200         smn.push_back(menu);
201         return menu;
202 }
203
204
205 string const fixlabel(string const & str)
206 {
207         return subst(str, "%", "%%");
208 }
209
210
211
212 } // namespace anon
213
214
215
216 int XFormsMenubar::create_submenu(Window win, XFormsView * view,
217                                   Menu const & menu,
218                                   vector<int> & smn, Funcs & funcs)
219 {
220         int const menuid = get_new_submenu(smn, win);
221         lyxerr[Debug::GUI] << "XFormsMenubar::create_submenu: creating "
222                            << menu.name() << " as menuid=" << menuid << endl;
223
224         // Compute the size of the largest label (because xforms is
225         // not able to support shortcuts correctly...)
226         int max_width = 0;
227         string widest_label;
228         Menu::const_iterator end = menu.end();
229         for (Menu::const_iterator i = menu.begin(); i != end; ++i) {
230                 MenuItem const & item = (*i);
231                 if (item.kind() == MenuItem::Command) {
232                         string const label = item.label() + '\t';
233                         int const width = string_width(label);
234                         if (width > max_width) {
235                                 max_width = width;
236                                 widest_label = label;
237                         }
238                 }
239         }
240         lyxerr[Debug::GUI] << "max_width=" << max_width
241                            << ", widest_label=\"" << widest_label
242                            << '"' << endl;
243
244         size_type count = 0;
245         int curmenuid = menuid;
246         for (Menu::const_iterator i = menu.begin(); i != end; ++i) {
247                 MenuItem const & item = (*i);
248
249                 ++count;
250                 // add a More... submenu if the menu is too long (but
251                 // not just for one extra entry!)
252                 if (count > max_number_of_items && (i + 1) != end) {
253                         int tmpmenuid = get_new_submenu(smn, win);
254                         lyxerr[Debug::GUI] << "Too many items, creating "
255                                            << "new menu " << tmpmenuid << endl;
256                         string label = _("More");
257                         label += "...%m";
258                         fl_addtopup(curmenuid, label.c_str(), tmpmenuid);
259                         count = 0;
260                         curmenuid = tmpmenuid;
261                 }
262
263                 switch (item.kind()) {
264                 case MenuItem::Command:
265                 case MenuItem::Submenu:
266                 {
267                         // Build the menu label from all the info
268                         string label = fixlabel(item.label());
269                         FuncStatus const flag = item.status();
270                         int submenuid = 0;
271
272                         // Is there a key binding?
273                         string const binding = item.binding();
274                         if (!binding.empty()) {
275                                 // Try to be clever and add  just enough
276                                 // tabs to align shortcuts.
277                                 do
278                                         label += '\t';
279                                 while (string_width(label) < max_width + 5);
280                                 label += binding;
281                         }
282
283                         // Is there a separator after the item?
284                         if ((i + 1) != end
285                             && (i + 1)->kind() == MenuItem::Separator)
286                                 label += "%l";
287
288                         // Modify the entry using the function status
289                         if (flag.onoff(true))
290                                 label += "%B";
291                         if (flag.onoff(false))
292                                 label += "%b";
293                         if (!flag.enabled())
294                                 label += "%i";
295
296                         // Add the shortcut
297                         string shortcut = item.shortcut();
298                         if (!shortcut.empty()) {
299                                 shortcut += lowercase(shortcut[0]);
300                                 label += "%h";
301                         }
302
303                         // Finally add the action/submenu
304                         if (item.kind() == MenuItem::Submenu) {
305                                 // create the submenu
306                                 submenuid =
307                                         create_submenu(win, view,
308                                                        *item.submenu(), smn, funcs);
309                                 if (submenuid == -1)
310                                         return -1;
311                                 label += "%x" + tostr(smn.size());
312                                 lyxerr[Debug::GUI]
313                                         << "Menu: " << submenuid
314                                         << " (at index " << smn.size()
315                                         << "), ";
316                         } else {
317                                 // Add the action
318                                 Funcs::iterator fit =
319                                         funcs.insert(funcs.end(), item.func());
320                                 int const action_count =
321                                         distance(funcs.begin(), fit);
322
323                                 label += "%x" + tostr(action_count + action_offset);
324                                 lyxerr[Debug::GUI] << "Action: \""
325                                                    << item.func().action
326                                                    << "(" << item.func().argument
327                                                    << ")\", ";
328                         }
329
330                         // Add everything to the menu
331                         fl_addtopup(curmenuid, label.c_str(),
332                                     shortcut.c_str());
333                         if (item.kind() == MenuItem::Submenu)
334                                 fl_setpup_submenu(curmenuid, smn.size(),
335                                                   submenuid);
336
337                         lyxerr[Debug::GUI] << "label \"" << label
338                                            << "\", binding \"" << binding
339                                            << "\", shortcut \"" << shortcut
340                                            << "\" (added to menu "
341                                            << curmenuid << ')' << endl;
342                         break;
343                 }
344
345                 case MenuItem::Separator:
346                         // already done, and if it was the first one,
347                         // we just ignore it.
348                         --count;
349                         break;
350
351
352                 default:
353                         lyxerr << "XFormsMenubar::create_submenu: "
354                                 "this should not happen" << endl;
355                         break;
356                 }
357         }
358         return menuid;
359 }
360
361
362 void XFormsMenubar::MenuCallback(FL_OBJECT * ob, long button)
363 {
364         ItemInfo * iteminfo = static_cast<ItemInfo *>(ob->u_vdata);
365         XFormsView * view = iteminfo->menubar_->owner_;
366         MenuItem const * item = iteminfo->item_.get();
367
368         if (button == 1) {
369                 // set the pseudo menu-button
370                 fl_set_object_boxtype(ob, FL_DOWN_BOX);
371                 fl_set_button(ob, 0);
372                 fl_redraw_object(ob);
373         }
374
375         // Paranoia check
376         BOOST_ASSERT(item->kind() == MenuItem::Submenu);
377
378         // set tabstop length
379         fl_set_tabstop(menu_tabstop);
380
381         MenuBackend const * menubackend_ = iteminfo->menubar_->menubackend_;
382         Menu tomenu;
383         Menu const frommenu = menubackend_->getMenu(item->submenuname());
384         menubackend_->expand(frommenu, tomenu, view);
385         Funcs funcs;
386         vector<int> submenus;
387         int menu = iteminfo->menubar_->create_submenu(FL_ObjWin(ob), view,
388                                                     tomenu, submenus, funcs);
389         if (menu != -1) {
390                 // place popup
391                 fl_setpup_position(view->getForm()->x + ob->x,
392                                    view->getForm()->y + ob->y + ob->h + 10);
393                 int choice = fl_dopup(menu);
394
395                 if (button == 1) {
396                                 // set the pseudo menu-button back
397                         fl_set_object_boxtype(ob, FL_FLAT_BOX);
398                         fl_redraw_object(ob);
399                 }
400
401                 // If the action value is too low, then it is not a
402                 // valid action, but something else.
403                 if (choice >= action_offset) {
404                         view->getLyXFunc().dispatch(funcs[choice - action_offset]);
405                 } else {
406                         lyxerr[Debug::GUI]
407                                 << "MenuCallback: ignoring bogus action "
408                                 << choice << endl;
409                 }
410         } else {
411                 lyxerr << "Error in MenuCallback" << endl;
412         }
413
414         for_each(submenus.begin(), submenus.end(), fl_freepup);
415         // restore tabstop length
416         fl_set_tabstop(default_tabstop);
417
418 }
419
420
421 XFormsMenubar::ItemInfo::ItemInfo
422         (XFormsMenubar * p, MenuItem const * i, FL_OBJECT * o)
423         : menubar_(p), obj_(o)
424 {
425         item_.reset(i);
426 }
427
428
429 XFormsMenubar::ItemInfo::~ItemInfo()
430 {}
431
432 } // namespace frontend
433 } // namespace lyx