|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "mozilla/dom/Event.h" |
|
7 #include "nsMenuUtilsX.h" |
|
8 #include "nsMenuBarX.h" |
|
9 #include "nsMenuX.h" |
|
10 #include "nsMenuItemX.h" |
|
11 #include "nsStandaloneNativeMenu.h" |
|
12 #include "nsObjCExceptions.h" |
|
13 #include "nsCocoaUtils.h" |
|
14 #include "nsCocoaWindow.h" |
|
15 #include "nsGkAtoms.h" |
|
16 #include "nsIDocument.h" |
|
17 #include "nsIDOMDocument.h" |
|
18 #include "nsIDOMXULCommandEvent.h" |
|
19 #include "nsPIDOMWindow.h" |
|
20 |
|
21 using namespace mozilla; |
|
22 |
|
23 void nsMenuUtilsX::DispatchCommandTo(nsIContent* aTargetContent) |
|
24 { |
|
25 NS_PRECONDITION(aTargetContent, "null ptr"); |
|
26 |
|
27 nsIDocument* doc = aTargetContent->OwnerDoc(); |
|
28 if (doc) { |
|
29 ErrorResult rv; |
|
30 nsRefPtr<dom::Event> event = |
|
31 doc->CreateEvent(NS_LITERAL_STRING("xulcommandevent"), rv); |
|
32 nsCOMPtr<nsIDOMXULCommandEvent> command = do_QueryObject(event); |
|
33 |
|
34 // FIXME: Should probably figure out how to init this with the actual |
|
35 // pressed keys, but this is a big old edge case anyway. -dwh |
|
36 if (command && |
|
37 NS_SUCCEEDED(command->InitCommandEvent(NS_LITERAL_STRING("command"), |
|
38 true, true, |
|
39 doc->GetWindow(), 0, |
|
40 false, false, false, |
|
41 false, nullptr))) { |
|
42 event->SetTrusted(true); |
|
43 bool dummy; |
|
44 aTargetContent->DispatchEvent(event, &dummy); |
|
45 } |
|
46 } |
|
47 } |
|
48 |
|
49 NSString* nsMenuUtilsX::GetTruncatedCocoaLabel(const nsString& itemLabel) |
|
50 { |
|
51 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; |
|
52 |
|
53 // We want to truncate long strings to some reasonable pixel length but there is no |
|
54 // good API for doing that which works for all OS versions and architectures. For now |
|
55 // we'll do nothing for consistency and depend on good user interface design to limit |
|
56 // string lengths. |
|
57 return [NSString stringWithCharacters:reinterpret_cast<const unichar*>(itemLabel.get()) |
|
58 length:itemLabel.Length()]; |
|
59 |
|
60 NS_OBJC_END_TRY_ABORT_BLOCK_NIL; |
|
61 } |
|
62 |
|
63 uint8_t nsMenuUtilsX::GeckoModifiersForNodeAttribute(const nsString& modifiersAttribute) |
|
64 { |
|
65 uint8_t modifiers = knsMenuItemNoModifier; |
|
66 char* str = ToNewCString(modifiersAttribute); |
|
67 char* newStr; |
|
68 char* token = strtok_r(str, ", \t", &newStr); |
|
69 while (token != NULL) { |
|
70 if (strcmp(token, "shift") == 0) |
|
71 modifiers |= knsMenuItemShiftModifier; |
|
72 else if (strcmp(token, "alt") == 0) |
|
73 modifiers |= knsMenuItemAltModifier; |
|
74 else if (strcmp(token, "control") == 0) |
|
75 modifiers |= knsMenuItemControlModifier; |
|
76 else if ((strcmp(token, "accel") == 0) || |
|
77 (strcmp(token, "meta") == 0)) { |
|
78 modifiers |= knsMenuItemCommandModifier; |
|
79 } |
|
80 token = strtok_r(newStr, ", \t", &newStr); |
|
81 } |
|
82 free(str); |
|
83 |
|
84 return modifiers; |
|
85 } |
|
86 |
|
87 unsigned int nsMenuUtilsX::MacModifiersForGeckoModifiers(uint8_t geckoModifiers) |
|
88 { |
|
89 unsigned int macModifiers = 0; |
|
90 |
|
91 if (geckoModifiers & knsMenuItemShiftModifier) |
|
92 macModifiers |= NSShiftKeyMask; |
|
93 if (geckoModifiers & knsMenuItemAltModifier) |
|
94 macModifiers |= NSAlternateKeyMask; |
|
95 if (geckoModifiers & knsMenuItemControlModifier) |
|
96 macModifiers |= NSControlKeyMask; |
|
97 if (geckoModifiers & knsMenuItemCommandModifier) |
|
98 macModifiers |= NSCommandKeyMask; |
|
99 |
|
100 return macModifiers; |
|
101 } |
|
102 |
|
103 nsMenuBarX* nsMenuUtilsX::GetHiddenWindowMenuBar() |
|
104 { |
|
105 nsIWidget* hiddenWindowWidgetNoCOMPtr = nsCocoaUtils::GetHiddenWindowWidget(); |
|
106 if (hiddenWindowWidgetNoCOMPtr) |
|
107 return static_cast<nsCocoaWindow*>(hiddenWindowWidgetNoCOMPtr)->GetMenuBar(); |
|
108 else |
|
109 return nullptr; |
|
110 } |
|
111 |
|
112 // It would be nice if we could localize these edit menu names. |
|
113 NSMenuItem* nsMenuUtilsX::GetStandardEditMenuItem() |
|
114 { |
|
115 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; |
|
116 |
|
117 // In principle we should be able to allocate this once and then always |
|
118 // return the same object. But wierd interactions happen between native |
|
119 // app-modal dialogs and Gecko-modal dialogs that open above them. So what |
|
120 // we return here isn't always released before it needs to be added to |
|
121 // another menu. See bmo bug 468393. |
|
122 NSMenuItem* standardEditMenuItem = |
|
123 [[[NSMenuItem alloc] initWithTitle:@"Edit" action:nil keyEquivalent:@""] autorelease]; |
|
124 NSMenu* standardEditMenu = [[NSMenu alloc] initWithTitle:@"Edit"]; |
|
125 [standardEditMenuItem setSubmenu:standardEditMenu]; |
|
126 [standardEditMenu release]; |
|
127 |
|
128 // Add Undo |
|
129 NSMenuItem* undoItem = [[NSMenuItem alloc] initWithTitle:@"Undo" action:@selector(undo:) keyEquivalent:@"z"]; |
|
130 [standardEditMenu addItem:undoItem]; |
|
131 [undoItem release]; |
|
132 |
|
133 // Add Redo |
|
134 NSMenuItem* redoItem = [[NSMenuItem alloc] initWithTitle:@"Redo" action:@selector(redo:) keyEquivalent:@"Z"]; |
|
135 [standardEditMenu addItem:redoItem]; |
|
136 [redoItem release]; |
|
137 |
|
138 // Add separator |
|
139 [standardEditMenu addItem:[NSMenuItem separatorItem]]; |
|
140 |
|
141 // Add Cut |
|
142 NSMenuItem* cutItem = [[NSMenuItem alloc] initWithTitle:@"Cut" action:@selector(cut:) keyEquivalent:@"x"]; |
|
143 [standardEditMenu addItem:cutItem]; |
|
144 [cutItem release]; |
|
145 |
|
146 // Add Copy |
|
147 NSMenuItem* copyItem = [[NSMenuItem alloc] initWithTitle:@"Copy" action:@selector(copy:) keyEquivalent:@"c"]; |
|
148 [standardEditMenu addItem:copyItem]; |
|
149 [copyItem release]; |
|
150 |
|
151 // Add Paste |
|
152 NSMenuItem* pasteItem = [[NSMenuItem alloc] initWithTitle:@"Paste" action:@selector(paste:) keyEquivalent:@"v"]; |
|
153 [standardEditMenu addItem:pasteItem]; |
|
154 [pasteItem release]; |
|
155 |
|
156 // Add Delete |
|
157 NSMenuItem* deleteItem = [[NSMenuItem alloc] initWithTitle:@"Delete" action:@selector(delete:) keyEquivalent:@""]; |
|
158 [standardEditMenu addItem:deleteItem]; |
|
159 [deleteItem release]; |
|
160 |
|
161 // Add Select All |
|
162 NSMenuItem* selectAllItem = [[NSMenuItem alloc] initWithTitle:@"Select All" action:@selector(selectAll:) keyEquivalent:@"a"]; |
|
163 [standardEditMenu addItem:selectAllItem]; |
|
164 [selectAllItem release]; |
|
165 |
|
166 return standardEditMenuItem; |
|
167 |
|
168 NS_OBJC_END_TRY_ABORT_BLOCK_NIL; |
|
169 } |
|
170 |
|
171 bool nsMenuUtilsX::NodeIsHiddenOrCollapsed(nsIContent* inContent) |
|
172 { |
|
173 return (inContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden, |
|
174 nsGkAtoms::_true, eCaseMatters) || |
|
175 inContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::collapsed, |
|
176 nsGkAtoms::_true, eCaseMatters)); |
|
177 } |
|
178 |
|
179 // Determines how many items are visible among the siblings in a menu that are |
|
180 // before the given child. This will not count the application menu. |
|
181 int nsMenuUtilsX::CalculateNativeInsertionPoint(nsMenuObjectX* aParent, |
|
182 nsMenuObjectX* aChild) |
|
183 { |
|
184 int insertionPoint = 0; |
|
185 nsMenuObjectTypeX parentType = aParent->MenuObjectType(); |
|
186 if (parentType == eMenuBarObjectType) { |
|
187 nsMenuBarX* menubarParent = static_cast<nsMenuBarX*>(aParent); |
|
188 uint32_t numMenus = menubarParent->GetMenuCount(); |
|
189 for (uint32_t i = 0; i < numMenus; i++) { |
|
190 nsMenuX* currMenu = menubarParent->GetMenuAt(i); |
|
191 if (currMenu == aChild) |
|
192 return insertionPoint; // we found ourselves, break out |
|
193 if (currMenu && [currMenu->NativeMenuItem() menu]) |
|
194 insertionPoint++; |
|
195 } |
|
196 } |
|
197 else if (parentType == eSubmenuObjectType || |
|
198 parentType == eStandaloneNativeMenuObjectType) { |
|
199 nsMenuX* menuParent; |
|
200 if (parentType == eSubmenuObjectType) |
|
201 menuParent = static_cast<nsMenuX*>(aParent); |
|
202 else |
|
203 menuParent = static_cast<nsStandaloneNativeMenu*>(aParent)->GetMenuXObject(); |
|
204 |
|
205 uint32_t numItems = menuParent->GetItemCount(); |
|
206 for (uint32_t i = 0; i < numItems; i++) { |
|
207 // Using GetItemAt instead of GetVisibleItemAt to avoid O(N^2) |
|
208 nsMenuObjectX* currItem = menuParent->GetItemAt(i); |
|
209 if (currItem == aChild) |
|
210 return insertionPoint; // we found ourselves, break out |
|
211 NSMenuItem* nativeItem = nil; |
|
212 nsMenuObjectTypeX currItemType = currItem->MenuObjectType(); |
|
213 if (currItemType == eSubmenuObjectType) |
|
214 nativeItem = static_cast<nsMenuX*>(currItem)->NativeMenuItem(); |
|
215 else |
|
216 nativeItem = (NSMenuItem*)(currItem->NativeData()); |
|
217 if ([nativeItem menu]) |
|
218 insertionPoint++; |
|
219 } |
|
220 } |
|
221 return insertionPoint; |
|
222 } |