~ubuntu-branches/ubuntu/precise/kompozer/precise

« back to all changes in this revision

Viewing changes to mozilla/widget/src/cocoa/nsMenuBarX.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Anthony Yarusso
  • Date: 2007-08-27 01:11:03 UTC
  • Revision ID: james.westby@ubuntu.com-20070827011103-2jgf4s6532gqu2ka
Tags: upstream-0.7.10
ImportĀ upstreamĀ versionĀ 0.7.10

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 
2
/* ***** BEGIN LICENSE BLOCK *****
 
3
 * Version: NPL 1.1/GPL 2.0/LGPL 2.1
 
4
 *
 
5
 * The contents of this file are subject to the Netscape Public License
 
6
 * Version 1.1 (the "License"); you may not use this file except in
 
7
 * compliance with the License. You may obtain a copy of the License at
 
8
 * http://www.mozilla.org/NPL/
 
9
 *
 
10
 * Software distributed under the License is distributed on an "AS IS" basis,
 
11
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 
12
 * for the specific language governing rights and limitations under the
 
13
 * License.
 
14
 *
 
15
 * The Original Code is mozilla.org code.
 
16
 *
 
17
 * The Initial Developer of the Original Code is 
 
18
 * Netscape Communications Corporation.
 
19
 * Portions created by the Initial Developer are Copyright (C) 1998
 
20
 * the Initial Developer. All Rights Reserved.
 
21
 *
 
22
 * Contributor(s):
 
23
 *
 
24
 * Alternatively, the contents of this file may be used under the terms of
 
25
 * either the GNU General Public License Version 2 or later (the "GPL"), or 
 
26
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 
27
 * in which case the provisions of the GPL or the LGPL are applicable instead
 
28
 * of those above. If you wish to allow use of your version of this file only
 
29
 * under the terms of either the GPL or the LGPL, and not to allow others to
 
30
 * use your version of this file under the terms of the NPL, indicate your
 
31
 * decision by deleting the provisions above and replace them with the notice
 
32
 * and other provisions required by the GPL or the LGPL. If you do not delete
 
33
 * the provisions above, a recipient may use your version of this file under
 
34
 * the terms of any one of the NPL, the GPL or the LGPL.
 
35
 *
 
36
 * ***** END LICENSE BLOCK ***** */
 
37
 
 
38
#include "nsCOMPtr.h"
 
39
#include "nsIServiceManager.h"
 
40
#include "nsIComponentManager.h"
 
41
#include "nsINameSpaceManager.h"
 
42
#include "nsIMenu.h"
 
43
#include "nsIMenuItem.h"
 
44
#include "nsIContent.h"
 
45
 
 
46
#include "nsMenuBarX.h"
 
47
#include "nsMenuX.h"
 
48
 
 
49
#include "nsISupports.h"
 
50
#include "nsIWidget.h"
 
51
#include "nsString.h"
 
52
#include "nsIStringBundle.h"
 
53
#include "nsIDocument.h"
 
54
#include "nsIDocShell.h"
 
55
#include "nsIDocumentViewer.h"
 
56
#include "nsIDocumentObserver.h"
 
57
 
 
58
#include "nsIDOMDocument.h"
 
59
#include "nsWidgetAtoms.h"
 
60
 
 
61
#include <Menus.h>
 
62
#include <TextUtils.h>
 
63
#include <Balloons.h>
 
64
#include <Resources.h>
 
65
#include <Appearance.h>
 
66
#include <Gestalt.h>
 
67
#include "nsMacResources.h"
 
68
 
 
69
#include "nsGUIEvent.h"
 
70
 
 
71
// CIDs
 
72
#include "nsWidgetsCID.h"
 
73
static NS_DEFINE_CID(kMenuCID, NS_MENU_CID);
 
74
 
 
75
NS_IMPL_ISUPPORTS6(nsMenuBarX, nsIMenuBar, nsIMenuListener, nsIDocumentObserver, 
 
76
                    nsIChangeManager, nsIMenuCommandDispatcher, nsISupportsWeakReference)
 
77
 
 
78
MenuRef nsMenuBarX::sAppleMenu = nsnull;
 
79
EventHandlerUPP nsMenuBarX::sCommandEventHandler = nsnull;
 
80
 
 
81
 
 
82
//
 
83
// nsMenuBarX constructor
 
84
//
 
85
nsMenuBarX::nsMenuBarX()
 
86
  : mCurrentCommandID(1),
 
87
    mNumMenus(0),
 
88
    mParent(nsnull),
 
89
    mIsMenuBarAdded(PR_FALSE),
 
90
    mDocument(nsnull)
 
91
{
 
92
  OSStatus status = ::CreateNewMenu(0, 0, &mRootMenu);
 
93
  NS_ASSERTION(status == noErr, "nsMenuBarX::nsMenuBarX:  creation of root menu failed.");
 
94
  
 
95
  // create our global carbon event command handler shared by all windows
 
96
  if ( !sCommandEventHandler )
 
97
    sCommandEventHandler = ::NewEventHandlerUPP(CommandEventHandler);
 
98
}
 
99
 
 
100
//
 
101
// nsMenuBarX destructor
 
102
//
 
103
nsMenuBarX::~nsMenuBarX()
 
104
{
 
105
  mMenusArray.Clear();    // release all menus
 
106
 
 
107
  // make sure we unregister ourselves as a document observer
 
108
  if ( mDocument ) {
 
109
    nsCOMPtr<nsIDocumentObserver> observer ( do_QueryInterface(NS_STATIC_CAST(nsIMenuBar*,this)) );
 
110
    mDocument->RemoveObserver(observer);
 
111
  }
 
112
 
 
113
  if ( mRootMenu )
 
114
    ::ReleaseMenu(mRootMenu);
 
115
}
 
116
 
 
117
nsEventStatus 
 
118
nsMenuBarX::MenuItemSelected(const nsMenuEvent & aMenuEvent)
 
119
{
 
120
  // Dispatch menu event
 
121
  nsEventStatus eventStatus = nsEventStatus_eIgnore;
 
122
 
 
123
  PRUint32  numItems;
 
124
  mMenusArray.Count(&numItems);
 
125
  
 
126
  for (PRUint32 i = numItems; i > 0; --i)
 
127
  {
 
128
    nsCOMPtr<nsISupports>     menuSupports = getter_AddRefs(mMenusArray.ElementAt(i - 1));
 
129
    nsCOMPtr<nsIMenuListener> menuListener = do_QueryInterface(menuSupports);
 
130
    if(menuListener)
 
131
    {
 
132
      eventStatus = menuListener->MenuItemSelected(aMenuEvent);
 
133
      if (nsEventStatus_eIgnore != eventStatus)
 
134
        return eventStatus;
 
135
    }
 
136
  }
 
137
  return eventStatus;
 
138
}
 
139
 
 
140
 
 
141
nsEventStatus 
 
142
nsMenuBarX::MenuSelected(const nsMenuEvent & aMenuEvent)
 
143
{
 
144
  // Dispatch event
 
145
  nsEventStatus eventStatus = nsEventStatus_eIgnore;
 
146
 
 
147
  nsCOMPtr<nsIMenuListener> menuListener;
 
148
  //((nsISupports*)mMenuVoidArray[i-1])->QueryInterface(NS_GET_IID(nsIMenuListener), (void**)&menuListener);
 
149
  //printf("gPreviousMenuStack.Count() = %d \n", gPreviousMenuStack.Count());
 
150
#if !TARGET_CARBON
 
151
  nsCOMPtr<nsIMenu> theMenu;
 
152
  gPreviousMenuStack.GetMenuAt(gPreviousMenuStack.Count() - 1, getter_AddRefs(theMenu));
 
153
  menuListener = do_QueryInterface(theMenu);
 
154
#endif
 
155
  if (menuListener) {
 
156
    //TODO: MenuSelected is the right thing to call...
 
157
    //eventStatus = menuListener->MenuSelected(aMenuEvent);
 
158
    eventStatus = menuListener->MenuItemSelected(aMenuEvent);
 
159
    if (nsEventStatus_eIgnore != eventStatus)
 
160
      return eventStatus;
 
161
  } else {
 
162
    // If it's the help menu, gPreviousMenuStack won't be accurate so we need to get the listener a different way 
 
163
    // We'll do it the old fashioned way of looping through and finding it
 
164
    PRUint32  numItems;
 
165
    mMenusArray.Count(&numItems);
 
166
    for (PRUint32 i = numItems; i > 0; --i)
 
167
    {
 
168
      nsCOMPtr<nsISupports>     menuSupports = getter_AddRefs(mMenusArray.ElementAt(i - 1));
 
169
      nsCOMPtr<nsIMenuListener> thisListener = do_QueryInterface(menuSupports);
 
170
            if (thisListener)
 
171
            {
 
172
        //TODO: MenuSelected is the right thing to call...
 
173
            //eventStatus = menuListener->MenuSelected(aMenuEvent);
 
174
            eventStatus = thisListener->MenuItemSelected(aMenuEvent);
 
175
            if(nsEventStatus_eIgnore != eventStatus)
 
176
              return eventStatus;
 
177
      }
 
178
    }
 
179
  }
 
180
  return eventStatus;
 
181
}
 
182
 
 
183
 
 
184
nsEventStatus 
 
185
nsMenuBarX::MenuDeselected(const nsMenuEvent & aMenuEvent)
 
186
{
 
187
  return nsEventStatus_eIgnore;
 
188
}
 
189
 
 
190
nsEventStatus 
 
191
nsMenuBarX::CheckRebuild(PRBool & aNeedsRebuild)
 
192
{
 
193
  aNeedsRebuild = PR_TRUE;
 
194
  return nsEventStatus_eIgnore;
 
195
}
 
196
 
 
197
nsEventStatus
 
198
nsMenuBarX::SetRebuild(PRBool aNeedsRebuild)
 
199
{
 
200
  return nsEventStatus_eIgnore;
 
201
}
 
202
 
 
203
void
 
204
nsMenuBarX :: GetDocument ( nsIWebShell* inWebShell, nsIDocument** outDocument )
 
205
{
 
206
  *outDocument = nsnull;
 
207
  
 
208
  nsCOMPtr<nsIDocShell> docShell ( do_QueryInterface(inWebShell) );
 
209
  nsCOMPtr<nsIContentViewer> cv;
 
210
  if ( docShell ) {
 
211
    docShell->GetContentViewer(getter_AddRefs(cv));
 
212
    if (cv) {
 
213
      // get the document
 
214
      nsCOMPtr<nsIDocumentViewer> docv(do_QueryInterface(cv));
 
215
      if (!docv)
 
216
        return;
 
217
      docv->GetDocument(outDocument);    // addrefs
 
218
    }
 
219
  }
 
220
}
 
221
 
 
222
 
 
223
//
 
224
// RegisterAsDocumentObserver
 
225
//
 
226
// Name says it all.
 
227
//
 
228
void
 
229
nsMenuBarX :: RegisterAsDocumentObserver ( nsIWebShell* inWebShell )
 
230
{
 
231
  nsCOMPtr<nsIDocument> doc;
 
232
  GetDocument(inWebShell, getter_AddRefs(doc));
 
233
  if (!doc)
 
234
    return;
 
235
 
 
236
  // register ourselves
 
237
  nsCOMPtr<nsIDocumentObserver> observer ( do_QueryInterface(NS_STATIC_CAST(nsIMenuBar*,this)) );
 
238
  doc->AddObserver(observer);
 
239
  // also get pointer to doc, just in case webshell goes away
 
240
  // we can still remove ourself as doc observer directly from doc
 
241
  mDocument = doc;
 
242
} // RegisterAsDocumentObesrver
 
243
 
 
244
 
 
245
//
 
246
// AquifyMenuBar
 
247
//
 
248
// Do what's necessary to conform to the Aqua guidelines for menus. Initially, this
 
249
// means removing 'Quit' from the file menu and 'Preferences' from the edit menu, along
 
250
// with their various separators (if present).
 
251
//
 
252
void
 
253
nsMenuBarX :: AquifyMenuBar ( )
 
254
{
 
255
  nsCOMPtr<nsIDOMDocument> domDoc ( do_QueryInterface(mMenuBarContent->GetDocument()) );
 
256
  if ( domDoc ) {
 
257
    // remove quit item and its separator
 
258
    HideItem ( domDoc, NS_LITERAL_STRING("menu_FileQuitSeparator"), nsnull );
 
259
    HideItem ( domDoc, NS_LITERAL_STRING("menu_FileQuitItem"), getter_AddRefs(mQuitItemContent) );
 
260
  
 
261
    // remove prefs item and its separator, but save off the pref content node
 
262
    // so we can invoke its command later.
 
263
    HideItem ( domDoc, NS_LITERAL_STRING("menu_PrefsSeparator"), nsnull );
 
264
    HideItem ( domDoc, NS_LITERAL_STRING("menu_preferences"), getter_AddRefs(mPrefItemContent) );
 
265
  }
 
266
      
 
267
} // AquifyMenuBar
 
268
 
 
269
 
 
270
//
 
271
// InstallCommandEventHandler
 
272
//
 
273
// Grab our window and install an event handler to handle command events which are
 
274
// used to drive the action when the user chooses an item from a menu. We have to install
 
275
// it on the window because the menubar isn't in the event chain for a menu command event.
 
276
//
 
277
OSStatus
 
278
nsMenuBarX :: InstallCommandEventHandler ( )
 
279
{
 
280
  OSStatus err = noErr;
 
281
  
 
282
  WindowRef myWindow = NS_REINTERPRET_CAST(WindowRef, mParent->GetNativeData(NS_NATIVE_DISPLAY));
 
283
  NS_ASSERTION ( myWindow, "Can't get WindowRef to install command handler!" );
 
284
  if ( myWindow && sCommandEventHandler ) {
 
285
    const EventTypeSpec commandEventList[] = { {kEventClassCommand, kEventCommandProcess},
 
286
                                               {kEventClassCommand, kEventCommandUpdateStatus} };
 
287
    err = ::InstallWindowEventHandler ( myWindow, sCommandEventHandler, 2, commandEventList, this, NULL );
 
288
    NS_ASSERTION ( err == noErr, "Uh oh, command handler not installed" );
 
289
  }
 
290
 
 
291
  return err;
 
292
  
 
293
} // InstallCommandEventHandler
 
294
 
 
295
 
 
296
//
 
297
// CommandEventHandler
 
298
//
 
299
// Processes Command carbon events from enabling/selecting of items in the menu.
 
300
//
 
301
pascal OSStatus
 
302
nsMenuBarX :: CommandEventHandler ( EventHandlerCallRef inHandlerChain, EventRef inEvent, void* userData )
 
303
{
 
304
  OSStatus handled = eventNotHandledErr;
 
305
 
 
306
  HICommand command;
 
307
  OSErr err1 = ::GetEventParameter ( inEvent, kEventParamDirectObject, typeHICommand,
 
308
                                        NULL, sizeof(HICommand), NULL, &command );      
 
309
  if ( err1 )
 
310
    return handled;
 
311
    
 
312
  nsMenuBarX* self = NS_REINTERPRET_CAST(nsMenuBarX*, userData);
 
313
  switch ( ::GetEventKind(inEvent) ) {
 
314
    // user selected a menu item. See if it's one we handle.
 
315
    case kEventCommandProcess:
 
316
    {
 
317
      switch ( command.commandID ) {
 
318
        case kHICommandPreferences:
 
319
        {
 
320
          nsEventStatus status = self->ExecuteCommand(self->mPrefItemContent);
 
321
          if ( status == nsEventStatus_eConsumeNoDefault )    // event handled, no other processing
 
322
            handled = noErr;
 
323
          break;
 
324
        }
 
325
        
 
326
        case kHICommandQuit:
 
327
        {
 
328
          nsEventStatus status = self->ExecuteCommand(self->mQuitItemContent);
 
329
          if ( status == nsEventStatus_eConsumeNoDefault )    // event handled, no other processing
 
330
            handled = noErr;
 
331
          break;
 
332
        }
 
333
        
 
334
        case kHICommandAbout:
 
335
        {
 
336
          // the 'about' command is special because we don't have a nsIMenu or nsIMenuItem
 
337
          // for the apple menu. Grovel for the content node with an id of "aboutName" 
 
338
          // and call it directly.
 
339
          nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(self->mDocument);
 
340
                if ( domDoc ) {
 
341
            nsCOMPtr<nsIDOMElement> domElement;
 
342
            domDoc->GetElementById(NS_LITERAL_STRING("aboutName"), getter_AddRefs(domElement));
 
343
            nsCOMPtr<nsIContent> aboutContent ( do_QueryInterface(domElement) );
 
344
            self->ExecuteCommand(aboutContent);
 
345
          }
 
346
          handled = noErr;
 
347
          break;
 
348
        }
 
349
 
 
350
        default:
 
351
        {
 
352
          // given the commandID, look it up in our hashtable and dispatch to
 
353
          // that content node. Recall that we store weak pointers to the content
 
354
          // nodes in the hash table.
 
355
          nsPRUint32Key key ( command.commandID );
 
356
          nsIMenuItem* content = NS_REINTERPRET_CAST(nsIMenuItem*, self->mObserverTable.Get(&key));
 
357
          if ( content )
 
358
            content->DoCommand();
 
359
          handled = noErr;          
 
360
          break; 
 
361
        }        
 
362
 
 
363
      } // switch on commandID
 
364
      break;
 
365
    }
 
366
    
 
367
    // enable/disable menu id's
 
368
    case kEventCommandUpdateStatus:
 
369
    {
 
370
      // only enable the preferences item in the app menu if we found a pref
 
371
      // item DOM node in this menubar.
 
372
      if ( command.commandID == kHICommandPreferences ) {
 
373
        if ( self->mPrefItemContent )
 
374
          ::EnableMenuCommand ( nsnull, kHICommandPreferences );
 
375
        else
 
376
          ::DisableMenuCommand ( nsnull, kHICommandPreferences );
 
377
        handled = noErr;
 
378
      }
 
379
      break;
 
380
    }
 
381
  } // switch on event type
 
382
  
 
383
  return handled;
 
384
  
 
385
} // CommandEventHandler
 
386
 
 
387
 
 
388
//
 
389
// ExecuteCommand
 
390
//
 
391
// Execute the menu item by sending a command message to the 
 
392
// DOM node specified in |inDispatchTo|.
 
393
//
 
394
nsEventStatus
 
395
nsMenuBarX :: ExecuteCommand ( nsIContent* inDispatchTo )
 
396
{
 
397
  nsEventStatus status = nsEventStatus_eIgnore;
 
398
  if ( inDispatchTo ) {
 
399
    nsCOMPtr<nsIWebShell> webShell = do_QueryReferent(mWebShellWeakRef);
 
400
    if (!webShell)
 
401
      return nsEventStatus_eConsumeNoDefault;
 
402
    nsCOMPtr<nsIPresContext> presContext;
 
403
    MenuHelpersX::WebShellToPresContext(webShell, getter_AddRefs(presContext));
 
404
 
 
405
    nsMouseEvent event(NS_XUL_COMMAND);
 
406
 
 
407
    inDispatchTo->HandleDOMEvent(presContext, &event, nsnull, NS_EVENT_FLAG_INIT, &status);
 
408
        }
 
409
        
 
410
        return status;
 
411
} // ExecuteCommand
 
412
 
 
413
 
 
414
//
 
415
// HideItem
 
416
//
 
417
// Hide the item in the menu by setting the 'hidden' attribute. Returns it in |outHiddenNode| so
 
418
// the caller can hang onto it if they so choose. It is acceptable to pass nsull
 
419
// for |outHiddenNode| if the caller doesn't care about the hidden node.
 
420
//
 
421
void
 
422
nsMenuBarX :: HideItem ( nsIDOMDocument* inDoc, const nsAString & inID, nsIContent** outHiddenNode )
 
423
{
 
424
  nsCOMPtr<nsIDOMElement> menuItem;
 
425
  inDoc->GetElementById(inID, getter_AddRefs(menuItem));  
 
426
  nsCOMPtr<nsIContent> menuContent ( do_QueryInterface(menuItem) );
 
427
  if ( menuContent ) {
 
428
    menuContent->SetAttr ( kNameSpaceID_None, nsWidgetAtoms::hidden, NS_LITERAL_STRING("true"), PR_FALSE );
 
429
    if ( outHiddenNode ) {
 
430
      *outHiddenNode = menuContent.get();
 
431
      NS_IF_ADDREF(*outHiddenNode);
 
432
    }
 
433
  }
 
434
 
 
435
} // HideItem
 
436
 
 
437
 
 
438
nsEventStatus
 
439
nsMenuBarX::MenuConstruct( const nsMenuEvent & aMenuEvent, nsIWidget* aParentWindow, 
 
440
                            void * menubarNode, void * aWebShell )
 
441
{
 
442
  mWebShellWeakRef = do_GetWeakReference(NS_STATIC_CAST(nsIWebShell*, aWebShell));
 
443
  nsIDOMNode* aDOMNode  = NS_STATIC_CAST(nsIDOMNode*, menubarNode);
 
444
  mMenuBarContent = do_QueryInterface(aDOMNode);           // strong ref
 
445
  NS_ASSERTION ( mMenuBarContent, "No content specified for this menubar" );
 
446
  if ( !mMenuBarContent )
 
447
    return nsEventStatus_eIgnore;
 
448
    
 
449
  Create(aParentWindow);
 
450
  
 
451
  // if we're on X (using aqua UI guidelines for menus), remove quit and prefs
 
452
  // from our menubar.
 
453
  SInt32 result = 0L;
 
454
  OSStatus err = ::Gestalt ( gestaltMenuMgrAttr, &result );
 
455
  if ( !err && (result & gestaltMenuMgrAquaLayoutMask) )
 
456
    AquifyMenuBar();
 
457
  err = InstallCommandEventHandler();
 
458
  if ( err )
 
459
    return nsEventStatus_eIgnore;
 
460
 
 
461
  nsCOMPtr<nsIWebShell> webShell = do_QueryReferent(mWebShellWeakRef);
 
462
  if (webShell) RegisterAsDocumentObserver(webShell);
 
463
 
 
464
  // set this as a nsMenuListener on aParentWindow
 
465
  aParentWindow->AddMenuListener((nsIMenuListener *)this);
 
466
 
 
467
  PRUint32 count = mMenuBarContent->GetChildCount();
 
468
  for ( PRUint32 i = 0; i < count; ++i ) { 
 
469
    nsIContent *menu = mMenuBarContent->GetChildAt(i);
 
470
    if ( menu ) {
 
471
      if (menu->Tag() == nsWidgetAtoms::menu &&
 
472
          menu->IsContentOfType(nsIContent::eXUL)) {
 
473
        nsAutoString menuName;
 
474
        nsAutoString menuAccessKey(NS_LITERAL_STRING(" "));
 
475
        menu->GetAttr(kNameSpaceID_None, nsWidgetAtoms::label, menuName);
 
476
        menu->GetAttr(kNameSpaceID_None, nsWidgetAtoms::accesskey, menuAccessKey);
 
477
                          
 
478
        // Don't create the whole menu yet, just add in the top level names
 
479
              
 
480
        // Create nsMenu, the menubar will own it
 
481
        nsCOMPtr<nsIMenu> pnsMenu ( do_CreateInstance(kMenuCID) );
 
482
        if ( pnsMenu ) {
 
483
          pnsMenu->Create(NS_STATIC_CAST(nsIMenuBar*, this), menuName, menuAccessKey, 
 
484
                          NS_STATIC_CAST(nsIChangeManager *, this), 
 
485
                          NS_REINTERPRET_CAST(nsIWebShell*, aWebShell), menu);
 
486
 
 
487
          // Make nsMenu a child of nsMenuBar. nsMenuBar takes ownership
 
488
          AddMenu(pnsMenu); 
 
489
                  
 
490
          nsAutoString menuIDstring;
 
491
          menu->GetAttr(kNameSpaceID_None, nsWidgetAtoms::id, menuIDstring);
 
492
          if ( menuIDstring == NS_LITERAL_STRING("menu_Help") ) {
 
493
            nsMenuEvent event;
 
494
            MenuHandle handle = nsnull;
 
495
#if !TARGET_CARBON
 
496
            ::HMGetHelpMenuHandle(&handle);
 
497
#endif
 
498
            event.mCommand = (unsigned int) handle;
 
499
            nsCOMPtr<nsIMenuListener> listener(do_QueryInterface(pnsMenu));
 
500
            listener->MenuSelected(event);
 
501
          }          
 
502
        }
 
503
      } 
 
504
    }
 
505
  } // for each menu
 
506
 
 
507
  // Give the aParentWindow this nsMenuBarX to hold onto.
 
508
  // The parent takes ownership
 
509
  aParentWindow->SetMenuBar(this);
 
510
 
 
511
  return nsEventStatus_eIgnore;
 
512
}
 
513
 
 
514
 
 
515
nsEventStatus 
 
516
nsMenuBarX::MenuDestruct(const nsMenuEvent & aMenuEvent)
 
517
{
 
518
  return nsEventStatus_eIgnore;
 
519
}
 
520
 
 
521
 
 
522
//-------------------------------------------------------------------------
 
523
//
 
524
// Create the proper widget
 
525
//
 
526
//-------------------------------------------------------------------------
 
527
NS_METHOD nsMenuBarX::Create(nsIWidget *aParent)
 
528
{
 
529
  SetParent(aParent);
 
530
  return NS_OK;
 
531
}
 
532
 
 
533
//-------------------------------------------------------------------------
 
534
NS_METHOD nsMenuBarX::GetParent(nsIWidget *&aParent)
 
535
{
 
536
  NS_IF_ADDREF(aParent = mParent);
 
537
  return NS_OK;
 
538
}
 
539
 
 
540
 
 
541
//-------------------------------------------------------------------------
 
542
NS_METHOD nsMenuBarX::SetParent(nsIWidget *aParent)
 
543
{
 
544
  mParent = aParent;    // weak ref  
 
545
  return NS_OK;
 
546
}
 
547
 
 
548
//-------------------------------------------------------------------------
 
549
NS_METHOD nsMenuBarX::AddMenu(nsIMenu * aMenu)
 
550
{
 
551
  // keep track of all added menus.
 
552
  mMenusArray.AppendElement(aMenu);    // owner
 
553
 
 
554
  if (mNumMenus == 0) {
 
555
    // if apple menu hasn't been created, create it.
 
556
    if ( !sAppleMenu ) {
 
557
      nsresult rv = CreateAppleMenu(aMenu);
 
558
      NS_ASSERTION ( NS_SUCCEEDED(rv), "Can't create Apple menu" );
 
559
    }
 
560
    
 
561
    // add shared Apple menu to our menubar
 
562
    if ( sAppleMenu ) {
 
563
      // InsertMenuItem() is 1-based, so the apple/application menu needs to
 
564
      // be at index 1. |mNumMenus| will be incremented below, so the following menu (File)
 
565
      // won't overwrite the apple menu by reusing the ID.
 
566
      mNumMenus = 1;
 
567
      ::InsertMenuItem(mRootMenu, "\pA", mNumMenus);
 
568
      ::SetMenuItemHierarchicalMenu(mRootMenu, 1, sAppleMenu);
 
569
    }
 
570
  }
 
571
 
 
572
  MenuRef menuRef = nsnull;
 
573
  aMenu->GetNativeData((void**)&menuRef);
 
574
 
 
575
  PRBool helpMenu;
 
576
  aMenu->IsHelpMenu(&helpMenu);
 
577
  if(!helpMenu) {
 
578
    nsCOMPtr<nsIContent> menu;
 
579
    aMenu->GetMenuContent(getter_AddRefs(menu));
 
580
    nsAutoString menuHidden;
 
581
    menu->GetAttr(kNameSpaceID_None, nsWidgetAtoms::hidden, menuHidden);
 
582
    if( menuHidden != NS_LITERAL_STRING("true")) {
 
583
        // make sure we only increment |mNumMenus| if the menu is visible, since
 
584
        // we use it as an index of where to insert the next menu.
 
585
      mNumMenus++;
 
586
      
 
587
      ::InsertMenuItem(mRootMenu, "\pPlaceholder", mNumMenus);
 
588
      OSStatus status = ::SetMenuItemHierarchicalMenu(mRootMenu, mNumMenus, menuRef);
 
589
      NS_ASSERTION(status == noErr, "nsMenuBarX::AddMenu: SetMenuItemHierarchicalMenu failed.");
 
590
    }
 
591
  }
 
592
 
 
593
  return NS_OK;
 
594
}
 
595
 
 
596
 
 
597
//
 
598
// CreateAppleMenu
 
599
//
 
600
// build the Apple menu shared by all menu bars.
 
601
//
 
602
nsresult
 
603
nsMenuBarX :: CreateAppleMenu ( nsIMenu* inMenu )
 
604
{
 
605
  Str32 menuStr = { 1, kMenuAppleLogoFilledGlyph };
 
606
  OSStatus s = ::CreateNewMenu(kAppleMenuID, 0, &sAppleMenu);
 
607
 
 
608
  if ( s == noErr && sAppleMenu )  {
 
609
    ::SetMenuTitle(sAppleMenu, menuStr);
 
610
    
 
611
    // this code reads the "label" attribute from the <menuitem/> with
 
612
    // id="aboutName" and puts its label in the Apple Menu
 
613
    nsAutoString label;
 
614
    nsCOMPtr<nsIContent> menu;
 
615
    inMenu->GetMenuContent(getter_AddRefs(menu));
 
616
    if (menu) {
 
617
      nsCOMPtr<nsIDocument> doc = menu->GetDocument();
 
618
      if (doc) {
 
619
        nsCOMPtr<nsIDOMDocument> domdoc ( do_QueryInterface(doc) );
 
620
        if ( domdoc ) {
 
621
          nsCOMPtr<nsIDOMElement> aboutMenuItem;
 
622
          domdoc->GetElementById(NS_LITERAL_STRING("aboutName"), getter_AddRefs(aboutMenuItem));
 
623
          if (aboutMenuItem)
 
624
            aboutMenuItem->GetAttribute(NS_LITERAL_STRING("label"), label);
 
625
        }
 
626
      }
 
627
    }
 
628
 
 
629
    CFStringRef labelRef = ::CFStringCreateWithCharacters(kCFAllocatorDefault, (UniChar*)label.get(), label.Length());
 
630
    if ( labelRef ) {
 
631
      ::InsertMenuItemTextWithCFString(sAppleMenu, labelRef, 1, 0, 0);
 
632
      ::CFRelease(labelRef);
 
633
    }
 
634
    
 
635
    ::SetMenuItemCommandID(sAppleMenu, 1, kHICommandAbout);
 
636
 
 
637
    ::AppendMenu(sAppleMenu, "\p-");
 
638
  }
 
639
 
 
640
  return (s == noErr && sAppleMenu) ? NS_OK : NS_ERROR_FAILURE;
 
641
}
 
642
 
 
643
        
 
644
//-------------------------------------------------------------------------
 
645
NS_METHOD nsMenuBarX::GetMenuCount(PRUint32 &aCount)
 
646
{
 
647
  aCount = mNumMenus;
 
648
  return NS_OK;
 
649
}
 
650
 
 
651
//-------------------------------------------------------------------------
 
652
NS_METHOD nsMenuBarX::GetMenuAt(const PRUint32 aCount, nsIMenu *& aMenu)
 
653
 
654
  aMenu = NULL;
 
655
  nsCOMPtr<nsISupports> supports = getter_AddRefs(mMenusArray.ElementAt(aCount));
 
656
  if (!supports) return NS_OK;
 
657
  
 
658
  return CallQueryInterface(supports, &aMenu); // addref
 
659
}
 
660
 
 
661
//-------------------------------------------------------------------------
 
662
NS_METHOD nsMenuBarX::InsertMenuAt(const PRUint32 aCount, nsIMenu *& aMenu)
 
663
{
 
664
  return NS_OK;
 
665
}
 
666
 
 
667
//-------------------------------------------------------------------------
 
668
NS_METHOD nsMenuBarX::RemoveMenu(const PRUint32 aCount)
 
669
{
 
670
  mMenusArray.RemoveElementAt(aCount);
 
671
  ::DeleteMenuItem(mRootMenu, aCount + 1);    // MenuManager is 1-based
 
672
  ::DrawMenuBar();
 
673
  return NS_OK;
 
674
}
 
675
 
 
676
//-------------------------------------------------------------------------
 
677
NS_METHOD nsMenuBarX::RemoveAll()
 
678
{
 
679
  NS_ASSERTION(0, "Not implemented!");
 
680
  // mMenusArray.Clear();    // maybe?
 
681
  return NS_OK;
 
682
}
 
683
 
 
684
//-------------------------------------------------------------------------
 
685
NS_METHOD nsMenuBarX::GetNativeData(void *& aData)
 
686
{
 
687
    aData = (void *) mRootMenu;
 
688
    return NS_OK;
 
689
}
 
690
 
 
691
//-------------------------------------------------------------------------
 
692
NS_METHOD nsMenuBarX::SetNativeData(void* aData)
 
693
{
 
694
#if 0
 
695
    Handle menubarHandle = (Handle)aData;
 
696
    if (mMacMBarHandle && mMacMBarHandle != menubarHandle)
 
697
        ::DisposeHandle(mMacMBarHandle);
 
698
    mMacMBarHandle = menubarHandle;
 
699
#endif
 
700
    return NS_OK;
 
701
}
 
702
 
 
703
//-------------------------------------------------------------------------
 
704
NS_METHOD nsMenuBarX::Paint()
 
705
{
 
706
    // hack to correctly swap menu bars.
 
707
    // hopefully this is fast enough.
 
708
    ::SetRootMenu(mRootMenu);
 
709
    ::DrawMenuBar();
 
710
    return NS_OK;
 
711
}
 
712
 
 
713
#pragma mark -
 
714
 
 
715
//
 
716
// nsIDocumentObserver
 
717
// this is needed for menubar changes
 
718
//
 
719
 
 
720
NS_IMPL_NSIDOCUMENTOBSERVER_LOAD_STUB(nsMenuBarX)
 
721
NS_IMPL_NSIDOCUMENTOBSERVER_REFLOW_STUB(nsMenuBarX)
 
722
NS_IMPL_NSIDOCUMENTOBSERVER_STATE_STUB(nsMenuBarX)
 
723
NS_IMPL_NSIDOCUMENTOBSERVER_STYLE_STUB(nsMenuBarX)
 
724
 
 
725
void
 
726
nsMenuBarX::BeginUpdate( nsIDocument * aDocument, nsUpdateType aUpdateType )
 
727
{
 
728
}
 
729
 
 
730
void
 
731
nsMenuBarX::EndUpdate( nsIDocument * aDocument, nsUpdateType aUpdateType )
 
732
{
 
733
}
 
734
 
 
735
void
 
736
nsMenuBarX::CharacterDataChanged( nsIDocument * aDocument, nsIContent * aContent, PRBool aAppend)
 
737
{
 
738
}
 
739
 
 
740
void
 
741
nsMenuBarX::ContentAppended( nsIDocument * aDocument, nsIContent  * aContainer,
 
742
                              PRInt32 aNewIndexInContainer)
 
743
{
 
744
  if ( aContainer == mMenuBarContent ) {
 
745
    //Register(aContainer, );
 
746
    //InsertMenu ( aNewIndexInContainer );
 
747
  }
 
748
  else {
 
749
    nsCOMPtr<nsIChangeObserver> obs;
 
750
    Lookup ( aContainer, getter_AddRefs(obs) );
 
751
    if ( obs )
 
752
      obs->ContentInserted ( aDocument, aContainer, aNewIndexInContainer );
 
753
    else {
 
754
      nsCOMPtr<nsIContent> parent = aContainer->GetParent();
 
755
      if(parent) {
 
756
        Lookup ( parent, getter_AddRefs(obs) );
 
757
        if ( obs )
 
758
          obs->ContentInserted ( aDocument, aContainer, aNewIndexInContainer );
 
759
      }
 
760
    }
 
761
  }
 
762
}
 
763
 
 
764
void
 
765
nsMenuBarX::ContentReplaced( nsIDocument * aDocument, nsIContent * aContainer, nsIContent * aOldChild,
 
766
                          nsIContent * aNewChild, PRInt32 aIndexInContainer)
 
767
{
 
768
}
 
769
 
 
770
void
 
771
nsMenuBarX::DocumentWillBeDestroyed( nsIDocument * aDocument )
 
772
{
 
773
  mDocument = nsnull;
 
774
}
 
775
 
 
776
 
 
777
void
 
778
nsMenuBarX::AttributeChanged( nsIDocument * aDocument, nsIContent * aContent, PRInt32 aNameSpaceID,
 
779
                              nsIAtom * aAttribute, PRInt32 aModType)
 
780
{
 
781
  // lookup and dispatch to registered thang.
 
782
  nsCOMPtr<nsIChangeObserver> obs;
 
783
  Lookup ( aContent, getter_AddRefs(obs) );
 
784
  if ( obs )
 
785
    obs->AttributeChanged ( aDocument, aNameSpaceID, aAttribute );
 
786
}
 
787
 
 
788
void
 
789
nsMenuBarX::ContentRemoved( nsIDocument * aDocument, nsIContent * aContainer,
 
790
                            nsIContent * aChild, PRInt32 aIndexInContainer )
 
791
{  
 
792
  if ( aContainer == mMenuBarContent ) {
 
793
    Unregister(aChild);
 
794
    RemoveMenu ( aIndexInContainer );
 
795
  }
 
796
  else {
 
797
    nsCOMPtr<nsIChangeObserver> obs;
 
798
    Lookup ( aContainer, getter_AddRefs(obs) );
 
799
    if ( obs )
 
800
      obs->ContentRemoved ( aDocument, aChild, aIndexInContainer );
 
801
    else {
 
802
      nsCOMPtr<nsIContent> parent = aContainer->GetParent();
 
803
      if(parent) {
 
804
        Lookup ( parent, getter_AddRefs(obs) );
 
805
        if ( obs )
 
806
          obs->ContentRemoved ( aDocument, aChild, aIndexInContainer );
 
807
      }
 
808
    }
 
809
  }
 
810
}
 
811
 
 
812
void
 
813
nsMenuBarX::ContentInserted( nsIDocument * aDocument, nsIContent * aContainer,
 
814
                            nsIContent * aChild, PRInt32 aIndexInContainer )
 
815
{  
 
816
  if ( aContainer == mMenuBarContent ) {
 
817
    //Register(aChild, );
 
818
    //InsertMenu ( aIndexInContainer );
 
819
  }
 
820
  else {
 
821
    nsCOMPtr<nsIChangeObserver> obs;
 
822
    Lookup ( aContainer, getter_AddRefs(obs) );
 
823
    if ( obs )
 
824
      obs->ContentInserted ( aDocument, aChild, aIndexInContainer );
 
825
    else {
 
826
      nsCOMPtr<nsIContent> parent = aContainer->GetParent();
 
827
      if(parent) {
 
828
        Lookup ( parent, getter_AddRefs(obs) );
 
829
        if ( obs )
 
830
          obs->ContentInserted ( aDocument, aChild, aIndexInContainer );
 
831
      }
 
832
    }
 
833
  }
 
834
}
 
835
 
 
836
#pragma mark - 
 
837
 
 
838
//
 
839
// nsIChangeManager
 
840
//
 
841
// We don't use a |nsSupportsHashtable| because we know that the lifetime of all these items
 
842
// is bouded by the lifetime of the menubar. No need to add any more strong refs to the
 
843
// picture because the containment hierarchy already uses strong refs.
 
844
//
 
845
 
 
846
NS_IMETHODIMP 
 
847
nsMenuBarX :: Register ( nsIContent *aContent, nsIChangeObserver *aMenuObject )
 
848
{
 
849
  nsVoidKey key ( aContent );
 
850
  mObserverTable.Put ( &key, aMenuObject );
 
851
  
 
852
  return NS_OK;
 
853
}
 
854
 
 
855
 
 
856
NS_IMETHODIMP 
 
857
nsMenuBarX :: Unregister ( nsIContent *aContent )
 
858
{
 
859
  nsVoidKey key ( aContent );
 
860
  mObserverTable.Remove ( &key );
 
861
  
 
862
  return NS_OK;
 
863
}
 
864
 
 
865
 
 
866
NS_IMETHODIMP 
 
867
nsMenuBarX :: Lookup ( nsIContent *aContent, nsIChangeObserver **_retval )
 
868
{
 
869
  *_retval = nsnull;
 
870
  
 
871
  nsVoidKey key ( aContent );
 
872
  *_retval = NS_REINTERPRET_CAST(nsIChangeObserver*, mObserverTable.Get(&key));
 
873
  NS_IF_ADDREF ( *_retval );
 
874
  
 
875
  return NS_OK;
 
876
}
 
877
 
 
878
 
 
879
#pragma mark -
 
880
 
 
881
 
 
882
//
 
883
// Implementation methods for nsIMenuCommandDispatcher
 
884
//
 
885
 
 
886
 
 
887
//
 
888
// Register
 
889
//
 
890
// Given a menu item, creates a unique 4-character command ID and
 
891
// maps it to the item. Returns the id for use by the client.
 
892
//
 
893
NS_IMETHODIMP 
 
894
nsMenuBarX :: Register ( nsIMenuItem* inMenuItem, PRUint32* outCommandID )
 
895
{
 
896
  // no real need to check for uniqueness. We always start afresh with each
 
897
  // window at 1. Even if we did get close to the reserved Apple command id's,
 
898
  // those don't start until at least '    ', which is integer 538976288. If
 
899
  // we have that many menu items in one window, I think we have other problems.
 
900
  
 
901
  // put it in the table, set out param for client
 
902
  nsPRUint32Key key ( mCurrentCommandID );
 
903
  mObserverTable.Put ( &key, inMenuItem );
 
904
  *outCommandID = mCurrentCommandID;
 
905
 
 
906
  // make id unique for next time
 
907
  ++mCurrentCommandID;
 
908
  
 
909
  return NS_OK;
 
910
}
 
911
 
 
912
 
 
913
// 
 
914
// Unregister
 
915
//
 
916
// Removes the mapping between the given 4-character command ID
 
917
// and its associated menu item.
 
918
//
 
919
NS_IMETHODIMP 
 
920
nsMenuBarX :: Unregister ( PRUint32 inCommandID )
 
921
{
 
922
  nsPRUint32Key key ( inCommandID );
 
923
  mObserverTable.Remove ( &key );
 
924
 
 
925
  return NS_OK;
 
926
}
 
927
 
 
928
 
 
929
#pragma mark -
 
930
 
 
931
 
 
932
//
 
933
// WebShellToPresContext
 
934
//
 
935
// Helper to dig out a pres context from a webshell. A common thing to do before
 
936
// sending an event into the dom.
 
937
//
 
938
nsresult
 
939
MenuHelpersX::WebShellToPresContext (nsIWebShell* inWebShell, nsIPresContext** outContext )
 
940
{
 
941
  NS_ENSURE_ARG_POINTER(outContext);
 
942
  *outContext = nsnull;
 
943
  if (!inWebShell)
 
944
    return NS_ERROR_INVALID_ARG;
 
945
  
 
946
  nsresult retval = NS_OK;
 
947
  
 
948
  nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(inWebShell));
 
949
 
 
950
  nsCOMPtr<nsIContentViewer> contentViewer;
 
951
  docShell->GetContentViewer(getter_AddRefs(contentViewer));
 
952
  if ( contentViewer ) {
 
953
    nsCOMPtr<nsIDocumentViewer> docViewer ( do_QueryInterface(contentViewer) );
 
954
    if ( docViewer )
 
955
      docViewer->GetPresContext(outContext);     // AddRefs for us
 
956
    else
 
957
      retval = NS_ERROR_FAILURE;
 
958
  }
 
959
  else
 
960
    retval = NS_ERROR_FAILURE;
 
961
  
 
962
  return retval;
 
963
  
 
964
} // WebShellToPresContext
 
965
 
 
966
 
 
967