mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-04-23 15:11:37 +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
836df5adbe
commit
00b16254bb
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>
|
||||
|
||||
* Source/NSLayoutManager.m (-drawGlyphsForGlyphRange:atPoint:):
|
||||
|
|
|
@ -37,18 +37,339 @@
|
|||
@class NSString;
|
||||
@class NSEvent;
|
||||
@class NSMatrix;
|
||||
@class NSMenuView;
|
||||
@class NSPopUpButton;
|
||||
@class NSPopUpButtonCell;
|
||||
@class NSView;
|
||||
@class NSWindow;
|
||||
@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>
|
||||
{
|
||||
NSString *_title;
|
||||
NSMutableArray *_items;
|
||||
NSMenuView *_view;
|
||||
NSView<NSMenuView>* _view;
|
||||
NSMenu *_superMenu;
|
||||
NSMenu *_attachedMenu;
|
||||
NSMutableArray *_notifications;
|
||||
|
@ -59,21 +380,25 @@
|
|||
|
||||
// GNUstepExtra category
|
||||
NSPopUpButtonCell *_popUpButtonCell;
|
||||
BOOL _follow_transient;
|
||||
BOOL _isPartlyOffScreen;
|
||||
BOOL _transient;
|
||||
|
||||
@private
|
||||
NSWindow *_aWindow;
|
||||
NSWindow *_bWindow;
|
||||
id _titleView;
|
||||
NSMenu *_oldAttachedMenu;
|
||||
int _oldHiglightedIndex;
|
||||
}
|
||||
|
||||
/* Controlling Allocation Zones */
|
||||
+ (void) setMenuZone: (NSZone*)zone;
|
||||
+ (NSZone*) menuZone;
|
||||
|
||||
/* Creating an NSMenu */
|
||||
|
||||
/**
|
||||
*
|
||||
*<init/>
|
||||
*/
|
||||
|
||||
- (id) initWithTitle: (NSString*)aTitle;
|
||||
|
||||
/* Setting Up the Menu Commands */
|
||||
|
@ -92,6 +417,9 @@
|
|||
- (void) removeItemAtIndex: (int)index;
|
||||
|
||||
/* Finding menu items */
|
||||
/**
|
||||
* Returns an array containing all menu items in this menu.
|
||||
*/
|
||||
- (NSArray*) itemArray;
|
||||
- (id <NSMenuItem>) itemAtIndex: (int)index;
|
||||
- (id <NSMenuItem>) itemWithTag: (int)aTag;
|
||||
|
@ -110,11 +438,52 @@
|
|||
/* Managing submenus */
|
||||
- (void) setSubmenu: (NSMenu*)aMenu forItem: (id <NSMenuItem>)anItem;
|
||||
- (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;
|
||||
/**
|
||||
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;
|
||||
/**
|
||||
If there are two instances of this menu visible, return NO.
|
||||
Otherwise, return YES if we are a detached menu and visible.
|
||||
*/
|
||||
- (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;
|
||||
/**
|
||||
Returns the supermenu of this menu. Return nil
|
||||
if this is the application menu.
|
||||
*/
|
||||
- (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;
|
||||
|
||||
/* Enabling and disabling menu items */
|
||||
|
@ -128,12 +497,34 @@
|
|||
/* Simulating Mouse Clicks */
|
||||
- (void) performActionForItemAtIndex: (int)index;
|
||||
|
||||
/* Setting the Title */
|
||||
/**
|
||||
Change the title of the menu.
|
||||
*/
|
||||
- (void) setTitle: (NSString*)aTitle;
|
||||
/**
|
||||
Returns the current 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;
|
||||
|
||||
/* Updating Menu Layout */
|
||||
|
@ -143,6 +534,7 @@
|
|||
|
||||
/* Displaying Context-Sensitive Help */
|
||||
- (void) helpRequested: (NSEvent*)event;
|
||||
|
||||
+ (void) popUpContextMenu: (NSMenu*)menu
|
||||
withEvent: (NSEvent*)event
|
||||
forView: (NSView*)view;
|
||||
|
@ -175,25 +567,76 @@
|
|||
- (BOOL) validateMenuItem: (NSMenuItem*)aMenuItem;
|
||||
@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)
|
||||
- (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;
|
||||
|
||||
/* 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;
|
||||
/**
|
||||
Display the transient version of the menu.
|
||||
*/
|
||||
- (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) 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 */
|
||||
- (void) nestedSetFrameOrigin: (NSPoint)aPoint;
|
||||
|
||||
/* Shift partly off-screen menus */
|
||||
- (BOOL) isPartlyOffScreen;
|
||||
- (void) nestedCheckOffScreen;
|
||||
- (BOOL) isPartlyOffScreen;
|
||||
- (void) shiftOnScreen;
|
||||
|
||||
/* Popup behaviour */
|
||||
|
|
|
@ -41,11 +41,39 @@
|
|||
#include <AppKit/NSView.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 NSPopUpButton;
|
||||
@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;
|
||||
BOOL _horizontal;
|
||||
|
@ -62,9 +90,8 @@
|
|||
NSSize _cellSize;
|
||||
@private
|
||||
id _items_link;
|
||||
BOOL _keepAttachedMenus;
|
||||
int _oldHighlightedItemIndex;
|
||||
int _leftBorderOffset;
|
||||
id _titleView;
|
||||
}
|
||||
|
||||
+ (float)menuBarHeight;
|
||||
|
@ -84,14 +111,14 @@
|
|||
- (NSMenuView *)attachedMenuView;
|
||||
- (NSMenu *)attachedMenu;
|
||||
- (BOOL)isAttached;
|
||||
- (void) detachSubmenu;
|
||||
- (void) attachSubmenuForItemAtIndex: (int) index;
|
||||
- (BOOL)isTornOff;
|
||||
- (void)setHorizontalEdgePadding:(float)pad;
|
||||
- (float)horizontalEdgePadding;
|
||||
- (void)itemChanged:(NSNotification *)notification;
|
||||
- (void)itemAdded:(NSNotification *)notification;
|
||||
- (void)itemRemoved:(NSNotification *)notification;
|
||||
- (void)detachSubmenu;
|
||||
- (void)attachSubmenuForItemAtIndex:(int)index;
|
||||
- (void)update;
|
||||
- (void)setNeedsSizing:(BOOL)flag;
|
||||
- (BOOL)needsSizing;
|
||||
|
@ -116,4 +143,5 @@
|
|||
- (BOOL)trackWithEvent:(NSEvent *)event;
|
||||
@end
|
||||
|
||||
|
||||
#endif
|
||||
|
|
596
Source/NSMenu.m
596
Source/NSMenu.m
|
@ -55,39 +55,78 @@
|
|||
#include <AppKit/NSScreen.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 */
|
||||
@interface NSMenuPanel : NSPanel
|
||||
@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)
|
||||
- (NSArray *)_itemCells;
|
||||
@end
|
||||
|
||||
|
||||
static NSZone *menuZone = NULL;
|
||||
static NSString *NSMenuLocationsKey = @"NSMenuLocations";
|
||||
static NSString *NSMenuLocationsKey = @"NSMenuLocations";
|
||||
static NSString *NSEnqueuedMenuMoveName = @"EnqueuedMoveNotificationName";
|
||||
static NSNotificationCenter *nc;
|
||||
|
||||
@interface NSMenu (GNUstepPrivate)
|
||||
|
||||
- (NSString *) _locationKey;
|
||||
- (NSMenuPanel *) _createWindow;
|
||||
- (void) _updateUserDefaults: (id) notification;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation NSMenuPanel
|
||||
- (BOOL) canBecomeKeyWindow
|
||||
{
|
||||
|
@ -139,6 +178,68 @@ static NSNotificationCenter *nc;
|
|||
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
|
||||
|
||||
|
||||
|
@ -168,7 +269,7 @@ static NSNotificationCenter *nc;
|
|||
}
|
||||
|
||||
/*
|
||||
* Initializing a New NSMenu
|
||||
*
|
||||
*/
|
||||
- (id) init
|
||||
{
|
||||
|
@ -185,14 +286,15 @@ static NSNotificationCenter *nc;
|
|||
RELEASE(_view);
|
||||
RELEASE(_aWindow);
|
||||
RELEASE(_bWindow);
|
||||
RELEASE(_titleView);
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
*/
|
||||
- (id) initWithTitle: (NSString*)aTitle
|
||||
{
|
||||
float height;
|
||||
NSView *contentView;
|
||||
|
||||
[super init];
|
||||
|
@ -203,19 +305,12 @@ static NSNotificationCenter *nc;
|
|||
// Create an array to store out menu items.
|
||||
_items = [[NSMutableArray alloc] init];
|
||||
|
||||
// We have no supermenu.
|
||||
// _superMenu = nil;
|
||||
// _is_tornoff = NO;
|
||||
// _follow_transient = NO;
|
||||
|
||||
_changedMessagesEnabled = YES;
|
||||
_notifications = [[NSMutableArray alloc] init];
|
||||
_changed = YES;
|
||||
// According to the spec, menus do autoenable by default.
|
||||
_autoenable = YES;
|
||||
|
||||
// Transient windows private stuff.
|
||||
// _oldAttachedMenu = nil;
|
||||
|
||||
/* Please note that we own all this menu network of objects. So,
|
||||
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 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 addSubview: _view];
|
||||
[contentView addSubview: _titleView];
|
||||
|
||||
/* Set up the notification to start the process of redisplaying
|
||||
the menus where the user left them the last time.
|
||||
|
@ -258,6 +346,16 @@ static NSNotificationCenter *nc;
|
|||
name: NSApplicationWillBecomeActiveNotification
|
||||
object: NSApp];
|
||||
|
||||
[nc addObserver: self
|
||||
selector: @selector (_menuMoved:)
|
||||
name: NSWindowDidMoveNotification
|
||||
object: _aWindow];
|
||||
|
||||
[nc addObserver: self
|
||||
selector: @selector (_updateUserDefaults:)
|
||||
name: NSEnqueuedMenuMoveName
|
||||
object: self];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
@ -558,18 +656,34 @@ static NSNotificationCenter *nc;
|
|||
|
||||
- (void) submenuAction: (id)sender
|
||||
{
|
||||
[_view detachSubmenu];
|
||||
}
|
||||
|
||||
|
||||
- (NSMenu *) attachedMenu
|
||||
{
|
||||
if (_attachedMenu && _follow_transient
|
||||
&& !_attachedMenu->_follow_transient)
|
||||
if (_attachedMenu && _transient
|
||||
&& !_attachedMenu->_transient)
|
||||
return nil;
|
||||
|
||||
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
|
||||
{
|
||||
return _superMenu && [_superMenu attachedMenu] == self;
|
||||
|
@ -835,6 +949,9 @@ static NSNotificationCenter *nc;
|
|||
//
|
||||
// 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
|
||||
{
|
||||
if (_changedMessagesEnabled != flag)
|
||||
|
@ -869,39 +986,25 @@ static NSNotificationCenter *nc;
|
|||
NSRect menuFrame;
|
||||
NSSize size;
|
||||
|
||||
//if ([_view needsSizing])
|
||||
[_view sizeToFit];
|
||||
[_view sizeToFit];
|
||||
|
||||
menuFrame = [_view frame];
|
||||
size = menuFrame.size;
|
||||
|
||||
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)
|
||||
{
|
||||
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)];
|
||||
[_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];
|
||||
|
||||
|
@ -1013,23 +1116,22 @@ static NSNotificationCenter *nc;
|
|||
#define IS_OFFSCREEN(WINDOW) \
|
||||
!(NSContainsRect([[NSScreen mainScreen] frame], [WINDOW frame]))
|
||||
|
||||
- (void) _setTornOff: (BOOL)flag
|
||||
- (void) setTornOff: (BOOL)flag
|
||||
{
|
||||
NSMenu *supermenu;
|
||||
|
||||
_is_tornoff = flag;
|
||||
_is_tornoff = flag;
|
||||
|
||||
if (flag)
|
||||
[_titleView addCloseButton];
|
||||
else
|
||||
[_titleView releaseCloseButton];
|
||||
|
||||
supermenu = [self supermenu];
|
||||
if (supermenu != nil)
|
||||
{
|
||||
[[supermenu menuRepresentation] setHighlightedItemIndex: -1];
|
||||
supermenu->_attachedMenu = nil;
|
||||
supermenu = [self supermenu];
|
||||
if (supermenu != nil)
|
||||
{
|
||||
[[supermenu menuRepresentation] setHighlightedItemIndex: -1];
|
||||
supermenu->_attachedMenu = nil;
|
||||
}
|
||||
}
|
||||
[_view update];
|
||||
}
|
||||
|
||||
- (void) _showTornOffMenuIfAny: (NSNotification*)notification
|
||||
|
@ -1051,7 +1153,7 @@ static NSNotificationCenter *nc;
|
|||
location = [menuLocations objectForKey: key];
|
||||
if (location && [location isKindOfClass: [NSString class]])
|
||||
{
|
||||
[self _setTornOff: YES];
|
||||
[self setTornOff: YES];
|
||||
[self display];
|
||||
}
|
||||
}
|
||||
|
@ -1068,52 +1170,25 @@ static NSNotificationCenter *nc;
|
|||
}
|
||||
}
|
||||
|
||||
- (BOOL) isFollowTransient
|
||||
- (BOOL) isTransient
|
||||
{
|
||||
return _follow_transient;
|
||||
return _transient;
|
||||
}
|
||||
|
||||
- (BOOL) isPartlyOffScreen
|
||||
{
|
||||
return _isPartlyOffScreen;
|
||||
}
|
||||
|
||||
- (void) nestedCheckOffScreen
|
||||
{
|
||||
// This method is used when the menu is moved.
|
||||
if (_attachedMenu)
|
||||
[_attachedMenu nestedCheckOffScreen];
|
||||
|
||||
_isPartlyOffScreen = IS_OFFSCREEN(_aWindow);
|
||||
return IS_OFFSCREEN ([self window]);
|
||||
}
|
||||
|
||||
- (void) _performMenuClose: (id)sender
|
||||
{
|
||||
NSString *key;
|
||||
|
||||
if (_attachedMenu)
|
||||
[_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];
|
||||
[self _setTornOff: NO];
|
||||
[self close];
|
||||
[self setTornOff: NO];
|
||||
[self _updateUserDefaults: nil];
|
||||
}
|
||||
|
||||
- (void) _rightMouseDisplay: (NSEvent*)theEvent
|
||||
|
@ -1125,6 +1200,11 @@ static NSNotificationCenter *nc;
|
|||
|
||||
- (void) display
|
||||
{
|
||||
if (_transient)
|
||||
{
|
||||
NSDebugLLog (@"NSMenu", @"trying to display while alreay displayed transient");
|
||||
}
|
||||
|
||||
if (_changed)
|
||||
[self sizeToFit];
|
||||
|
||||
|
@ -1142,10 +1222,7 @@ static NSNotificationCenter *nc;
|
|||
[self setGeometry];
|
||||
}
|
||||
}
|
||||
|
||||
[_aWindow orderFrontRegardless];
|
||||
|
||||
_isPartlyOffScreen = IS_OFFSCREEN(_aWindow);
|
||||
}
|
||||
|
||||
- (void) displayTransient
|
||||
|
@ -1153,8 +1230,15 @@ static NSNotificationCenter *nc;
|
|||
NSPoint location;
|
||||
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.
|
||||
* Otherwise, raise menu under the mouse.
|
||||
|
@ -1180,20 +1264,14 @@ static NSNotificationCenter *nc;
|
|||
[_bWindow setFrameOrigin: location];
|
||||
|
||||
[_view removeFromSuperviewWithoutNeedingDisplay];
|
||||
[_titleView removeFromSuperviewWithoutNeedingDisplay];
|
||||
|
||||
if (_is_tornoff)
|
||||
[_titleView releaseCloseButton];
|
||||
|
||||
contentView = [_bWindow contentView];
|
||||
[contentView addSubview: _view];
|
||||
[contentView addSubview: _titleView];
|
||||
|
||||
[_view update];
|
||||
[_bWindow orderFront: self];
|
||||
|
||||
_isPartlyOffScreen = IS_OFFSCREEN(_bWindow);
|
||||
}
|
||||
|
||||
|
||||
- (void) setGeometry
|
||||
{
|
||||
NSString *key;
|
||||
|
@ -1232,6 +1310,12 @@ static NSNotificationCenter *nc;
|
|||
{
|
||||
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
|
||||
* 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 setFrameOrigin: NSMakePoint (0, 0)];
|
||||
|
||||
if (_superMenu)
|
||||
_superMenu->_attachedMenu = nil;
|
||||
if (_superMenu && ![self isTornOff])
|
||||
{
|
||||
_superMenu->_attachedMenu = nil;
|
||||
[[_superMenu menuRepresentation] setHighlightedItemIndex: -1];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) closeTransient
|
||||
{
|
||||
NSView *contentView;
|
||||
|
||||
if (_transient == NO)
|
||||
{
|
||||
NSDebugLLog (@"NSMenu", @"Closing transient: %@ while it is NOT transient now", _title);
|
||||
return;
|
||||
}
|
||||
|
||||
[_bWindow orderOut: self];
|
||||
[_view removeFromSuperviewWithoutNeedingDisplay];
|
||||
[_titleView removeFromSuperviewWithoutNeedingDisplay];
|
||||
|
||||
contentView = [_aWindow contentView];
|
||||
[contentView addSubview: _view];
|
||||
|
||||
if (_is_tornoff)
|
||||
[_titleView addCloseButton];
|
||||
|
||||
[contentView addSubview: _titleView];
|
||||
[contentView setNeedsDisplay: YES];
|
||||
[contentView setNeedsDisplay: YES];
|
||||
|
||||
// Restore the old submenu (if any).
|
||||
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
|
||||
{
|
||||
if (_follow_transient)
|
||||
if (_transient)
|
||||
return (NSWindow *)_bWindow;
|
||||
else
|
||||
return (NSWindow *)_aWindow;
|
||||
|
@ -1290,7 +1383,7 @@ static NSNotificationCenter *nc;
|
|||
*/
|
||||
- (void) nestedSetFrameOrigin: (NSPoint) aPoint
|
||||
{
|
||||
NSWindow *theWindow = _follow_transient ? _bWindow : _aWindow;
|
||||
NSWindow *theWindow = [self window];
|
||||
|
||||
// Move ourself and get our width.
|
||||
[theWindow setFrameOrigin: aPoint];
|
||||
|
@ -1307,39 +1400,37 @@ static NSNotificationCenter *nc;
|
|||
|
||||
- (void) shiftOnScreen
|
||||
{
|
||||
NSWindow *theWindow = _follow_transient ? _bWindow : _aWindow;
|
||||
NSWindow *theWindow = _transient ? _bWindow : _aWindow;
|
||||
NSRect frameRect = [theWindow frame];
|
||||
NSRect screenRect = [[NSScreen mainScreen] frame];
|
||||
NSPoint vector = {0.0, 0.0};
|
||||
BOOL moveIt = YES;
|
||||
|
||||
if (frameRect.origin.y < 0)
|
||||
BOOL moveIt = NO;
|
||||
|
||||
// 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 = SHIFT_DELTA;
|
||||
else
|
||||
vector.y = -frameRect.origin.y;
|
||||
vector.y = MIN (SHIFT_DELTA, -NSMinY (frameRect));
|
||||
moveIt = YES;
|
||||
}
|
||||
else if (frameRect.origin.x < 0)
|
||||
else if (NSMaxY (frameRect) > NSMaxY (screenRect))
|
||||
{
|
||||
if (frameRect.origin.x + SHIFT_DELTA <= 0)
|
||||
vector.x = SHIFT_DELTA;
|
||||
else
|
||||
vector.x = -frameRect.origin.x;
|
||||
vector.y = -MIN (SHIFT_DELTA, NSMaxY (frameRect) - NSMaxY (screenRect));
|
||||
moveIt = YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
vector.x = frameRect.origin.x + frameRect.size.width;
|
||||
vector.x -= [[NSScreen mainScreen] frame].size.width;
|
||||
|
||||
if (vector.x > 0)
|
||||
{
|
||||
if (vector.x - SHIFT_DELTA <= 0)
|
||||
vector.x = -SHIFT_DELTA;
|
||||
else
|
||||
vector.x = -vector.x - 2;
|
||||
}
|
||||
else
|
||||
moveIt = NO;
|
||||
// 2 - determine the amount we need to shift in the x direction.
|
||||
if (NSMinX (frameRect) < 0)
|
||||
{
|
||||
vector.x = MIN (SHIFT_DELTA, -NSMinX (frameRect));
|
||||
moveIt = YES;
|
||||
}
|
||||
// Note the -3. This is done so the menu, after shifting completely
|
||||
// has some spare room on the right hand side. This is needed otherwise
|
||||
// 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)
|
||||
|
@ -1353,7 +1444,7 @@ static NSNotificationCenter *nc;
|
|||
for (candidateMenu = masterMenu = self;
|
||||
(candidateMenu = masterMenu->_superMenu)
|
||||
&& (!masterMenu->_is_tornoff
|
||||
|| masterMenu->_follow_transient);
|
||||
|| masterMenu->_transient);
|
||||
masterMenu = candidateMenu);
|
||||
|
||||
masterLocation = [[masterMenu window] frame].origin;
|
||||
|
@ -1362,8 +1453,6 @@ static NSNotificationCenter *nc;
|
|||
|
||||
[masterMenu nestedSetFrameOrigin: destinationPoint];
|
||||
}
|
||||
else
|
||||
_isPartlyOffScreen = NO;
|
||||
}
|
||||
|
||||
- (BOOL)_ownedByPopUp
|
||||
|
@ -1378,191 +1467,18 @@ static NSNotificationCenter *nc;
|
|||
_popUpButtonCell = popUp;
|
||||
if (popUp != nil)
|
||||
{
|
||||
[_titleView removeFromSuperviewWithoutNeedingDisplay];
|
||||
[_aWindow 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
|
||||
|
||||
|
||||
@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.
|
||||
If not, write to the Free Software Foundation,
|
||||
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
*/
|
||||
|
||||
#include <Foundation/NSRunLoop.h>
|
||||
|
||||
|
@ -34,9 +33,40 @@
|
|||
#include <AppKit/NSEvent.h>
|
||||
#include <AppKit/NSFont.h>
|
||||
#include <AppKit/NSMenuView.h>
|
||||
#include <AppKit/NSMenu.h>
|
||||
#include <AppKit/NSButton.h>
|
||||
#include <AppKit/NSWindow.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
|
||||
|
||||
static NSRect
|
||||
|
@ -80,6 +110,13 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
|||
{
|
||||
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.
|
||||
|
@ -108,6 +145,11 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
|||
// Create an array to store our menu item cells.
|
||||
_itemCells = [NSMutableArray new];
|
||||
|
||||
// Create title view and add it. CHECKME, should we do this here?
|
||||
_titleView = [[NSMenuWindowTitleView alloc] init];
|
||||
|
||||
[self addSubview: _titleView];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
@ -171,6 +213,7 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
|||
name: NSMenuDidRemoveItemNotification
|
||||
object: _menu];
|
||||
|
||||
[_titleView setMenu: _menu]; // WO CHECKME does this needs reorganizing?
|
||||
// Force menu view's layout to be recalculated.
|
||||
[self setNeedsSizing: YES];
|
||||
}
|
||||
|
@ -259,12 +302,7 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
|||
|
||||
- (NSMenuView*) attachedMenuView
|
||||
{
|
||||
NSMenu *attachedMenu;
|
||||
|
||||
if ((attachedMenu = [_menu attachedMenu]))
|
||||
return [attachedMenu menuRepresentation];
|
||||
else
|
||||
return nil;
|
||||
return [[_menu attachedMenu] menuRepresentation];
|
||||
}
|
||||
|
||||
- (NSMenu*) attachedMenu
|
||||
|
@ -366,6 +404,7 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
|||
/*
|
||||
* Working with Submenus.
|
||||
*/
|
||||
|
||||
- (void) detachSubmenu
|
||||
{
|
||||
NSMenu *attachedMenu = [_menu attachedMenu];
|
||||
|
@ -378,17 +417,24 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
|||
|
||||
[attachedMenuView detachSubmenu];
|
||||
|
||||
[attachedMenuView setHighlightedItemIndex: -1];
|
||||
|
||||
if ([attachedMenu isFollowTransient])
|
||||
NSDebugLLog (@"NSMenu", @"detach submenu: %@ from: %@",
|
||||
attachedMenu, _menu);
|
||||
|
||||
if ([attachedMenu isTransient])
|
||||
{
|
||||
[attachedMenu closeTransient];
|
||||
[attachedMenuView setHighlightedItemIndex: _oldHighlightedItemIndex];
|
||||
}
|
||||
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
|
||||
{
|
||||
/*
|
||||
|
@ -397,17 +443,26 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
|||
* 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.
|
||||
*/
|
||||
NSMenu *attachableMenu = [[_items_link objectAtIndex: index] submenu];
|
||||
NSMenu *attachableMenu;
|
||||
|
||||
if ([attachableMenu isTornOff] || [_menu isFollowTransient])
|
||||
if (index < 0)
|
||||
{
|
||||
_oldHighlightedItemIndex = [[attachableMenu menuRepresentation]
|
||||
highlightedItemIndex];
|
||||
return;
|
||||
}
|
||||
|
||||
attachableMenu = [[_items_link objectAtIndex: index] submenu];
|
||||
|
||||
if ([attachableMenu isTornOff] || [_menu isTransient])
|
||||
{
|
||||
NSDebugLLog (@"NSMenu", @"Will open transient: %@", attachableMenu);
|
||||
[attachableMenu displayTransient];
|
||||
[[attachableMenu menuRepresentation] setHighlightedItemIndex: -1];
|
||||
[[attachableMenu menuRepresentation] setHighlightedItemIndex: -1];
|
||||
}
|
||||
else
|
||||
[attachableMenu display];
|
||||
{
|
||||
NSDebugLLog (@"NSMenu", @"Will open normal: %@", attachableMenu);
|
||||
[attachableMenu display];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -417,6 +472,15 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
|||
{
|
||||
[_menu update];
|
||||
|
||||
if ([_menu isTornOff] && ![_menu isTransient])
|
||||
{
|
||||
[_titleView addCloseButton];
|
||||
}
|
||||
else
|
||||
{
|
||||
[_titleView removeCloseButton];
|
||||
}
|
||||
|
||||
if (_needsSizing)
|
||||
[self sizeToFit];
|
||||
}
|
||||
|
@ -527,8 +591,12 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
|||
|
||||
if (_horizontal == NO)
|
||||
{
|
||||
float menuBarHeight = [[self class] menuBarHeight];
|
||||
|
||||
[self setFrameSize: NSMakeSize(_cellSize.width + _leftBorderOffset,
|
||||
(howMany * _cellSize.height))];
|
||||
(howMany * _cellSize.height) + menuBarHeight)];
|
||||
[_titleView setFrame: NSMakeRect (0, howMany * _cellSize.height,
|
||||
NSWidth (_bounds), menuBarHeight)];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -617,7 +685,7 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
|||
*/
|
||||
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;
|
||||
}
|
||||
else
|
||||
|
@ -635,6 +703,11 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
|||
return theRect;
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the index of the item below point.
|
||||
Returns -1 if mouse is not above
|
||||
a menu item.
|
||||
*/
|
||||
- (int) indexOfItemAtPoint: (NSPoint)point
|
||||
{
|
||||
unsigned howMany = [_itemCells count];
|
||||
|
@ -774,7 +847,7 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
|||
// TODO
|
||||
|
||||
// Compute position for popups, if needed
|
||||
if (selectedItemIndex > -1)
|
||||
if (selectedItemIndex != -1)
|
||||
{
|
||||
if (_horizontal == NO)
|
||||
{
|
||||
|
@ -852,7 +925,8 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
|||
NSMenu *candidateMenu = _menu;
|
||||
NSMenuView *targetMenuView;
|
||||
int indexToHighlight = index;
|
||||
|
||||
int oldHighlightedIndex;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
NSMenu *superMenu = [candidateMenu supermenu];
|
||||
|
@ -872,9 +946,7 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
|||
}
|
||||
}
|
||||
|
||||
if ([targetMenuView attachedMenu])
|
||||
[targetMenuView detachSubmenu];
|
||||
|
||||
oldHighlightedIndex = [targetMenuView highlightedItemIndex];
|
||||
[targetMenuView setHighlightedItemIndex: indexToHighlight];
|
||||
|
||||
/* 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])
|
||||
{
|
||||
[targetMenuView setHighlightedItemIndex: -1];
|
||||
[targetMenuView setHighlightedItemIndex: oldHighlightedIndex];
|
||||
}
|
||||
}
|
||||
|
||||
#define MOVE_THRESHOLD_DELTA 2.0
|
||||
#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
|
||||
{
|
||||
unsigned eventMask = NSPeriodicMask;
|
||||
NSDate *theDistantFuture = [NSDate distantFuture];
|
||||
int index;
|
||||
NSPoint location;
|
||||
NSPoint lastLocation = {0,0};
|
||||
NSMenu *alreadyAttachedMenu = NO;
|
||||
BOOL mouseMoved = NO;
|
||||
BOOL delayedSelect = NO;
|
||||
BOOL justAttachedNewSubmenu = NO;
|
||||
BOOL subMenusNeedRemoving = YES;
|
||||
int delayCount = 0;
|
||||
float xDelta = MOVE_THRESHOLD_DELTA;
|
||||
int indexOfActionToExecute = -1;
|
||||
NSEvent *original;
|
||||
NSEventType type = [event type];
|
||||
NSEventType type;
|
||||
NSEventType end;
|
||||
|
||||
|
||||
/*
|
||||
* The original event is unused except to determine whether the method
|
||||
* was invoked in response to a right or left mouse down.
|
||||
|
@ -917,122 +1037,144 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
|||
* submenu.
|
||||
*/
|
||||
original = AUTORELEASE(RETAIN(event));
|
||||
if (type == NSRightMouseDown)
|
||||
|
||||
type = [event type];
|
||||
|
||||
if (type == NSRightMouseDown || type == NSRightMouseDragged)
|
||||
{
|
||||
end = NSRightMouseUp;
|
||||
eventMask |= NSRightMouseUpMask | NSRightMouseDraggedMask;
|
||||
}
|
||||
else if (type == NSOtherMouseDown)
|
||||
else if (type == NSOtherMouseDown || type == NSOtherMouseDragged)
|
||||
{
|
||||
end = NSOtherMouseUp;
|
||||
eventMask |= NSOtherMouseUpMask | NSOtherMouseDraggedMask;
|
||||
}
|
||||
else
|
||||
else if (type == NSLeftMouseDown || type == NSLeftMouseDragged)
|
||||
{
|
||||
end = NSLeftMouseUp;
|
||||
eventMask |= NSLeftMouseUpMask | NSLeftMouseDraggedMask;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
NSLog (@"Unexpected event: %d during event tracking in NSMenuView", type);
|
||||
end = NSLeftMouseUp;
|
||||
eventMask |= NSLeftMouseUpMask | NSLeftMouseDraggedMask;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
location = [_window mouseLocationOutsideOfEventStream];
|
||||
index = [self indexOfItemAtPoint: location];
|
||||
if (type == NSPeriodic || event == original)
|
||||
{
|
||||
NSPoint location;
|
||||
int index;
|
||||
|
||||
location = [_window mouseLocationOutsideOfEventStream];
|
||||
index = [self indexOfItemAtPoint: location];
|
||||
|
||||
if (index != _highlightedItemIndex)
|
||||
{
|
||||
mouseMoved = YES; /* Ok - had an initial movement. */
|
||||
}
|
||||
if (type == NSPeriodic)
|
||||
{
|
||||
// 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.
|
||||
|
||||
if ([_menu isPartlyOffScreen])
|
||||
{
|
||||
NSPoint pointerLoc = [_window convertBaseToScreen: location];
|
||||
|
||||
// TODO: Why 1 in the Y axis?
|
||||
// The +/-1 in the y - direction is because the flipping between X-coordinates
|
||||
// and GNUstep coordinates let the GNUstep screen coordinates start with 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];
|
||||
}
|
||||
|
||||
if (delayedSelect && mouseMoved && [event type] == NSPeriodic)
|
||||
{
|
||||
float xDiff = location.x - lastLocation.x;
|
||||
|
||||
if (xDiff > xDelta)
|
||||
{
|
||||
delayCount++;
|
||||
if (delayCount >= DELAY_MULTIPLIER)
|
||||
delayedSelect = NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
delayedSelect = NO;
|
||||
}
|
||||
// 2 - Check if we have to reset the justAttachedNewSubmenu flag to NO.
|
||||
if (justAttachedNewSubmenu && index != -1 && index != _highlightedItemIndex)
|
||||
{
|
||||
if (location.x - lastLocation.x > MOVE_THRESHOLD_DELTA)
|
||||
{
|
||||
delayCount ++;
|
||||
if (delayCount >= DELAY_MULTIPLIER)
|
||||
{
|
||||
justAttachedNewSubmenu = NO;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
justAttachedNewSubmenu = NO;
|
||||
}
|
||||
}
|
||||
|
||||
lastLocation = location;
|
||||
}
|
||||
}
|
||||
|
||||
if (index == -1)
|
||||
{
|
||||
NSWindow *w;
|
||||
// 3 - If we have moved outside this menu, take appropriate action
|
||||
if (index == -1)
|
||||
{
|
||||
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];
|
||||
}
|
||||
|
||||
/*
|
||||
* If the mouse is back in the supermenu, we return NO so that
|
||||
* our caller knows the button was not released.
|
||||
*/
|
||||
w = [[_menu supermenu] window];
|
||||
if (w != nil && NSMouseInRect(location, [w frame], NO) == YES)
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
/*
|
||||
* if the mouse is in our attached menu - get that menu to track it.
|
||||
*/
|
||||
w = [[_menu attachedMenu] window];
|
||||
if (w != nil && NSMouseInRect(location, [w frame], NO) == YES)
|
||||
{
|
||||
if ([[self attachedMenuView] trackWithEvent: original])
|
||||
return YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (index != _highlightedItemIndex)
|
||||
[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];
|
||||
// 3b - Check if we enter the attached submenu
|
||||
windowUnderMouse = [[_menu attachedMenu] window];
|
||||
if (windowUnderMouse != nil && NSMouseInRect (locationInScreenCoordinates,
|
||||
[windowUnderMouse frame], NO))
|
||||
{
|
||||
BOOL wasTransient = [_menu isTransient];
|
||||
BOOL subMenuResult = [[self attachedMenuView] trackWithEvent: original];
|
||||
|
||||
if (subMenuResult && wasTransient == [_menu isTransient])
|
||||
{
|
||||
[self detachSubmenu];
|
||||
}
|
||||
return subMenuResult;
|
||||
}
|
||||
}
|
||||
|
||||
// 4 - We changed the selected item and should update.
|
||||
if (!justAttachedNewSubmenu && index != _highlightedItemIndex)
|
||||
{
|
||||
subMenusNeedRemoving = NO;
|
||||
[self detachSubmenu];
|
||||
[self setHighlightedItemIndex: index];
|
||||
|
||||
if ([_menu attachedMenu])
|
||||
[self detachSubmenu];
|
||||
|
||||
if ((alreadyAttachedMenu =
|
||||
[[_items_link objectAtIndex: index] submenu]))
|
||||
{
|
||||
[self attachSubmenuForItemAtIndex: index];
|
||||
mouseMoved = NO;
|
||||
delayedSelect = YES;
|
||||
delayCount = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
delayedSelect = NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
// WO: Question? Why the ivar _items_link
|
||||
if (index >= 0 && [[_items_link objectAtIndex: index] submenu])
|
||||
{
|
||||
[self attachSubmenuForItemAtIndex: index];
|
||||
justAttachedNewSubmenu = YES;
|
||||
delayCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Update last seen location for the justAttachedNewSubmenu logic.
|
||||
lastLocation = location;
|
||||
}
|
||||
|
||||
event = [NSApp nextEventMatchingMask: eventMask
|
||||
|
@ -1043,71 +1185,147 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
|||
}
|
||||
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])
|
||||
[self setHighlightedItemIndex: -1];
|
||||
// Ok, we released the mouse
|
||||
// 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.
|
||||
if (!_keepAttachedMenus || index == -1
|
||||
|| (alreadyAttachedMenu && [alreadyAttachedMenu isFollowTransient]))
|
||||
[_menu performActionForItemAtIndex: indexOfActionToExecute];
|
||||
|
||||
// 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;
|
||||
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];
|
||||
}
|
||||
[self setHighlightedItemIndex: -1];
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
NSRect currentFrame;
|
||||
NSRect originalFrame;
|
||||
NSPoint currentTopLeft;
|
||||
NSPoint originalTopLeft;
|
||||
BOOL restorePosition;
|
||||
/*
|
||||
* Only for non transient menus do we want
|
||||
* to remember the position.
|
||||
*/
|
||||
restorePosition = ![_menu isTransient];
|
||||
|
||||
_keepAttachedMenus = YES;
|
||||
originalFrame = [_window frame];
|
||||
originalTopLeft = originalFrame.origin;
|
||||
originalTopLeft.y += originalFrame.size.height;
|
||||
|
||||
if (restorePosition)
|
||||
{ // store old position;
|
||||
originalFrame = [_window frame];
|
||||
originalTopLeft = originalFrame.origin;
|
||||
originalTopLeft.y += originalFrame.size.height;
|
||||
}
|
||||
|
||||
[NSEvent startPeriodicEventsAfterDelay: 0.1 withPeriod: 0.05];
|
||||
[self trackWithEvent: theEvent];
|
||||
[NSEvent stopPeriodicEvents];
|
||||
|
||||
currentFrame = [_window frame];
|
||||
currentTopLeft = currentFrame.origin;
|
||||
currentTopLeft.y += currentFrame.size.height;
|
||||
|
||||
if (NSEqualPoints(currentTopLeft, originalTopLeft) == NO)
|
||||
if (restorePosition)
|
||||
{
|
||||
NSPoint origin = currentFrame.origin;
|
||||
currentFrame = [_window frame];
|
||||
currentTopLeft = currentFrame.origin;
|
||||
currentTopLeft.y += currentFrame.size.height;
|
||||
|
||||
origin.x += (originalTopLeft.x - currentTopLeft.x);
|
||||
origin.y += (originalTopLeft.y - currentTopLeft.y);
|
||||
[_menu nestedSetFrameOrigin: origin];
|
||||
[_menu nestedCheckOffScreen];
|
||||
if (NSEqualPoints(currentTopLeft, originalTopLeft) == NO)
|
||||
{
|
||||
NSPoint origin = currentFrame.origin;
|
||||
|
||||
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
|
||||
|
@ -1115,6 +1333,7 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
|||
return [_menu performKeyEquivalent: theEvent];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* NSCoding Protocol
|
||||
*
|
||||
|
@ -1155,8 +1374,165 @@ _addLeftBorderOffsetToRect(NSRect aRect, BOOL isHorizontal)
|
|||
@end
|
||||
|
||||
@implementation NSMenuView (GNUstepPrivate)
|
||||
|
||||
- (NSArray *)_itemCells
|
||||
{
|
||||
return _itemCells;
|
||||
}
|
||||
|
||||
|
||||
@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…
Reference in a new issue