1/** <title>NSMenuItem</title>
2
3   <abstract>The menu cell class.</abstract>
4
5   Copyright (C) 1996 Free Software Foundation, Inc.
6
7   Author: Ovidiu Predescu <ovidiu@net-community.com>
8   Date: May 1997
9   Author:  David Lazaro Saz <khelekir@encomix.es>
10   Date: Sep 1999
11
12   This file is part of the GNUstep GUI Library.
13
14   This library is free software; you can redistribute it and/or
15   modify it under the terms of the GNU Lesser General Public
16   License as published by the Free Software Foundation; either
17   version 2 of the License, or (at your option) any later version.
18
19   This library is distributed in the hope that it will be useful,
20   but WITHOUT ANY WARRANTY; without even the implied warranty of
21   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
22   Lesser General Public License for more details.
23
24   You should have received a copy of the GNU Lesser General Public
25   License along with this library; see the file COPYING.LIB.
26   If not, see <http://www.gnu.org/licenses/> or write to the
27   Free Software Foundation, 51 Franklin Street, Fifth Floor,
28   Boston, MA 02110-1301, USA.
29*/
30
31#import "config.h"
32#import <Foundation/NSAttributedString.h>
33#import <Foundation/NSDebug.h>
34#import <Foundation/NSDictionary.h>
35#import <Foundation/NSException.h>
36#import <Foundation/NSUserDefaults.h>
37
38#import "AppKit/NSCell.h"
39#import "AppKit/NSEvent.h"
40#import "AppKit/NSImage.h"
41#import "AppKit/NSKeyValueBinding.h"
42#import "AppKit/NSMenuItem.h"
43#import "AppKit/NSMenu.h"
44#import "GSBindingHelpers.h"
45
46static BOOL usesUserKeyEquivalents = NO;
47static Class imageClass;
48
49@interface GSMenuSeparator : NSMenuItem
50
51@end
52
53@implementation GSMenuSeparator
54
55- (Class) classForCoder
56{
57  return [NSMenuItem class];
58}
59
60- (id) init
61{
62  self = [super initWithTitle: @"-----------"
63		action: NULL
64		keyEquivalent: @""];
65  if (self == nil)
66    return nil;
67
68  _enabled = NO;
69  _changesState = NO;
70  return self;
71}
72
73- (BOOL) isSeparatorItem
74{
75  return YES;
76}
77
78// FIXME: We need a lot of methods to switch off changes for a separator
79@end
80
81
82@implementation NSMenuItem
83
84+ (void) initialize
85{
86  if (self == [NSMenuItem class])
87    {
88      [self setVersion: 4];
89      imageClass = [NSImage class];
90
91      [self exposeBinding: NSEnabledBinding];
92    }
93}
94
95+ (void) setUsesUserKeyEquivalents: (BOOL)flag
96{
97  usesUserKeyEquivalents = flag;
98}
99
100+ (BOOL) usesUserKeyEquivalents
101{
102  return usesUserKeyEquivalents;
103}
104
105+ (id <NSMenuItem>) separatorItem
106{
107  return AUTORELEASE([GSMenuSeparator new]);
108}
109
110- (id) init
111{
112  return [self initWithTitle: @""
113	       action: NULL
114	       keyEquivalent: @""];
115}
116
117- (void) dealloc
118{
119  // Remove all key value bindings for this view.
120  [GSKeyValueBinding unbindAllForObject: self];
121
122  TEST_RELEASE(_title);
123  TEST_RELEASE(_keyEquivalent);
124  TEST_RELEASE(_image);
125  TEST_RELEASE(_onStateImage);
126  TEST_RELEASE(_offStateImage);
127  TEST_RELEASE(_mixedStateImage);
128  TEST_RELEASE(_submenu);
129  TEST_RELEASE(_representedObject);
130  TEST_RELEASE(_toolTip);
131  [super dealloc];
132}
133
134- (id) initWithTitle: (NSString*)aString
135	      action: (SEL)aSelector
136       keyEquivalent: (NSString*)charCode
137{
138  self = [super init];
139  if (self == nil)
140    return nil;
141
142  //_menu = nil;
143  [self setKeyEquivalent: charCode];
144  _keyEquivalentModifierMask = NSCommandKeyMask;
145  [self setTitle: aString]; // do this AFTER setKeyEquivalent: in case NSUserKeyEquivalents is defined
146  _mnemonicLocation = 255; // No mnemonic
147  _state = NSOffState;
148  _enabled = YES;
149  //_image = nil;
150  // Set the images according to the spec. On: check mark; off: dash.
151  [self setOnStateImage: [imageClass imageNamed: @"NSMenuCheckmark"]];
152  [self setMixedStateImage: [imageClass imageNamed: @"NSMenuMixedState"]];
153  //_offStateImage = nil;
154  //_target = nil;
155  _action = aSelector;
156  //_changesState = NO;
157  return self;
158}
159
160- (void) _updateKeyEquivalent
161{
162  // Update keyEquivalent based on any entries in NSUserKeyEquivalents in the defaults database
163  // TODO: also check in other defaults domains, to allow NSUserKeyEquivalents dictionaries
164  // in different domains to provide overrides of different menu items.
165  NSString *userKeyEquivalent = [(NSDictionary*)[[NSUserDefaults standardUserDefaults]
166				     objectForKey: @"NSUserKeyEquivalents"]
167				    objectForKey: _title];
168  if (userKeyEquivalent)
169    {
170      // check for leading symbols representing modifier flags: @, ~, $, ^
171      NSUInteger modifierMask = 0;
172      while ([userKeyEquivalent length] > 1)
173        {
174          if ([userKeyEquivalent hasPrefix:@"@"])
175            {
176              modifierMask |= NSCommandKeyMask;
177              userKeyEquivalent = [userKeyEquivalent substringFromIndex:1];
178            }
179          else if ([userKeyEquivalent hasPrefix:@"~"])
180            {
181              modifierMask |= NSAlternateKeyMask;
182              userKeyEquivalent = [userKeyEquivalent substringFromIndex:1];
183            }
184          else if ([userKeyEquivalent hasPrefix:@"$"])
185            {
186              modifierMask |= NSShiftKeyMask;
187              userKeyEquivalent = [userKeyEquivalent substringFromIndex:1];
188            }
189          else if ([userKeyEquivalent hasPrefix:@"^"])
190            {
191              modifierMask |= NSControlKeyMask;
192              userKeyEquivalent = [userKeyEquivalent substringFromIndex:1];
193            }
194          else
195            {
196              break;
197            }
198        }
199      [self setKeyEquivalent:userKeyEquivalent];
200      [self setKeyEquivalentModifierMask:modifierMask];
201    }
202}
203
204- (void) setMenu: (NSMenu*)menu
205{
206  /* The menu is retaining us.  Do not retain it.  */
207  _menu = menu;
208  if (_submenu != nil)
209    {
210      [_submenu setSupermenu: menu];
211      [self setTarget: _menu];
212    }
213}
214
215- (NSMenu*) menu
216{
217  return _menu;
218}
219
220- (BOOL) hasSubmenu
221{
222  return (_submenu == nil) ? NO : YES;
223}
224
225- (void) setSubmenu: (NSMenu*)submenu
226{
227  if (submenu == _submenu)
228    return; // no change
229
230  if ([submenu supermenu] != nil)
231    {
232      [NSException raise: NSInvalidArgumentException
233		   format: @"submenu (%@) already has supermenu (%@)",
234		   [submenu title], [[submenu supermenu] title]];
235    }
236  ASSIGN(_submenu, submenu);
237  if (submenu != nil)
238    {
239      [submenu setSupermenu: _menu];
240      [submenu setTitle: _title];
241    }
242  [self setTarget: _menu];
243  [self setAction: @selector(submenuAction:)];
244  [_menu itemChanged: self];
245}
246
247- (NSMenu*) submenu
248{
249  return _submenu;
250}
251
252- (void) setTitle: (NSString*)aString
253{
254  if (nil == aString)
255    aString = @"";
256
257  if ([_title isEqualToString:aString])
258    return; // no change
259
260  ASSIGNCOPY(_title,  aString);
261  [self _updateKeyEquivalent];
262  [_menu itemChanged: self];
263}
264
265- (NSString*) title
266{
267  return _title;
268}
269
270- (BOOL) isSeparatorItem
271{
272  return NO;
273}
274
275- (void) setKeyEquivalent: (NSString*)aKeyEquivalent
276{
277  if (nil == aKeyEquivalent)
278    {
279      /* We warn about nil for compatibiliy with MacOS X, which refuses
280         nil.  */
281      NSDebugMLLog(@"MacOSXCompatibility",
282                   @"Attempt to use nil as key equivalent");
283      aKeyEquivalent = @"";
284    }
285  if ([_keyEquivalent isEqualToString:aKeyEquivalent])
286    return; // no change
287
288  ASSIGNCOPY(_keyEquivalent,  aKeyEquivalent);
289  [_menu itemChanged: self];
290}
291
292- (NSString*) keyEquivalent
293{
294  if (usesUserKeyEquivalents)
295    return [self userKeyEquivalent];
296  else
297    return _keyEquivalent;
298}
299
300- (void) setKeyEquivalentModifierMask: (NSUInteger)mask
301{
302  if (_keyEquivalentModifierMask == mask)
303    return; // no change
304  _keyEquivalentModifierMask = mask;
305  [_menu itemChanged: self];
306}
307
308- (NSUInteger) keyEquivalentModifierMask
309{
310  return _keyEquivalentModifierMask;
311}
312
313- (NSString*) userKeyEquivalent
314{
315  NSString *userKeyEquivalent = [(NSDictionary*)[[[NSUserDefaults standardUserDefaults]
316				      persistentDomainForName: NSGlobalDomain]
317				     objectForKey: @"NSCommandKeys"]
318				    objectForKey: _title];
319
320  if (nil == userKeyEquivalent)
321    userKeyEquivalent = @"";
322
323  return userKeyEquivalent;
324}
325
326- (NSUInteger) userKeyEquivalentModifierMask
327{
328  // FIXME
329  return NSCommandKeyMask;
330}
331
332- (void) setMnemonicLocation: (NSUInteger)location
333{
334  if (_mnemonicLocation == location)
335    return; // no change
336
337  _mnemonicLocation = location;
338  [_menu itemChanged: self];
339}
340
341- (NSUInteger) mnemonicLocation
342{
343  if (_mnemonicLocation != 255)
344    return _mnemonicLocation;
345  else
346    return NSNotFound;
347}
348
349- (NSString*) mnemonic
350{
351  if (_mnemonicLocation != 255)
352    return [_title substringWithRange: NSMakeRange(_mnemonicLocation, 1)];
353  else
354    return @"";
355}
356
357- (void) setTitleWithMnemonic: (NSString*)stringWithAmpersand
358{
359  NSUInteger location = [stringWithAmpersand rangeOfString: @"&"].location;
360
361  [self setTitle: [stringWithAmpersand stringByReplacingString: @"&"
362				       withString: @""]];
363  [self setMnemonicLocation: location];
364}
365
366- (void) setImage: (NSImage *)image
367{
368  NSAssert(image == nil || [image isKindOfClass: imageClass],
369    NSInvalidArgumentException);
370
371  if (_image == image)
372    return; // no change
373
374  ASSIGN(_image, image);
375  [_menu itemChanged: self];
376}
377
378- (NSImage*) image
379{
380  return _image;
381}
382
383- (void) setState: (NSInteger)state
384{
385  if (_state == state)
386    return;
387
388  _state = state;
389  _changesState = YES;
390  [_menu itemChanged: self];
391}
392
393- (NSInteger) state
394{
395  return _state;
396}
397
398- (void) setOnStateImage: (NSImage*)image
399{
400  NSAssert(image == nil || [image isKindOfClass: imageClass],
401    NSInvalidArgumentException);
402
403  if (_onStateImage == image)
404    return; // no change
405
406  ASSIGN(_onStateImage, image);
407  [_menu itemChanged: self];
408}
409
410- (NSImage*) onStateImage
411{
412  return _onStateImage;
413}
414
415- (void) setOffStateImage: (NSImage*)image
416{
417  NSAssert(image == nil || [image isKindOfClass: imageClass],
418    NSInvalidArgumentException);
419
420  if (_offStateImage == image)
421    return; // no change
422
423  ASSIGN(_offStateImage, image);
424  [_menu itemChanged: self];
425}
426
427- (NSImage*) offStateImage
428{
429  return _offStateImage;
430}
431
432- (void) setMixedStateImage: (NSImage*)image
433{
434  NSAssert(image == nil || [image isKindOfClass: imageClass],
435    NSInvalidArgumentException);
436
437  if (_mixedStateImage == image)
438    return; // no change
439
440  ASSIGN(_mixedStateImage, image);
441  [_menu itemChanged: self];
442}
443
444- (NSImage*) mixedStateImage
445{
446  return _mixedStateImage;
447}
448
449- (void) setEnabled: (BOOL)flag
450{
451  if (flag == _enabled)
452    return;
453
454  _enabled = flag;
455  [_menu itemChanged: self];
456}
457
458- (BOOL) isEnabled
459{
460  return _enabled;
461}
462
463- (void) setTarget: (id)anObject
464{
465  if (_target == anObject)
466    return;
467
468  _target = anObject;
469  [_menu itemChanged: self];
470}
471
472- (id) target
473{
474  return _target;
475}
476
477- (void) setAction: (SEL)aSelector
478{
479  if (_action == aSelector)
480    return;
481
482  _action = aSelector;
483  [_menu itemChanged: self];
484}
485
486- (SEL) action
487{
488  return _action;
489}
490
491- (void) setTag: (NSInteger)anInt
492{
493  _tag = anInt;
494}
495
496- (NSInteger) tag
497{
498  return _tag;
499}
500
501- (void) setRepresentedObject: (id)anObject
502{
503  ASSIGN(_representedObject, anObject);
504}
505
506- (id) representedObject
507{
508  return _representedObject;
509}
510
511- (NSAttributedString *)attributedTitle
512{
513  // FIXME
514  return nil;
515}
516
517-(void) setAttributedTitle: (NSAttributedString *)title
518{
519  // FIXME
520  [self setTitle: [title string]];
521}
522
523- (NSInteger)indentationLevel
524{
525  return _indentation;
526}
527
528- (void)setIndentationLevel: (NSInteger)level
529{
530  _indentation = level;
531}
532
533- (BOOL)isAlternate
534{
535  return _isAlternate;
536}
537
538- (void) setAlternate: (BOOL)isAlternate
539{
540  _isAlternate = isAlternate;
541}
542
543- (void) setToolTip: (NSString *)toolTip
544{
545  ASSIGN(_toolTip, toolTip);
546}
547
548- (NSString *) toolTip
549{
550  return _toolTip;
551}
552
553/*
554 * NSCopying protocol
555 */
556- (id) copyWithZone: (NSZone*)zone
557{
558  NSMenuItem *copy = (NSMenuItem*)NSCopyObject (self, 0, zone);
559
560  // We reset the menu to nil to allow the reuse of the copy
561  copy->_menu = nil;
562  copy->_title = [_title copyWithZone: zone];
563  copy->_keyEquivalent = [_keyEquivalent copyWithZone: zone];
564  copy->_image = [_image copyWithZone: zone];
565  copy->_onStateImage = [_onStateImage copyWithZone: zone];
566  copy->_offStateImage = [_offStateImage copyWithZone: zone];
567  copy->_mixedStateImage = [_mixedStateImage copyWithZone: zone];
568  copy->_representedObject = RETAIN(_representedObject);
569  copy->_submenu = [_submenu copy];
570  copy->_toolTip = RETAIN(_toolTip);
571  copy->_target = _target;
572
573  return copy;
574}
575
576/*
577 * NSCoding protocol
578 */
579- (void) encodeWithCoder: (NSCoder*)aCoder
580{
581  if ([aCoder allowsKeyedCoding])
582    {
583      if ([self isSeparatorItem])
584        {
585          [aCoder encodeBool: YES forKey: @"NSIsSeparator"];
586        }
587      [aCoder encodeObject: _title forKey: @"NSTitle"];
588      [aCoder encodeObject: NSStringFromSelector(_action) forKey: @"NSAction"];
589      [aCoder encodeObject: _keyEquivalent forKey: @"NSKeyEquiv"];
590      [aCoder encodeObject: _onStateImage forKey: @"NSOnImage"];
591      [aCoder encodeObject: _offStateImage forKey: @"NSOffImage"]; // ???????
592      [aCoder encodeObject: _mixedStateImage forKey: @"NSMixedImage"];
593      [aCoder encodeObject: _target forKey: @"NSTarget"];
594      [aCoder encodeObject: _menu forKey: @"NSMenu"];
595
596      // If the menu is owned by a popup, then don't encode the children.
597      // This prevents an assertion error in IB as these keys should not
598      // be present in a menu item when it's encoded as part of a popup.
599      if([_menu _ownedByPopUp] == NO)
600	{
601	  [aCoder encodeObject: _submenu forKey: @"NSSubmenu"];
602	}
603
604      [aCoder encodeInt: _keyEquivalentModifierMask forKey: @"NSKeyEquivModMask"];
605      [aCoder encodeInt: _mnemonicLocation forKey: @"NSMnemonicLoc"];
606      [aCoder encodeInt: _state forKey: @"NSState"];
607      [aCoder encodeBool: ![self isEnabled] forKey: @"NSIsDisabled"];
608      if (_tag)
609        {
610          [aCoder encodeInt: _tag forKey: @"NSTag"];
611        }
612    }
613  else
614    {
615      [aCoder encodeObject: _title];
616      [aCoder encodeObject: _keyEquivalent];
617      [aCoder encodeValueOfObjCType: @encode(NSUInteger) at: &_keyEquivalentModifierMask];
618      [aCoder encodeValueOfObjCType: @encode(NSUInteger) at: &_mnemonicLocation];
619      [aCoder encodeValueOfObjCType: @encode(NSInteger) at: &_state];
620      [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_enabled];
621      [aCoder encodeObject: _image];
622      [aCoder encodeObject: _onStateImage];
623      [aCoder encodeObject: _offStateImage];
624      [aCoder encodeObject: _mixedStateImage];
625      [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_changesState];
626      [aCoder encodeValueOfObjCType: @encode(SEL) at: &_action];
627      [aCoder encodeValueOfObjCType: @encode(NSInteger) at: &_tag];
628      [aCoder encodeConditionalObject: _representedObject];
629      [aCoder encodeObject: _submenu];
630      [aCoder encodeConditionalObject: _target];
631
632      // version 3
633      [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_isAlternate];
634      [aCoder encodeValueOfObjCType: @encode(char) at: &_indentation];
635      [aCoder encodeObject: _toolTip];
636    }
637}
638
639- (id) initWithCoder: (NSCoder*)aDecoder
640{
641  if ([aDecoder allowsKeyedCoding])
642    {
643      NSString *title;
644      NSString *action;
645      NSString *key;
646      BOOL isSeparator = NO;
647      int keyMask;
648
649      if ([aDecoder containsValueForKey: @"NSIsSeparator"])
650        {
651          isSeparator = [aDecoder decodeBoolForKey: @"NSIsSeparator"];
652        }
653
654      if (isSeparator && ![self isSeparatorItem])
655        {
656          RELEASE(self);
657
658	  //
659	  // An object returned from initWithCoder:
660	  // should not be autoreleased.  Do a retain
661	  // to prevent it from being released automatically.
662	  //
663          self = RETAIN([NSMenuItem separatorItem]);
664        }
665
666      //
667      // Not retained, because we're calling the designated init with
668      // the values.
669      //
670      title = [aDecoder decodeObjectForKey: @"NSTitle"];
671      action = [aDecoder decodeObjectForKey: @"NSAction"];
672      key = [aDecoder decodeObjectForKey: @"NSKeyEquiv"];
673
674      self = [self initWithTitle: title
675                   action: NSSelectorFromString(action)
676                   keyEquivalent: key];
677      //[aDecoder decodeObjectForKey: @"NSMenu"];
678
679      if ([aDecoder containsValueForKey: @"NSTarget"])
680        {
681         id target = [aDecoder decodeObjectForKey: @"NSTarget"];
682         [self setTarget: target];
683        }
684      if ([aDecoder containsValueForKey: @"NSMixedImage"])
685        {
686          NSImage *mixedImage = [aDecoder decodeObjectForKey: @"NSMixedImage"];
687          [self setMixedStateImage: mixedImage];
688        }
689      if ([aDecoder containsValueForKey: @"NSOnImage"])
690        {
691          NSImage *onImage = [aDecoder decodeObjectForKey: @"NSOnImage"];
692          [self setOnStateImage: onImage];
693        }
694      if ([aDecoder containsValueForKey: @"NSSubmenu"])
695        {
696          NSMenu *submenu = [aDecoder decodeObjectForKey: @"NSSubmenu"];
697          [self setSubmenu: submenu];
698        }
699
700      // Set the key mask regardless of whether it is present;
701      // i.e. set it to 0 if it is not present in the nib.
702      keyMask = [aDecoder decodeIntForKey: @"NSKeyEquivModMask"];
703      [self setKeyEquivalentModifierMask: keyMask];
704
705      if ([aDecoder containsValueForKey: @"NSMnemonicLoc"])
706        {
707          int loc = [aDecoder decodeIntForKey: @"NSMnemonicLoc"];
708          [self setMnemonicLocation: loc];
709        }
710      if ([aDecoder containsValueForKey: @"NSState"])
711        {
712          _state = [aDecoder decodeIntForKey: @"NSState"];
713        }
714      if ([aDecoder containsValueForKey: @"NSIsDisabled"])
715        {
716          BOOL flag = [aDecoder decodeBoolForKey: @"NSIsDisabled"];
717          [self setEnabled: !flag];
718        }
719      if ([aDecoder containsValueForKey: @"NSTag"])
720        {
721          int tag = [aDecoder decodeIntForKey: @"NSTag"];
722          [self setTag: tag];
723        }
724    }
725  else
726    {
727      int version = [aDecoder versionForClassName:
728				  @"NSMenuItem"];
729
730      [aDecoder decodeValueOfObjCType: @encode(id) at: &_title];
731      [aDecoder decodeValueOfObjCType: @encode(id) at: &_keyEquivalent];
732      [aDecoder decodeValueOfObjCType: @encode(NSUInteger) at: &_keyEquivalentModifierMask];
733      if (version <= 3)
734        {
735          _keyEquivalentModifierMask = _keyEquivalentModifierMask << 16;
736        }
737      [aDecoder decodeValueOfObjCType: @encode(NSUInteger) at: &_mnemonicLocation];
738      [aDecoder decodeValueOfObjCType: @encode(NSInteger) at: &_state];
739      [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_enabled];
740      [aDecoder decodeValueOfObjCType: @encode(id) at: &_image];
741      [aDecoder decodeValueOfObjCType: @encode(id) at: &_onStateImage];
742      [aDecoder decodeValueOfObjCType: @encode(id) at: &_offStateImage];
743      [aDecoder decodeValueOfObjCType: @encode(id) at: &_mixedStateImage];
744      [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_changesState];
745      if (version == 1)
746        {
747          _target = [aDecoder decodeObject];
748        }
749      [aDecoder decodeValueOfObjCType: @encode(SEL) at: &_action];
750      [aDecoder decodeValueOfObjCType: @encode(NSInteger) at: &_tag];
751      [aDecoder decodeValueOfObjCType: @encode(id) at: &_representedObject];
752      [aDecoder decodeValueOfObjCType: @encode(id) at: &_submenu];
753      if (version >= 2)
754        {
755          _target = [aDecoder decodeObject];
756        }
757      if (version >= 3)
758        {
759          [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_isAlternate];
760          [aDecoder decodeValueOfObjCType: @encode(char) at: &_indentation];
761          [aDecoder decodeValueOfObjCType: @encode(id) at: &_toolTip];
762        }
763    }
764
765  [self _updateKeyEquivalent];
766  return self;
767}
768
769- (void) bind: (NSString *)binding
770     toObject: (id)anObject
771  withKeyPath: (NSString *)keyPath
772      options: (NSDictionary *)options
773{
774  if ([binding hasPrefix: NSEnabledBinding])
775    {
776      GSKeyValueBinding *kvb;
777
778      [self unbind: binding];
779      kvb = [[GSKeyValueAndBinding alloc] initWithBinding: NSEnabledBinding
780                                                 withName: binding
781                                                 toObject: anObject
782                                              withKeyPath: keyPath
783                                                  options: options
784                                               fromObject: self];
785      // The binding will be retained in the binding table
786      RELEASE(kvb);
787    }
788  else
789    {
790      [super bind: binding
791             toObject: anObject
792             withKeyPath: keyPath
793             options: options];
794    }
795}
796
797@end
798
799@implementation NSMenuItem (GNUstepExtra)
800
801/*
802 * These methods support the special arranging in columns of menu
803 * items in GNUstep.  There's no need to use them outside but if
804 * they are used the display is more pleasant.
805 */
806- (void) setChangesState: (BOOL)flag
807{
808  _changesState = flag;
809}
810
811- (BOOL) changesState
812{
813  return _changesState;
814}
815
816@end
817