~ubuntu-branches/debian/sid/menu/sid

« back to all changes in this revision

Viewing changes to install-menu/menu-tree.cc

  • Committer: Bazaar Package Importer
  • Author(s): Bill Allombert
  • Date: 2005-05-24 10:34:47 UTC
  • mfrom: (1.1.2 hoary)
  • Revision ID: james.westby@ubuntu.com-20050524103447-9zu50so5d0nte3o9
Tags: 2.1.24
* The "head or tail" release 
* Fix .menus typo in menufile.5. Closes: #306564. Thanks Sean Finney.
* Add Vietnamese menu messages and menu sections translations.
  Thanks Clytie Siddall. Closes: #307450, #308953.
* Update Esperanto menu sections translation. Thanks MJ Ray.
* Add Esperanto menu messages translation. Thanks MJ Ray.
* Unfuzzy Norwegian Bokmål menu messages translation.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Debian menu system -- install-menu
 
3
 * install-menus/menu-tree.cc
 
4
 *
 
5
 * Copyright (C) 1996-2003  Joost Witteveen, 
 
6
 * Copyright (C) 2002-2004  Bill Allombert and Morten Brix Pedersen.
 
7
 * 
 
8
 * This program is free software; you can redistribute it and/or modify
 
9
 * it under the terms of the GNU General Public License as published by
 
10
 * the Free Software Foundation; either version 2 of the License, or
 
11
 * (at your option) any later version.
 
12
 *
 
13
 * This program is distributed in the hope that it will be useful,
 
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
 * GNU General Public License for more details.
 
17
 *
 
18
 * You should have received a copy of the GNU General Public License with
 
19
 * the Debian GNU/Linux distribution in file /usr/share/common-licenses/GPL;
 
20
 * if not, write to the Free Software Foundation, Inc., 59 Temple Place,
 
21
 * Suite 330, Boston, MA  02111-1307  USA
 
22
 *
 
23
 *
 
24
 * Written by Joost Witteveen.
 
25
 */
 
26
 
 
27
#include <cctype>
 
28
#include <list>
 
29
#include <set>
1
30
#include "menu-tree.h"
2
31
#include "install-menu.h"
3
32
#include "hints.h"
4
 
#include <ctype.h>
5
 
#include <list>
6
 
#include <set>
7
 
 
8
 
bool operator< (const StrVec &left, const StrVec &right){
9
 
  cStrVec::iterator i,j;
10
 
 
11
 
  for(i=left.begin(), j=right.begin();
12
 
      (i!=left.end()) && (j!=right.end());
13
 
      i++, j++){
14
 
    if((*i)<(*j))
15
 
      return true;
16
 
    else if((*i)>(*j))
17
 
      return false;
18
 
  }
19
 
  if(j==right.end())
20
 
    return false;
21
 
  else
22
 
    return true;
23
 
}
24
 
extern menuentry menu;
25
 
 
26
 
void menuentry::debug(int level){
27
 
  submenu_container::iterator i;
28
 
  int li;
29
 
 
30
 
  for(li=level ;li>0; li--){
31
 
    cout<<"  ";
32
 
  }
33
 
  cout<<vars[TITLE_VAR]<<":"<<vars[SECTION_VAR]<<":"<<vars["icon"]<<endl;
34
 
  for(i=submenus.begin(); i!=submenus.end(); i++){
35
 
    (*i).second->debug(level+1);
36
 
  }
37
 
}
38
 
 
39
 
void menuentry::add_entry(StrVec s,
40
 
                          map <String, String, less<String> > &v){
41
 
  submenu_container::iterator f,firstsec_i;
42
 
  map <String, String, less<String> >::iterator vi;
43
 
  map <String, String, less<String> >::iterator vj;
44
 
  StrVec subs;
45
 
  StrVec firstsec;
46
 
  cStrVec::iterator i;
47
 
  bool use_forced=false;
48
 
  
49
 
  //there are two ways to store the menuentries in the `tree'
50
 
  // - `flat', all menuentries in the toplevel submenus map.
51
 
  //   This is used when working with `hints'. In this case, we
52
 
  //   will later sort out the tree.
53
 
  // - as a true tree.
54
 
  //However, even in the `flat', hint-processing case, if there already
55
 
  //exists a submenu with exactly the right s[0], then we do desent,
56
 
  //if it has a v[FORCED_VAR] set.
57
 
 
58
 
  if(!s.size()){
59
 
    for(vi=v.begin(); vi!=v.end(); vi++){
60
 
      if((*vi).first == TITLE_VAR)
61
 
        continue;
62
 
      if((*vi).first == SECTION_VAR)
63
 
        continue;
64
 
      vj=vars.find((*vi).first);
65
 
      if((vj==vars.end())||
66
 
         (((*vj).second).size()==0)||
67
 
         (((*vj).second)=="none")){
68
 
        vars[(*vi).first]=(*vi).second;
69
 
      }
 
33
#include <stringtoolbox.h>
 
34
 
 
35
using std::vector;
 
36
using std::string;
 
37
using std::list;
 
38
using std::set;
 
39
using std::map;
 
40
 
 
41
/** Return prioritized string of useful shortcut keys
 
42
 *
 
43
 * As input takes a string, usually a menu entry title, and then parses it
 
44
 * and returns the best possible characters for a shortcut key.
 
45
 *
 
46
 */
 
47
string sort_hotkey(const string& str)
 
48
{
 
49
  string keys;
 
50
  string::size_type size = str.length();
 
51
  char *s = strdup(str.c_str());
 
52
 
 
53
  if (str.empty())
 
54
      return keys;
 
55
 
 
56
  keys = s[0];
 
57
  s[0] = '\0';
 
58
  // Adds all uppercase letters which appears after a space or punctation
 
59
  // mark.
 
60
  for (string::size_type i = 1; i != size; ++i)
 
61
    if ((isspace(s[i-1]) || ispunct(s[i-1])) && isupper(s[i])) {
 
62
      keys += s[i];
 
63
      s[i] = '\0';
 
64
    }
 
65
  // Adds letters or digit which appears after a space or punctation mark.
 
66
  for (string::size_type i = 1; i != size; ++i)
 
67
    if ((isspace(s[i-1]) || ispunct(s[i-1])) && isalnum(s[i])) {
 
68
      keys += s[i];
 
69
      s[i] = '\0';
 
70
    }
 
71
  // Adds uppercase letters.
 
72
  for (string::size_type i = 1; i != size; ++i)
 
73
    if (isupper(s[i])) {
 
74
      keys += s[i];
 
75
      s[i] = '\0';
 
76
    }
 
77
  // Adds letters.
 
78
  for (string::size_type i = 1; i != size; ++i)
 
79
    if (isalpha(s[i])) {
 
80
      keys += s[i];
 
81
      s[i] = '\0';
 
82
    }
 
83
  // Adds letters and digits.
 
84
  for (string::size_type i = 1; i != size; ++i)
 
85
    if (isalnum(s[i])) {
 
86
      keys += s[i];
 
87
      s[i] = '\0';
 
88
    }
 
89
  free(s);
 
90
  return keys;
 
91
}
 
92
 
 
93
/** Adds a new entry in the menu hierarchy.
 
94
 *
 
95
 * Arguments:
 
96
 *
 
97
 *     sections: A vector of strings, holding the section names.
 
98
 *     entry_vars: Variables of the entry to be added.
 
99
 * 
 
100
 * There are two ways to store the menuentries in the `tree'
 
101
 * - `flat', all menuentries in the toplevel submenus map.
 
102
 *   This is used when working with `hints'. In this case, we
 
103
 *   will later sort out the tree.
 
104
 * - as a true tree.
 
105
 * However, even in the `flat', hint-processing case, if there already
 
106
 * exists a submenu with exactly the right sections[0], then we do desent,
 
107
 * if it has a entry_vars[FORCED_VAR] set.
 
108
 */
 
109
void menuentry::add_entry(std::vector<std::string> sections, std::map<std::string, std::string> &entry_vars)
 
110
{
 
111
  map<string, string>::iterator vi;
 
112
  map<string, string>::iterator vj;
 
113
  bool use_forced = false;
 
114
 
 
115
  if (sections.empty()) {
 
116
    // No sections defined, this means that we probably are at the last
 
117
    // node, so we just need to fill in variables that aren't already
 
118
    // defined.
 
119
    for (vi = entry_vars.begin(); vi != entry_vars.end(); ++vi)
 
120
    {
 
121
      if (vi->first == TITLE_VAR)
 
122
          continue;
 
123
      if (vi->first == SECTION_VAR)
 
124
          continue;
 
125
 
 
126
      vj = vars.find(vi->first);
 
127
      if ((vj == vars.end()) || vj->second.empty() || (vj->second == "none"))
 
128
          vars[vi->first] = vi->second;
70
129
    }
71
130
    return;
72
131
  }
73
132
 
74
 
  firstsec.push_back(s[0]);
75
 
  firstsec_i=submenus.find(firstsec);
76
 
 
77
 
  if(firstsec_i!=submenus.end())
78
 
    if((*firstsec_i).second->forced)
79
 
      use_forced=true;
80
 
 
81
 
  if(((!config->hint_optimize)||use_forced)&&
82
 
     (s.size()>1)){
83
 
    
84
 
    for(i=s.begin()+1;i!=s.end(); i++)
85
 
      if((*i).size())
86
 
        subs.push_back((*i));
87
 
    
88
 
    if(firstsec_i==submenus.end()){
89
 
      submenus[firstsec]=new menuentry;
90
 
      submenus[firstsec]->section=subs;
91
 
      (submenus[firstsec]->vars)[TITLE_VAR]=firstsec[0];
 
133
  vector<string> firstsection;
 
134
  firstsection.push_back(sections[0]);
 
135
  submenu_container::iterator firstsection_i = submenus.find(firstsection);
 
136
 
 
137
  if (firstsection_i != submenus.end())
 
138
      if (firstsection_i->second->forced)
 
139
          use_forced = true;
 
140
 
 
141
  if ((!menumethod->hint_optimize || use_forced) && (sections.size() > 1)) {
 
142
    // We are either not using hint_optimize, or we have to add this
 
143
    // section, because it's forced to be created by the forcetree.
 
144
    //
 
145
    // And because sections.size() > 1, there are more sections to add.
 
146
 
 
147
    // Add new subsections to our new section (but strip the first one)
 
148
    vector<string> subsections;
 
149
    for (vector<string>::iterator i = sections.begin()+1; i != sections.end(); ++i)
 
150
        if (!i->empty())
 
151
            subsections.push_back(*i);
 
152
 
 
153
    // If the first subsection doesn't already exist, then create a new one.
 
154
    if (firstsection_i == submenus.end()) {
 
155
      submenus[firstsection] = new menuentry;
 
156
      submenus[firstsection]->vars[TITLE_VAR] = firstsection[0];
92
157
    }
93
 
    submenus[firstsec]->add_entry(subs,v);
94
 
  }else {
95
 
    f=submenus.find(s);
96
 
    if(f==submenus.end()){
97
 
      // Adding a new entry.
98
 
      menuentry *m;
99
 
      m=new menuentry;
100
 
      submenus[s]=m;
101
 
      m->section=s;
102
 
      m->vars=v;
 
158
 
 
159
    submenus[firstsection]->add_entry(subsections, entry_vars);
 
160
  } else {
 
161
    submenu_container::iterator f = submenus.find(sections);
 
162
    if (f == submenus.end()) {
 
163
      // Add a new entry, because these sections doesn't exist.
 
164
      submenus[sections] = new menuentry;
 
165
      submenus[sections]->vars = entry_vars;
103
166
    } else {
104
 
      // Modifying an already existing entry. Add all varables
 
167
      // Modifying an already existing entry. Add all variables
105
168
      // that aren't already defined.
106
 
      for(vi=v.begin(); vi!=v.end(); vi++){
107
 
        menuentry *m;
108
 
        m=(*f).second;
109
 
        vj=m->vars.find((*vi).first);
110
 
        if((vj==m->vars.end())||
111
 
           (((*vj).second).size()==0)||
112
 
           (((*vj).second)=="none")){
113
 
          m->vars[(*vi).first]=(*vi).second;
114
 
        }
 
169
      for (vi = entry_vars.begin(); vi != entry_vars.end(); ++vi)
 
170
      {
 
171
        menuentry *m = f->second;
 
172
        vj = m->vars.find(vi->first);
 
173
        if ((vj==m->vars.end()) || vj->second.empty() || (vj->second == "none"))
 
174
            m->vars[vi->first] = vi->second;
115
175
      }
116
176
    }
117
177
  }
118
178
}
119
179
 
120
 
void menuentry::add_menuentry_ptr(StrVec s,
121
 
                                  menuentry *m){
122
 
  submenu_container::iterator f;
123
 
  StrVec subs,firstsec;
124
 
  cStrVec::iterator i;
125
 
 
126
 
  if(s.size()>1){
127
 
    for(i=s.begin()+1;i!=s.end(); i++)
128
 
      if((*i).size())
129
 
        subs.push_back((*i));
130
 
    
131
 
    firstsec.push_back(s[0]);
132
 
    f=submenus.find(firstsec);
133
 
    if(f==submenus.end()){
134
 
      submenus[firstsec]=new menuentry;
135
 
      submenus[firstsec]->section=subs;
136
 
      submenus[firstsec]->forced=m->forced;
137
 
      (submenus[firstsec]->vars)[TITLE_VAR]=firstsec[0];
 
180
/** Adds a new entry in the menu hierarchy, using an already created
 
181
 * menuentry pointer.
 
182
 *
 
183
 * Arguments:
 
184
 *
 
185
 *     sections: A vector of strings, holding the section names.
 
186
 *     entry: a pointer to a menuentry.
 
187
 */
 
188
void menuentry::add_entry_ptr(std::vector<std::string> sections, menuentry *entry)
 
189
{
 
190
  if (sections.size() > 1) {
 
191
    // There are more sections to add...
 
192
 
 
193
    // Add new subsections to our new section (but strip the first one)
 
194
    vector<string> subsections;
 
195
    for (vector<string>::iterator i = sections.begin()+1; i != sections.end(); ++i)
 
196
        if (!i->empty())
 
197
            subsections.push_back(*i);
 
198
 
 
199
    // If the first subsection doesn't already exist, then create a new one.
 
200
    vector<string> firstsection;
 
201
    firstsection.push_back(sections[0]);
 
202
    if (submenus.find(firstsection) == submenus.end()) {
 
203
      submenus[firstsection] = new menuentry;
 
204
      submenus[firstsection]->forced = entry->forced;
 
205
      submenus[firstsection]->vars[TITLE_VAR] = firstsection[0];
138
206
    }
139
 
    submenus[firstsec]->add_menuentry_ptr(subs,m);
140
 
  }else {
141
 
    f=submenus.find(s);
142
 
    if(f==submenus.end())
143
 
      submenus[s]=m;
 
207
    submenus[firstsection]->add_entry_ptr(subsections, entry);
 
208
  } else if (submenus.find(sections) == submenus.end()) {
 
209
    // Add a new entry, because this section doesn't exist.
 
210
    submenus[sections] = entry;
144
211
  }
145
212
}
146
213
 
147
 
void menuentry::output(){
148
 
  String treew=config->treewalk();
149
 
  //  map <String, menuentry *, less<String> >::iterator i;
150
 
  submenu_container::iterator j;
151
 
  multimap <String, menuentry *, less<String> > sorted;
152
 
  multimap <String, menuentry *, less<String> >::iterator i;
153
 
 
154
 
  
155
 
  //sort the submenus in sorted:
156
 
  for(j=submenus.begin(); j!=submenus.end(); j++){
157
 
    String s;
158
 
    if(config->sort)
159
 
      s=config->sort->soutput((*j).second->vars);
 
214
struct menusort {
 
215
 
 
216
  bool operator()(std::string s1, std::string s2) const {
 
217
    return strcoll(s1.c_str(), s2.c_str())<0;
 
218
  }
 
219
};
 
220
 
 
221
/** Output menu tree.
 
222
 *
 
223
 * Uses the 'treewalk' variable to define what to output in which order,
 
224
 * this documentation was taken from the menu manual:
 
225
 *
 
226
 *   `treewalk="c(m)"'
 
227
 *        This string defines in what order to dump the `$startmenu',
 
228
 *        `$endmenu', and `$submenutitle' (and its children).  Each char in
 
229
 *        the string refers to:
 
230
 *
 
231
 *  c  : dump children of menu.
 
232
 *  m  : dump this menu's $submenutitles
 
233
 *  (  : dump $startmenu
 
234
 *  )  : dump $endmenu
 
235
 *  M  : dump all $submenutitles of this menu and this menu's children.
 
236
 *
 
237
 *       The default is "c(m)".  For olvwm, one needs: "(M)"
 
238
 */
 
239
void menuentry::output()
 
240
{
 
241
  string treew = menumethod->treewalk();
 
242
  submenu_container::iterator sub_i;
 
243
  std::multimap<string, menuentry *, menusort> sorted;
 
244
  std::multimap<string, menuentry *>::iterator i;
 
245
 
 
246
  // Sort the submenus in sorted:
 
247
  for (sub_i = submenus.begin(); sub_i != submenus.end(); ++sub_i)
 
248
  {
 
249
    string s;
 
250
    if (menumethod->sort)
 
251
      s = menumethod->sort->soutput(sub_i->second->vars);
160
252
    else
161
 
      s=(*j).second->vars[SORT_VAR] + ":" + (*j).second->vars[TITLE_VAR];
162
 
    cout<<"sorting on: "<<s<<endl;
163
 
    sorted.insert(pair<String,menuentry *>(s,(*j).second));
164
 
  };
165
 
 
166
 
  for(string::size_type j=0;j<treew.length(); j++){
167
 
    bool children_too=false;
168
 
    switch(treew[j]){
169
 
    case 'c':
170
 
      for(i=sorted.begin(); i!=sorted.end(); i++)
171
 
        if((*i).second->submenus.size())
172
 
          (*i).second->output();
173
 
      break;
174
 
    case '(':
175
 
      if(submenus.size())
176
 
        if(config->startmenu&& testuniqueness(vars))
177
 
          config->startmenu->output(vars);
178
 
      break;
179
 
    case ')':
180
 
      if(submenus.size())
181
 
        if(config->endmenu && testuniqueness(vars))
182
 
          config->endmenu->output(vars);
183
 
      break;
184
 
    case 'M':
185
 
      children_too=true;
186
 
    case 'm':;
187
 
      for(i=sorted.begin(); i!=sorted.end(); i++)
188
 
        if(((*i).second->vars[COMMAND_VAR].length())&&
189
 
           testuniqueness((*i).second->vars))
190
 
          supported->subst((*i).second->vars);
191
 
        else{
192
 
          if((config->submenutitle)&&
193
 
             ((*i).second->submenus.size())&&
194
 
             testuniqueness((*i).second->vars))
195
 
            config->submenutitle->output((*i).second->vars);
196
 
          if(children_too)
197
 
            (*i).second->output();
 
253
      s = sub_i->second->vars[SORT_VAR] + ':' + sub_i->second->vars[TITLE_VAR];
 
254
 
 
255
    sorted.insert(std::pair<string, menuentry *>(s, sub_i->second));
 
256
  }
 
257
 
 
258
  // Output the menu according to the treewalk variable.
 
259
  for (string::size_type j = 0; j < treew.length(); ++j)
 
260
  {
 
261
    bool children_too = false;
 
262
    switch (treew[j])
 
263
    {
 
264
      case 'c':
 
265
        for (i = sorted.begin(); i != sorted.end(); ++i)
 
266
            if (!i->second->submenus.empty())
 
267
                i->second->output();
 
268
        break;
 
269
      case '(':
 
270
        if (!submenus.empty())
 
271
            if (menumethod->startmenu && testuniqueness(vars))
 
272
                menumethod->startmenu->output(vars);
 
273
        break;
 
274
      case ')':
 
275
        if (!submenus.empty())
 
276
            if (menumethod->endmenu && testuniqueness(vars))
 
277
                menumethod->endmenu->output(vars);
 
278
        break;
 
279
      case 'M':
 
280
        children_too = true;
 
281
      case 'm':
 
282
        for (i = sorted.begin(); i != sorted.end(); ++i)
 
283
        {
 
284
          if (!i->second->vars[COMMAND_VAR].empty() &&
 
285
              testuniqueness(i->second->vars)) {
 
286
            supported->subst(i->second->vars);
 
287
          } else {
 
288
            if (menumethod->submenutitle && !i->second->submenus.empty() &&
 
289
                testuniqueness(i->second->vars))
 
290
                menumethod->submenutitle->output(i->second->vars);
 
291
            if (children_too)
 
292
                i->second->output();
 
293
          }
198
294
        }
199
295
    }
200
296
  }
201
297
}
202
298
 
203
 
void menuentry::store_hints(){
204
 
  /* put the $hint variables contents in the hint of
205
 
     the submenu[] map. For submenu entries (without a command),
206
 
     put the $hint variable in all menuentries that lie below
207
 
     that one.
208
 
  */
209
 
  submenu_container::iterator i,j;
210
 
  cStrVec::iterator k;
 
299
/** Put the $hint variable contents in the hint of the submenu[] map. For
 
300
 * submenu entries (without a command), put the $hint variable in all
 
301
 *  menuentries that lie below that one.
 
302
 */
 
303
void menuentry::store_hints()
 
304
{
 
305
  submenu_container::iterator i, j;
211
306
  unsigned int l;
212
307
 
213
 
  // Make sure menuhints are empty:
214
 
  for(i=submenus.begin(); i!=submenus.end(); i++){
215
 
    (*i).second->menuhints.erase((*i).second->menuhints.begin(),
216
 
                                 (*i).second->menuhints.end());
217
 
  }
218
 
 
219
 
  for(i=submenus.begin(); i!=submenus.end(); i++){
220
 
    const String &hs=(*i).second->vars[HINTS_VAR];
221
 
 
222
 
    if(hs.size() != 0){
223
 
      StrVec h;
224
 
      
225
 
      break_commas(hs,h);
226
 
      j=i;
227
 
      do{
228
 
        for(k=h.begin(); k!=h.end(); k++)
229
 
          (*j).second->menuhints.push_back(*k);
230
 
        j++;
231
 
        for(l=0; (l!=(*i).first.size());  l++)
232
 
          if((*i).first[l]!=(*j).first[l])
233
 
            break;
234
 
      } while (l==(*i).first.size());
 
308
  // Make sure menuhints are empty
 
309
  for(i = submenus.begin(); i != submenus.end(); ++i)
 
310
      i->second->menuhints.erase(i->second->menuhints.begin(), i->second->menuhints.end());
 
311
 
 
312
  for (i = submenus.begin(); i != submenus.end(); ++i)
 
313
  {
 
314
    const string &hints_str = i->second->vars[HINTS_VAR];
 
315
 
 
316
    if (!hints_str.empty()) {
 
317
      vector<string> hints;
 
318
 
 
319
      break_char(hints_str, hints, ',');
 
320
 
 
321
      j = i;
 
322
 
 
323
      do {
 
324
        for (vector<string>::iterator k = hints.begin(); k != hints.end(); ++k)
 
325
            j->second->menuhints.push_back(*k);
 
326
 
 
327
        j++;
 
328
        if (j == submenus.end())
 
329
            break;
 
330
 
 
331
        for (l = 0; l != i->first.size(); ++l)
 
332
            if (i->first[l] != j->first[l])
 
333
                break;
 
334
      } while (l == i->first.size());
 
335
 
235
336
    }
236
 
  }  
 
337
  }
237
338
}
238
339
 
239
 
 
240
 
void menuentry::process_hints(const StrVec &pref){
241
 
  vector <StrVec> hint_list, hint_out;
242
 
  vector <const StrVec>::iterator k,m;
243
 
  cStrVec::iterator l;
 
340
/** Process the menu hierarchy through the hints algorithms.
 
341
 *
 
342
 * Basically, it has 4 steps (also marked as comments in the code):
 
343
 *
 
344
 *  Step 1: Fetch appropriate sections to put in hint_list vector.
 
345
 *  Step 2: Initialize hints class and calculate a new tree, using hint_list.
 
346
 *  Step 3: Remove all submenus.
 
347
 *  Step 4: Using the new hint_out vector, initialize our new sorted tree,
 
348
 *  thus re-creating the previously removed submenus (but in a different
 
349
 *  order).
 
350
 */
 
351
void menuentry::process_hints()
 
352
{
 
353
  vector<vector<string> > hint_list, hint_out;
 
354
  vector<vector<string> >::iterator k, m;
 
355
  vector<string>::const_iterator l;
244
356
  submenu_container::iterator i;
245
 
  submenu_container oldsubmenus;
 
357
 
 
358
  // First, process hints of children.
 
359
  for (i = submenus.begin(); i != submenus.end(); ++i)
 
360
      if (i->second->vars[COMMAND_VAR].empty())
 
361
          i->second->process_hints();
 
362
 
 
363
  store_hints();
 
364
 
 
365
  // Step 1.
 
366
  // Go through all submenus, and add their sections to hint_list.
 
367
  for (i = submenus.begin(); i != submenus.end(); ++i)
 
368
  {
 
369
      vector<string> sections;
 
370
 
 
371
      for (l = i->first.begin(); l != i->first.end(); ++l)
 
372
          sections.push_back(*l);
 
373
 
 
374
      // If there are more than one element in 'sections', pop off the last
 
375
      // one, since that's the title of the application.
 
376
      if (sections.size() > 1) {
 
377
        sections.pop_back();
 
378
        if (menumethod->hint_debug)
 
379
            std::cout << "Adding to hint_list: " << i->first << ", hints="
 
380
                << i->second->menuhints << std::endl;
 
381
        for (l=i->second->menuhints.begin(); l!=i->second->menuhints.end(); ++l)
 
382
            sections.push_back(*l);
 
383
        hint_list.push_back(sections);
 
384
      } else {
 
385
        vector<string> empty; // to make sure hint_list[i] and submenus[i] match
 
386
        hint_list.push_back(empty);
 
387
      }
 
388
  }
 
389
  // Step 2.
246
390
  hints h;
247
 
 
248
 
  // first, process hints of children.
249
 
  for(i=submenus.begin(); i!=submenus.end(); i++){
250
 
    if(((*i).second->vars[COMMAND_VAR]).size()==0){
251
 
      StrVec v(pref);
252
 
      cStrVec::iterator j;
253
 
      for(j=(*i).first.begin(); j!=(*i).first.end(); j++)
254
 
        v.push_back((*j));
255
 
      (*i).second->process_hints(v);
256
 
    }
257
 
  }
258
 
  store_hints();
259
 
 
260
 
  for(i=submenus.begin(); i!=submenus.end(); i++){
261
 
    //if(((*i).second->vars[COMMAND_VAR]).size()!=0){
262
 
      StrVec v;
263
 
      
264
 
      for(l=(*i).first.begin(); l!=(*i).first.end(); l++)
265
 
        v.push_back(*l);
266
 
      //if(((*i).second->vars[COMMAND_VAR]).size()!=0)
267
 
      if(v.size()>1){
268
 
        v.pop_back();
269
 
        if(config->hint_debug)
270
 
          cout<<"Adding to hint_list: "<<(*i).first
271
 
              <<", hints="<<(*i).second->menuhints<<endl;
272
 
        for(l=(*i).second->menuhints.begin(); 
273
 
            l!=(*i).second->menuhints.end(); 
274
 
            l++)
275
 
          v.push_back(*l);
276
 
        hint_list.push_back(v);
277
 
      } else{
278
 
        StrVec empty; // to make sure hint_list[i] and submenus[i] match
279
 
        hint_list.push_back(empty);
280
 
      }
281
 
      //}
282
 
  }
283
 
  h.set_nentry(      config->hint_nentry);
284
 
  h.set_topnentry(config->hint_topnentry);
285
 
  h.set_mixedpenalty(config->hint_mixedpenalty);
286
 
  h.set_minhintfreq(config->hint_minhintfreq);
287
 
  h.set_max_local_penalty(config->hint_mlpenalty);
288
 
  h.set_max_ntry(config->hint_max_ntry);
289
 
  h.set_max_iter_hint(config->hint_max_iter_hint);
290
 
  h.set_debug(      config->hint_debug);
291
 
 
292
 
  h.calc_tree(hint_list,hint_out);
293
 
 
294
 
  oldsubmenus=submenus;
 
391
  h.set_nentry(menumethod->hint_nentry);
 
392
  h.set_topnentry(menumethod->hint_topnentry);
 
393
  h.set_mixedpenalty(menumethod->hint_mixedpenalty);
 
394
  h.set_minhintfreq(menumethod->hint_minhintfreq);
 
395
  h.set_max_local_penalty(menumethod->hint_mlpenalty);
 
396
  h.set_max_ntry(menumethod->hint_max_ntry);
 
397
  h.set_max_iter_hint(menumethod->hint_max_iter_hint);
 
398
  h.set_debug(menumethod->hint_debug);
 
399
 
 
400
  // Calculate our new tree, using hints. A lot of magic happens here.
 
401
  h.calc_tree(hint_list, hint_out);
 
402
 
 
403
  // Step 3.
 
404
  submenu_container oldsubmenus = submenus;
295
405
  submenus.erase(submenus.begin(), submenus.end());
296
406
 
297
 
  for(k=hint_list.begin(), m=hint_out.begin(), i=oldsubmenus.begin(); 
298
 
      k!=hint_list.end(); 
299
 
      k++, m++, i++){
300
 
    StrVec v;
301
 
 
302
 
    v=(*m);
303
 
    v.push_back((*i).first[(*i).first.size()-1]); // restore title
304
 
 
305
 
    add_menuentry_ptr(v,(*i).second);
 
407
  // Step 4.
 
408
  for (k = hint_list.begin(), m = hint_out.begin(), i = oldsubmenus.begin();
 
409
      k != hint_list.end();
 
410
      k++, m++, i++)
 
411
  {
 
412
    vector<string> sections = *m;
 
413
 
 
414
    // Restore title (we popped it of earlier in this function).
 
415
    sections.push_back(i->first.back());
 
416
 
 
417
    // Finally re-add the entry...
 
418
    add_entry_ptr(sections, i->second);
306
419
  }
307
420
}
308
421
 
309
 
void menuentry::postprocess(int n_parent, int level, String prev){
310
 
  // Postprocess will set or correct some variables in the
311
 
  // menuentry tree (vars), that were not known at the time of
312
 
  // creation of the menuentry classes (due to hints, or other reasons)
313
 
  //
314
 
  // n_parent: number of elements in menu of parent
315
 
  // level: how deep this entry is nested in menu tree.
316
 
 
 
422
/** Postprocess will set or correct some variables in the
 
423
 * menuentry tree (vars), that were not known at the time of
 
424
 * creation of the menuentry classes (due to hints, or other reasons)
 
425
 *
 
426
 * Arguments:
 
427
 *
 
428
 *      n_parent: number of elements in menu of parent
 
429
 *      level: how deep this entry is nested in menu tree.
 
430
 *      prev_section: parent section name
 
431
 */
 
432
void menuentry::postprocess(int n_parent, int level, const std::string& prev_section)
 
433
{
317
434
  submenu_container::iterator i, i_next;
318
 
  menuentry *me;
319
 
  int index;
320
 
  
321
 
  vars[PRIVATE_LEVEL_VAR]=itoString(level);
322
 
  if(!level)
323
 
    vars[SECTION_VAR]=prev;
324
 
  for(i=submenus.begin(), index=0; 
325
 
      i!=submenus.end(); 
326
 
      index++){
327
 
    String title;
328
 
    cStrVec::iterator j;
329
 
    
 
435
  int index = 0;
 
436
 
 
437
  vars[PRIVATE_LEVEL_VAR] = itostring(level);
 
438
 
 
439
  // If we are at the bottom of the hierarchy, use the "parents" section name.
 
440
  if (!level)
 
441
      vars[SECTION_VAR] = prev_section;
 
442
 
 
443
  for (i = submenus.begin(); i != submenus.end(); ++index)
 
444
  {
330
445
    i_next=i;
331
446
    i_next++;
332
 
    
333
 
    title=prev;
334
 
    for(j=(*i).first.begin(); j!=(*i).first.end(); j++)
335
 
      title += String("/") + (*j);
336
 
 
337
 
    me=(*i).second;
338
 
    me->vars[SECTION_VAR]=title;
339
 
 
340
 
    me->vars[PRIVATE_ENTRYINDEX_VAR]=itoString(index);
341
 
    
342
 
    if(me->submenus.size())
343
 
      me->postprocess(submenus.size(), level+1, title);
344
 
    /* number of entries may have been changed by above call, need
345
 
       to test again */
346
 
    if(!me->submenus.size()){
347
 
      if(me->vars.find(COMMAND_VAR)==me->vars.end()){
348
 
        /* this is an empty menu (without comand or submenus), so
349
 
           delete it */
350
 
        submenus.erase(i);
351
 
 
352
 
        index--;
 
447
 
 
448
    string newsection = prev_section;
 
449
    for(vector<string>::const_iterator j = i->first.begin(); j != i->first.end(); ++j)
 
450
        newsection += '/' + *j;
 
451
 
 
452
    menuentry *me = i->second;
 
453
    me->vars[SECTION_VAR] = newsection;
 
454
 
 
455
    // Get the real section name by removing title of the section name. This
 
456
    // is useful when for example the title is "Foo version/456".
 
457
    me->vars[BASESECTION_VAR] = newsection.substr(0, newsection.rfind(me->vars[TITLE_VAR]) - 1);
 
458
 
 
459
    me->vars[PRIVATE_ENTRYINDEX_VAR] = itostring(index);
 
460
 
 
461
    if (!me->submenus.empty())
 
462
        me->postprocess(submenus.size(), level+1, newsection);
 
463
 
 
464
    // Number of entries may have been changed by above call, so we need to
 
465
    // test again.
 
466
    if (me->submenus.empty()) {
 
467
      if (me->vars.find(COMMAND_VAR) == me->vars.end()) {
 
468
        // This is an empty menu (without comand or submenus), so delete it.
 
469
        submenus.erase(i);
 
470
 
 
471
        index--;
353
472
      } else {
354
 
        (*i).second->vars[PRIVATE_ENTRYCOUNT_VAR]=itoString(submenus.size());
355
 
        (*i).second->vars[PRIVATE_LEVEL_VAR]=itoString(level+1);
 
473
        i->second->vars[PRIVATE_ENTRYCOUNT_VAR] = itostring(submenus.size());
 
474
        i->second->vars[PRIVATE_LEVEL_VAR] = itostring(level+1);
356
475
      }
357
476
    }
358
 
    /* don't use i here any more, it may have been erased above */
359
 
    i=i_next; /* don't do i++, as *i now may not be defined */
 
477
    // don't use i here any more, it may have been erased above
 
478
    i = i_next; // don't do i++, as *i now may not be defined
360
479
    index++;
361
480
  }
362
481
 
363
482
  generate_hotkeys();
364
 
  
365
 
  vars[PRIVATE_ENTRYCOUNT_VAR]=itoString(n_parent);
 
483
 
 
484
  vars[PRIVATE_ENTRYCOUNT_VAR] = itostring(n_parent);
366
485
}
367
486
 
368
 
char menuentry::hotkeyconv(char h){
369
 
  if (config->hotkeycase)
 
487
char menuentry::hotkeyconv(char h)
 
488
{
 
489
  if (menumethod->hotkeycase)
370
490
    return h;
371
491
  else
372
492
    return tolower(h);
373
493
}
374
 
void menuentry::generate_hotkeys(){
 
494
 
 
495
void menuentry::generate_hotkeys()
 
496
{
375
497
  string::size_type i,j;
376
498
  list<int>::iterator k, old_k;
377
499
  submenu_container::iterator subi;
378
 
  map <String, String, less<String> >::iterator l;
379
 
  StrVec keys;
 
500
  map<string, string>::iterator l;
 
501
  vector<string> keys;
380
502
  list<int> todo;
381
 
  set<char, less<char> > used_chars;
382
 
  String s;
383
 
  String str0;
 
503
  set<char> used_chars;
 
504
  string s;
384
505
  char c;
385
506
 
386
 
  str0="0";
 
507
  string str0 = "0";
387
508
  str0[0]='\0';
388
 
  if(config->hkexclude)
389
 
    s=config->hkexclude->soutput(vars);
390
 
  for(i=0;i!=s.length();i++)
 
509
  if (menumethod->hkexclude)
 
510
      s = menumethod->hkexclude->soutput(vars);
 
511
  for (i = 0; i != s.length(); i++)
391
512
    used_chars.insert(hotkeyconv(s[i]));
392
513
 
393
 
  for(subi=submenus.begin(), i=0; subi!=submenus.end(); subi++, i++){
 
514
  for(subi = submenus.begin(), i = 0; subi != submenus.end(); subi++, i++)
 
515
  {
394
516
    todo.push_back(i);
395
 
    l=(*subi).second->vars.find(HOTKEY_VAR);
396
 
    if(l!=(*subi).second->vars.end())
397
 
      keys.push_back((*l).second);
 
517
    l = subi->second->vars.find(HOTKEY_VAR);
 
518
    if (l != subi->second->vars.end())
 
519
        keys.push_back(l->second);
398
520
    else
399
 
      keys.push_back(str0);
400
 
    keys[i]+=sort_hotkey((*subi).second->vars[TITLE_VAR]);
 
521
        keys.push_back(str0);
 
522
    keys[i] += sort_hotkey(subi->second->vars[TITLE_VAR]);
401
523
  }
402
 
  j=0;
403
 
  while(todo.size()){
404
 
    for(k=todo.begin();k!=todo.end();){
405
 
      i=*k; 
406
 
      old_k=k++; //k++ here, to be able to todo.erase(old_k) safely.
407
 
      if(j>=keys[i].length()){
408
 
        keys[i]=str0;
409
 
        todo.erase(old_k);  //no hotkey found -- give up on this entry.
410
 
        continue;
 
524
  j = 0;
 
525
  while (!todo.empty())
 
526
  {
 
527
    for (k = todo.begin();k != todo.end();)
 
528
    {
 
529
      i = *k; 
 
530
      old_k = k++; //k++ here, to be able to todo.erase(old_k) safely.
 
531
      if (j >= keys[i].length()) {
 
532
        keys[i] = str0;
 
533
        todo.erase(old_k);  //no hotkey found -- give up on this entry.
 
534
        continue;
411
535
      }
412
 
      c=keys[i][j];
413
 
      if(c){
414
 
        if(used_chars.find(hotkeyconv(c))==used_chars.end()){
415
 
          todo.erase(old_k); //found a hotkey for this entry.
416
 
          keys[i]=String("")+c;
417
 
          used_chars.insert(hotkeyconv(c));
418
 
          continue;
419
 
        }
420
 
        else
421
 
          keys[i].replace(j,1,str0);
 
536
      c = keys[i][j];
 
537
      if (c) {
 
538
        if (used_chars.find(hotkeyconv(c)) == used_chars.end()) {
 
539
          todo.erase(old_k); //found a hotkey for this entry.
 
540
          keys[i] = c;
 
541
          used_chars.insert(hotkeyconv(c));
 
542
          continue;
 
543
        } else {
 
544
          keys[i].replace(j,1,str0);
 
545
        }
422
546
      }
423
547
    }
424
548
    j++;
425
549
  }
426
 
  for(subi=submenus.begin(), i=0; subi!=submenus.end(); subi++, i++){
427
 
    c=keys[i][0];
428
 
    if(c){
429
 
      (*subi).second->vars[HOTKEY_VAR]=String("")+c;
430
 
    }
 
550
  for (subi = submenus.begin(), i = 0; subi != submenus.end(); subi++, i++)
 
551
  {
 
552
    c = keys[i][0];
 
553
    if (c)
 
554
        subi->second->vars[HOTKEY_VAR] = c;
431
555
  }
432
556
}
433
 
 
434