]> git.lyx.org Git - lyx.git/blob - src/frontends/xforms/Menubar_pimpl.C
7e486ad3fe98366328e2e37caca5dc4971fd688b
[lyx.git] / src / frontends / xforms / Menubar_pimpl.C
1 /* This file is part of
2 * ======================================================
3
4 *           LyX, The Document Processor
5 *        
6 *           Copyright (C) 1999 The LyX Team.
7 *
8 *======================================================*/
9
10 #ifdef __GNUG__
11 #pragma implementation
12 #endif
13
14 #include <config.h>
15
16 #include <algorithm>
17 #include <cctype>
18 #include "support/lstrings.h"
19 #include "support/filetools.h"
20 #include "support/StrPool.h"
21 #include "support/LAssert.h"
22 #include "debug.h"
23 #include "LyXAction.h"
24 #include "lyxfunc.h"
25 #include "kbmap.h"
26 #include "bufferlist.h"
27 #include "lastfiles.h"
28 #include "LyXView.h"
29 #include "MenuBackend.h"
30 #include "Menubar_pimpl.h"
31
32 using std::endl;
33
34 extern kb_keymap * toplevel_keymap;
35 extern LyXAction lyxaction;
36 extern BufferList bufferlist;
37 extern LastFiles * lastfiles; 
38
39 // Some constants
40 const int MENU_LABEL_SIZE = FL_NORMAL_SIZE;
41 const int mheight = 30;
42 const int mbheight= 22;
43 // where to place the menubar?
44 const int yloc = (mheight - mbheight)/2; //air + bw;
45 const int mbadd = 20; // menu button add (to width)
46 // Some space between buttons on the menubar 
47 const int air = 2;
48 char const * menu_tabstop = "aa";
49 char const * default_tabstop = "aaaaaaaa";
50
51
52 Menubar::Pimpl::Pimpl(LyXView * view, MenuBackend const & mb) 
53         : frame_(0), owner_(view), menubackend_(&mb)
54 {
55         // Should we do something here?
56 }
57
58 Menubar::Pimpl::~Pimpl() 
59 {
60         // Should we do something here?
61 }
62
63 // This is used a few times below.
64 inline
65 int string_width(string const & str) 
66 {
67         return fl_get_string_widthTAB(FL_NORMAL_STYLE, MENU_LABEL_SIZE,
68                                       str.c_str(),      str.length());
69 }
70
71 //Defined later, used in set().
72 extern "C"
73 void C_Menubar_Pimpl_MenuCallback(FL_OBJECT * ob, long button);
74
75 void Menubar::Pimpl::set(string const & menu_name) 
76 {
77         lyxerr[Debug::GUI] << "Entering Menubar::Pimpl::set " 
78                            << "for menu `" << menu_name << "'" << endl;
79
80         if (menu_name == current_menu) {
81                 lyxerr[Debug::GUI] << "Nothing to do." << endl;
82                 return;
83         }
84
85         // If the backend has not been initialized yet, we use a
86         // default instead.  
87         if (menubackend_->empty()) {
88                 lyxerr << "Menubar::Pimpl::set: menubackend is empty! "
89                         "using default values." << endl;
90                 MenuBackend * mb = new MenuBackend();
91                 mb->defaults();
92                 menubackend_ = mb;
93         }
94
95         if (!menubackend_->hasMenu(menu_name)){ 
96                 lyxerr << "ERROR:set: Unknown menu `" << menu_name
97                        << "'" << endl;
98                 return;
99         }
100
101         Menu menu = menubackend_->getMenu(menu_name);
102
103         if (!menu.menubar()) {
104                 lyxerr << "Only a menubar-type object can go in a "
105                         "toplevel menu" << endl;
106                 return;
107         }
108
109         current_menu = menu_name;
110         FL_FORM * form = owner_->getForm(); 
111         int moffset = 0;
112         bool form_was_open, form_was_frozen;
113
114         if (fl_current_form == form)
115                 form_was_open = true;
116         else if (fl_current_form == 0) {
117                 form_was_open = false;
118                 fl_addto_form(form);
119         } 
120         else {
121                 lyxerr << "Something is wrong: unknown form " 
122                        << fl_current_form << " is already open" 
123                        << "(main form is " << form << ")" << endl;
124                 return;
125         }
126         if (form->frozen)
127                 form_was_frozen = true;
128         else {
129                 form_was_frozen = false;
130                 fl_freeze_form(form);
131         }
132
133         // Delete old buttons if there are some.
134         for(ButtonList::const_iterator cit = buttonlist_.begin();
135             cit != buttonlist_.end(); ++cit) {
136                 if ((*cit)->obj_) {
137                         fl_delete_object((*cit)->obj_);
138                         fl_free_object((*cit)->obj_);
139                 }
140                 delete (*cit);
141         }
142         buttonlist_.clear();
143
144         // Create menu frame if there is non yet.
145         if (!frame_) {
146                 frame_ = fl_add_frame(FL_UP_FRAME, 0, 0, form->w, mheight, "");
147                 fl_set_object_resize(frame_, FL_RESIZE_ALL);
148                 fl_set_object_gravity(frame_, NorthWestGravity, 
149                                       NorthEastGravity);
150         } 
151
152         for (Menu::const_iterator i = menu.begin(); 
153              i != menu.end(); ++i) {
154                 FL_OBJECT * obj;
155                 if (i->kind() != MenuItem::Submenu) {
156                         lyxerr << "ERROR: Menubar::Pimpl::Pimpl:"
157                                 " only submenus can appear in a menubar";
158                         break;
159                 }
160                 string label = i->label();
161                 string shortcut = i->shortcut();
162                 int width = string_width(label);
163                 obj = fl_add_button(FL_TOUCH_BUTTON,
164                                     air + moffset, yloc,
165                                     width + mbadd,
166                                     mbheight, 
167                                     label.c_str());
168                 fl_set_object_boxtype(obj, FL_FLAT_BOX);
169                 fl_set_object_color(obj, FL_MCOL, FL_MCOL);
170                 fl_set_object_lsize(obj, MENU_LABEL_SIZE);
171                 fl_set_object_lstyle(obj, FL_NORMAL_STYLE);
172                 fl_set_object_resize(obj, FL_RESIZE_ALL);
173                 fl_set_object_gravity(obj, NorthWestGravity, 
174                                       NorthWestGravity);
175                 moffset += obj->w + air;
176                 fl_set_object_shortcut(obj, shortcut.c_str(), 1);
177                 fl_set_object_callback(obj, C_Menubar_Pimpl_MenuCallback, 1);
178                 ItemInfo * iteminfo = new ItemInfo(this, 
179                                                    new MenuItem(*i), obj);
180                 buttonlist_.push_back(iteminfo);
181                 obj->u_vdata = iteminfo;
182 //              lyxerr << "MenuCallback: ItemInfo address=" << iteminfo
183 //                     << " Val=(pimpl_=" << iteminfo->pimpl_
184 //                     << ", item_=" << iteminfo->item_
185 //                     << ", obj_=" << iteminfo->obj_ << ")" <<endl;
186         }
187
188         if (!form_was_frozen) {
189                 fl_unfreeze_form(form);
190         }
191         if (!form_was_open) 
192                 fl_end_form();
193
194         lyxerr[Debug::GUI] << "Menubar set." << endl;
195
196
197 void Menubar::Pimpl::openByName(string const & name)
198 {
199         for(ButtonList::const_iterator cit = buttonlist_.begin();
200             cit != buttonlist_.end(); ++cit) {
201                 if ((*cit)->item_->submenu() == name) {
202                         MenuCallback((*cit)->obj_, 1);
203                         return;
204                 }
205         }
206         lyxerr << "Menubar::Pimpl::openByName: menu "
207                << name << " not found" << endl;
208 }
209
210
211 void Menubar::Pimpl::add_lastfiles(int menu, string const & extra_label,
212                                    std::vector<int> & /*smn*/, 
213                                    StrPool & strpool) 
214 {
215         int ii = 1;
216         for (LastFiles::const_iterator cit = lastfiles->begin();
217              cit != lastfiles->end() && ii < 10; ++cit, ++ii) {
218
219                 int action =
220                         lyxaction.getPseudoAction(LFUN_FILE_OPEN, (*cit));
221                 string label = tostr(ii) + ". "
222                         + MakeDisplayPath((*cit),30)
223                         + "%x" + tostr(action);
224                 if ((cit + 1) == lastfiles->end())
225                         label += extra_label;
226                 string shortcut = tostr(ii) + "#" + tostr(ii); 
227                 lyxerr[Debug::GUI] << "shortcut is " << shortcut <<
228                         endl;
229
230                 int n = fl_addtopup(menu, strpool.add(label));
231                 fl_setpup_shortcut(menu, n, strpool.add(shortcut));
232         }
233
234 }
235
236 void Menubar::Pimpl::add_documents(int menu, string const & extra_label,
237                                    std::vector<int> & /*smn*/, 
238                                    StrPool & strpool) 
239 {
240         std::vector<string> names = bufferlist.getFileNames();
241
242         if (names.empty()) {
243                 fl_addtopup(menu,_("No Documents Open!%i"));
244                 return;
245         }
246
247         for (std::vector<string>::const_iterator cit = names.begin();
248              cit != names.end() ; ++cit) {
249                 int action =
250                         lyxaction.getPseudoAction(LFUN_SWITCHBUFFER, *cit);
251                 string label = MakeDisplayPath(*cit, 30)
252                         + "%x" + tostr(action);
253                 if ((cit + 1) == names.end())
254                         label += extra_label;
255                                 
256                 fl_addtopup(menu, strpool.add(label));
257         }
258
259 }
260
261
262 int Menubar::Pimpl::create_submenu(Window win, LyXView * view, 
263                                    string const & menu_name, 
264                                    std::vector<int> & smn, StrPool & strpool) 
265 {
266         if (!menubackend_->hasMenu(menu_name)){ 
267                 lyxerr << "ERROR:create_submenu: Unknown menu `" 
268                        << menu_name << "'" << endl;
269                 return -1;
270         }
271         Menu md = menubackend_->getMenu(menu_name);
272
273         int menu = fl_newpup(win);
274         fl_setpup_softedge(menu, true);
275         fl_setpup_bw(menu, -1);
276         lyxerr[Debug::GUI] << "Adding menu " << menu 
277                            << " in deletion list" << endl;
278         smn.push_back(menu);
279
280         // Compute the size of the largest label (because xforms is
281         // not able to support shortcuts correctly...)
282         int max_width = 0;
283         string widest_label;
284         for (Menu::const_iterator i = md.begin(); i != md.end(); ++i) {
285                 MenuItem item = (*i);
286                 if (item.kind() == MenuItem::Command) {
287                         string label = item.label() + '\t';
288                         int width = string_width(label);
289                         if (width > max_width) {
290                                 max_width = width;
291                                 widest_label = label;
292                         }
293                 }
294         }
295         lyxerr[Debug::GUI] << "max_width=" << max_width 
296                            << ", widest_label=`" << widest_label 
297                            << "'" << endl;
298
299         for (Menu::const_iterator i = md.begin(); i != md.end(); ++i) {
300                 MenuItem item = (*i);
301                 // Is there a separator after this item?
302                 string extra_label;
303                 if ((i+1) != md.end()  
304                     && (i+1)->kind() == MenuItem::Separator)
305                         extra_label = "%l";
306
307                 switch(item.kind()) {
308                 case MenuItem::Command: {
309                         LyXFunc::func_status flag = 
310                                 view->getLyXFunc()->getStatus(item.action()); 
311
312                         // handle optional entries.
313                         if (item.optional() && (flag & LyXFunc::Disabled)) {
314                                 lyxerr[Debug::GUI] 
315                                         << "Skipping optional item " 
316                                         << item.label() << endl; 
317                                 break;
318                         }
319
320                         // Get the keys bound to this action, but keep only the
321                         // first one later
322                         string accel = toplevel_keymap->findbinding(item.action());
323                         // Build the menu label from all the info
324                         string label = item.label();
325
326                         if (!accel.empty()) {
327                                 // Try to be clever and add  just enough
328                                 // tabs to align shortcuts.
329                                 do 
330                                         label += '\t';
331                                 while (string_width(label) < max_width);
332                                 label += accel.substr(1,accel.find(']') - 1);
333                         }
334                         label += "%x" + tostr(item.action()) + extra_label;
335                         
336                         // Modify the entry using the function status
337                         string pupmode;
338                         if (flag & (LyXFunc::Disabled | LyXFunc::Unknown))
339                                 pupmode += "%i";
340                         if (flag & LyXFunc::ToggleOn)
341                                 pupmode += "%B";
342                         if (flag & LyXFunc::ToggleOff)
343                                 pupmode += "%b";
344                         label += pupmode;
345
346                         // Finally the menu shortcut
347                         string shortcut = item.shortcut();
348                         string xfshortcut;
349                         if (!shortcut.empty()) {
350                                 xfshortcut += uppercase(shortcut[0]);
351                                 xfshortcut += '#';
352                                 xfshortcut += uppercase(shortcut[0]);
353                                 xfshortcut += lowercase(shortcut[0]);
354                                 xfshortcut += '#';
355                                 xfshortcut += lowercase(shortcut[0]);
356                                 lyxerr[Debug::GUI] << "shortcut is " 
357                                                    << xfshortcut << endl;
358                                 label += "%h";
359                                 fl_addtopup(menu, strpool.add(label), 
360                                             strpool.add(xfshortcut));
361                         } else
362                                 fl_addtopup(menu, strpool.add(label));
363                         
364                         lyxerr[Debug::GUI] << "Command: \""  
365                                            << lyxaction.getActionName(item.action())
366                                            << "\", Binding " << accel 
367                                            << ", shortcut " << xfshortcut 
368                                            << endl;
369
370
371                         break;
372                 }
373
374                 case MenuItem::Submenu: {
375                         int submenu = create_submenu(win, view, 
376                                                      item.submenu(),
377                                                      smn, strpool);
378                         if (submenu == -1)
379                                 return -1;
380                         string label = item.label();
381                         label += extra_label + "%m";
382                         string shortcut = item.shortcut();
383                         int n = fl_addtopup(menu, strpool.add(label), submenu);
384                         fl_setpup_shortcut(menu, n, strpool.add(shortcut));
385                         break;
386                 }
387
388                 case MenuItem::Separator:
389                         // already done, and if it was the first one,
390                         // we just ignore it.
391                         break;
392
393                 case MenuItem::Documents: 
394                         add_documents(menu, extra_label, smn, strpool);
395                         break;
396
397
398                 case MenuItem::Lastfiles: 
399                         add_lastfiles(menu, extra_label, smn, strpool);
400                         break;
401
402                 }
403         }
404         return menu;
405 }
406
407 extern "C"
408 void C_Menubar_Pimpl_MenuCallback(FL_OBJECT * ob, long button)
409 {
410         Menubar::Pimpl::MenuCallback(ob, button);
411 }
412
413
414 void Menubar::Pimpl::MenuCallback(FL_OBJECT * ob, long button)
415 {
416         ItemInfo * iteminfo = static_cast<ItemInfo *>(ob->u_vdata);
417 //      lyxerr << "MenuCallback: ItemInfo address=" << iteminfo
418 //             << "Val=(pimpl_=" << iteminfo->pimpl_
419 //             << ", item_=" << iteminfo->item_
420 //             << ", obj_=" << iteminfo->obj_ << ")" <<endl;
421
422         LyXView * view = iteminfo->pimpl_->owner_;
423         MenuItem const * item = iteminfo->item_;
424
425         if (button == 1) {
426                 // set the pseudo menu-button
427                 fl_set_object_boxtype(ob, FL_DOWN_BOX);
428                 fl_set_button(ob, 0);
429                 fl_redraw_object(ob);
430         }
431
432         // Paranoia check
433         Assert(item->kind() == MenuItem::Submenu);
434         
435         // set tabstop length
436         fl_set_tabstop(menu_tabstop);
437         std::vector<int> submenus;
438         StrPool strpool;
439         int menu = iteminfo->pimpl_->
440                 create_submenu(FL_ObjWin(ob), view, 
441                                item->submenu(), 
442                                submenus, strpool);
443         if (menu != -1) {
444                 // place popup
445                 fl_setpup_position(view->getForm()->x + ob->x,
446                                    view->getForm()->y + ob->y + ob->h + 10);   
447                 int choice = fl_dopup(menu);
448                 
449                 if (button == 1) {
450                                 // set the pseudo menu-button back
451                         fl_set_object_boxtype(ob, FL_FLAT_BOX);
452                         fl_redraw_object(ob);
453                 }
454                 
455                 if (choice >= 1) {
456                         view->getLyXFunc()->Dispatch(choice);
457                 }
458         }
459         else 
460                 lyxerr << "Error in MenuCallback" << endl;
461         
462         std::for_each(submenus.begin(), submenus.end(), fl_freepup);
463         // restore tabstop length
464         fl_set_tabstop(default_tabstop);
465
466 }