ccarlen%netscape.com 914f804e6b NOT PART OF THE BUILD
Added Open File and Open Directory
Added support for GetURL AppleEvent


git-svn-id: svn://10.0.0.236/trunk@83721 18797224-902f-48f8-a5cc-f745e15eee43
2000-12-15 13:30:40 +00:00

574 lines
17 KiB
C++
Raw Blame History

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is the Mozilla browser.
*
* The Initial Developer of the Original Code is Netscape
* Communications, Inc. Portions created by Netscape are
* Copyright (C) 1999, Mozilla. All Rights Reserved.
*
* Contributor(s):
* Conrad Carlen <ccarlen@netscape.com>
*/
#include "CBrowserApp.h"
#include <LGrowZone.h>
#include <PP_Messages.h>
#include <PP_Resources.h>
#include <UDrawingState.h>
#include <UMemoryMgr.h>
#include <URegistrar.h>
#include <LWindow.h>
#include <LCaption.h>
#include <LIconControl.h>
#include <UControlRegistry.h>
#include <UGraphicUtils.h>
#include <UEnvironment.h>
#include <Appearance.h>
#include <UConditionalDialogs.h>
#include <LCMAttachment.h>
#include <UCMMUtils.h>
#include "ApplIDs.h"
#include "CBrowserWindow.h"
#include "CBrowserShell.h"
#include "CUrlField.h"
#include "CThrobber.h"
#include "CWebBrowserCMAttachment.h"
#include "UMacUnicode.h"
#include "nsIImageManager.h"
#include "nsIServiceManager.h"
#include "nsIEventQueueService.h"
#include "nsIDirectoryService.h"
#include "nsDirectoryServiceDefs.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsIPref.h"
#include "nsRepeater.h"
#include "nsILocalFile.h"
#include "nsILocalFileMac.h"
#include "nsIFileSpec.h"
#include "nsEmbedAPI.h"
#include "nsMPFileLocProvider.h"
#include "nsXPIDLString.h"
#include "macstdlibextras.h"
#include "SIOUX.h"
#include "nsIURL.h"
#include "nsINetDataCacheManager.h"
#include <TextServices.h>
extern "C" void NS_SetupRegistry();
static NS_DEFINE_CID(kPrefCID, NS_PREF_CID);
static const char* kProgramName = "PP Browser";
// ===========================================================================
// <09> Main Program
// ===========================================================================
int main()
{
SetDebugThrow_(PP_PowerPlant::debugAction_Alert); // Set Debugging options
SetDebugSignal_(PP_PowerPlant::debugAction_Alert);
PP_PowerPlant::InitializeHeap(3); // Initialize Memory Manager
// Parameter is number of Master Pointer
// blocks to allocate
PP_PowerPlant::UQDGlobals::InitializeToolbox(&qd); // Initialize standard Toolbox managers
#if DEBUG
::InitializeSIOUX(false);
#endif
::InitTSMAwareApplication();
new PP_PowerPlant::LGrowZone(20000); // Install a GrowZone function to catch low memory situations.
{
CBrowserApp theApp; // create instance of your application
theApp.Run();
}
::CloseTSMAwareApplication();
return 0;
}
// ---------------------------------------------------------------------------
// <09> CBrowserApp
// ---------------------------------------------------------------------------
// Constructor
CBrowserApp::CBrowserApp()
{
if ( PP_PowerPlant::UEnvironment::HasFeature( PP_PowerPlant::env_HasAppearance ) ) {
::RegisterAppearanceClient();
}
RegisterClass_(PP_PowerPlant::LWindow); // You must register each kind of
RegisterClass_(PP_PowerPlant::LCaption); // PowerPlant classes that you use in your PPob resource.
RegisterClass_(PP_PowerPlant::LTabGroupView);
RegisterClass_(PP_PowerPlant::LIconControl);
RegisterClass_(PP_PowerPlant::LView);
// Register the Appearance Manager/GA classes
PP_PowerPlant::UControlRegistry::RegisterClasses();
// Register classes used by embedding
RegisterClass_(CBrowserShell);
RegisterClass_(CBrowserWindow);
RegisterClass_(CUrlField);
RegisterClass_(CThrobber);
// Contexual Menu Support
UCMMUtils::Initialize();
RegisterClass_(LCMAttachment);
RegisterClass_(CWebBrowserCMAttachment);
AddAttachment(new LCMAttachment);
// We need to idle threads often
SetSleepTime(0);
// Get the directory which contains the mozilla parts
// In this case it is the app directory but it could
// be anywhere (an existing install of mozilla)
nsresult rv;
ProcessSerialNumber psn;
ProcessInfoRec processInfo;
FSSpec appSpec;
nsCOMPtr<nsILocalFileMac> macDir;
nsCOMPtr<nsILocalFile> appDir; // If this ends up being NULL, default is used
if (!::GetCurrentProcess(&psn)) {
processInfo.processInfoLength = sizeof(processInfo);
processInfo.processName = NULL;
processInfo.processAppSpec = &appSpec;
if (!::GetProcessInformation(&psn, &processInfo)) {
// Turn the FSSpec of the app into an FSSpec of the app's directory
::FSMakeFSSpec(appSpec.vRefNum, appSpec.parID, "\p", &appSpec);
// Make an nsILocalFile out of it
rv = NS_NewLocalFileWithFSSpec(&appSpec, PR_TRUE, getter_AddRefs(macDir));
if (NS_SUCCEEDED(rv))
appDir = do_QueryInterface(macDir);
}
}
rv = NS_InitEmbedding(appDir, nsnull);
}
// ---------------------------------------------------------------------------
// <09> ~CBrowserApp
// ---------------------------------------------------------------------------
// Destructor
//
CBrowserApp::~CBrowserApp()
{
nsresult rv;
NS_WITH_SERVICE(nsIPref, prefs, NS_PREF_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv) && prefs)
prefs->SavePrefFile();
NS_TermEmbedding();
}
// ---------------------------------------------------------------------------
// <09> StartUp
// ---------------------------------------------------------------------------
// This method lets you do something when the application starts up
// without a document. For example, you could issue your own new command.
void
CBrowserApp::StartUp()
{
nsresult rv;
// If we don't want different user profiles, all that's needed is
// to make an nsMPFileLocProvider. This will provide the same file
// locations as the profile service but always within the specified folder.
nsCOMPtr<nsIFile> rootDir;
nsMPFileLocProvider *locationProvider = new nsMPFileLocProvider;
ThrowIfNil_(locationProvider);
rv = NS_GetSpecialDirectory(NS_MAC_PREFS_DIR, getter_AddRefs(rootDir));
ThrowIfNil_(rootDir);
rv = locationProvider->Initialize(rootDir, kProgramName);
ThrowIfError_(rv);
NS_WITH_SERVICE(nsIPref, prefs, kPrefCID, &rv);
ThrowIfNil_(prefs);
// Needed because things read default prefs during startup
prefs->ResetPrefs();
prefs->ReadUserPrefs();
InitializePrefs();
ObeyCommand(PP_PowerPlant::cmd_New, nil); // EXAMPLE, create a new window
}
// ---------------------------------------------------------------------------
// <09> MakeMenuBar
// ---------------------------------------------------------------------------
void
CBrowserApp::MakeMenuBar()
{
LApplication::MakeMenuBar();
// Insert a menu which is not in the menu bar but which contains
// items which appear in contextual menus. We have to do this hack
// because LCMAttachment::AddCommand needs a command which is in
// some LMenu in order to get the text for a contextual menu item.
LMenuBar::GetCurrentMenuBar()->InstallMenu(new LMenu(menu_Buzzwords), hierMenu);
}
// ---------------------------------------------------------------------------
// <09> ProcessNextEvent [public]
// ---------------------------------------------------------------------------
// Retrieve and handle the next event in the event queue
void
CBrowserApp::ProcessNextEvent()
{
EventRecord macEvent;
// When on duty (application is in the foreground), adjust the
// cursor shape before waiting for the next event. Except for the
// very first time, this is the same as adjusting the cursor
// after every event.
if (IsOnDuty()) {
// Calling OSEventAvail with a zero event mask will always
// pass back a null event. However, it fills the EventRecord
// with the information we need to set the cursor shape--
// the mouse location in global coordinates and the state
// of the modifier keys.
::OSEventAvail(0, &macEvent);
AdjustCursor(macEvent);
}
// Retrieve the next event. Context switch could happen here.
SetUpdateCommandStatus(false);
Boolean gotEvent = ::WaitNextEvent(everyEvent, &macEvent, mSleepTime,
mMouseRgn);
// Let Attachments process the event. Continue with normal
// event dispatching unless suppressed by an Attachment.
if (LAttachable::ExecuteAttachments(msg_Event, &macEvent)) {
if (gotEvent) {
#if DEBUG
if (macEvent.what == kHighLevelEvent || !SIOUXHandleOneEvent(&macEvent))
#endif
DispatchEvent(macEvent);
} else {
UseIdleTime(macEvent);
Repeater::DoIdlers(macEvent);
// yield to other threads
::PR_Sleep(PR_INTERVAL_NO_WAIT);
}
}
// Repeaters get time after every event
LPeriodical::DevoteTimeToRepeaters(macEvent);
Repeater::DoRepeaters(macEvent);
// Update status of menu items
if (IsOnDuty() && GetUpdateCommandStatus()) {
UpdateMenus();
}
}
// ---------------------------------------------------------------------------
// <09> HandleAppleEvent [public]
// ---------------------------------------------------------------------------
void CBrowserApp::HandleAppleEvent(const AppleEvent& inAppleEvent,
AppleEvent& outAEReply,
AEDesc& outResult,
long inAENumber)
{
switch (inAENumber) {
case 5000:
{
OSErr err;
Handle dataH;
StAEDescriptor urlDesc;
err = ::AEGetParamDesc(&inAppleEvent, keyDirectObject, typeWildCard, urlDesc);
ThrowIfOSErr_(err);
StAEDescriptor coerceDesc;
if (urlDesc.DescriptorType() != typeChar) {
err = ::AECoerceDesc(urlDesc, typeChar, coerceDesc);
ThrowIfOSErr_(err);
dataH = ((AEDesc)coerceDesc).dataHandle;
}
else
dataH = ((AEDesc)urlDesc).dataHandle;
Size dataSize = ::GetHandleSize(dataH);
StHandleLocker lock(dataH);
CBrowserWindow *theWindow = CBrowserWindow::CreateWindow(nsIWebBrowserChrome::CHROME_DEFAULT, -1, -1);
ThrowIfNil_(theWindow);
theWindow->SetSizeToContent(false);
theWindow->GetBrowserShell()->LoadURL(*dataH, dataSize);
theWindow->Show();
}
break;
default:
LApplication::HandleAppleEvent(inAppleEvent, outAEReply, outResult, inAENumber);
}
}
// ---------------------------------------------------------------------------
// <09> ObeyCommand
// ---------------------------------------------------------------------------
// This method lets the application respond to commands like Menu commands
Boolean
CBrowserApp::ObeyCommand(
PP_PowerPlant::CommandT inCommand,
void *ioParam)
{
Boolean cmdHandled = true;
switch (inCommand) {
case PP_PowerPlant::cmd_New:
{
CBrowserWindow *theWindow = CBrowserWindow::CreateWindow(nsIWebBrowserChrome::CHROME_DEFAULT, -1, -1);
ThrowIfNil_(theWindow);
theWindow->SetSizeToContent(false);
// Just for demo sake, load a URL
theWindow->GetBrowserShell()->LoadURL("http://www.mozilla.org");
theWindow->Show();
}
break;
case PP_PowerPlant::cmd_Open:
case cmd_OpenDirectory:
{
FSSpec fileSpec;
if (SelectFileObject(inCommand, fileSpec))
{
nsresult rv;
nsCOMPtr<nsILocalFileMac> macFile;
rv = NS_NewLocalFileWithFSSpec(&fileSpec, PR_TRUE, getter_AddRefs(macFile));
ThrowIfError_(NS_ERROR_GET_CODE(rv));
nsCOMPtr<nsILocalFile> localFile(do_QueryInterface(macFile, &rv));
ThrowIfError_(NS_ERROR_GET_CODE(rv));
nsCOMPtr<nsIFileURL> aURL(do_CreateInstance("@mozilla.org/network/standard-url;1", &rv));
ThrowIfError_(NS_ERROR_GET_CODE(rv));
rv = aURL->SetFile(localFile);
ThrowIfError_(NS_ERROR_GET_CODE(rv));
nsXPIDLCString urlSpec;
rv = aURL->GetSpec(getter_Copies(urlSpec));
ThrowIfError_(NS_ERROR_GET_CODE(rv));
CBrowserWindow *theWindow = CBrowserWindow::CreateWindow(nsIWebBrowserChrome::CHROME_DEFAULT, -1, -1);
ThrowIfNil_(theWindow);
theWindow->SetSizeToContent(false);
theWindow->GetBrowserShell()->LoadURL(urlSpec.get());
theWindow->Show();
}
}
break;
// Any that you don't handle, such as cmd_About and cmd_Quit,
// will be passed up to LApplication
default:
cmdHandled = PP_PowerPlant::LApplication::ObeyCommand(inCommand, ioParam);
break;
}
return cmdHandled;
}
// ---------------------------------------------------------------------------
// <09> FindCommandStatus
// ---------------------------------------------------------------------------
// This function enables menu commands.
//
void
CBrowserApp::FindCommandStatus(
PP_PowerPlant::CommandT inCommand,
Boolean &outEnabled,
Boolean &outUsesMark,
PP_PowerPlant::Char16 &outMark,
Str255 outName)
{
switch (inCommand) {
// Return menu item status according to command messages.
case PP_PowerPlant::cmd_New:
outEnabled = true;
break;
case PP_PowerPlant::cmd_Open:
case cmd_OpenDirectory:
outEnabled = true;
break;
// Any that you don't handle, such as cmd_About and cmd_Quit,
// will be passed up to LApplication
default:
PP_PowerPlant::LApplication::FindCommandStatus(inCommand, outEnabled,
outUsesMark, outMark, outName);
break;
}
}
Boolean CBrowserApp::AttemptQuitSelf(SInt32 inSaveOption)
{
// IMPORTANT: This is one unfortunate thing about Powerplant - Windows don't
// get destroyed until the destructor of LCommander. We need to delete
// all of the CBrowserWindows though before we terminate embedding.
TArrayIterator<LCommander*> iterator(mSubCommanders, LArrayIterator::from_End);
LCommander* theSub;
while (iterator.Previous(theSub)) {
if (dynamic_cast<CBrowserWindow*>(theSub)) {
mSubCommanders.RemoveItemsAt(1, iterator.GetCurrentIndex());
delete theSub;
}
}
return true;
}
nsresult CBrowserApp::InitializePrefs()
{
nsresult rv;
NS_WITH_SERVICE(nsIPref, prefs, kPrefCID, &rv);
if (NS_SUCCEEDED(rv)) {
rv = InitCachePrefs();
NS_ASSERTION(NS_SUCCEEDED(rv), "Could not initialize cache prefs");
// We are using the default prefs from mozilla. If you were
// disributing your own, this would be done simply by editing
// the default pref files.
PRBool inited;
rv = prefs->GetBoolPref("ppbrowser.prefs_inited", &inited);
if (NS_FAILED(rv) || !inited)
{
prefs->SetIntPref("font.size.variable.x-western", 12);
prefs->SetIntPref("font.size.fixed.x-western", 12);
rv = prefs->SetBoolPref("ppbrowser.prefs_inited", PR_TRUE);
if (NS_SUCCEEDED(rv))
rv = prefs->SavePrefFile();
}
}
else
NS_ASSERTION(PR_FALSE, "Could not get preferences service");
return rv;
}
nsresult CBrowserApp::InitCachePrefs()
{
const char * const CACHE_DIR_PREF = "browser.cache.directory";
nsresult rv;
NS_WITH_SERVICE(nsIPref, prefs, NS_PREF_CONTRACTID, &rv);
if (NS_FAILED(rv)) return rv;
// See if we have a pref to a dir which exists
nsCOMPtr<nsILocalFile> prefDir;
rv = prefs->GetFileXPref(CACHE_DIR_PREF, getter_AddRefs(prefDir));
if (NS_SUCCEEDED(rv)) {
PRBool isDir;
rv = prefDir->IsDirectory(&isDir);
if (NS_SUCCEEDED(rv) && isDir)
return NS_OK;
}
// Set up the new pref
nsCOMPtr<nsIFile> profileDir;
rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(profileDir));
NS_ASSERTION(profileDir, "NS_APP_USER_PROFILE_50_DIR is not defined");
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsILocalFile> cacheDir(do_QueryInterface(profileDir));
NS_ASSERTION(cacheDir, "Cannot get nsILocalFile from cache dir");
PRBool exists;
cacheDir->Append("Cache");
rv = cacheDir->Exists(&exists);
if (NS_SUCCEEDED(rv) && !exists)
rv = cacheDir->Create(nsIFile::DIRECTORY_TYPE, 0775);
if (NS_FAILED(rv)) return rv;
return prefs->SetFileXPref(CACHE_DIR_PREF, cacheDir);
}
Boolean CBrowserApp::SelectFileObject(PP_PowerPlant::CommandT inCommand,
FSSpec& outSpec)
{
// LFileChooser presents the standard dialog for asking
// the user to open a file. It supports both StandardFile
// and Navigation Services. The latter allows opening
// multiple files.
UConditionalDialogs::LFileChooser chooser;
NavDialogOptions *theDialogOptions = chooser.GetDialogOptions();
if (theDialogOptions) {
theDialogOptions->dialogOptionFlags |= kNavSelectAllReadableItem;
}
Boolean result;
SInt32 dirID;
if (inCommand == cmd_OpenDirectory)
{
result = chooser.AskChooseFolder(outSpec, dirID);
}
else
{
result = chooser.AskOpenFile(LFileTypeList(fileTypes_All));
if (result)
chooser.GetFileSpec(1, outSpec);
}
return result;
}