]> git.lyx.org Git - lyx.git/blob - src/frontends/xforms/Menubar_pimpl.C
major GUII cleanup + Baruchs patch + Angus's patch + removed a couple of generated...
[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
179                 ItemInfo * iteminfo = new ItemInfo(this, 
180                                                    new MenuItem(*i), obj);
181                 buttonlist_.push_back(iteminfo);
182                 obj->u_vdata = iteminfo;
183 //              lyxerr << "MenuCallback: ItemInfo address=" << iteminfo
184 //                     << " Val=(pimpl_=" << iteminfo->pimpl_
185 //                     << ", item_=" << iteminfo->item_
186 //                     << ", obj_=" << iteminfo->obj_ << ")" <<endl;
187         }
188
189         if (!form_was_frozen) {
190                 fl_unfreeze_form(form);
191         }
192         if (!form_was_open) 
193                 fl_end_form();
194
195         // Force the redraw of the buttons (probably not the best
196         // method, but...) 
197         for(ButtonList::const_iterator cit = buttonlist_.begin();
198             cit != buttonlist_.end(); ++cit) {
199                 if ((*cit)->obj_) {
200                         fl_redraw_object((*cit)->obj_);
201                 }
202         }
203
204         lyxerr[Debug::GUI] << "Menubar set." << endl;
205
206
207 void Menubar::Pimpl::openByName(string const & name)
208 {
209         for(ButtonList::const_iterator cit = buttonlist_.begin();
210             cit != buttonlist_.end(); ++cit) {
211                 if ((*cit)->item_->submenu() == name) {
212                         MenuCallback((*cit)->obj_, 1);
213                         return;
214                 }
215         }
216         lyxerr << "Menubar::Pimpl::openByName: menu "
217                << name << " not found" << endl;
218 }
219
220
221 void Menubar::Pimpl::add_lastfiles(int menu, string const & extra_label,
222                                    std::vector<int> & /*smn*/, 
223                                    StrPool & strpool) 
224 {
225         int ii = 1;
226         for (LastFiles::const_iterator cit = lastfiles->begin();
227              cit != lastfiles->end() && ii < 10; ++cit, ++ii) {
228
229                 int action =
230                         lyxaction.getPseudoAction(LFUN_FILE_OPEN, (*cit));
231                 string label = tostr(ii) + ". "
232                         + MakeDisplayPath((*cit),30)
233                         + "%x" + tostr(action) + "%h";
234                 if ((cit + 1) == lastfiles->end())
235                         label += extra_label;
236                 string shortcut = tostr(ii) + "#" + tostr(ii); 
237                 lyxerr[Debug::GUI] << "shortcut is " << shortcut <<
238                         endl;
239
240                 fl_addtopup(menu, strpool.add(label), strpool.add(shortcut));
241         }
242
243 }
244
245 void Menubar::Pimpl::add_documents(int menu, string const & extra_label,
246                                    std::vector<int> & /*smn*/, 
247                                    StrPool & strpool) 
248 {
249         std::vector<string> names = bufferlist.getFileNames();
250
251         if (names.empty()) {
252                 fl_addtopup(menu,_("No Documents Open!%i"));
253                 return;
254         }
255
256         for (std::vector<string>::const_iterator cit = names.begin();
257              cit != names.end() ; ++cit) {
258                 int action =
259                         lyxaction.getPseudoAction(LFUN_SWITCHBUFFER, *cit);
260                 string label = MakeDisplayPath(*cit, 30)
261                         + "%x" + tostr(action);
262                 if ((cit + 1) == names.end())
263                         label += extra_label;
264                                 
265                 fl_addtopup(menu, strpool.add(label));
266         }
267
268 }
269
270
271 int Menubar::Pimpl::create_submenu(Window win, LyXView * view, 
272                                    string const & menu_name, 
273                                    std::vector<int> & smn, StrPool & strpool) 
274 {
275         if (!menubackend_->hasMenu(menu_name)){ 
276                 lyxerr << "ERROR:create_submenu: Unknown menu `" 
277                        << menu_name << "'" << endl;
278                 return -1;
279         }
280         Menu md = menubackend_->getMenu(menu_name);
281
282         int menu = fl_newpup(win);
283         fl_setpup_softedge(menu, true);
284         fl_setpup_bw(menu, -1);
285         lyxerr[Debug::GUI] << "Adding menu " << menu 
286                            << " in deletion list" << endl;
287         smn.push_back(menu);
288
289         // Compute the size of the largest label (because xforms is
290         // not able to support shortcuts correctly...)
291         int max_width = 0;
292         string widest_label;
293         Menu::const_iterator end = md.end();
294         for (Menu::const_iterator i = md.begin(); i != end; ++i) {
295                 MenuItem item = (*i);
296                 if (item.kind() == MenuItem::Command) {
297                         string label = item.label() + '\t';
298                         int width = string_width(label);
299                         if (width > max_width) {
300                                 max_width = width;
301                                 widest_label = label;
302                         }
303                 }
304         }
305         lyxerr[Debug::GUI] << "max_width=" << max_width 
306                            << ", widest_label=`" << widest_label 
307                            << "'" << endl;
308
309         for (Menu::const_iterator i = md.begin(); i != end; ++i) {
310                 MenuItem item = (*i);
311                 // Is there a separator after this item?
312                 string extra_label;
313                 if ((i+1) != end  
314                     && (i+1)->kind() == MenuItem::Separator)
315                         extra_label = "%l";
316
317                 switch(item.kind()) {
318                 case MenuItem::Command: {
319                         LyXFunc::func_status flag = 
320                                 view->getLyXFunc()->getStatus(item.action()); 
321
322                         // handle optional entries.
323                         if (item.optional() && (flag & LyXFunc::Disabled)) {
324                                 lyxerr[Debug::GUI] 
325                                         << "Skipping optional item " 
326                                         << item.label() << endl; 
327                                 break;
328                         }
329
330                         // Get the keys bound to this action, but keep only the
331                         // first one later
332                         string accel = toplevel_keymap->findbinding(item.action());
333                         // Build the menu label from all the info
334                         string label = item.label();
335
336                         if (!accel.empty()) {
337                                 // Try to be clever and add  just enough
338                                 // tabs to align shortcuts.
339                                 do 
340                                         label += '\t';
341                                 while (string_width(label) < max_width);
342                                 label += accel.substr(1,accel.find(']') - 1);
343                         }
344                         label += "%x" + tostr(item.action()) + extra_label;
345                         
346                         // Modify the entry using the function status
347                         string pupmode;
348                         if (flag & (LyXFunc::Disabled | LyXFunc::Unknown))
349                                 pupmode += "%i";
350                         if (flag & LyXFunc::ToggleOn)
351                                 pupmode += "%B";
352                         if (flag & LyXFunc::ToggleOff)
353                                 pupmode += "%b";
354                         label += pupmode;
355
356                         // Finally the menu shortcut
357                         string shortcut = item.shortcut();
358
359                         if (!shortcut.empty()) {
360                                 shortcut += lowercase(shortcut[0]);
361                                 label += "%h";
362                                 fl_addtopup(menu, strpool.add(label), 
363                                             strpool.add(shortcut));
364                         } else
365                                 fl_addtopup(menu, strpool.add(label));
366                         
367                         lyxerr[Debug::GUI] << "Command: \""  
368                                            << lyxaction.getActionName(item.action())
369                                            << "\", Binding " << accel 
370                                            << ", shortcut " << shortcut 
371                                            << endl;
372
373
374                         break;
375                 }
376
377                 case MenuItem::Submenu: {
378                         int submenu = create_submenu(win, view, 
379                                                      item.submenu(),
380                                                      smn, strpool);
381                         if (submenu == -1)
382                                 return -1;
383                         string label = item.label();
384                         label += extra_label + "%m";
385                         string shortcut = item.shortcut();
386                         if (!shortcut.empty()) {
387                                 shortcut += lowercase(shortcut[0]);
388                                 fl_addtopup(menu, strpool.add(label + "%h"),
389                                             submenu, strpool.add(shortcut));
390                         }
391                         else {
392                                 fl_addtopup(menu, strpool.add(label), submenu);
393                         }
394                         break;
395                 }
396
397                 case MenuItem::Separator:
398                         // already done, and if it was the first one,
399                         // we just ignore it.
400                         break;
401
402                 case MenuItem::Documents: 
403                         add_documents(menu, extra_label, smn, strpool);
404                         break;
405
406
407                 case MenuItem::Lastfiles: 
408                         add_lastfiles(menu, extra_label, smn, strpool);
409                         break;
410
411                 }
412         }
413         return menu;
414 }
415
416 extern "C"
417 void C_Menubar_Pimpl_MenuCallback(FL_OBJECT * ob, long button)
418 {
419         Menubar::Pimpl::MenuCallback(ob, button);
420 }
421
422
423 void Menubar::Pimpl::MenuCallback(FL_OBJECT * ob, long button)
424 {
425         ItemInfo * iteminfo = static_cast<ItemInfo *>(ob->u_vdata);
426 //      lyxerr << "MenuCallback: ItemInfo address=" << iteminfo
427 //             << "Val=(pimpl_=" << iteminfo->pimpl_
428 //             << ", item_=" << iteminfo->item_
429 //             << ", obj_=" << iteminfo->obj_ << ")" <<endl;
430
431         LyXView * view = iteminfo->pimpl_->owner_;
432         MenuItem const * item = iteminfo->item_;
433
434         if (button == 1) {
435                 // set the pseudo menu-button
436                 fl_set_object_boxtype(ob, FL_DOWN_BOX);
437                 fl_set_button(ob, 0);
438                 fl_redraw_object(ob);
439         }
440
441         // Paranoia check
442         Assert(item->kind() == MenuItem::Submenu);
443         
444         // set tabstop length
445         fl_set_tabstop(menu_tabstop);
446         std::vector<int> submenus;
447         StrPool strpool;
448         int menu = iteminfo->pimpl_->
449                 create_submenu(FL_ObjWin(ob), view, 
450                                item->submenu(), 
451                                submenus, strpool);
452         if (menu != -1) {
453                 // place popup
454                 fl_setpup_position(view->getForm()->x + ob->x,
455                                    view->getForm()->y + ob->y + ob->h + 10);   
456                 int choice = fl_dopup(menu);
457                 
458                 if (button == 1) {
459                                 // set the pseudo menu-button back
460                         fl_set_object_boxtype(ob, FL_FLAT_BOX);
461                         fl_redraw_object(ob);
462                 }
463                 
464                 if (choice >= 1) {
465                         view->getLyXFunc()->Dispatch(choice);
466                 }
467         }
468         else 
469                 lyxerr << "Error in MenuCallback" << endl;
470         
471         std::for_each(submenus.begin(), submenus.end(), fl_freepup);
472         // restore tabstop length
473         fl_set_tabstop(default_tabstop);
474
475 }