mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-05-31 19:00:47 +00:00
Menu patch by Wim
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@16143 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
5c6debd73e
commit
b76e5053fd
5 changed files with 1340 additions and 526 deletions
51
ChangeLog
51
ChangeLog
|
@ -1,3 +1,54 @@
|
||||||
|
2003-03-06 Willem Rein Oudshoorn <woudshoo@xs4all.nl>
|
||||||
|
|
||||||
|
* Source/NSMenuView.m: Added NSMenuTitleView (moved
|
||||||
|
from NSMenu).
|
||||||
|
([NSMenuView -detachSubmenu]): Removed setting highlighting.
|
||||||
|
([NSMenuView -attachSubmenuForItemAtIndex:]) added logging.
|
||||||
|
([NSMenuView -update]): Handle _titleView.
|
||||||
|
([NSMenuView -sizeToFit]): Take titleView into account
|
||||||
|
([NSMenuView -performActionWithHighlightingForItemAtIndex:]):
|
||||||
|
restore old highlighting.
|
||||||
|
([NSMenuView -trackWithEvent:]): Rewritten.
|
||||||
|
([NSMenuView -mouseDown:]): Only restore menu position
|
||||||
|
when needed.
|
||||||
|
([NSMenuView -rightMouseDown:theEvent]): On menus treat
|
||||||
|
as ordinary mouse down to avoid rightclick menus on menus.
|
||||||
|
|
||||||
|
* Source/NSMenu.m (NSView): Moved NSMenuWindowTitleView to
|
||||||
|
NSMenuView. Added comments. Added -_updateUserDefaults:,
|
||||||
|
-_menuMoved: methods to track moving menus.
|
||||||
|
Renamed -_setTornOff: to -setTornOff:, -isFollowTransient to
|
||||||
|
-isTransient.
|
||||||
|
([NSMenu -initWithTitle:]): Remove some old comments, remove
|
||||||
|
reference to titleView, listen to window move notification and
|
||||||
|
enqueued move notification.
|
||||||
|
([NSMenu -attachedMenu]): Adjusted for renamed ivar.
|
||||||
|
([NSMenu -sizeToFit]): Remove references to titleView
|
||||||
|
([NSMenu -setTornOff:]): Remove references to titleView,
|
||||||
|
call update to NSMenuView instead.
|
||||||
|
([NSMenu -isPartlyOffScreen]): Do not depend in removed ivar.
|
||||||
|
([NSMenu -_performMenuClose:]): adjust to renamed methods, will
|
||||||
|
force synchronization of userdefaults.
|
||||||
|
([NSMenu -displayTransient]): Removed references to _titleView,
|
||||||
|
remember the highlighted index to restore with -closeTransient.
|
||||||
|
([NSMenu -close]): remove references to _titleview, use methods instead
|
||||||
|
of relying on ivars. added call to update on menu representation.
|
||||||
|
([NSMenu -closeTransient]): Remove references to _titleView, restore
|
||||||
|
highlighted index. Call update on menu representantation.
|
||||||
|
([NSMenu -shiftOnScreen]): Rewrote to move in x and y direction
|
||||||
|
simultanuously.
|
||||||
|
([NSMenuWindowTitleView -acceptsFirstMouse:]): Removed useless
|
||||||
|
code to set menuview for itemCells.
|
||||||
|
|
||||||
|
* Headers/gnustep/gui/NSMenuView.h: added ivar _titleView.
|
||||||
|
(moved from NSMenu).
|
||||||
|
|
||||||
|
* Headers/gnustep/gui/NSMenu.h: Added NSMenuView protocol,
|
||||||
|
added documentation to NSMenu class.
|
||||||
|
(MSMenu): Removed ivar _titleView, _isPartlyOffScreen, added
|
||||||
|
ivar _oldHighlightedIndex. Renamed method -isFollowTransient to
|
||||||
|
-isTransient. Removed method -nestedCheckOffScreen
|
||||||
|
|
||||||
2003-03-05 17:07 Alexander Malmberg <alexander@malmberg.org>
|
2003-03-05 17:07 Alexander Malmberg <alexander@malmberg.org>
|
||||||
|
|
||||||
* Source/NSLayoutManager.m (-drawGlyphsForGlyphRange:atPoint:):
|
* Source/NSLayoutManager.m (-drawGlyphsForGlyphRange:atPoint:):
|
||||||
|
|
|
@ -37,18 +37,339 @@
|
||||||
@class NSString;
|
@class NSString;
|
||||||
@class NSEvent;
|
@class NSEvent;
|
||||||
@class NSMatrix;
|
@class NSMatrix;
|
||||||
@class NSMenuView;
|
|
||||||
@class NSPopUpButton;
|
@class NSPopUpButton;
|
||||||
@class NSPopUpButtonCell;
|
@class NSPopUpButtonCell;
|
||||||
@class NSView;
|
@class NSView;
|
||||||
@class NSWindow;
|
@class NSWindow;
|
||||||
@class NSMutableArray;
|
@class NSMutableArray;
|
||||||
|
|
||||||
|
|
||||||
|
@protocol NSMenuView
|
||||||
|
/**
|
||||||
|
Set the menu that this view object will be drawing.
|
||||||
|
This method will NOT retain the menu.
|
||||||
|
In normal usage an instance of NSMenu will
|
||||||
|
use this method to supply the NSMenuView with reference
|
||||||
|
to itself. The NSMenu will retain the NSMenuView.
|
||||||
|
*/
|
||||||
|
- (void)setMenu:(NSMenu *)menu;
|
||||||
|
/**
|
||||||
|
Set the currently highlighted item.
|
||||||
|
|
||||||
|
This is used by the NSMenu class to restore
|
||||||
|
the selected item when it is temporary set to
|
||||||
|
another item. This happens when both the regular
|
||||||
|
version and the transient version are on the screen.
|
||||||
|
|
||||||
|
A value of -1 means that no item will be highlighted.
|
||||||
|
*/
|
||||||
|
- (void)setHighlightedItemIndex:(int)index;
|
||||||
|
/**
|
||||||
|
Returns the currently highlighted item. Returns -1
|
||||||
|
if no item is highlighted.
|
||||||
|
*/
|
||||||
|
- (int)highlightedItemIndex;
|
||||||
|
|
||||||
|
/**
|
||||||
|
This should ensure that if there is an attached
|
||||||
|
submenu this submenu will be detached.
|
||||||
|
|
||||||
|
Detaching means that this particular menu representation
|
||||||
|
should be removed from the screen.
|
||||||
|
|
||||||
|
It should implement a deep detach, that is, all
|
||||||
|
attached submenus of this menu should also be detached.
|
||||||
|
*/
|
||||||
|
- (void) detachSubmenu;
|
||||||
|
|
||||||
|
/**
|
||||||
|
This will relayout the NSMenuView.
|
||||||
|
|
||||||
|
It should be called when the menu changes. Changes include
|
||||||
|
becoming detached, adding or removing submenu items etcetera.
|
||||||
|
|
||||||
|
However, normally it does not need to be called directly because
|
||||||
|
Because the NSMenuView is supposed to listen to the NSMenu notifications
|
||||||
|
for the item added, removed and change notifications.
|
||||||
|
|
||||||
|
It should be called explicitly when other changes occur, such as
|
||||||
|
becoming detached or changing the title.
|
||||||
|
*/
|
||||||
|
- (void)update;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Hm, why is this method needed? Shouldn't this be done by
|
||||||
|
the update method?
|
||||||
|
*/
|
||||||
|
- (void)sizeToFit; //!!!
|
||||||
|
|
||||||
|
/**
|
||||||
|
Method used by NSMenuItemCell to draw itself correctly and nicely
|
||||||
|
lined up with the other menu items.
|
||||||
|
*/
|
||||||
|
- (float)stateImageWidth;
|
||||||
|
/**
|
||||||
|
Method used by NSMenuItemCell to draw itself correctly and nicely
|
||||||
|
lined up with the other menu items
|
||||||
|
*/
|
||||||
|
- (float)imageAndTitleOffset;
|
||||||
|
/**
|
||||||
|
Methos used by NSMenuItemCell to draw itself correctly and nicely
|
||||||
|
lined up with the other menu items.
|
||||||
|
*/
|
||||||
|
- (float)imageAndTitleWidth;
|
||||||
|
/**
|
||||||
|
Methos used by NSMenuItemCell to draw itself correctly and nicely
|
||||||
|
lined up with the other menu items.
|
||||||
|
*/
|
||||||
|
- (float)keyEquivalentOffset;
|
||||||
|
/**
|
||||||
|
Used by NSItemCell to ...
|
||||||
|
*/
|
||||||
|
- (float)keyEquivalentWidth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Used by the NSMenu to determine where to position a
|
||||||
|
submenu.
|
||||||
|
*/
|
||||||
|
- (NSPoint)locationForSubmenu:(NSMenu *)aSubmenu;
|
||||||
|
|
||||||
|
- (void)performActionWithHighlightingForItemAtIndex:(int)index; //????
|
||||||
|
|
||||||
|
/**
|
||||||
|
<p>
|
||||||
|
This is method is responsible for handling all events while
|
||||||
|
the user is interacting with this menu. It should pass on this
|
||||||
|
call to another menurepresentation when the user moves the
|
||||||
|
mouse cursor over either a submenu or over the supermenu.
|
||||||
|
</p><p>
|
||||||
|
The method returns when the interaction from the user with the
|
||||||
|
menu system is over.
|
||||||
|
</p><p>
|
||||||
|
The method returns NO when the user releases the mouse button
|
||||||
|
above a submenu item and YES in all other cases.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
This return value can be used to determine if submenus should
|
||||||
|
be removed from the screen or that they are supposed to stay.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The implementation should roughly follow the following logic:
|
||||||
|
</p>
|
||||||
|
<example>
|
||||||
|
{
|
||||||
|
while (have not released mouse button)
|
||||||
|
{
|
||||||
|
if (mouse hovers over submenu, or supermenu)
|
||||||
|
{
|
||||||
|
if ([(menurepresentation under mouse)
|
||||||
|
trackWithEvent: the event])
|
||||||
|
{
|
||||||
|
[self detachSubmenu];
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
//highlight item under mouse
|
||||||
|
|
||||||
|
if (highlighting submenu item)
|
||||||
|
{
|
||||||
|
[self attachSubmenuAtIndex:..];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
[self detachSubmenu];
|
||||||
|
}
|
||||||
|
get next event.
|
||||||
|
}
|
||||||
|
|
||||||
|
execute the menu action if applicable;
|
||||||
|
|
||||||
|
return YES | NO depending on the situation;
|
||||||
|
}
|
||||||
|
</example>
|
||||||
|
|
||||||
|
Note that actual implementations tend to be more complicated because
|
||||||
|
because of all kind of useability issues. Useabilities issues to
|
||||||
|
look out for are:
|
||||||
|
<list>
|
||||||
|
<item> Menus that are only partly on the screen. Those need to be moved while
|
||||||
|
navigation the menu.</item>
|
||||||
|
<item> Submenus that are hard to reach. If the natural route to the content of a submenu
|
||||||
|
travels through other menu items you do not want to remove the submenu immediately.
|
||||||
|
</item>
|
||||||
|
<item> Transient menus require slightly different behaviour from the normal menus.
|
||||||
|
For example, when selecting a action from a transient menu that brings up a modal
|
||||||
|
panel you would expect the transient menu to dissappear. However in the normal
|
||||||
|
menu system you want it to stay, so you still have feedback on which menu action
|
||||||
|
triggered the modal panel.
|
||||||
|
</item>
|
||||||
|
</list>
|
||||||
|
|
||||||
|
*/
|
||||||
|
- (BOOL)trackWithEvent:(NSEvent *)event;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
<p>
|
||||||
|
Menus provide the user with a list of actions and/or submenus.
|
||||||
|
Submenus themselves are full fledged menus and so a heirarchical
|
||||||
|
structure of appears.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Every application has one special menu, the so called Application menu.
|
||||||
|
This menu is always visible on the screen when the application is active.
|
||||||
|
This menu normally contains items like, <em>info</em>,
|
||||||
|
<em>services</em>, <em>print</em>, <em>hide</em> and <em>quit</em>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
After the <em>info</em> item normally some submenus follow containing
|
||||||
|
the application specific actions.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
On GNUstep the content of the menu is stacked vertically as oppossed to
|
||||||
|
the Windows and Mac world, where they are stacked horizontally.
|
||||||
|
Also because the menus are not part of any normal window they can be dragged
|
||||||
|
around opened and closed independend of the application windows.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
This can lead to a few menus being open simultanuously.
|
||||||
|
The collection of open menus is remembered,
|
||||||
|
when the program is started again, all the torn off menus aka
|
||||||
|
detached menus, are displayed at their last known position.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The menu behaviour is richer than in most other environments and
|
||||||
|
bear some explanation. This explanation is aimed at users of Menus but more so
|
||||||
|
at the developer of custom menus.
|
||||||
|
</p>
|
||||||
|
<deflist>
|
||||||
|
<term>Application menu</term>
|
||||||
|
<desc>There alwasy at least one menu
|
||||||
|
present and visible while the application is active. This is the application menu.
|
||||||
|
This window can never be closed.
|
||||||
|
</desc>
|
||||||
|
<term>Attached menu</term>
|
||||||
|
<desc>
|
||||||
|
Normally when you click in a menu
|
||||||
|
on a submenu item, the submenu is shown directly next to the menu you click in.
|
||||||
|
The submenu is now called an <em>attached</em> menu. It is attached to the
|
||||||
|
menu that was clicked in.
|
||||||
|
</desc>
|
||||||
|
<term>Detached menu</term>
|
||||||
|
<desc>
|
||||||
|
A menu is detached when it is not attached
|
||||||
|
to its parent menu. A menu can become
|
||||||
|
detached when the user drags a submenu away from its parents.
|
||||||
|
A detached window contains in its title a close button.
|
||||||
|
</desc>
|
||||||
|
<term>Transient menu</term>
|
||||||
|
<desc>
|
||||||
|
A transient menu is a menu that dissappears as
|
||||||
|
soon as the user stops interacting with the menus.
|
||||||
|
Typically a transient menu is created when a right mouse click appears in an
|
||||||
|
application window. The right mouse click will bring up the Application menu
|
||||||
|
at the place the user clicks. While keeping the mouse button down the
|
||||||
|
user can select items by moving around. When releasing the button, all
|
||||||
|
transient menus will be removed from the screen and the action will be executed.
|
||||||
|
<p>
|
||||||
|
It is important to note that it is impossible to click in transient menus.
|
||||||
|
</p>
|
||||||
|
</desc>
|
||||||
|
<term>Attached transient menu</term>
|
||||||
|
<desc>
|
||||||
|
This is a menu that is attached and transient at the same time.
|
||||||
|
</desc>
|
||||||
|
</deflist>
|
||||||
|
|
||||||
|
A single NSMenu instance can be displayed zero or one times when the user is
|
||||||
|
not interaction with the menus.
|
||||||
|
When the user is interaction with the menus it can occur that the same NSMenu
|
||||||
|
is displayed in two locations at the same time. This is only possible
|
||||||
|
when one of the displayed instances is a transient menu.
|
||||||
|
|
||||||
|
To understand how the diffent kind of menus are created lets look at some user actions:
|
||||||
|
|
||||||
|
<list>
|
||||||
|
<item>The user clicks on an item which is not a submenu.<br/>
|
||||||
|
The item is highlighted until the action corresponding with the item is completed.
|
||||||
|
More precisely, the application highlights the menu item, performs the action, and unhighlights the item.
|
||||||
|
</item>
|
||||||
|
<item>The user clicks on a submenu item which is not highlighted already<br/>
|
||||||
|
If the submenu is not a detached menu, the submenu will become an attached
|
||||||
|
menu to the menu that is clicked in. The item that is clicked in will
|
||||||
|
become highlighted and stays highlighted.
|
||||||
|
<p>
|
||||||
|
If the submenu is a detached menu, the transient version of the submenu
|
||||||
|
will be shown
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</item>
|
||||||
|
<item>The user clicks on a submenu item which is highlighted<br/>
|
||||||
|
This means that the submenu is an attached submenu for this menu.
|
||||||
|
After clicking the submenu item will no be no longer highlighted and
|
||||||
|
the submenu will be removed from the screen.
|
||||||
|
</item>
|
||||||
|
<item>The user drags over a menu item<br/>
|
||||||
|
The item will be highlighted, if the item is a submenu item, the submenu
|
||||||
|
will be shown as an attached submenu. This can be transient, or non transient.
|
||||||
|
</item>
|
||||||
|
</list>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
<strong>Customizing the look of Menus</strong>
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
There are basically three ways of customizing the look of NSMenu
|
||||||
|
<enum>
|
||||||
|
<item>Using custom NSMenuItemCell's. This you should do when you want to influence
|
||||||
|
the look of the items displayed in the menu.</item>
|
||||||
|
<item>Using custom NSMenuView. This is the class to modify if you want to change
|
||||||
|
the way the menu is layout on the screen. So if you want to stack the menu
|
||||||
|
items horizontally, you should change this class. This should be rarely needed.
|
||||||
|
</item>
|
||||||
|
<item>Reimplement NSMenu. This you should not do. But, if you implement
|
||||||
|
everything yourself you can achieve anything.
|
||||||
|
</item>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
<strong>Information for implementing custom NSMenuView class</strong>
|
||||||
|
<br/>
|
||||||
|
When implementing a custom NSMenuView class it is important
|
||||||
|
to keep the following information in mind.
|
||||||
|
|
||||||
|
<list>
|
||||||
|
<item> The menus (or the menu items) form a tree. Navigating through this tree
|
||||||
|
is done with the methods [NSMenu-supermenu], which returns the parent menu
|
||||||
|
of the receiver, and with [NSMenu-itemAtIndex:] which returns a
|
||||||
|
NSMenuItem on which we can call [NSMenuItem-submenu] for a child menu.
|
||||||
|
</item>
|
||||||
|
<item> The menus as displayed on the screen do NOT form a tree.
|
||||||
|
This because detached and transient menus lead to duplicate menus on the screen.
|
||||||
|
</item>
|
||||||
|
</list>
|
||||||
|
|
||||||
|
The displayed menus on the screen have the following structure:
|
||||||
|
|
||||||
|
<enum>
|
||||||
|
<item>The ordered graph of displayed menus (note, NOT the set of NSMenus) form a collection of line graphs.</item>
|
||||||
|
<item>The attached menus are precisely the non root vertices in this graph.</item>
|
||||||
|
<item>An attached menu of a transient menu is itself a transient menu.</item>
|
||||||
|
<item>The collection of transient menus form connect subgraph of the menu graph.</item>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
*/
|
||||||
@interface NSMenu : NSObject <NSCoding, NSCopying>
|
@interface NSMenu : NSObject <NSCoding, NSCopying>
|
||||||
{
|
{
|
||||||
NSString *_title;
|
NSString *_title;
|
||||||
NSMutableArray *_items;
|
NSMutableArray *_items;
|
||||||
NSMenuView *_view;
|
NSView<NSMenuView>* _view;
|
||||||
NSMenu *_superMenu;
|
NSMenu *_superMenu;
|
||||||
NSMenu *_attachedMenu;
|
NSMenu *_attachedMenu;
|
||||||
NSMutableArray *_notifications;
|
NSMutableArray *_notifications;
|
||||||
|
@ -59,21 +380,25 @@
|
||||||
|
|
||||||
// GNUstepExtra category
|
// GNUstepExtra category
|
||||||
NSPopUpButtonCell *_popUpButtonCell;
|
NSPopUpButtonCell *_popUpButtonCell;
|
||||||
BOOL _follow_transient;
|
BOOL _transient;
|
||||||
BOOL _isPartlyOffScreen;
|
|
||||||
|
|
||||||
@private
|
@private
|
||||||
NSWindow *_aWindow;
|
NSWindow *_aWindow;
|
||||||
NSWindow *_bWindow;
|
NSWindow *_bWindow;
|
||||||
id _titleView;
|
|
||||||
NSMenu *_oldAttachedMenu;
|
NSMenu *_oldAttachedMenu;
|
||||||
|
int _oldHiglightedIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Controlling Allocation Zones */
|
/* Controlling Allocation Zones */
|
||||||
+ (void) setMenuZone: (NSZone*)zone;
|
+ (void) setMenuZone: (NSZone*)zone;
|
||||||
+ (NSZone*) menuZone;
|
+ (NSZone*) menuZone;
|
||||||
|
|
||||||
/* Creating an NSMenu */
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*<init/>
|
||||||
|
*/
|
||||||
|
|
||||||
- (id) initWithTitle: (NSString*)aTitle;
|
- (id) initWithTitle: (NSString*)aTitle;
|
||||||
|
|
||||||
/* Setting Up the Menu Commands */
|
/* Setting Up the Menu Commands */
|
||||||
|
@ -92,6 +417,9 @@
|
||||||
- (void) removeItemAtIndex: (int)index;
|
- (void) removeItemAtIndex: (int)index;
|
||||||
|
|
||||||
/* Finding menu items */
|
/* Finding menu items */
|
||||||
|
/**
|
||||||
|
* Returns an array containing all menu items in this menu.
|
||||||
|
*/
|
||||||
- (NSArray*) itemArray;
|
- (NSArray*) itemArray;
|
||||||
- (id <NSMenuItem>) itemAtIndex: (int)index;
|
- (id <NSMenuItem>) itemAtIndex: (int)index;
|
||||||
- (id <NSMenuItem>) itemWithTag: (int)aTag;
|
- (id <NSMenuItem>) itemWithTag: (int)aTag;
|
||||||
|
@ -110,11 +438,52 @@
|
||||||
/* Managing submenus */
|
/* Managing submenus */
|
||||||
- (void) setSubmenu: (NSMenu*)aMenu forItem: (id <NSMenuItem>)anItem;
|
- (void) setSubmenu: (NSMenu*)aMenu forItem: (id <NSMenuItem>)anItem;
|
||||||
- (void) submenuAction: (id)sender;
|
- (void) submenuAction: (id)sender;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns the menu that is attached to this menu.
|
||||||
|
<p>
|
||||||
|
If two instances of this menu are visible,
|
||||||
|
return the attached window of the transient version
|
||||||
|
of this menu.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
If no menu is attached return nil.
|
||||||
|
</p>
|
||||||
|
*/
|
||||||
- (NSMenu*) attachedMenu;
|
- (NSMenu*) attachedMenu;
|
||||||
|
/**
|
||||||
|
Returns if this menu is attached to its supermenu,
|
||||||
|
return nil if it does not have a parent menu.
|
||||||
|
<p>
|
||||||
|
If two instances of this menu are visible, return
|
||||||
|
the outcome of the check for the transient version
|
||||||
|
of the menu.
|
||||||
|
</p>
|
||||||
|
*/
|
||||||
- (BOOL) isAttached;
|
- (BOOL) isAttached;
|
||||||
|
/**
|
||||||
|
If there are two instances of this menu visible, return NO.
|
||||||
|
Otherwise, return YES if we are a detached menu and visible.
|
||||||
|
*/
|
||||||
- (BOOL) isTornOff;
|
- (BOOL) isTornOff;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns the position where submenu will be displayed
|
||||||
|
when it will be displayed as an attached menu of this menu.
|
||||||
|
The result is undefined when aSubmenu is not actually a submenu
|
||||||
|
of this menu.
|
||||||
|
*/
|
||||||
- (NSPoint) locationForSubmenu:(NSMenu*)aSubmenu;
|
- (NSPoint) locationForSubmenu:(NSMenu*)aSubmenu;
|
||||||
|
/**
|
||||||
|
Returns the supermenu of this menu. Return nil
|
||||||
|
if this is the application menu.
|
||||||
|
*/
|
||||||
- (NSMenu*) supermenu;
|
- (NSMenu*) supermenu;
|
||||||
|
/**
|
||||||
|
Set the supermenu of this menu.
|
||||||
|
TODO: add explanation if this will change remove this menu
|
||||||
|
from the old supermenu or if it does not.
|
||||||
|
*/
|
||||||
- (void) setSupermenu: (NSMenu *)supermenu;
|
- (void) setSupermenu: (NSMenu *)supermenu;
|
||||||
|
|
||||||
/* Enabling and disabling menu items */
|
/* Enabling and disabling menu items */
|
||||||
|
@ -128,12 +497,34 @@
|
||||||
/* Simulating Mouse Clicks */
|
/* Simulating Mouse Clicks */
|
||||||
- (void) performActionForItemAtIndex: (int)index;
|
- (void) performActionForItemAtIndex: (int)index;
|
||||||
|
|
||||||
/* Setting the Title */
|
/**
|
||||||
|
Change the title of the menu.
|
||||||
|
*/
|
||||||
- (void) setTitle: (NSString*)aTitle;
|
- (void) setTitle: (NSString*)aTitle;
|
||||||
|
/**
|
||||||
|
Returns the current title.
|
||||||
|
*/
|
||||||
- (NSString*) title;
|
- (NSString*) title;
|
||||||
|
|
||||||
/* Setting the representing object */
|
/**
|
||||||
- (void) setMenuRepresentation: (id)menuRep;
|
Set the View that should be used to display the menu.
|
||||||
|
<p>
|
||||||
|
The default is NSMenuView, but a user can supply its
|
||||||
|
own NSView object as long as it
|
||||||
|
</p>
|
||||||
|
<list>
|
||||||
|
<item>Inherits from NSView</item>
|
||||||
|
<item>Implements NSMenuView protocol</item>
|
||||||
|
</list>
|
||||||
|
*/
|
||||||
|
- (void) setMenuRepresentation: (id) menuRep;
|
||||||
|
/**
|
||||||
|
Return the NSView that is used for drawing
|
||||||
|
the menu.
|
||||||
|
It is the view set with [NSMenu-setMenuRepresentation:] and
|
||||||
|
therefore it should be safe to assume it is an NSView
|
||||||
|
implementing the NSMenuView protocol.
|
||||||
|
*/
|
||||||
- (id) menuRepresentation;
|
- (id) menuRepresentation;
|
||||||
|
|
||||||
/* Updating Menu Layout */
|
/* Updating Menu Layout */
|
||||||
|
@ -143,6 +534,7 @@
|
||||||
|
|
||||||
/* Displaying Context-Sensitive Help */
|
/* Displaying Context-Sensitive Help */
|
||||||
- (void) helpRequested: (NSEvent*)event;
|
- (void) helpRequested: (NSEvent*)event;
|
||||||
|
|
||||||
+ (void) popUpContextMenu: (NSMenu*)menu
|
+ (void) popUpContextMenu: (NSMenu*)menu
|
||||||
withEvent: (NSEvent*)event
|
withEvent: (NSEvent*)event
|
||||||
forView: (NSView*)view;
|
forView: (NSView*)view;
|
||||||
|
@ -175,25 +567,76 @@
|
||||||
- (BOOL) validateMenuItem: (NSMenuItem*)aMenuItem;
|
- (BOOL) validateMenuItem: (NSMenuItem*)aMenuItem;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
/**
|
||||||
|
This interface exist contains methods that are meant
|
||||||
|
for the NSMenuView. If you write your own implementation
|
||||||
|
of the NSMenuView interface you can use these methods
|
||||||
|
to popup other menus or close them.
|
||||||
|
*/
|
||||||
@interface NSMenu (GNUstepExtra)
|
@interface NSMenu (GNUstepExtra)
|
||||||
- (BOOL) isFollowTransient;
|
|
||||||
|
/**
|
||||||
|
Returns YES if there is a transient version
|
||||||
|
of this menu displayed on the screen.
|
||||||
|
*/
|
||||||
|
- (BOOL) isTransient;
|
||||||
|
/**
|
||||||
|
Returns the window in which this menu is displayed.
|
||||||
|
If there is a transient version it will return the
|
||||||
|
window in which the transient version is displayed.
|
||||||
|
If the Menu is not displayed at all the result
|
||||||
|
is meaningless.
|
||||||
|
*/
|
||||||
- (NSWindow*) window;
|
- (NSWindow*) window;
|
||||||
|
|
||||||
/* Shows the menu window on screen */
|
/* Shows the menu window on screen */
|
||||||
|
/**
|
||||||
|
Show menu on the screen. This method can/should be used by
|
||||||
|
the menurepresentation to display a submenu on the screen.
|
||||||
|
*/
|
||||||
- (void) display;
|
- (void) display;
|
||||||
|
/**
|
||||||
|
Display the transient version of the menu.
|
||||||
|
*/
|
||||||
- (void) displayTransient;
|
- (void) displayTransient;
|
||||||
- (void) setGeometry;
|
|
||||||
|
|
||||||
/* Close the associated window menu */
|
/**
|
||||||
|
Positions the menu according to the standard user defaults.
|
||||||
|
If the position is not found in the defaults revert to positioning
|
||||||
|
the window in the upper left corner.
|
||||||
|
*/
|
||||||
|
- (void) setGeometry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
When the flag is YES
|
||||||
|
this method will detach the receiver from its parent and
|
||||||
|
update the menurepresentation so it will display a close
|
||||||
|
button if appropriate.
|
||||||
|
|
||||||
|
If the flag is NO this method will update the menurepresentation
|
||||||
|
so it will be able to remove the close button if needed.
|
||||||
|
Note that it will not reattach to its parent menu.
|
||||||
|
*/
|
||||||
|
- (void) setTornOff: (BOOL) flag;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Remove the window from the screen. This method can/should be
|
||||||
|
used by the menurepresentation to remove a submenu from the screen.
|
||||||
|
*/
|
||||||
- (void) close;
|
- (void) close;
|
||||||
- (void) closeTransient;
|
/**
|
||||||
|
Remove the transient version of the window from the screen.
|
||||||
|
This method is used by NSMenuView implementations that need
|
||||||
|
to open/close transient menus.
|
||||||
|
*/
|
||||||
|
- (void) closeTransient;
|
||||||
|
|
||||||
/* Moving menus */
|
/* Moving menus */
|
||||||
- (void) nestedSetFrameOrigin: (NSPoint)aPoint;
|
- (void) nestedSetFrameOrigin: (NSPoint)aPoint;
|
||||||
|
|
||||||
/* Shift partly off-screen menus */
|
/* Shift partly off-screen menus */
|
||||||
- (BOOL) isPartlyOffScreen;
|
- (BOOL) isPartlyOffScreen;
|
||||||
- (void) nestedCheckOffScreen;
|
|
||||||
- (void) shiftOnScreen;
|
- (void) shiftOnScreen;
|
||||||
|
|
||||||
/* Popup behaviour */
|
/* Popup behaviour */
|
||||||
|
|
|
@ -41,11 +41,39 @@
|
||||||
#include <AppKit/NSView.h>
|
#include <AppKit/NSView.h>
|
||||||
#include <AppKit/NSWindow.h>
|
#include <AppKit/NSWindow.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
The NSMenu class uses an object implementing the NSMenuView protocol to
|
||||||
|
do the actual drawing.
|
||||||
|
|
||||||
|
Normally there is no good reason to write your own class implementing
|
||||||
|
this protocol. However if you want to customize your menus you should
|
||||||
|
implement this protocol to ensure that it works nicely together
|
||||||
|
with sub/super menus not using your custom menurepresentation.
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
<strong>How menus are drawn</strong><br/>
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
@class NSColor;
|
@class NSColor;
|
||||||
@class NSPopUpButton;
|
@class NSPopUpButton;
|
||||||
@class NSFont;
|
@class NSFont;
|
||||||
|
|
||||||
@interface NSMenuView : NSView <NSCoding>
|
/**
|
||||||
|
This class implements several menu look and feels at the same time.
|
||||||
|
The looks and feels implemented are:
|
||||||
|
<list>
|
||||||
|
<item>Ordinary vertically stacked menus with the NeXT submenu positioning behavour.</item>
|
||||||
|
<item>Vertically stacked menus with the WindowMaker submenu placement. This behaviour
|
||||||
|
is selected by choosing the <strong>GSWindowMakerInterfaceStyle</strong>. </item>
|
||||||
|
<item>Horizontally stacked menus. This can be only set on a individual basis by
|
||||||
|
using [NSMenuView-setHorizontal:]. </item>
|
||||||
|
<item>PopupButtons are actually menus. This class implements also the behaviour for the
|
||||||
|
NSPopButtons. See for the the class NSPopButton.</item>
|
||||||
|
</list>
|
||||||
|
*/
|
||||||
|
@interface NSMenuView : NSView <NSCoding, NSMenuView>
|
||||||
{
|
{
|
||||||
NSMutableArray *_itemCells;
|
NSMutableArray *_itemCells;
|
||||||
BOOL _horizontal;
|
BOOL _horizontal;
|
||||||
|
@ -62,9 +90,8 @@
|
||||||
NSSize _cellSize;
|
NSSize _cellSize;
|
||||||
@private
|
@private
|
||||||
id _items_link;
|
id _items_link;
|
||||||
BOOL _keepAttachedMenus;
|
|
||||||
int _oldHighlightedItemIndex;
|
|
||||||
int _leftBorderOffset;
|
int _leftBorderOffset;
|
||||||
|
id _titleView;
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (float)menuBarHeight;
|
+ (float)menuBarHeight;
|
||||||
|
@ -84,14 +111,14 @@
|
||||||
- (NSMenuView *)attachedMenuView;
|
- (NSMenuView *)attachedMenuView;
|
||||||
- (NSMenu *)attachedMenu;
|
- (NSMenu *)attachedMenu;
|
||||||
- (BOOL)isAttached;
|
- (BOOL)isAttached;
|
||||||
|
- (void) detachSubmenu;
|
||||||
|
- (void) attachSubmenuForItemAtIndex: (int) index;
|
||||||
- (BOOL)isTornOff;
|
- (BOOL)isTornOff;
|
||||||
- (void)setHorizontalEdgePadding:(float)pad;
|
- (void)setHorizontalEdgePadding:(float)pad;
|
||||||
- (float)horizontalEdgePadding;
|
- (float)horizontalEdgePadding;
|
||||||
- (void)itemChanged:(NSNotification *)notification;
|
- (void)itemChanged:(NSNotification *)notification;
|
||||||
- (void)itemAdded:(NSNotification *)notification;
|
- (void)itemAdded:(NSNotification *)notification;
|
||||||
- (void)itemRemoved:(NSNotification *)notification;
|
- (void)itemRemoved:(NSNotification *)notification;
|
||||||
- (void)detachSubmenu;
|
|
||||||
- (void)attachSubmenuForItemAtIndex:(int)index;
|
|
||||||
- (void)update;
|
- (void)update;
|
||||||
- (void)setNeedsSizing:(BOOL)flag;
|
- (void)setNeedsSizing:(BOOL)flag;
|
||||||
- (BOOL)needsSizing;
|
- (BOOL)needsSizing;
|
||||||
|
@ -116,4 +143,5 @@
|
||||||
- (BOOL)trackWithEvent:(NSEvent *)event;
|
- (BOOL)trackWithEvent:(NSEvent *)event;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
596
Source/NSMenu.m
596
Source/NSMenu.m
|
@ -55,39 +55,78 @@
|
||||||
#include <AppKit/NSScreen.h>
|
#include <AppKit/NSScreen.h>
|
||||||
#include <AppKit/NSAttributedString.h>
|
#include <AppKit/NSAttributedString.h>
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Drawing related:
|
||||||
|
|
||||||
|
NSMenu superMenu (if not root menu, the parent meu)
|
||||||
|
^
|
||||||
|
|
|
||||||
|
| +------------------> NSMenuView view (content, draws the menu items)
|
||||||
|
| |
|
||||||
|
NSMenu +----------+-------> NSMenuPanel A (regular window, torn off window)
|
||||||
|
| | `-------> NSMenuPanel B (transient window)
|
||||||
|
| |
|
||||||
|
| +------------------> NSString title (title)
|
||||||
|
|
|
||||||
|
v
|
||||||
|
NSMenu attachedMenu (the menu that is attached to this one, during navigation)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
+--[NSMenuPanel]------+
|
||||||
|
| +-[NSMenuView]----+ |
|
||||||
|
| | title if applic | |
|
||||||
|
| | +-------------+ | |
|
||||||
|
| | | NSMenuItem- | | |
|
||||||
|
| | | Cell | | |
|
||||||
|
| | +-------------+ | |
|
||||||
|
| | . | |
|
||||||
|
| | . | |
|
||||||
|
| +-----------------+ |
|
||||||
|
+---------------------+
|
||||||
|
|
||||||
|
The two windows
|
||||||
|
---------------
|
||||||
|
|
||||||
|
Basically we have for a menu two windows, window A and window B.
|
||||||
|
Window A is the regular window and Window B is used for transient windows.
|
||||||
|
|
||||||
|
At any one time, the views, like title view, NSMenuView are put either in
|
||||||
|
window A or in window B. They are moved over from one window to the oter
|
||||||
|
when needed.
|
||||||
|
|
||||||
|
the code is supposed to know when it is using window A or B.
|
||||||
|
But it will probably only work correctly when
|
||||||
|
|
||||||
|
window A correspond to _transient == NO
|
||||||
|
window B correspond to _transient == YES
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
/* Subclass of NSPanel since menus cannot become key */
|
/* Subclass of NSPanel since menus cannot become key */
|
||||||
@interface NSMenuPanel : NSPanel
|
@interface NSMenuPanel : NSPanel
|
||||||
@end
|
@end
|
||||||
|
|
||||||
/* A menu's title is an instance of this class */
|
|
||||||
@interface NSMenuWindowTitleView : NSView
|
|
||||||
{
|
|
||||||
id menu;
|
|
||||||
NSButton *button;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) addCloseButton;
|
|
||||||
- (void) releaseCloseButton;
|
|
||||||
- (void) createButton;
|
|
||||||
- (void) setMenu: (NSMenu*)menu;
|
|
||||||
- (NSMenu*) menu;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface NSMenuView (GNUstepPrivate)
|
@interface NSMenuView (GNUstepPrivate)
|
||||||
- (NSArray *)_itemCells;
|
- (NSArray *)_itemCells;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
static NSZone *menuZone = NULL;
|
static NSZone *menuZone = NULL;
|
||||||
static NSString *NSMenuLocationsKey = @"NSMenuLocations";
|
static NSString *NSMenuLocationsKey = @"NSMenuLocations";
|
||||||
|
static NSString *NSEnqueuedMenuMoveName = @"EnqueuedMoveNotificationName";
|
||||||
static NSNotificationCenter *nc;
|
static NSNotificationCenter *nc;
|
||||||
|
|
||||||
@interface NSMenu (GNUstepPrivate)
|
@interface NSMenu (GNUstepPrivate)
|
||||||
|
|
||||||
- (NSString *) _locationKey;
|
- (NSString *) _locationKey;
|
||||||
- (NSMenuPanel *) _createWindow;
|
- (NSMenuPanel *) _createWindow;
|
||||||
|
- (void) _updateUserDefaults: (id) notification;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
@implementation NSMenuPanel
|
@implementation NSMenuPanel
|
||||||
- (BOOL) canBecomeKeyWindow
|
- (BOOL) canBecomeKeyWindow
|
||||||
{
|
{
|
||||||
|
@ -139,6 +178,68 @@ static NSNotificationCenter *nc;
|
||||||
return win;
|
return win;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Will track the mouse movement. It will trigger the updating of the user
|
||||||
|
defaults in due time.
|
||||||
|
*/
|
||||||
|
- (void) _menuMoved: (id) notification
|
||||||
|
{
|
||||||
|
NSNotification *resend;
|
||||||
|
|
||||||
|
resend = [NSNotification notificationWithName: NSEnqueuedMenuMoveName
|
||||||
|
object: self];
|
||||||
|
|
||||||
|
[[NSNotificationQueue defaultQueue]
|
||||||
|
enqueueNotification: resend
|
||||||
|
postingStyle: NSPostASAP
|
||||||
|
coalesceMask: NSNotificationCoalescingOnSender
|
||||||
|
forModes: [NSArray arrayWithObject: NSDefaultRunLoopMode]];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Save the current menu position in the standard user defaults
|
||||||
|
*/
|
||||||
|
- (void) _updateUserDefaults: (id) notification
|
||||||
|
{
|
||||||
|
NSString *key;
|
||||||
|
|
||||||
|
NSDebugLLog (@"NSMenu", @"Synchronizing user defaults");
|
||||||
|
key = [self _locationKey];
|
||||||
|
if (key != nil)
|
||||||
|
{
|
||||||
|
NSUserDefaults *defaults;
|
||||||
|
NSMutableDictionary *menuLocations;
|
||||||
|
NSString *locString;
|
||||||
|
|
||||||
|
defaults = [NSUserDefaults standardUserDefaults];
|
||||||
|
menuLocations = [[defaults objectForKey: NSMenuLocationsKey] mutableCopy];
|
||||||
|
|
||||||
|
if ([_aWindow isVisible] && [self isTornOff])
|
||||||
|
{
|
||||||
|
if (menuLocations == nil)
|
||||||
|
{
|
||||||
|
menuLocations = AUTORELEASE([[NSMutableDictionary alloc] initWithCapacity: 2]);
|
||||||
|
}
|
||||||
|
locString = [[self window] stringWithSavedFrame];
|
||||||
|
[menuLocations setObject: locString forKey: key];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
[menuLocations removeObjectForKey: key];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([menuLocations count] > 0)
|
||||||
|
{
|
||||||
|
[defaults setObject: menuLocations forKey: NSMenuLocationsKey];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
[defaults removeObjectForKey: NSMenuLocationsKey];
|
||||||
|
}
|
||||||
|
[defaults synchronize];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
@ -168,7 +269,7 @@ static NSNotificationCenter *nc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initializing a New NSMenu
|
*
|
||||||
*/
|
*/
|
||||||
- (id) init
|
- (id) init
|
||||||
{
|
{
|
||||||
|
@ -185,14 +286,15 @@ static NSNotificationCenter *nc;
|
||||||
RELEASE(_view);
|
RELEASE(_view);
|
||||||
RELEASE(_aWindow);
|
RELEASE(_aWindow);
|
||||||
RELEASE(_bWindow);
|
RELEASE(_bWindow);
|
||||||
RELEASE(_titleView);
|
|
||||||
|
|
||||||
[super dealloc];
|
[super dealloc];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
*/
|
||||||
- (id) initWithTitle: (NSString*)aTitle
|
- (id) initWithTitle: (NSString*)aTitle
|
||||||
{
|
{
|
||||||
float height;
|
|
||||||
NSView *contentView;
|
NSView *contentView;
|
||||||
|
|
||||||
[super init];
|
[super init];
|
||||||
|
@ -203,19 +305,12 @@ static NSNotificationCenter *nc;
|
||||||
// Create an array to store out menu items.
|
// Create an array to store out menu items.
|
||||||
_items = [[NSMutableArray alloc] init];
|
_items = [[NSMutableArray alloc] init];
|
||||||
|
|
||||||
// We have no supermenu.
|
|
||||||
// _superMenu = nil;
|
|
||||||
// _is_tornoff = NO;
|
|
||||||
// _follow_transient = NO;
|
|
||||||
|
|
||||||
_changedMessagesEnabled = YES;
|
_changedMessagesEnabled = YES;
|
||||||
_notifications = [[NSMutableArray alloc] init];
|
_notifications = [[NSMutableArray alloc] init];
|
||||||
_changed = YES;
|
_changed = YES;
|
||||||
// According to the spec, menus do autoenable by default.
|
// According to the spec, menus do autoenable by default.
|
||||||
_autoenable = YES;
|
_autoenable = YES;
|
||||||
|
|
||||||
// Transient windows private stuff.
|
|
||||||
// _oldAttachedMenu = nil;
|
|
||||||
|
|
||||||
/* Please note that we own all this menu network of objects. So,
|
/* Please note that we own all this menu network of objects. So,
|
||||||
none of these objects should be retaining us. When we are deallocated,
|
none of these objects should be retaining us. When we are deallocated,
|
||||||
|
@ -231,15 +326,8 @@ static NSNotificationCenter *nc;
|
||||||
_view = [[NSMenuView alloc] initWithFrame: NSMakeRect(0,0,50,50)];
|
_view = [[NSMenuView alloc] initWithFrame: NSMakeRect(0,0,50,50)];
|
||||||
[_view setMenu: self];
|
[_view setMenu: self];
|
||||||
|
|
||||||
// Create the title view
|
|
||||||
height = [[_view class] menuBarHeight];
|
|
||||||
_titleView = [[NSMenuWindowTitleView alloc] initWithFrame:
|
|
||||||
NSMakeRect(0, 0, 50, height)];
|
|
||||||
[_titleView setMenu: self];
|
|
||||||
|
|
||||||
contentView = [_aWindow contentView];
|
contentView = [_aWindow contentView];
|
||||||
[contentView addSubview: _view];
|
[contentView addSubview: _view];
|
||||||
[contentView addSubview: _titleView];
|
|
||||||
|
|
||||||
/* Set up the notification to start the process of redisplaying
|
/* Set up the notification to start the process of redisplaying
|
||||||
the menus where the user left them the last time.
|
the menus where the user left them the last time.
|
||||||
|
@ -258,6 +346,16 @@ static NSNotificationCenter *nc;
|
||||||
name: NSApplicationWillBecomeActiveNotification
|
name: NSApplicationWillBecomeActiveNotification
|
||||||
object: NSApp];
|
object: NSApp];
|
||||||
|
|
||||||
|
[nc addObserver: self
|
||||||
|
selector: @selector (_menuMoved:)
|
||||||
|
name: NSWindowDidMoveNotification
|
||||||
|
object: _aWindow];
|
||||||
|
|
||||||
|
[nc addObserver: self
|
||||||
|
selector: @selector (_updateUserDefaults:)
|
||||||
|
name: NSEnqueuedMenuMoveName
|
||||||
|
object: self];
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -558,18 +656,34 @@ static NSNotificationCenter *nc;
|
||||||
|
|
||||||
- (void) submenuAction: (id)sender
|
- (void) submenuAction: (id)sender
|
||||||
{
|
{
|
||||||
[_view detachSubmenu];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (NSMenu *) attachedMenu
|
- (NSMenu *) attachedMenu
|
||||||
{
|
{
|
||||||
if (_attachedMenu && _follow_transient
|
if (_attachedMenu && _transient
|
||||||
&& !_attachedMenu->_follow_transient)
|
&& !_attachedMenu->_transient)
|
||||||
return nil;
|
return nil;
|
||||||
|
|
||||||
return _attachedMenu;
|
return _attachedMenu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Look for the semantics in the header. Note that
|
||||||
|
this implementation works because there are ... cases:
|
||||||
|
<enum>
|
||||||
|
<item>
|
||||||
|
This menu is transient, its supermenu is also transient.
|
||||||
|
In this case we just do the check between the transient windows
|
||||||
|
and everything is fine
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
The menu is transient, its supermenu is not transient.
|
||||||
|
This can go WRONG
|
||||||
|
</item>
|
||||||
|
</enum>
|
||||||
|
*/
|
||||||
- (BOOL) isAttached
|
- (BOOL) isAttached
|
||||||
{
|
{
|
||||||
return _superMenu && [_superMenu attachedMenu] == self;
|
return _superMenu && [_superMenu attachedMenu] == self;
|
||||||
|
@ -835,6 +949,9 @@ static NSNotificationCenter *nc;
|
||||||
//
|
//
|
||||||
// Updating the Menu Layout
|
// Updating the Menu Layout
|
||||||
//
|
//
|
||||||
|
// Wim 20030301: Question, what happens when the notification trigger
|
||||||
|
// new notifications? I think it is not allowed to add items
|
||||||
|
// to the _notifications array while enumerating it.
|
||||||
- (void) setMenuChangedMessagesEnabled: (BOOL)flag
|
- (void) setMenuChangedMessagesEnabled: (BOOL)flag
|
||||||
{
|
{
|
||||||
if (_changedMessagesEnabled != flag)
|
if (_changedMessagesEnabled != flag)
|
||||||
|
@ -869,39 +986,25 @@ static NSNotificationCenter *nc;
|
||||||
NSRect menuFrame;
|
NSRect menuFrame;
|
||||||
NSSize size;
|
NSSize size;
|
||||||
|
|
||||||
//if ([_view needsSizing])
|
[_view sizeToFit];
|
||||||
[_view sizeToFit];
|
|
||||||
|
|
||||||
menuFrame = [_view frame];
|
menuFrame = [_view frame];
|
||||||
size = menuFrame.size;
|
size = menuFrame.size;
|
||||||
|
|
||||||
windowFrame = [_aWindow frame];
|
windowFrame = [_aWindow frame];
|
||||||
|
[_aWindow setContentSize: size];
|
||||||
|
[_aWindow setFrameTopLeftPoint:
|
||||||
|
NSMakePoint(NSMinX(windowFrame),NSMaxY(windowFrame))];
|
||||||
|
|
||||||
|
windowFrame = [_bWindow frame];
|
||||||
|
[_bWindow setContentSize: size];
|
||||||
|
[_bWindow setFrameTopLeftPoint:
|
||||||
|
NSMakePoint(NSMinX(windowFrame),NSMaxY(windowFrame))];
|
||||||
|
|
||||||
if (_popUpButtonCell == nil)
|
if (_popUpButtonCell == nil)
|
||||||
{
|
{
|
||||||
float height = [[_view class] menuBarHeight];
|
|
||||||
|
|
||||||
size.height += height;
|
|
||||||
[_aWindow setContentSize: size];
|
|
||||||
[_aWindow setFrameTopLeftPoint:
|
|
||||||
NSMakePoint(NSMinX(windowFrame),NSMaxY(windowFrame))];
|
|
||||||
|
|
||||||
windowFrame = [_bWindow frame];
|
|
||||||
[_bWindow setContentSize: size];
|
|
||||||
[_bWindow setFrameTopLeftPoint:
|
|
||||||
NSMakePoint(NSMinX(windowFrame),NSMaxY(windowFrame))];
|
|
||||||
|
|
||||||
[_view setFrameOrigin: NSMakePoint (0, 0)];
|
[_view setFrameOrigin: NSMakePoint (0, 0)];
|
||||||
[_titleView setFrame: NSMakeRect (0, size.height - height,
|
}
|
||||||
size.width, height)];
|
|
||||||
[_titleView setNeedsDisplay: YES];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[_aWindow setContentSize: size];
|
|
||||||
[_aWindow setFrameTopLeftPoint:
|
|
||||||
NSMakePoint(NSMinX(windowFrame),NSMaxY(windowFrame))];
|
|
||||||
}
|
|
||||||
|
|
||||||
[_view setNeedsDisplay: YES];
|
[_view setNeedsDisplay: YES];
|
||||||
|
|
||||||
|
@ -1013,23 +1116,22 @@ static NSNotificationCenter *nc;
|
||||||
#define IS_OFFSCREEN(WINDOW) \
|
#define IS_OFFSCREEN(WINDOW) \
|
||||||
!(NSContainsRect([[NSScreen mainScreen] frame], [WINDOW frame]))
|
!(NSContainsRect([[NSScreen mainScreen] frame], [WINDOW frame]))
|
||||||
|
|
||||||
- (void) _setTornOff: (BOOL)flag
|
- (void) setTornOff: (BOOL)flag
|
||||||
{
|
{
|
||||||
NSMenu *supermenu;
|
NSMenu *supermenu;
|
||||||
|
|
||||||
_is_tornoff = flag;
|
_is_tornoff = flag;
|
||||||
|
|
||||||
if (flag)
|
if (flag)
|
||||||
[_titleView addCloseButton];
|
|
||||||
else
|
|
||||||
[_titleView releaseCloseButton];
|
|
||||||
|
|
||||||
supermenu = [self supermenu];
|
|
||||||
if (supermenu != nil)
|
|
||||||
{
|
{
|
||||||
[[supermenu menuRepresentation] setHighlightedItemIndex: -1];
|
supermenu = [self supermenu];
|
||||||
supermenu->_attachedMenu = nil;
|
if (supermenu != nil)
|
||||||
|
{
|
||||||
|
[[supermenu menuRepresentation] setHighlightedItemIndex: -1];
|
||||||
|
supermenu->_attachedMenu = nil;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
[_view update];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) _showTornOffMenuIfAny: (NSNotification*)notification
|
- (void) _showTornOffMenuIfAny: (NSNotification*)notification
|
||||||
|
@ -1051,7 +1153,7 @@ static NSNotificationCenter *nc;
|
||||||
location = [menuLocations objectForKey: key];
|
location = [menuLocations objectForKey: key];
|
||||||
if (location && [location isKindOfClass: [NSString class]])
|
if (location && [location isKindOfClass: [NSString class]])
|
||||||
{
|
{
|
||||||
[self _setTornOff: YES];
|
[self setTornOff: YES];
|
||||||
[self display];
|
[self display];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1068,52 +1170,25 @@ static NSNotificationCenter *nc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL) isFollowTransient
|
- (BOOL) isTransient
|
||||||
{
|
{
|
||||||
return _follow_transient;
|
return _transient;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL) isPartlyOffScreen
|
- (BOOL) isPartlyOffScreen
|
||||||
{
|
{
|
||||||
return _isPartlyOffScreen;
|
return IS_OFFSCREEN ([self window]);
|
||||||
}
|
|
||||||
|
|
||||||
- (void) nestedCheckOffScreen
|
|
||||||
{
|
|
||||||
// This method is used when the menu is moved.
|
|
||||||
if (_attachedMenu)
|
|
||||||
[_attachedMenu nestedCheckOffScreen];
|
|
||||||
|
|
||||||
_isPartlyOffScreen = IS_OFFSCREEN(_aWindow);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) _performMenuClose: (id)sender
|
- (void) _performMenuClose: (id)sender
|
||||||
{
|
{
|
||||||
NSString *key;
|
|
||||||
|
|
||||||
if (_attachedMenu)
|
if (_attachedMenu)
|
||||||
[_view detachSubmenu];
|
[_view detachSubmenu];
|
||||||
|
|
||||||
key = [self _locationKey];
|
|
||||||
if (key != nil)
|
|
||||||
{
|
|
||||||
NSUserDefaults *defaults;
|
|
||||||
NSMutableDictionary *menuLocations;
|
|
||||||
|
|
||||||
defaults = [NSUserDefaults standardUserDefaults];
|
|
||||||
menuLocations = [[defaults objectForKey: NSMenuLocationsKey] mutableCopy];
|
|
||||||
[menuLocations removeObjectForKey: key];
|
|
||||||
if ([menuLocations count] > 0)
|
|
||||||
[defaults setObject: menuLocations forKey: NSMenuLocationsKey];
|
|
||||||
else
|
|
||||||
[defaults removeObjectForKey: NSMenuLocationsKey];
|
|
||||||
RELEASE(menuLocations);
|
|
||||||
[defaults synchronize];
|
|
||||||
}
|
|
||||||
|
|
||||||
[_view setHighlightedItemIndex: -1];
|
[_view setHighlightedItemIndex: -1];
|
||||||
[self _setTornOff: NO];
|
|
||||||
[self close];
|
[self close];
|
||||||
|
[self setTornOff: NO];
|
||||||
|
[self _updateUserDefaults: nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) _rightMouseDisplay: (NSEvent*)theEvent
|
- (void) _rightMouseDisplay: (NSEvent*)theEvent
|
||||||
|
@ -1125,6 +1200,11 @@ static NSNotificationCenter *nc;
|
||||||
|
|
||||||
- (void) display
|
- (void) display
|
||||||
{
|
{
|
||||||
|
if (_transient)
|
||||||
|
{
|
||||||
|
NSDebugLLog (@"NSMenu", @"trying to display while alreay displayed transient");
|
||||||
|
}
|
||||||
|
|
||||||
if (_changed)
|
if (_changed)
|
||||||
[self sizeToFit];
|
[self sizeToFit];
|
||||||
|
|
||||||
|
@ -1142,10 +1222,7 @@ static NSNotificationCenter *nc;
|
||||||
[self setGeometry];
|
[self setGeometry];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[_aWindow orderFrontRegardless];
|
[_aWindow orderFrontRegardless];
|
||||||
|
|
||||||
_isPartlyOffScreen = IS_OFFSCREEN(_aWindow);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) displayTransient
|
- (void) displayTransient
|
||||||
|
@ -1153,8 +1230,15 @@ static NSNotificationCenter *nc;
|
||||||
NSPoint location;
|
NSPoint location;
|
||||||
NSView *contentView;
|
NSView *contentView;
|
||||||
|
|
||||||
_follow_transient = YES;
|
if (_transient)
|
||||||
|
{
|
||||||
|
NSDebugLLog (@"NSMenu", @"displaying transient while it is transient");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_oldHiglightedIndex = [[self menuRepresentation] highlightedItemIndex];
|
||||||
|
_transient = YES;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Cache the old submenu if any and query the supermenu our position.
|
* Cache the old submenu if any and query the supermenu our position.
|
||||||
* Otherwise, raise menu under the mouse.
|
* Otherwise, raise menu under the mouse.
|
||||||
|
@ -1180,20 +1264,14 @@ static NSNotificationCenter *nc;
|
||||||
[_bWindow setFrameOrigin: location];
|
[_bWindow setFrameOrigin: location];
|
||||||
|
|
||||||
[_view removeFromSuperviewWithoutNeedingDisplay];
|
[_view removeFromSuperviewWithoutNeedingDisplay];
|
||||||
[_titleView removeFromSuperviewWithoutNeedingDisplay];
|
|
||||||
|
|
||||||
if (_is_tornoff)
|
|
||||||
[_titleView releaseCloseButton];
|
|
||||||
|
|
||||||
contentView = [_bWindow contentView];
|
contentView = [_bWindow contentView];
|
||||||
[contentView addSubview: _view];
|
[contentView addSubview: _view];
|
||||||
[contentView addSubview: _titleView];
|
[_view update];
|
||||||
|
|
||||||
[_bWindow orderFront: self];
|
[_bWindow orderFront: self];
|
||||||
|
|
||||||
_isPartlyOffScreen = IS_OFFSCREEN(_bWindow);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void) setGeometry
|
- (void) setGeometry
|
||||||
{
|
{
|
||||||
NSString *key;
|
NSString *key;
|
||||||
|
@ -1232,6 +1310,12 @@ static NSNotificationCenter *nc;
|
||||||
{
|
{
|
||||||
NSMenu *sub = [self attachedMenu];
|
NSMenu *sub = [self attachedMenu];
|
||||||
|
|
||||||
|
|
||||||
|
if (_transient)
|
||||||
|
{
|
||||||
|
NSDebugLLog (@"NSMenu", @"We should not close ordinary menu while transient version is still open");
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we have an attached submenu, we must close that too - but then make
|
* If we have an attached submenu, we must close that too - but then make
|
||||||
* sure we still have a record of it so that it can be re-displayed if we
|
* sure we still have a record of it so that it can be re-displayed if we
|
||||||
|
@ -1245,39 +1329,48 @@ static NSNotificationCenter *nc;
|
||||||
[_aWindow orderOut: self];
|
[_aWindow orderOut: self];
|
||||||
[_aWindow setFrameOrigin: NSMakePoint (0, 0)];
|
[_aWindow setFrameOrigin: NSMakePoint (0, 0)];
|
||||||
|
|
||||||
if (_superMenu)
|
if (_superMenu && ![self isTornOff])
|
||||||
_superMenu->_attachedMenu = nil;
|
{
|
||||||
|
_superMenu->_attachedMenu = nil;
|
||||||
|
[[_superMenu menuRepresentation] setHighlightedItemIndex: -1];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) closeTransient
|
- (void) closeTransient
|
||||||
{
|
{
|
||||||
NSView *contentView;
|
NSView *contentView;
|
||||||
|
|
||||||
|
if (_transient == NO)
|
||||||
|
{
|
||||||
|
NSDebugLLog (@"NSMenu", @"Closing transient: %@ while it is NOT transient now", _title);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
[_bWindow orderOut: self];
|
[_bWindow orderOut: self];
|
||||||
[_view removeFromSuperviewWithoutNeedingDisplay];
|
[_view removeFromSuperviewWithoutNeedingDisplay];
|
||||||
[_titleView removeFromSuperviewWithoutNeedingDisplay];
|
|
||||||
|
|
||||||
contentView = [_aWindow contentView];
|
contentView = [_aWindow contentView];
|
||||||
[contentView addSubview: _view];
|
[contentView addSubview: _view];
|
||||||
|
[contentView setNeedsDisplay: YES];
|
||||||
if (_is_tornoff)
|
|
||||||
[_titleView addCloseButton];
|
|
||||||
|
|
||||||
[contentView addSubview: _titleView];
|
|
||||||
[contentView setNeedsDisplay: YES];
|
|
||||||
|
|
||||||
// Restore the old submenu (if any).
|
// Restore the old submenu (if any).
|
||||||
if (_superMenu != nil)
|
if (_superMenu != nil)
|
||||||
_superMenu->_attachedMenu = _oldAttachedMenu;
|
{
|
||||||
|
_superMenu->_attachedMenu = _oldAttachedMenu;
|
||||||
|
[[_superMenu menuRepresentation] setHighlightedItemIndex:
|
||||||
|
[_superMenu indexOfItemWithSubmenu: _superMenu->_attachedMenu]];
|
||||||
|
}
|
||||||
|
|
||||||
_follow_transient = NO;
|
[[self menuRepresentation] setHighlightedItemIndex: _oldHiglightedIndex];
|
||||||
|
|
||||||
|
_transient = NO;
|
||||||
|
|
||||||
_isPartlyOffScreen = IS_OFFSCREEN(_aWindow);
|
[_view update];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSWindow*) window
|
- (NSWindow*) window
|
||||||
{
|
{
|
||||||
if (_follow_transient)
|
if (_transient)
|
||||||
return (NSWindow *)_bWindow;
|
return (NSWindow *)_bWindow;
|
||||||
else
|
else
|
||||||
return (NSWindow *)_aWindow;
|
return (NSWindow *)_aWindow;
|
||||||
|
@ -1290,7 +1383,7 @@ static NSNotificationCenter *nc;
|
||||||
*/
|
*/
|
||||||
- (void) nestedSetFrameOrigin: (NSPoint) aPoint
|
- (void) nestedSetFrameOrigin: (NSPoint) aPoint
|
||||||
{
|
{
|
||||||
NSWindow *theWindow = _follow_transient ? _bWindow : _aWindow;
|
NSWindow *theWindow = [self window];
|
||||||
|
|
||||||
// Move ourself and get our width.
|
// Move ourself and get our width.
|
||||||
[theWindow setFrameOrigin: aPoint];
|
[theWindow setFrameOrigin: aPoint];
|
||||||
|
@ -1307,39 +1400,37 @@ static NSNotificationCenter *nc;
|
||||||
|
|
||||||
- (void) shiftOnScreen
|
- (void) shiftOnScreen
|
||||||
{
|
{
|
||||||
NSWindow *theWindow = _follow_transient ? _bWindow : _aWindow;
|
NSWindow *theWindow = _transient ? _bWindow : _aWindow;
|
||||||
NSRect frameRect = [theWindow frame];
|
NSRect frameRect = [theWindow frame];
|
||||||
|
NSRect screenRect = [[NSScreen mainScreen] frame];
|
||||||
NSPoint vector = {0.0, 0.0};
|
NSPoint vector = {0.0, 0.0};
|
||||||
BOOL moveIt = YES;
|
BOOL moveIt = NO;
|
||||||
|
|
||||||
if (frameRect.origin.y < 0)
|
// 1 - determine the amount we need to shift in the y direction.
|
||||||
|
if (NSMinY (frameRect) < 0)
|
||||||
{
|
{
|
||||||
if (frameRect.origin.y + SHIFT_DELTA <= 0)
|
vector.y = MIN (SHIFT_DELTA, -NSMinY (frameRect));
|
||||||
vector.y = SHIFT_DELTA;
|
moveIt = YES;
|
||||||
else
|
|
||||||
vector.y = -frameRect.origin.y;
|
|
||||||
}
|
}
|
||||||
else if (frameRect.origin.x < 0)
|
else if (NSMaxY (frameRect) > NSMaxY (screenRect))
|
||||||
{
|
{
|
||||||
if (frameRect.origin.x + SHIFT_DELTA <= 0)
|
vector.y = -MIN (SHIFT_DELTA, NSMaxY (frameRect) - NSMaxY (screenRect));
|
||||||
vector.x = SHIFT_DELTA;
|
moveIt = YES;
|
||||||
else
|
|
||||||
vector.x = -frameRect.origin.x;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
vector.x = frameRect.origin.x + frameRect.size.width;
|
|
||||||
vector.x -= [[NSScreen mainScreen] frame].size.width;
|
|
||||||
|
|
||||||
if (vector.x > 0)
|
// 2 - determine the amount we need to shift in the x direction.
|
||||||
{
|
if (NSMinX (frameRect) < 0)
|
||||||
if (vector.x - SHIFT_DELTA <= 0)
|
{
|
||||||
vector.x = -SHIFT_DELTA;
|
vector.x = MIN (SHIFT_DELTA, -NSMinX (frameRect));
|
||||||
else
|
moveIt = YES;
|
||||||
vector.x = -vector.x - 2;
|
}
|
||||||
}
|
// Note the -3. This is done so the menu, after shifting completely
|
||||||
else
|
// has some spare room on the right hand side. This is needed otherwise
|
||||||
moveIt = NO;
|
// the user can never access submenus of this menu.
|
||||||
|
else if (NSMaxX (frameRect) > NSMaxX (screenRect) - 3)
|
||||||
|
{
|
||||||
|
vector.x = -MIN (SHIFT_DELTA, NSMaxX (frameRect) - NSMaxX (screenRect) + 3);
|
||||||
|
moveIt = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (moveIt)
|
if (moveIt)
|
||||||
|
@ -1353,7 +1444,7 @@ static NSNotificationCenter *nc;
|
||||||
for (candidateMenu = masterMenu = self;
|
for (candidateMenu = masterMenu = self;
|
||||||
(candidateMenu = masterMenu->_superMenu)
|
(candidateMenu = masterMenu->_superMenu)
|
||||||
&& (!masterMenu->_is_tornoff
|
&& (!masterMenu->_is_tornoff
|
||||||
|| masterMenu->_follow_transient);
|
|| masterMenu->_transient);
|
||||||
masterMenu = candidateMenu);
|
masterMenu = candidateMenu);
|
||||||
|
|
||||||
masterLocation = [[masterMenu window] frame].origin;
|
masterLocation = [[masterMenu window] frame].origin;
|
||||||
|
@ -1362,8 +1453,6 @@ static NSNotificationCenter *nc;
|
||||||
|
|
||||||
[masterMenu nestedSetFrameOrigin: destinationPoint];
|
[masterMenu nestedSetFrameOrigin: destinationPoint];
|
||||||
}
|
}
|
||||||
else
|
|
||||||
_isPartlyOffScreen = NO;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)_ownedByPopUp
|
- (BOOL)_ownedByPopUp
|
||||||
|
@ -1378,191 +1467,18 @@ static NSNotificationCenter *nc;
|
||||||
_popUpButtonCell = popUp;
|
_popUpButtonCell = popUp;
|
||||||
if (popUp != nil)
|
if (popUp != nil)
|
||||||
{
|
{
|
||||||
[_titleView removeFromSuperviewWithoutNeedingDisplay];
|
|
||||||
[_aWindow setLevel: NSPopUpMenuWindowLevel];
|
[_aWindow setLevel: NSPopUpMenuWindowLevel];
|
||||||
[_bWindow setLevel: NSPopUpMenuWindowLevel];
|
[_bWindow setLevel: NSPopUpMenuWindowLevel];
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
NSArray *itemCells = [_view _itemCells];
|
|
||||||
int i;
|
|
||||||
int count = [itemCells count];
|
|
||||||
|
|
||||||
for ( i = 0; i < count; i++ )
|
|
||||||
{
|
|
||||||
[[itemCells objectAtIndex: i] setMenuView: _view];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
[self update];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString*) description
|
||||||
|
{
|
||||||
|
return [NSString stringWithFormat: @"NSMenu: %@ (%@)",
|
||||||
|
_title, _transient ? @"Transient": @"Normal"];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
||||||
@implementation NSMenuWindowTitleView
|
|
||||||
|
|
||||||
- (BOOL) acceptsFirstMouse: (NSEvent *)theEvent
|
|
||||||
{
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) setMenu: (NSMenu*)aMenu
|
|
||||||
{
|
|
||||||
menu = aMenu;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSMenu*) menu
|
|
||||||
{
|
|
||||||
return menu;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) drawRect: (NSRect)rect
|
|
||||||
{
|
|
||||||
NSRect workRect = [self bounds];
|
|
||||||
NSRectEdge sides[] = {NSMinXEdge, NSMaxYEdge};
|
|
||||||
float grays[] = {NSDarkGray, NSDarkGray};
|
|
||||||
/* Cache the title attributes */
|
|
||||||
static NSDictionary *attr = nil;
|
|
||||||
|
|
||||||
// Draw the dark gray upper left lines.
|
|
||||||
workRect = NSDrawTiledRects(workRect, workRect, sides, grays, 2);
|
|
||||||
|
|
||||||
// Draw the title box's button.
|
|
||||||
NSDrawButton(workRect, workRect);
|
|
||||||
|
|
||||||
// Paint it Black!
|
|
||||||
workRect.origin.x += 1;
|
|
||||||
workRect.origin.y += 2;
|
|
||||||
workRect.size.height -= 3;
|
|
||||||
workRect.size.width -= 3;
|
|
||||||
[[NSColor windowFrameColor] set];
|
|
||||||
NSRectFill(workRect);
|
|
||||||
|
|
||||||
// Draw the title
|
|
||||||
if (attr == nil)
|
|
||||||
{
|
|
||||||
attr = [[NSDictionary alloc]
|
|
||||||
initWithObjectsAndKeys:
|
|
||||||
[NSFont boldSystemFontOfSize: 0], NSFontAttributeName,
|
|
||||||
[NSColor windowFrameTextColor], NSForegroundColorAttributeName,
|
|
||||||
nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
// This gives the correct position
|
|
||||||
workRect.origin.x += 5;
|
|
||||||
workRect.size.width -= 5;
|
|
||||||
workRect.size.height -= 2;
|
|
||||||
[[menu title] drawInRect: workRect withAttributes: attr];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) mouseDown: (NSEvent*)theEvent
|
|
||||||
{
|
|
||||||
NSString *key;
|
|
||||||
NSPoint lastLocation;
|
|
||||||
NSPoint location;
|
|
||||||
unsigned eventMask = NSLeftMouseUpMask | NSLeftMouseDraggedMask;
|
|
||||||
BOOL done = NO;
|
|
||||||
NSDate *theDistantFuture = [NSDate distantFuture];
|
|
||||||
|
|
||||||
lastLocation = [theEvent locationInWindow];
|
|
||||||
|
|
||||||
if (![menu isTornOff] && [menu supermenu])
|
|
||||||
{
|
|
||||||
[menu _setTornOff: YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!done)
|
|
||||||
{
|
|
||||||
theEvent = [NSApp nextEventMatchingMask: eventMask
|
|
||||||
untilDate: theDistantFuture
|
|
||||||
inMode: NSEventTrackingRunLoopMode
|
|
||||||
dequeue: YES];
|
|
||||||
|
|
||||||
switch ([theEvent type])
|
|
||||||
{
|
|
||||||
case NSLeftMouseUp:
|
|
||||||
done = YES;
|
|
||||||
break;
|
|
||||||
case NSLeftMouseDragged:
|
|
||||||
location = [_window mouseLocationOutsideOfEventStream];
|
|
||||||
if (NSEqualPoints(location, lastLocation) == NO)
|
|
||||||
{
|
|
||||||
NSPoint origin = [_window frame].origin;
|
|
||||||
|
|
||||||
origin.x += (location.x - lastLocation.x);
|
|
||||||
origin.y += (location.y - lastLocation.y);
|
|
||||||
[menu nestedSetFrameOrigin: origin];
|
|
||||||
[menu nestedCheckOffScreen];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Same current menu frame in defaults database.
|
|
||||||
*/
|
|
||||||
key = [menu _locationKey];
|
|
||||||
if (key != nil)
|
|
||||||
{
|
|
||||||
NSUserDefaults *defaults;
|
|
||||||
NSMutableDictionary *menuLocations;
|
|
||||||
NSString *locString;
|
|
||||||
|
|
||||||
defaults = [NSUserDefaults standardUserDefaults];
|
|
||||||
menuLocations = [[defaults objectForKey: NSMenuLocationsKey] mutableCopy];
|
|
||||||
if (menuLocations == nil)
|
|
||||||
{
|
|
||||||
menuLocations = AUTORELEASE([[NSMutableDictionary alloc] initWithCapacity: 2]);
|
|
||||||
}
|
|
||||||
locString = [[menu window] stringWithSavedFrame];
|
|
||||||
[menuLocations setObject: locString forKey: key];
|
|
||||||
[defaults setObject: menuLocations forKey: NSMenuLocationsKey];
|
|
||||||
[defaults synchronize];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) createButton
|
|
||||||
{
|
|
||||||
// create the menu's close button
|
|
||||||
NSImage* closeImage = [NSImage imageNamed: @"common_Close"];
|
|
||||||
NSImage* closeHImage = [NSImage imageNamed: @"common_CloseH"];
|
|
||||||
NSSize imageSize = [closeImage size];
|
|
||||||
NSRect rect = { { _frame.size.width - imageSize.width - 4,
|
|
||||||
(_frame.size.height - imageSize.height) / 2},
|
|
||||||
{ imageSize.height, imageSize.width } };
|
|
||||||
|
|
||||||
button = [[NSButton alloc] initWithFrame: rect];
|
|
||||||
[button setButtonType: NSMomentaryLight];
|
|
||||||
[button setImagePosition: NSImageOnly];
|
|
||||||
[button setImage: closeImage];
|
|
||||||
[button setAlternateImage: closeHImage];
|
|
||||||
[button setBordered: NO];
|
|
||||||
[button setTarget: menu];
|
|
||||||
[button setAction: @selector(_performMenuClose:)];
|
|
||||||
[button setAutoresizingMask: NSViewMinXMargin];
|
|
||||||
|
|
||||||
[self setAutoresizingMask: NSViewMinXMargin | NSViewMinYMargin | NSViewMaxYMargin];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) releaseCloseButton
|
|
||||||
{
|
|
||||||
[button removeFromSuperview];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) addCloseButton
|
|
||||||
{
|
|
||||||
if (button == nil)
|
|
||||||
[self createButton];
|
|
||||||
[self addSubview: button];
|
|
||||||
[self setNeedsDisplay: YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) rightMouseDown: (NSEvent*)theEvent
|
|
||||||
{
|
|
||||||
// Dont show our menu
|
|
||||||
}
|
|
||||||
|
|
||||||
@end /* NSMenuWindowTitleView */
|
|
||||||
|
|
|
@ -25,8 +25,7 @@
|
||||||
License along with this library; see the file COPYING.LIB.
|
License along with this library; see the file COPYING.LIB.
|
||||||
If not, write to the Free Software Foundation,
|
If not, write to the Free Software Foundation,
|
||||||
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include <Foundation/NSRunLoop.h>
|
#include <Foundation/NSRunLoop.h>
|
||||||
|
|
||||||
|
@ -34,9 +33,40 @@
|
||||||
#include <AppKit/NSEvent.h>
|
#include <AppKit/NSEvent.h>
|
||||||
#include <AppKit/NSFont.h>
|
#include <AppKit/NSFont.h>
|
||||||
#include <AppKit/NSMenuView.h>
|
#include <AppKit/NSMenuView.h>
|
||||||
|
#include <AppKit/NSMenu.h>
|
||||||
|
#include <AppKit/NSButton.h>
|
||||||
#include <AppKit/NSWindow.h>
|
#include <AppKit/NSWindow.h>
|
||||||
#include <AppKit/PSOperators.h>
|
#include <AppKit/PSOperators.h>
|
||||||
|
|
||||||
|
#include <Foundation/NSDebug.h>
|
||||||
|
|
||||||
|
#include <AppKit/NSImage.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
NSMenuView contains:
|
||||||
|
|
||||||
|
a) Title, if needed, this is a subview
|
||||||
|
b) menu items
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
/* A menu's title is an instance of this class */
|
||||||
|
@class NSButton;
|
||||||
|
|
||||||
|
@interface NSMenuWindowTitleView : NSView
|
||||||
|
{
|
||||||
|
NSMenu *menu;
|
||||||
|
NSButton *button;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) addCloseButton;
|
||||||
|
- (void) removeCloseButton;
|
||||||
|
- (void) createButton;
|
||||||
|
- (void) setMenu: (NSMenu*)menu;
|
||||||
|
- (NSMenu*) menu;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
@implementation NSMenuView
|
@implementation NSMenuView
|
||||||
|
|
||||||
static NSRect
|
static NSRect
|
||||||
|
@ -80,6 +110,13 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
||||||
{
|
{
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
// We do not want to popup menus in this menu.
|
||||||
|
- (id) menuForEvent: (NSEvent*) theEvent
|
||||||
|
{
|
||||||
|
NSDebugLLog (@"NSMenu", @"Query for menu in view");
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Init methods.
|
* Init methods.
|
||||||
|
@ -108,6 +145,11 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
||||||
// Create an array to store our menu item cells.
|
// Create an array to store our menu item cells.
|
||||||
_itemCells = [NSMutableArray new];
|
_itemCells = [NSMutableArray new];
|
||||||
|
|
||||||
|
// Create title view and add it. CHECKME, should we do this here?
|
||||||
|
_titleView = [[NSMenuWindowTitleView alloc] init];
|
||||||
|
|
||||||
|
[self addSubview: _titleView];
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,6 +213,7 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
||||||
name: NSMenuDidRemoveItemNotification
|
name: NSMenuDidRemoveItemNotification
|
||||||
object: _menu];
|
object: _menu];
|
||||||
|
|
||||||
|
[_titleView setMenu: _menu]; // WO CHECKME does this needs reorganizing?
|
||||||
// Force menu view's layout to be recalculated.
|
// Force menu view's layout to be recalculated.
|
||||||
[self setNeedsSizing: YES];
|
[self setNeedsSizing: YES];
|
||||||
}
|
}
|
||||||
|
@ -259,12 +302,7 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
||||||
|
|
||||||
- (NSMenuView*) attachedMenuView
|
- (NSMenuView*) attachedMenuView
|
||||||
{
|
{
|
||||||
NSMenu *attachedMenu;
|
return [[_menu attachedMenu] menuRepresentation];
|
||||||
|
|
||||||
if ((attachedMenu = [_menu attachedMenu]))
|
|
||||||
return [attachedMenu menuRepresentation];
|
|
||||||
else
|
|
||||||
return nil;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSMenu*) attachedMenu
|
- (NSMenu*) attachedMenu
|
||||||
|
@ -366,6 +404,7 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
||||||
/*
|
/*
|
||||||
* Working with Submenus.
|
* Working with Submenus.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
- (void) detachSubmenu
|
- (void) detachSubmenu
|
||||||
{
|
{
|
||||||
NSMenu *attachedMenu = [_menu attachedMenu];
|
NSMenu *attachedMenu = [_menu attachedMenu];
|
||||||
|
@ -378,17 +417,24 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
||||||
|
|
||||||
[attachedMenuView detachSubmenu];
|
[attachedMenuView detachSubmenu];
|
||||||
|
|
||||||
[attachedMenuView setHighlightedItemIndex: -1];
|
NSDebugLLog (@"NSMenu", @"detach submenu: %@ from: %@",
|
||||||
|
attachedMenu, _menu);
|
||||||
if ([attachedMenu isFollowTransient])
|
|
||||||
|
if ([attachedMenu isTransient])
|
||||||
{
|
{
|
||||||
[attachedMenu closeTransient];
|
[attachedMenu closeTransient];
|
||||||
[attachedMenuView setHighlightedItemIndex: _oldHighlightedItemIndex];
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
[attachedMenu close];
|
{
|
||||||
|
[attachedMenu close];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Attach submenu if the item at index is a submenu.
|
||||||
|
It will figure out if the new submenu should be transient
|
||||||
|
or not.
|
||||||
|
*/
|
||||||
- (void) attachSubmenuForItemAtIndex: (int)index
|
- (void) attachSubmenuForItemAtIndex: (int)index
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
@ -397,17 +443,26 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
||||||
* soon as we release the mouse the user will be able to leave submenus
|
* soon as we release the mouse the user will be able to leave submenus
|
||||||
* open on the screen and interact with other menus at the same time.
|
* open on the screen and interact with other menus at the same time.
|
||||||
*/
|
*/
|
||||||
NSMenu *attachableMenu = [[_items_link objectAtIndex: index] submenu];
|
NSMenu *attachableMenu;
|
||||||
|
|
||||||
if ([attachableMenu isTornOff] || [_menu isFollowTransient])
|
if (index < 0)
|
||||||
{
|
{
|
||||||
_oldHighlightedItemIndex = [[attachableMenu menuRepresentation]
|
return;
|
||||||
highlightedItemIndex];
|
}
|
||||||
|
|
||||||
|
attachableMenu = [[_items_link objectAtIndex: index] submenu];
|
||||||
|
|
||||||
|
if ([attachableMenu isTornOff] || [_menu isTransient])
|
||||||
|
{
|
||||||
|
NSDebugLLog (@"NSMenu", @"Will open transient: %@", attachableMenu);
|
||||||
[attachableMenu displayTransient];
|
[attachableMenu displayTransient];
|
||||||
[[attachableMenu menuRepresentation] setHighlightedItemIndex: -1];
|
[[attachableMenu menuRepresentation] setHighlightedItemIndex: -1];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
[attachableMenu display];
|
{
|
||||||
|
NSDebugLLog (@"NSMenu", @"Will open normal: %@", attachableMenu);
|
||||||
|
[attachableMenu display];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -417,6 +472,15 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
||||||
{
|
{
|
||||||
[_menu update];
|
[_menu update];
|
||||||
|
|
||||||
|
if ([_menu isTornOff] && ![_menu isTransient])
|
||||||
|
{
|
||||||
|
[_titleView addCloseButton];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
[_titleView removeCloseButton];
|
||||||
|
}
|
||||||
|
|
||||||
if (_needsSizing)
|
if (_needsSizing)
|
||||||
[self sizeToFit];
|
[self sizeToFit];
|
||||||
}
|
}
|
||||||
|
@ -527,8 +591,12 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
||||||
|
|
||||||
if (_horizontal == NO)
|
if (_horizontal == NO)
|
||||||
{
|
{
|
||||||
|
float menuBarHeight = [[self class] menuBarHeight];
|
||||||
|
|
||||||
[self setFrameSize: NSMakeSize(_cellSize.width + _leftBorderOffset,
|
[self setFrameSize: NSMakeSize(_cellSize.width + _leftBorderOffset,
|
||||||
(howMany * _cellSize.height))];
|
(howMany * _cellSize.height) + menuBarHeight)];
|
||||||
|
[_titleView setFrame: NSMakeRect (0, howMany * _cellSize.height,
|
||||||
|
NSWidth (_bounds), menuBarHeight)];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -617,7 +685,7 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
||||||
*/
|
*/
|
||||||
if (_horizontal == NO)
|
if (_horizontal == NO)
|
||||||
{
|
{
|
||||||
theRect.origin.y = _bounds.size.height - (_cellSize.height * (index + 1));
|
theRect.origin.y = _cellSize.height * ([_itemCells count] - index - 1);
|
||||||
theRect.origin.x = _leftBorderOffset;
|
theRect.origin.x = _leftBorderOffset;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -635,6 +703,11 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
||||||
return theRect;
|
return theRect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns the index of the item below point.
|
||||||
|
Returns -1 if mouse is not above
|
||||||
|
a menu item.
|
||||||
|
*/
|
||||||
- (int) indexOfItemAtPoint: (NSPoint)point
|
- (int) indexOfItemAtPoint: (NSPoint)point
|
||||||
{
|
{
|
||||||
unsigned howMany = [_itemCells count];
|
unsigned howMany = [_itemCells count];
|
||||||
|
@ -774,7 +847,7 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
||||||
// TODO
|
// TODO
|
||||||
|
|
||||||
// Compute position for popups, if needed
|
// Compute position for popups, if needed
|
||||||
if (selectedItemIndex > -1)
|
if (selectedItemIndex != -1)
|
||||||
{
|
{
|
||||||
if (_horizontal == NO)
|
if (_horizontal == NO)
|
||||||
{
|
{
|
||||||
|
@ -852,7 +925,8 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
||||||
NSMenu *candidateMenu = _menu;
|
NSMenu *candidateMenu = _menu;
|
||||||
NSMenuView *targetMenuView;
|
NSMenuView *targetMenuView;
|
||||||
int indexToHighlight = index;
|
int indexToHighlight = index;
|
||||||
|
int oldHighlightedIndex;
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
NSMenu *superMenu = [candidateMenu supermenu];
|
NSMenu *superMenu = [candidateMenu supermenu];
|
||||||
|
@ -872,9 +946,7 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ([targetMenuView attachedMenu])
|
oldHighlightedIndex = [targetMenuView highlightedItemIndex];
|
||||||
[targetMenuView detachSubmenu];
|
|
||||||
|
|
||||||
[targetMenuView setHighlightedItemIndex: indexToHighlight];
|
[targetMenuView setHighlightedItemIndex: indexToHighlight];
|
||||||
|
|
||||||
/* We need to let the run loop run a little so that the fact that
|
/* We need to let the run loop run a little so that the fact that
|
||||||
|
@ -887,29 +959,77 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
||||||
|
|
||||||
if (![_menu _ownedByPopUp])
|
if (![_menu _ownedByPopUp])
|
||||||
{
|
{
|
||||||
[targetMenuView setHighlightedItemIndex: -1];
|
[targetMenuView setHighlightedItemIndex: oldHighlightedIndex];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MOVE_THRESHOLD_DELTA 2.0
|
#define MOVE_THRESHOLD_DELTA 2.0
|
||||||
#define DELAY_MULTIPLIER 10
|
#define DELAY_MULTIPLIER 10
|
||||||
|
|
||||||
|
/**
|
||||||
|
This method is responsible for tracking the mouse while this menu
|
||||||
|
is on the screen and the user is busy navigating the menu or one
|
||||||
|
of it submenus. Responsible does not mean that this method does it
|
||||||
|
all. For submenus for example it will call, indirectly, itself for
|
||||||
|
submenu under consideration.
|
||||||
|
|
||||||
|
It will return YES if user released mouse, not above a submenu item.
|
||||||
|
NO in all other circumstances.
|
||||||
|
|
||||||
|
Implementation detail:
|
||||||
|
|
||||||
|
<list>
|
||||||
|
<item> It use periodic events to update the highlight state
|
||||||
|
and attach / detach submenus.
|
||||||
|
</item>
|
||||||
|
<item> The flag justAttachedNewSubmenu is set to YES when
|
||||||
|
a new submenu is attached. The effect is that the
|
||||||
|
highlightin / attaching / detaching is surpressed
|
||||||
|
for this menu. This is done so the user is given
|
||||||
|
a change to move the mouse pointer into the newly
|
||||||
|
attached submenu. Otherwise it would immediately
|
||||||
|
be removed as the mouse pointer move over another
|
||||||
|
item.
|
||||||
|
|
||||||
|
The logic for resetting the flag is rather adhoc.
|
||||||
|
|
||||||
|
<item> the flag subMenusNeedRemoving means that we
|
||||||
|
will remove all the submenus after we are done.
|
||||||
|
|
||||||
|
This flag is used to clean up the submenus
|
||||||
|
when the user has opened a submenu by clicking
|
||||||
|
and wants to close it again by clicking on the
|
||||||
|
hihglighted item.
|
||||||
|
</item>
|
||||||
|
<item> When the user released the mouse this method
|
||||||
|
will cleanup all the transient menus.
|
||||||
|
|
||||||
|
Not only its own, but also its attached menu
|
||||||
|
and all its transient super menus.
|
||||||
|
</item>
|
||||||
|
<item> The clean up is done BEFORE the action is executed.
|
||||||
|
This is needed otherwise `hiding' the application
|
||||||
|
leaves a dangling menu. If this is not acceptable,
|
||||||
|
there should be another mechanism of handling
|
||||||
|
the hiding. BTW besides the `hiding' the application,
|
||||||
|
model panels are also a problem when the menu
|
||||||
|
is not cleared before executing the action.
|
||||||
|
</item>
|
||||||
|
</list>
|
||||||
|
*/
|
||||||
- (BOOL) trackWithEvent: (NSEvent*)event
|
- (BOOL) trackWithEvent: (NSEvent*)event
|
||||||
{
|
{
|
||||||
unsigned eventMask = NSPeriodicMask;
|
unsigned eventMask = NSPeriodicMask;
|
||||||
NSDate *theDistantFuture = [NSDate distantFuture];
|
NSDate *theDistantFuture = [NSDate distantFuture];
|
||||||
int index;
|
|
||||||
NSPoint location;
|
|
||||||
NSPoint lastLocation = {0,0};
|
NSPoint lastLocation = {0,0};
|
||||||
NSMenu *alreadyAttachedMenu = NO;
|
BOOL justAttachedNewSubmenu = NO;
|
||||||
BOOL mouseMoved = NO;
|
BOOL subMenusNeedRemoving = YES;
|
||||||
BOOL delayedSelect = NO;
|
|
||||||
int delayCount = 0;
|
int delayCount = 0;
|
||||||
float xDelta = MOVE_THRESHOLD_DELTA;
|
int indexOfActionToExecute = -1;
|
||||||
NSEvent *original;
|
NSEvent *original;
|
||||||
NSEventType type = [event type];
|
NSEventType type;
|
||||||
NSEventType end;
|
NSEventType end;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The original event is unused except to determine whether the method
|
* The original event is unused except to determine whether the method
|
||||||
* was invoked in response to a right or left mouse down.
|
* was invoked in response to a right or left mouse down.
|
||||||
|
@ -917,122 +1037,144 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
||||||
* submenu.
|
* submenu.
|
||||||
*/
|
*/
|
||||||
original = AUTORELEASE(RETAIN(event));
|
original = AUTORELEASE(RETAIN(event));
|
||||||
if (type == NSRightMouseDown)
|
|
||||||
|
type = [event type];
|
||||||
|
|
||||||
|
if (type == NSRightMouseDown || type == NSRightMouseDragged)
|
||||||
{
|
{
|
||||||
end = NSRightMouseUp;
|
end = NSRightMouseUp;
|
||||||
eventMask |= NSRightMouseUpMask | NSRightMouseDraggedMask;
|
eventMask |= NSRightMouseUpMask | NSRightMouseDraggedMask;
|
||||||
}
|
}
|
||||||
else if (type == NSOtherMouseDown)
|
else if (type == NSOtherMouseDown || type == NSOtherMouseDragged)
|
||||||
{
|
{
|
||||||
end = NSOtherMouseUp;
|
end = NSOtherMouseUp;
|
||||||
eventMask |= NSOtherMouseUpMask | NSOtherMouseDraggedMask;
|
eventMask |= NSOtherMouseUpMask | NSOtherMouseDraggedMask;
|
||||||
}
|
}
|
||||||
else
|
else if (type == NSLeftMouseDown || type == NSLeftMouseDragged)
|
||||||
{
|
{
|
||||||
end = NSLeftMouseUp;
|
end = NSLeftMouseUp;
|
||||||
eventMask |= NSLeftMouseUpMask | NSLeftMouseDraggedMask;
|
eventMask |= NSLeftMouseUpMask | NSLeftMouseDraggedMask;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NSLog (@"Unexpected event: %d during event tracking in NSMenuView", type);
|
||||||
|
end = NSLeftMouseUp;
|
||||||
|
eventMask |= NSLeftMouseUpMask | NSLeftMouseDraggedMask;
|
||||||
|
}
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
location = [_window mouseLocationOutsideOfEventStream];
|
if (type == NSPeriodic || event == original)
|
||||||
index = [self indexOfItemAtPoint: location];
|
{
|
||||||
|
NSPoint location;
|
||||||
|
int index;
|
||||||
|
|
||||||
|
location = [_window mouseLocationOutsideOfEventStream];
|
||||||
|
index = [self indexOfItemAtPoint: location];
|
||||||
|
|
||||||
if (index != _highlightedItemIndex)
|
// 1 - if menus is only partly visible and the mouse is at the
|
||||||
{
|
// edge of the screen we move the menu so it will be visible.
|
||||||
mouseMoved = YES; /* Ok - had an initial movement. */
|
|
||||||
}
|
|
||||||
if (type == NSPeriodic)
|
|
||||||
{
|
|
||||||
if ([_menu isPartlyOffScreen])
|
if ([_menu isPartlyOffScreen])
|
||||||
{
|
{
|
||||||
NSPoint pointerLoc = [_window convertBaseToScreen: location];
|
NSPoint pointerLoc = [_window convertBaseToScreen: location];
|
||||||
|
// The +/-1 in the y - direction is because the flipping between X-coordinates
|
||||||
// TODO: Why 1 in the Y axis?
|
// and GNUstep coordinates let the GNUstep screen coordinates start with 1.
|
||||||
if (pointerLoc.x == 0 || pointerLoc.y == 1
|
if (pointerLoc.x == 0 || pointerLoc.y == 1
|
||||||
|| pointerLoc.x == [[_window screen] frame].size.width - 1)
|
|| pointerLoc.x == [[_window screen] frame].size.width - 1
|
||||||
|
|| pointerLoc.y == [[_window screen] frame].size.height)
|
||||||
[_menu shiftOnScreen];
|
[_menu shiftOnScreen];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (delayedSelect && mouseMoved && [event type] == NSPeriodic)
|
|
||||||
{
|
|
||||||
float xDiff = location.x - lastLocation.x;
|
|
||||||
|
|
||||||
if (xDiff > xDelta)
|
// 2 - Check if we have to reset the justAttachedNewSubmenu flag to NO.
|
||||||
{
|
if (justAttachedNewSubmenu && index != -1 && index != _highlightedItemIndex)
|
||||||
delayCount++;
|
{
|
||||||
if (delayCount >= DELAY_MULTIPLIER)
|
if (location.x - lastLocation.x > MOVE_THRESHOLD_DELTA)
|
||||||
delayedSelect = NO;
|
{
|
||||||
}
|
delayCount ++;
|
||||||
else
|
if (delayCount >= DELAY_MULTIPLIER)
|
||||||
{
|
{
|
||||||
delayedSelect = NO;
|
justAttachedNewSubmenu = NO;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
justAttachedNewSubmenu = NO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
lastLocation = location;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index == -1)
|
// 3 - If we have moved outside this menu, take appropriate action
|
||||||
{
|
if (index == -1)
|
||||||
NSWindow *w;
|
{
|
||||||
|
NSPoint locationInScreenCoordinates;
|
||||||
|
NSWindow *windowUnderMouse;
|
||||||
|
NSMenu *candidateMenu;
|
||||||
|
|
||||||
|
subMenusNeedRemoving = NO;
|
||||||
|
|
||||||
|
locationInScreenCoordinates = [_window convertBaseToScreen: location];
|
||||||
|
|
||||||
location = [_window convertBaseToScreen: location];
|
// 3a - Check if moved into one of the ancester menus.
|
||||||
|
// This is tricky, there are a few possibilities:
|
||||||
|
// We are a transient attached menu of a non-transient menu
|
||||||
|
// We are a non-transient attached menu
|
||||||
|
// We are a root: isTornOff of AppMenu
|
||||||
|
candidateMenu = [_menu supermenu];
|
||||||
|
while (candidateMenu
|
||||||
|
&& !NSMouseInRect (locationInScreenCoordinates, [[candidateMenu window] frame], NO) // not found yet
|
||||||
|
&& (! ([candidateMenu isTornOff] && ![candidateMenu isTransient])) // no root of display tree
|
||||||
|
&& [candidateMenu isAttached]) // has displayed parent
|
||||||
|
{
|
||||||
|
candidateMenu = [candidateMenu supermenu];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (candidateMenu != nil && NSMouseInRect (locationInScreenCoordinates,
|
||||||
|
[[candidateMenu window] frame], NO))
|
||||||
|
{
|
||||||
|
// The call to fetch attachedMenu is not needed. But putting
|
||||||
|
// it here avoids flicker when we go back to an ancester meu
|
||||||
|
// and the attached menu is alreay correct.
|
||||||
|
[[[candidateMenu attachedMenu] menuRepresentation] detachSubmenu];
|
||||||
|
return [[candidateMenu menuRepresentation]
|
||||||
|
trackWithEvent: original];
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
// 3b - Check if we enter the attached submenu
|
||||||
* If the mouse is back in the supermenu, we return NO so that
|
windowUnderMouse = [[_menu attachedMenu] window];
|
||||||
* our caller knows the button was not released.
|
if (windowUnderMouse != nil && NSMouseInRect (locationInScreenCoordinates,
|
||||||
*/
|
[windowUnderMouse frame], NO))
|
||||||
w = [[_menu supermenu] window];
|
{
|
||||||
if (w != nil && NSMouseInRect(location, [w frame], NO) == YES)
|
BOOL wasTransient = [_menu isTransient];
|
||||||
{
|
BOOL subMenuResult = [[self attachedMenuView] trackWithEvent: original];
|
||||||
return NO;
|
|
||||||
}
|
if (subMenuResult && wasTransient == [_menu isTransient])
|
||||||
/*
|
{
|
||||||
* if the mouse is in our attached menu - get that menu to track it.
|
[self detachSubmenu];
|
||||||
*/
|
}
|
||||||
w = [[_menu attachedMenu] window];
|
return subMenuResult;
|
||||||
if (w != nil && NSMouseInRect(location, [w frame], NO) == YES)
|
}
|
||||||
{
|
}
|
||||||
if ([[self attachedMenuView] trackWithEvent: original])
|
|
||||||
return YES;
|
// 4 - We changed the selected item and should update.
|
||||||
}
|
if (!justAttachedNewSubmenu && index != _highlightedItemIndex)
|
||||||
else
|
{
|
||||||
{
|
subMenusNeedRemoving = NO;
|
||||||
if (index != _highlightedItemIndex)
|
[self detachSubmenu];
|
||||||
[self setHighlightedItemIndex: index];
|
[self setHighlightedItemIndex: index];
|
||||||
}
|
|
||||||
#if 0
|
|
||||||
if (([_menu supermenu] && ![_menu isTornOff])
|
|
||||||
|| [_menu isFollowTransient])
|
|
||||||
return NO;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (index != _highlightedItemIndex)
|
|
||||||
{
|
|
||||||
if (![_menu attachedMenu] || !delayedSelect)
|
|
||||||
{
|
|
||||||
[self setHighlightedItemIndex: index];
|
|
||||||
|
|
||||||
if ([_menu attachedMenu])
|
// WO: Question? Why the ivar _items_link
|
||||||
[self detachSubmenu];
|
if (index >= 0 && [[_items_link objectAtIndex: index] submenu])
|
||||||
|
{
|
||||||
if ((alreadyAttachedMenu =
|
[self attachSubmenuForItemAtIndex: index];
|
||||||
[[_items_link objectAtIndex: index] submenu]))
|
justAttachedNewSubmenu = YES;
|
||||||
{
|
delayCount = 0;
|
||||||
[self attachSubmenuForItemAtIndex: index];
|
}
|
||||||
mouseMoved = NO;
|
}
|
||||||
delayedSelect = YES;
|
|
||||||
delayCount = 0;
|
// Update last seen location for the justAttachedNewSubmenu logic.
|
||||||
}
|
lastLocation = location;
|
||||||
else
|
|
||||||
{
|
|
||||||
delayedSelect = NO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
event = [NSApp nextEventMatchingMask: eventMask
|
event = [NSApp nextEventMatchingMask: eventMask
|
||||||
|
@ -1043,71 +1185,147 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
||||||
}
|
}
|
||||||
while (type != end);
|
while (type != end);
|
||||||
|
|
||||||
// Perform actions as needed.
|
|
||||||
if (index != -1 && !alreadyAttachedMenu)
|
|
||||||
{
|
|
||||||
// Stop the periodic events before performing the action
|
|
||||||
[NSEvent stopPeriodicEvents];
|
|
||||||
[_menu performActionForItemAtIndex: index];
|
|
||||||
|
|
||||||
if (![_menu isFollowTransient] && ![_menu _ownedByPopUp])
|
// Ok, we released the mouse
|
||||||
[self setHighlightedItemIndex: -1];
|
// There are now a few possibilities:
|
||||||
|
// A - We released the mouse outside the menu.
|
||||||
|
// Then we want the situation as it was before
|
||||||
|
// we entered everything.
|
||||||
|
// B - We released the mouse on a submenu item
|
||||||
|
// (i) - this was highlighted before we started clicking:
|
||||||
|
// Remove attached menus
|
||||||
|
// (ii) - this was not highlighted before pressed the mouse button;
|
||||||
|
// Keep attached menus.
|
||||||
|
// C - We released the mouse above an ordinary action:
|
||||||
|
// Execute the action.
|
||||||
|
//
|
||||||
|
// In case A, B and C we want the transient menus to be removed
|
||||||
|
// In case A and C we want to remove the menus that were created
|
||||||
|
// during the dragging.
|
||||||
|
|
||||||
|
// So we should do the following things:
|
||||||
|
//
|
||||||
|
// 1 - Stop periodic events,
|
||||||
|
// 2 - Determine the action.
|
||||||
|
// 3 - Remove the Transient menus from the screen.
|
||||||
|
// 4 - Perform the action if there is one.
|
||||||
|
|
||||||
|
[NSEvent stopPeriodicEvents];
|
||||||
|
|
||||||
|
// We need to store this, because _highlightedItemIndex
|
||||||
|
// will not be valid after we removed this menu from the screen.
|
||||||
|
indexOfActionToExecute = _highlightedItemIndex;
|
||||||
|
|
||||||
|
// remove transient menus. --------------------------------------------
|
||||||
|
{
|
||||||
|
NSMenu *currentMenu = _menu;
|
||||||
|
|
||||||
|
while (currentMenu && ![currentMenu isTransient])
|
||||||
|
{
|
||||||
|
currentMenu = [currentMenu attachedMenu];
|
||||||
|
}
|
||||||
|
|
||||||
|
while ([currentMenu isTransient] &&
|
||||||
|
[currentMenu supermenu])
|
||||||
|
{
|
||||||
|
currentMenu = [currentMenu supermenu];
|
||||||
|
}
|
||||||
|
|
||||||
|
[[currentMenu menuRepresentation] detachSubmenu];
|
||||||
|
|
||||||
|
if ([currentMenu isTransient])
|
||||||
|
{
|
||||||
|
[currentMenu closeTransient];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------
|
||||||
|
if (indexOfActionToExecute == -1)
|
||||||
|
{
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (indexOfActionToExecute >= 0
|
||||||
|
&& [_menu attachedMenu] != nil && [_menu attachedMenu] ==
|
||||||
|
[[_items_link objectAtIndex: indexOfActionToExecute] submenu])
|
||||||
|
{
|
||||||
|
if (subMenusNeedRemoving)
|
||||||
|
{
|
||||||
|
[self detachSubmenu];
|
||||||
|
}
|
||||||
|
// Clicked on a submenu.
|
||||||
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close menus if needed.
|
[_menu performActionForItemAtIndex: indexOfActionToExecute];
|
||||||
if (!_keepAttachedMenus || index == -1
|
|
||||||
|| (alreadyAttachedMenu && [alreadyAttachedMenu isFollowTransient]))
|
// Remove highlighting.
|
||||||
|
// We first check if it still highlighted because it could be the
|
||||||
|
// case that we choose an action in a transient window which
|
||||||
|
// has already dissappeared.
|
||||||
|
if (indexOfActionToExecute == _highlightedItemIndex)
|
||||||
{
|
{
|
||||||
NSMenu *parentMenu;
|
[self setHighlightedItemIndex: -1];
|
||||||
NSMenu *masterMenu;
|
|
||||||
|
|
||||||
for (parentMenu = masterMenu = _menu;
|
|
||||||
(parentMenu = [masterMenu supermenu])
|
|
||||||
&& (![masterMenu isTornOff] || [masterMenu isFollowTransient]);
|
|
||||||
masterMenu = parentMenu);
|
|
||||||
|
|
||||||
if ([masterMenu attachedMenu])
|
|
||||||
{
|
|
||||||
NSMenuView *masterMenuView = [masterMenu menuRepresentation];
|
|
||||||
|
|
||||||
[masterMenuView detachSubmenu];
|
|
||||||
[masterMenuView setHighlightedItemIndex: -1];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
This method is called when the user clicks on a button
|
||||||
|
in the menu. Or, if a right click happens and the
|
||||||
|
app menu is brought up.
|
||||||
|
|
||||||
|
The original position is stored, so we can restore
|
||||||
|
the position of menu. The position of the menu
|
||||||
|
can change during the event tracking because
|
||||||
|
the menu will automatillay move when parts
|
||||||
|
are outside the screen and the user move the
|
||||||
|
mouse pointer to the edge of the screen.
|
||||||
|
*/
|
||||||
- (void) mouseDown: (NSEvent*)theEvent
|
- (void) mouseDown: (NSEvent*)theEvent
|
||||||
{
|
{
|
||||||
NSRect currentFrame;
|
NSRect currentFrame;
|
||||||
NSRect originalFrame;
|
NSRect originalFrame;
|
||||||
NSPoint currentTopLeft;
|
NSPoint currentTopLeft;
|
||||||
NSPoint originalTopLeft;
|
NSPoint originalTopLeft;
|
||||||
|
BOOL restorePosition;
|
||||||
|
/*
|
||||||
|
* Only for non transient menus do we want
|
||||||
|
* to remember the position.
|
||||||
|
*/
|
||||||
|
restorePosition = ![_menu isTransient];
|
||||||
|
|
||||||
_keepAttachedMenus = YES;
|
if (restorePosition)
|
||||||
originalFrame = [_window frame];
|
{ // store old position;
|
||||||
originalTopLeft = originalFrame.origin;
|
originalFrame = [_window frame];
|
||||||
originalTopLeft.y += originalFrame.size.height;
|
originalTopLeft = originalFrame.origin;
|
||||||
|
originalTopLeft.y += originalFrame.size.height;
|
||||||
|
}
|
||||||
|
|
||||||
[NSEvent startPeriodicEventsAfterDelay: 0.1 withPeriod: 0.05];
|
[NSEvent startPeriodicEventsAfterDelay: 0.1 withPeriod: 0.05];
|
||||||
[self trackWithEvent: theEvent];
|
[self trackWithEvent: theEvent];
|
||||||
[NSEvent stopPeriodicEvents];
|
[NSEvent stopPeriodicEvents];
|
||||||
|
|
||||||
currentFrame = [_window frame];
|
if (restorePosition)
|
||||||
currentTopLeft = currentFrame.origin;
|
|
||||||
currentTopLeft.y += currentFrame.size.height;
|
|
||||||
|
|
||||||
if (NSEqualPoints(currentTopLeft, originalTopLeft) == NO)
|
|
||||||
{
|
{
|
||||||
NSPoint origin = currentFrame.origin;
|
currentFrame = [_window frame];
|
||||||
|
currentTopLeft = currentFrame.origin;
|
||||||
|
currentTopLeft.y += currentFrame.size.height;
|
||||||
|
|
||||||
origin.x += (originalTopLeft.x - currentTopLeft.x);
|
if (NSEqualPoints(currentTopLeft, originalTopLeft) == NO)
|
||||||
origin.y += (originalTopLeft.y - currentTopLeft.y);
|
{
|
||||||
[_menu nestedSetFrameOrigin: origin];
|
NSPoint origin = currentFrame.origin;
|
||||||
[_menu nestedCheckOffScreen];
|
|
||||||
|
origin.x += (originalTopLeft.x - currentTopLeft.x);
|
||||||
|
origin.y += (originalTopLeft.y - currentTopLeft.y);
|
||||||
|
[_menu nestedSetFrameOrigin: origin];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_keepAttachedMenus = NO;
|
}
|
||||||
|
|
||||||
|
- (void) rightMouseDown: (NSEvent*) theEvent
|
||||||
|
{
|
||||||
|
[self mouseDown: theEvent];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL) performKeyEquivalent: (NSEvent *)theEvent
|
- (BOOL) performKeyEquivalent: (NSEvent *)theEvent
|
||||||
|
@ -1115,6 +1333,7 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
||||||
return [_menu performKeyEquivalent: theEvent];
|
return [_menu performKeyEquivalent: theEvent];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* NSCoding Protocol
|
* NSCoding Protocol
|
||||||
*
|
*
|
||||||
|
@ -1155,8 +1374,165 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation NSMenuView (GNUstepPrivate)
|
@implementation NSMenuView (GNUstepPrivate)
|
||||||
|
|
||||||
- (NSArray *)_itemCells
|
- (NSArray *)_itemCells
|
||||||
{
|
{
|
||||||
return _itemCells;
|
return _itemCells;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@implementation NSMenuWindowTitleView
|
||||||
|
|
||||||
|
- (BOOL) acceptsFirstMouse: (NSEvent *)theEvent
|
||||||
|
{
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) setMenu: (NSMenu*)aMenu
|
||||||
|
{
|
||||||
|
menu = aMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSMenu*) menu
|
||||||
|
{
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) drawRect: (NSRect)rect
|
||||||
|
{
|
||||||
|
NSRect workRect = [self bounds];
|
||||||
|
NSRectEdge sides[] = {NSMinXEdge, NSMaxYEdge}; float grays[] = {NSDarkGray, NSDarkGray};
|
||||||
|
/* Cache the title attributes */
|
||||||
|
static NSDictionary *attr = nil;
|
||||||
|
|
||||||
|
// Draw the dark gray upper left lines.
|
||||||
|
workRect = NSDrawTiledRects(workRect, workRect, sides, grays, 2);
|
||||||
|
|
||||||
|
// Draw the title box's button.
|
||||||
|
NSDrawButton(workRect, workRect);
|
||||||
|
|
||||||
|
// Paint it Black!
|
||||||
|
workRect.origin.x += 1;
|
||||||
|
workRect.origin.y += 2;
|
||||||
|
workRect.size.height -= 3;
|
||||||
|
workRect.size.width -= 3;
|
||||||
|
[[NSColor windowFrameColor] set];
|
||||||
|
NSRectFill(workRect);
|
||||||
|
|
||||||
|
// Draw the title
|
||||||
|
if (attr == nil)
|
||||||
|
{
|
||||||
|
attr = [[NSDictionary alloc]
|
||||||
|
initWithObjectsAndKeys:
|
||||||
|
[NSFont boldSystemFontOfSize: 0], NSFontAttributeName,
|
||||||
|
[NSColor windowFrameTextColor], NSForegroundColorAttributeName,
|
||||||
|
nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
// This gives the correct position
|
||||||
|
// WO: No it doesn't!
|
||||||
|
workRect.origin.x += 5;
|
||||||
|
workRect.size.width -= 5;
|
||||||
|
workRect.size.height -= 2;
|
||||||
|
[[menu title] drawInRect: workRect withAttributes: attr];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) mouseDown: (NSEvent*)theEvent
|
||||||
|
{
|
||||||
|
NSPoint lastLocation;
|
||||||
|
NSPoint location;
|
||||||
|
unsigned eventMask = NSLeftMouseUpMask | NSLeftMouseDraggedMask;
|
||||||
|
BOOL done = NO;
|
||||||
|
NSDate *theDistantFuture = [NSDate distantFuture];
|
||||||
|
|
||||||
|
NSDebugLLog (@"NSMenu", @"Mouse down in title!");
|
||||||
|
|
||||||
|
lastLocation = [theEvent locationInWindow];
|
||||||
|
|
||||||
|
if (![menu isTornOff] && [menu supermenu])
|
||||||
|
{
|
||||||
|
[menu setTornOff: YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!done)
|
||||||
|
{
|
||||||
|
theEvent = [NSApp nextEventMatchingMask: eventMask
|
||||||
|
untilDate: theDistantFuture
|
||||||
|
inMode: NSEventTrackingRunLoopMode
|
||||||
|
dequeue: YES];
|
||||||
|
|
||||||
|
switch ([theEvent type])
|
||||||
|
{
|
||||||
|
case NSRightMouseUp:
|
||||||
|
case NSLeftMouseUp:
|
||||||
|
done = YES;
|
||||||
|
break;
|
||||||
|
case NSRightMouseDragged:
|
||||||
|
case NSLeftMouseDragged:
|
||||||
|
location = [_window mouseLocationOutsideOfEventStream];
|
||||||
|
if (NSEqualPoints(location, lastLocation) == NO)
|
||||||
|
{
|
||||||
|
NSPoint origin = [_window frame].origin;
|
||||||
|
|
||||||
|
origin.x += (location.x - lastLocation.x);
|
||||||
|
origin.y += (location.y - lastLocation.y);
|
||||||
|
[menu nestedSetFrameOrigin: origin];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) createButton
|
||||||
|
{
|
||||||
|
// create the menu's close button
|
||||||
|
NSImage* closeImage = [NSImage imageNamed: @"common_Close"];
|
||||||
|
NSImage* closeHImage = [NSImage imageNamed: @"common_CloseH"];
|
||||||
|
NSSize imageSize = [closeImage size];
|
||||||
|
NSRect rect = { { _frame.size.width - imageSize.width - 4,
|
||||||
|
(_frame.size.height - imageSize.height) / 2},
|
||||||
|
{ imageSize.height, imageSize.width } };
|
||||||
|
|
||||||
|
button = [[NSButton alloc] initWithFrame: rect];
|
||||||
|
[button setButtonType: NSMomentaryLight];
|
||||||
|
[button setImagePosition: NSImageOnly];
|
||||||
|
[button setImage: closeImage];
|
||||||
|
[button setAlternateImage: closeHImage];
|
||||||
|
[button setBordered: NO];
|
||||||
|
[button setTarget: menu];
|
||||||
|
[button setAction: @selector(_performMenuClose:)];
|
||||||
|
[button setAutoresizingMask: NSViewMinXMargin];
|
||||||
|
|
||||||
|
[self setAutoresizingMask: NSViewMinXMargin | NSViewMinYMargin | NSViewMaxYMargin];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) removeCloseButton
|
||||||
|
{
|
||||||
|
[button removeFromSuperview];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) addCloseButton
|
||||||
|
{
|
||||||
|
if (button == nil)
|
||||||
|
[self createButton];
|
||||||
|
[self addSubview: button];
|
||||||
|
[self setNeedsDisplay: YES];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
- (void) rightMouseDown: (NSEvent*)theEvent
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// We do not want to popup menus in this menu.
|
||||||
|
- (id) menuForEvent: (NSEvent*) theEvent
|
||||||
|
{
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end /* NSMenuWindowTitleView */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue