2010-01-13 20:51:30 +00:00
|
|
|
/** <title>GSThemePanel</title>
|
|
|
|
|
|
|
|
<abstract>Theme management utility</abstract>
|
|
|
|
|
|
|
|
Copyright (C) 2010 Free Software Foundation, Inc.
|
|
|
|
|
|
|
|
Author: Gregory John Casamento <greg.casamento@gmail.com>
|
|
|
|
Date: 2010
|
|
|
|
|
|
|
|
This file is part of the GNU Objective C User interface library.
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU Lesser General Public
|
|
|
|
License as published by the Free Software Foundation; either
|
|
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Lesser General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
|
|
License along with this library; see the file COPYING.LIB.
|
|
|
|
If not, see <http://www.gnu.org/licenses/> or write to the
|
|
|
|
Free Software Foundation, 51 Franklin Street, Fifth Floor,
|
|
|
|
Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
2011-03-04 11:33:22 +00:00
|
|
|
#import <Foundation/NSString.h>
|
|
|
|
#import <Foundation/NSArchiver.h>
|
|
|
|
#import "AppKit/NSMenu.h"
|
2013-07-18 23:04:22 +00:00
|
|
|
#import "AppKit/NSPanel.h"
|
2011-03-04 11:33:22 +00:00
|
|
|
#import "AppKit/NSWindow.h"
|
|
|
|
#import "AppKit/NSMenuView.h"
|
|
|
|
#import "AppKit/NSApplication.h"
|
2023-09-22 22:57:15 +00:00
|
|
|
#import "AppKit/NSImage.h"
|
2010-01-13 20:51:30 +00:00
|
|
|
|
2011-03-04 11:33:22 +00:00
|
|
|
#import "GNUstepGUI/GSTheme.h"
|
|
|
|
#import "GNUstepGUI/GSWindowDecorationView.h"
|
2010-01-13 20:51:30 +00:00
|
|
|
|
2011-03-04 11:33:22 +00:00
|
|
|
#import "NSToolbarFrameworkPrivate.h"
|
2011-09-13 01:45:13 +00:00
|
|
|
#import "GSGuiPrivate.h"
|
2010-01-13 20:51:30 +00:00
|
|
|
|
2013-09-25 05:42:27 +00:00
|
|
|
|
2010-01-13 20:51:30 +00:00
|
|
|
@interface NSWindow (Private)
|
|
|
|
- (GSWindowDecorationView *) windowView;
|
2010-12-27 07:05:38 +00:00
|
|
|
- (void) _setMenu: (NSMenu *)menu;
|
2010-01-13 20:51:30 +00:00
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation NSWindow (Private)
|
|
|
|
- (GSWindowDecorationView *) windowView
|
|
|
|
{
|
|
|
|
return _wv;
|
|
|
|
}
|
2010-12-27 07:05:38 +00:00
|
|
|
|
|
|
|
- (void) _setMenu: (NSMenu *)menu
|
|
|
|
{
|
|
|
|
[super setMenu: menu];
|
|
|
|
}
|
2010-01-13 20:51:30 +00:00
|
|
|
@end
|
|
|
|
|
2023-09-22 22:57:15 +00:00
|
|
|
@interface NSMenu (GNUstepPrivate)
|
|
|
|
|
|
|
|
- (BOOL) _isMain;
|
|
|
|
|
|
|
|
- (void) _organizeMenu;
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
2010-01-13 20:51:30 +00:00
|
|
|
@implementation GSTheme (Menu)
|
|
|
|
- (void) setMenu: (NSMenu *)menu
|
|
|
|
forWindow: (NSWindow *)window
|
|
|
|
{
|
|
|
|
GSWindowDecorationView *wv = [window windowView];
|
2011-10-01 11:17:16 +00:00
|
|
|
|
|
|
|
// protect against stupid calls from updateAllWindowsWithMenu:
|
|
|
|
if ([window menu] == menu)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Prevent recursion
|
|
|
|
[window _setMenu: menu];
|
|
|
|
|
2012-10-26 06:33:45 +00:00
|
|
|
// Remove any possible old menu view
|
|
|
|
[wv removeMenuView];
|
|
|
|
|
2011-10-01 11:17:16 +00:00
|
|
|
//NSLog(@"Adding menu %@ to window %@", menu, window);
|
|
|
|
if (menu != nil)
|
|
|
|
{
|
|
|
|
NSMenuView *menuView = [[NSMenuView alloc] initWithFrame: NSZeroRect];
|
|
|
|
|
|
|
|
[menuView setMenu: menu];
|
|
|
|
[menuView setHorizontal: YES];
|
|
|
|
[menuView setInterfaceStyle: NSWindows95InterfaceStyle];
|
|
|
|
[wv addMenuView: menuView];
|
|
|
|
[menuView sizeToFit];
|
|
|
|
RELEASE(menuView);
|
|
|
|
}
|
2010-01-13 20:51:30 +00:00
|
|
|
}
|
|
|
|
|
2010-05-12 22:47:10 +00:00
|
|
|
- (void) rightMouseDisplay: (NSMenu *)menu
|
|
|
|
forEvent: (NSEvent *)theEvent
|
|
|
|
{
|
|
|
|
NSMenuView *mv = [menu menuRepresentation];
|
|
|
|
if ([mv isHorizontal] == NO)
|
|
|
|
{
|
|
|
|
[menu displayTransient];
|
|
|
|
[mv mouseDown: theEvent];
|
|
|
|
[menu closeTransient];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-20 08:09:08 +00:00
|
|
|
- (void) displayPopUpMenu: (NSMenuView *)mr
|
|
|
|
withCellFrame: (NSRect)cellFrame
|
|
|
|
controlViewWindow: (NSWindow *)cvWin
|
|
|
|
preferredEdge: (NSRectEdge)edge
|
|
|
|
selectedItem: (int)selectedItem
|
2013-09-25 05:42:27 +00:00
|
|
|
{
|
|
|
|
BOOL pe = [[GSTheme theme] doesProcessEventsForPopUpMenu];
|
|
|
|
|
2013-07-18 23:04:22 +00:00
|
|
|
/* Ensure the window responds when run in modal and should
|
|
|
|
* process events. Or revert this if theme has changed.
|
|
|
|
*/
|
2013-09-25 05:42:27 +00:00
|
|
|
if (pe && ![[mr window] worksWhenModal])
|
2013-07-18 23:04:22 +00:00
|
|
|
{
|
|
|
|
[(NSPanel *)[mr window] setWorksWhenModal: YES];
|
|
|
|
}
|
|
|
|
|
2013-09-25 05:42:27 +00:00
|
|
|
if (!pe && [[mr window] worksWhenModal])
|
2013-07-18 23:04:22 +00:00
|
|
|
{
|
|
|
|
[(NSPanel *)[mr window] setWorksWhenModal: NO];
|
|
|
|
}
|
|
|
|
|
2010-05-20 08:09:08 +00:00
|
|
|
// Ask the MenuView to attach the menu to this rect
|
|
|
|
[mr setWindowFrameForAttachingToRect: cellFrame
|
|
|
|
onScreen: [cvWin screen]
|
|
|
|
preferredEdge: edge
|
|
|
|
popUpSelectedItem: selectedItem];
|
|
|
|
|
|
|
|
// Set to be above the main window
|
|
|
|
[cvWin addChildWindow: [mr window] ordered: NSWindowAbove];
|
|
|
|
|
|
|
|
// Last, display the window
|
|
|
|
[[mr window] orderFrontRegardless];
|
|
|
|
}
|
|
|
|
|
2010-01-13 20:51:30 +00:00
|
|
|
- (void) processCommand: (void *)context
|
|
|
|
{
|
|
|
|
// this is only implemented when we handle native menus.
|
|
|
|
// put code in here to handle commands from the native menu structure.
|
|
|
|
}
|
2010-01-15 18:10:04 +00:00
|
|
|
|
|
|
|
- (float) menuHeightForWindow: (NSWindow *)window
|
|
|
|
{
|
|
|
|
return [NSMenuView menuBarHeight];
|
|
|
|
}
|
2010-01-23 23:41:07 +00:00
|
|
|
|
|
|
|
- (void) updateMenu: (NSMenu *)menu forWindow: (NSWindow *)window
|
|
|
|
{
|
2010-12-27 07:05:38 +00:00
|
|
|
[self setMenu: menu
|
|
|
|
forWindow: window];
|
2010-01-23 23:41:07 +00:00
|
|
|
}
|
2010-01-30 03:11:56 +00:00
|
|
|
|
|
|
|
- (void) updateAllWindowsWithMenu: (NSMenu *) menu
|
|
|
|
{
|
2010-12-27 07:05:38 +00:00
|
|
|
NSEnumerator *en = [[NSApp windows] objectEnumerator];
|
|
|
|
id o = nil;
|
|
|
|
|
|
|
|
while ((o = [en nextObject]) != nil)
|
|
|
|
{
|
|
|
|
if([o canBecomeMainWindow])
|
|
|
|
{
|
|
|
|
[self updateMenu: menu forWindow: o];
|
|
|
|
}
|
|
|
|
}
|
2010-01-30 03:11:56 +00:00
|
|
|
}
|
2010-10-29 14:31:43 +00:00
|
|
|
|
|
|
|
- (BOOL) doesProcessEventsForPopUpMenu
|
|
|
|
{
|
|
|
|
return NO; // themes that handle events in a popUpMenu should return YES
|
|
|
|
}
|
|
|
|
|
2011-04-10 20:14:53 +00:00
|
|
|
- (BOOL) menuShouldShowIcon
|
|
|
|
{
|
|
|
|
return YES; // override whether or not to show the icon in the menu.
|
|
|
|
}
|
|
|
|
|
2023-09-20 02:20:12 +00:00
|
|
|
- (NSRect) modifyRect: (NSRect)aRect
|
|
|
|
forMenu: (NSMenu *)aMenu
|
|
|
|
isHorizontal: (BOOL) horizontal;
|
|
|
|
{
|
|
|
|
return aRect;
|
|
|
|
}
|
|
|
|
|
2023-09-22 19:20:23 +00:00
|
|
|
- (CGFloat) proposedTitleWidth: (CGFloat)proposedWidth
|
|
|
|
forMenuView: (NSMenuView *)aMenuView
|
2023-09-20 02:20:12 +00:00
|
|
|
{
|
|
|
|
return proposedWidth;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *) keyForKeyEquivalent: (NSString *)aString
|
|
|
|
{
|
|
|
|
return aString;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *) proposedTitle: (NSString *)title
|
|
|
|
forMenuItem: (NSMenuItem *)menuItem
|
|
|
|
{
|
|
|
|
return title;
|
|
|
|
}
|
|
|
|
|
2023-09-22 22:57:15 +00:00
|
|
|
- (void) organizeMenu: (NSMenu *)menu
|
|
|
|
isHorizontal: (BOOL)horizontal
|
2023-09-20 16:52:01 +00:00
|
|
|
{
|
2023-09-22 22:57:15 +00:00
|
|
|
NSString *infoString = _(@"Info");
|
|
|
|
NSString *servicesString = _(@"Services");
|
|
|
|
int i;
|
2023-09-20 16:52:01 +00:00
|
|
|
|
2023-09-22 22:57:15 +00:00
|
|
|
if ([menu _isMain])
|
|
|
|
{
|
|
|
|
NSString *appTitle;
|
|
|
|
NSMenu *appMenu;
|
|
|
|
id <NSMenuItem> appItem;
|
|
|
|
|
|
|
|
appTitle = [[[NSBundle mainBundle] localizedInfoDictionary]
|
|
|
|
objectForKey: @"ApplicationName"];
|
|
|
|
if (nil == appTitle)
|
|
|
|
{
|
|
|
|
appTitle = [[NSProcessInfo processInfo] processName];
|
|
|
|
}
|
|
|
|
appItem = [menu itemWithTitle: appTitle];
|
|
|
|
appMenu = [appItem submenu];
|
|
|
|
|
|
|
|
if (horizontal == YES)
|
|
|
|
{
|
|
|
|
NSMutableArray *itemsToMove;
|
|
|
|
|
|
|
|
itemsToMove = [NSMutableArray new];
|
|
|
|
|
|
|
|
if (appMenu == nil)
|
|
|
|
{
|
|
|
|
[menu insertItemWithTitle: appTitle
|
|
|
|
action: NULL
|
|
|
|
keyEquivalent: @""
|
|
|
|
atIndex: 0];
|
|
|
|
appItem = [menu itemAtIndex: 0];
|
|
|
|
appMenu = [NSMenu new];
|
|
|
|
[menu setSubmenu: appMenu forItem: appItem];
|
|
|
|
RELEASE(appMenu);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int index = [menu indexOfItem: appItem];
|
|
|
|
|
|
|
|
if (index != 0)
|
|
|
|
{
|
|
|
|
RETAIN (appItem);
|
|
|
|
[menu removeItemAtIndex: index];
|
|
|
|
[menu insertItem: appItem atIndex: 0];
|
|
|
|
RELEASE (appItem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ([[GSTheme theme] menuShouldShowIcon])
|
|
|
|
{
|
|
|
|
NSImage *ti;
|
|
|
|
float bar;
|
|
|
|
ti = [[NSApp applicationIconImage] copy];
|
|
|
|
if (ti == nil)
|
|
|
|
{
|
|
|
|
ti = [[NSImage imageNamed: @"GNUstep"] copy];
|
|
|
|
}
|
|
|
|
[ti setScalesWhenResized: YES];
|
|
|
|
bar = [NSMenuView menuBarHeight] - 4;
|
|
|
|
[ti setSize: NSMakeSize(bar, bar)];
|
|
|
|
[appItem setImage: ti];
|
|
|
|
RELEASE(ti);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Collect all simple items plus "Info" and "Services"
|
|
|
|
for (i = 1; i < [[menu itemArray] count]; i++)
|
|
|
|
{
|
|
|
|
NSMenuItem *anItem = [[menu itemArray] objectAtIndex: i];
|
|
|
|
NSString *title = [anItem title];
|
|
|
|
NSMenu *submenu = [anItem submenu];
|
|
|
|
|
|
|
|
if (submenu == nil)
|
|
|
|
{
|
|
|
|
[itemsToMove addObject: anItem];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// The menu may not be localized, so we have to
|
|
|
|
// check both the English and the local version.
|
|
|
|
if ([title isEqual: @"Info"] ||
|
|
|
|
[title isEqual: @"Services"] ||
|
|
|
|
[title isEqual: infoString] ||
|
|
|
|
[title isEqual: servicesString])
|
|
|
|
{
|
|
|
|
[itemsToMove addObject: anItem];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < [itemsToMove count]; i++)
|
|
|
|
{
|
|
|
|
NSMenuItem *anItem = [itemsToMove objectAtIndex: i];
|
|
|
|
|
|
|
|
[menu removeItem: anItem];
|
|
|
|
[appMenu addItem: anItem];
|
|
|
|
}
|
|
|
|
|
|
|
|
RELEASE(itemsToMove);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[appItem setImage: nil];
|
|
|
|
if (appMenu != nil)
|
|
|
|
{
|
|
|
|
NSArray *array = [NSArray arrayWithArray: [appMenu itemArray]];
|
|
|
|
/*
|
|
|
|
* Everything above the Serives menu goes into the info submenu,
|
|
|
|
* the rest into the main menu.
|
|
|
|
*/
|
|
|
|
int k = [appMenu indexOfItemWithTitle: servicesString];
|
|
|
|
|
|
|
|
// The menu may not be localized, so we have to
|
|
|
|
// check both the English and the local version.
|
|
|
|
if (k == -1)
|
|
|
|
k = [appMenu indexOfItemWithTitle: @"Services"];
|
|
|
|
|
|
|
|
if ((k > 0) && ([[array objectAtIndex: k - 1] isSeparatorItem]))
|
|
|
|
k--;
|
|
|
|
|
|
|
|
if (k == 1)
|
|
|
|
{
|
|
|
|
// Exactly one info item
|
|
|
|
NSMenuItem *anItem = [array objectAtIndex: 0];
|
|
|
|
|
|
|
|
[appMenu removeItem: anItem];
|
|
|
|
[menu insertItem: anItem atIndex: 0];
|
|
|
|
}
|
|
|
|
else if (k > 1)
|
|
|
|
{
|
|
|
|
id <NSMenuItem> infoItem;
|
|
|
|
NSMenu *infoMenu;
|
|
|
|
|
|
|
|
// Multiple info items, add a submenu for them
|
|
|
|
[menu insertItemWithTitle: infoString
|
|
|
|
action: NULL
|
|
|
|
keyEquivalent: @""
|
|
|
|
atIndex: 0];
|
|
|
|
infoItem = [menu itemAtIndex: 0];
|
|
|
|
infoMenu = [NSMenu new];
|
|
|
|
[menu setSubmenu: infoMenu forItem: infoItem];
|
|
|
|
RELEASE(infoMenu);
|
|
|
|
|
|
|
|
for (i = 0; i < k; i++)
|
|
|
|
{
|
|
|
|
NSMenuItem *anItem = [array objectAtIndex: i];
|
|
|
|
|
|
|
|
[appMenu removeItem: anItem];
|
|
|
|
[infoMenu addItem: anItem];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// No service menu, or it is the first item.
|
|
|
|
// We still look for an info item.
|
|
|
|
NSMenuItem *anItem = [array objectAtIndex: 0];
|
|
|
|
NSString *title = [anItem title];
|
|
|
|
|
|
|
|
// The menu may not be localized, so we have to
|
|
|
|
// check both the English and the local version.
|
|
|
|
if ([title isEqual: @"Info"] ||
|
|
|
|
[title isEqual: infoString])
|
|
|
|
{
|
|
|
|
[appMenu removeItem: anItem];
|
|
|
|
[menu insertItem: anItem atIndex: 0];
|
|
|
|
k = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
k = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy the remaining entries.
|
|
|
|
for (i = k; i < [array count]; i++)
|
|
|
|
{
|
|
|
|
NSMenuItem *anItem = [array objectAtIndex: i];
|
|
|
|
|
|
|
|
[appMenu removeItem: anItem];
|
|
|
|
[menu addItem: anItem];
|
|
|
|
}
|
|
|
|
|
|
|
|
[menu removeItem: appItem];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// recurse over all submenus
|
|
|
|
for (i = 0; i < [[menu itemArray] count]; i++)
|
|
|
|
{
|
|
|
|
NSMenuItem *anItem = [[menu itemArray] objectAtIndex: i];
|
|
|
|
NSMenu *submenu = [anItem submenu];
|
|
|
|
|
|
|
|
if (submenu != nil)
|
|
|
|
{
|
|
|
|
if ([submenu isTransient])
|
|
|
|
{
|
|
|
|
[submenu closeTransient];
|
|
|
|
}
|
|
|
|
[submenu close];
|
|
|
|
[submenu _organizeMenu];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[[menu menuRepresentation] update];
|
|
|
|
[menu sizeToFit];
|
2023-09-20 16:52:01 +00:00
|
|
|
}
|
2024-10-27 21:02:20 +00:00
|
|
|
|
|
|
|
- (BOOL) proposedVisibility: (BOOL)visible
|
|
|
|
forMenu: (NSMenu *) menu
|
|
|
|
{
|
|
|
|
return visible;
|
|
|
|
}
|
2010-01-13 20:51:30 +00:00
|
|
|
@end
|
|
|
|
|