/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ #include "CEditorWindow.h" #include "CBrowserContext.h" #include "resgui.h" #include "CFormattingToolBar.h" #include "CFontMenuAttachment.h" #include "CToolbarPopup.h" #include "CPatternButtonPopupText.h" #include "COpenRecentlyEditedPopup.h" #include "CColorPopup.h" #include "CColorCaption.h" #include "CEditDictionary.h" #include "CTextTable.h" #include "CTableKeySingleSelector.h" // macfe #include "mversion.h" // ID_STRINGS #include "URobustCreateWindow.h" #include "uprefd.h" #include "uerrmgr.h" #include "uapp.h" #include "mforms.h" // CTabbedDialog #include "meditdlg.h" // CTabbedDialog #include "meditor.h" // HandleModalDialog #include "CEditView.h" // Netscape #include "net.h" // NET_cinfo_find_type #include "proto.h" // XP_IsContextBusy #include "edt.h" #include "shist.h" #include "prefapi.h" // PREF_GetBoolPref #ifdef PROFILE #pragma profile on #endif // pane id constants - for ShowOneDragBar const PaneIDT Paragraph_Bar_PaneID = 'DBar'; const PaneIDT Character_Bar_PaneID = 'NBar'; const char* Pref_ShowParagraphBar = "editor.show_paragraph_toolbar"; const char* Pref_ShowCharacterBar = "editor.show_character_toolbar"; enum { eParagraphBar, eCharacterBar }; CEditorWindow::CEditorWindow( LStream* inStream ) :CBrowserWindow(inStream) { SetWindowType( WindowType_Editor ); } static // PRIVATE CMediatedWindow* GetFrontWindowByType( OSType windowType ) { CMediatedWindow* theWindow = NULL; CWindowIterator iter(windowType); iter.Next(theWindow); return theWindow; } // given an mwcontext (non-busy, with a history), select the matching editor window // or open an editor window based on that mwcontext // if all else fails (and memory is not low), create an empty editor window void CEditorWindow::MakeEditWindowFromBrowser( MWContext* mwcontext ) { if ( Memory_MemoryIsLow() ) return; History_entry* entry = NULL; if ( mwcontext && !XP_IsContextBusy( mwcontext ) ) entry = SHIST_GetCurrent(&mwcontext->hist); if ( entry && entry->address) { // If there is already an editor window open for this url // just switch to it and keep this browser window open. CMediatedWindow * win; CWindowIterator iter(WindowType_Editor); while (iter.Next(win)) { CNSContext* curContext = ((CBrowserWindow *)win)->GetWindowContext(); MWContext* context; context = curContext ? curContext->operator MWContext*() : NULL; if (context && EDT_IS_EDITOR(context)) { History_entry* newEntry = SHIST_GetCurrent(&context->hist); if (newEntry && newEntry->address && !strcmp(newEntry->address, entry->address)) { win->Show(); win->Select(); return; } } } if (CEditorWindow::MakeEditWindow(mwcontext, NULL) == NULL) // new window based on history of this window return; // don't close this one on error. } else { CEditorWindow::MakeEditWindow(NULL, NULL); // make a completely new window } } // EDT_PreOpenCallbackFn static void createEditorWindowCallback( XP_Bool userCanceled, char* pURL, void* hook ) { if ( !userCanceled ) { EditorCreationStruct *edtCreatePtr = (EditorCreationStruct *)hook; if ( hook ) { if ( edtCreatePtr->existingURLstruct && edtCreatePtr->existingURLstruct->address ) { XP_FREE( edtCreatePtr->existingURLstruct->address ); edtCreatePtr->existingURLstruct->address = XP_STRDUP( pURL ); } } // substitute new URL CEditorWindow::CreateEditorWindowStage2( edtCreatePtr ); } } CEditorWindow* CEditorWindow::MakeEditWindow( MWContext* old_context, URL_Struct* url ) { Boolean urlCameInNULL = (url == NULL); // if we don't have an URL, try to get it from the old_context's window history if (url == NULL && old_context != NULL) { CBrowserContext *browserContext = (CBrowserContext *)old_context->fe.newContext; History_entry* entry = browserContext->GetCurrentHistoryEntry(); // Take the last instead of the first history entry. if ( entry ) { url = SHIST_CreateURLStructFromHistoryEntry( old_context, entry ); if ( url ) { url->force_reload = NET_NORMAL_RELOAD; XP_MEMSET( &url->savedData, 0, sizeof( SHIST_SavedData ) ); } } // if we don't have a history entry, we're kind of screwed-->just load a blank page } // we want to open a new blank edit window // or we are still having troubles... fall back to our old tried and true blank page if (url == NULL) url = NET_CreateURLStruct ("about:editfilenew", NET_NORMAL_RELOAD ); // FIX THIS!!! the above line should use "XP_GetString(XP_EDIT_NEW_DOC_NAME)" // time of reckoning. We really, really need an URL and address at this point... // I don't know what an url without an address is anyway... if (url == NULL || url->address == NULL) return NULL; // now make sure that the url is a valid type to edit. NET_cinfo *cinfo = NET_cinfo_find_type (url->address); if (cinfo == NULL || cinfo->type == NULL || (strcasecomp (cinfo->type, TEXT_HTML) && strcasecomp (cinfo->type, UNKNOWN_CONTENT_TYPE) && strcasecomp (cinfo->type, TEXT_PLAIN))) { ErrorManager::PlainAlert( NOT_HTML ); return NULL; } EditorCreationStruct *edtCreatePtr = (EditorCreationStruct *)XP_ALLOC( sizeof( EditorCreationStruct ) ); if ( edtCreatePtr ) { edtCreatePtr->context = old_context; edtCreatePtr->existingURLstruct = url; edtCreatePtr->isEmptyPage = old_context == NULL && urlCameInNULL; } if ( urlCameInNULL && old_context == NULL ) { return CreateEditorWindowStage2( edtCreatePtr ); } else { EDT_PreOpen( old_context, url->address, createEditorWindowCallback, edtCreatePtr ); return NULL; } } CEditorWindow *CEditorWindow::CreateEditorWindowStage2( EditorCreationStruct *edtCreatePtr ) { // now we can create an editor window since we don't already have one for this url. /* instead of just calling LWindow::CreateWindow(), we do it ourselves by hand here * so that we can set the window bounds before we call FinishCreate(). */ if ( edtCreatePtr == NULL ) return NULL; CEditorWindow* newWindow = NULL; SetDefaultCommander(CFrontApp::GetApplication()); try { OSErr error; URobustCreateWindow::ReadObjects( 'PPob', CEditorWindow::res_ID, &newWindow, &error ); Assert_(newWindow); FailOSErr_(error); if (newWindow == NULL) { XP_FREE( edtCreatePtr ); return NULL; } newWindow->FinishCreate(); if (newWindow->HasAttribute(windAttr_ShowNew)) newWindow->Show(); UReanimator::LinkListenerToControls( newWindow, newWindow, CEditorWindow::res_ID ); } catch(...) { if ( newWindow ) delete newWindow; XP_FREE( edtCreatePtr ); return NULL; } Boolean hasURLstruct = edtCreatePtr->existingURLstruct && edtCreatePtr->existingURLstruct->address; CBrowserContext *nscontext = new CBrowserContext(); newWindow->SetWindowContext( nscontext ); CURLDispatcher::DispatchURL( edtCreatePtr->existingURLstruct, nscontext, false, false, CEditorWindow::res_ID ); if ( edtCreatePtr->context ) { nscontext->InitHistoryFromContext( (CBrowserContext *)edtCreatePtr->context->fe.newContext ); } else { // the url will eventually be freed when the load is complete. } // set window title here (esp. for "open") if ( !edtCreatePtr->isEmptyPage && edtCreatePtr->existingURLstruct && edtCreatePtr->existingURLstruct->address ) { char *pSlash = strrchr( edtCreatePtr->existingURLstruct->address, '/' ); if ( pSlash ) pSlash += 1; // move past '/' newWindow->NoteDocTitleChanged( pSlash ); } XP_FREE( edtCreatePtr ); return newWindow; } void CEditorWindow::SetWindowContext(CBrowserContext* inContext) { if ( inContext ) { MWContext *context; context = inContext->operator MWContext*(); if ( context ) { context->is_editor = true; NET_CheckForTimeBomb( context ); } } CBrowserWindow::SetWindowContext( inContext ); } void CEditorWindow::NoteDocTitleChanged( const char* inNewTitle ) { // there is one bogus set-title from layout that we want to skip... // We are hard coding "editfilenew" here because it is already hardcoded // a million other places... if ( inNewTitle && XP_STRCMP( inNewTitle, "editfilenew" ) == 0 ) return; CNSContext *theContext = GetWindowContext(); char *baseName = LO_GetBaseURL( theContext->operator MWContext*() ); // don't free this... // strip out username and password so user doesn't see them in window title char *location = NULL; if ( baseName ) NET_ParseUploadURL( baseName, &location, NULL, NULL ); CStr255 csBaseURL(location); // if this page has a local "file:" url, then just show the file name (skip the url and directory crap.) if ( location && NET_IsLocalFileURL(location) ) { char *localName = NULL; XP_ConvertUrlToLocalFile( location, &localName ); if (localName) { #if 0 char *pSlash = strrchr(localName, '/'); if (pSlash) { // is there is a slash, move everything AFTER the last slash to the front pSlash++; XP_STRCPY(localName, pSlash); } #endif csBaseURL = localName; XP_FREE(localName); } } CStr255 netscapeTitle; CStr255 subTitle; ::GetIndString( netscapeTitle, ID_STRINGS, APPNAME_STRING_INDEX ); ::GetIndString( subTitle, WINDOW_TITLES_RESID, 3 ); netscapeTitle += " "; netscapeTitle += subTitle; netscapeTitle += " - ["; // set up page title manually; rather than rely on XP string passed in EDT_PageData * pageData = EDT_GetPageData( theContext->operator MWContext*() ); if ( pageData && pageData->pTitle && pageData->pTitle[0] ) { netscapeTitle += pageData->pTitle; if (csBaseURL.Length()) netscapeTitle += " : "; } if ( pageData ) EDT_FreePageData( pageData ); // add file path to end netscapeTitle += csBaseURL; netscapeTitle += "]"; SetDescriptor( netscapeTitle ); if ( location ) XP_FREE( location ); } void CEditorWindow::RegisterViewTypes() { // Registers all its view types RegisterClass_( CEditorWindow); RegisterClass_( CEditView); RegisterClass_( MultipleSelectionSingleColumn); // newer, better class? RegisterClass_( CTarget); RegisterClass_( CLineProp); RegisterClass_( CFormattingToolBar); RegisterClass_( CHTMLAreaToolBar); RegisterClass_( CToolbarPopup); // newer, better class? RegisterClass_( CIconToolbarPopup); // newer, better class? RegisterClass_( CColorPopup); RegisterClass_( CFontMenuPopup ); RegisterClass_( CChameleonCaption); // newer, better class? RegisterClass_( CChameleonView); // newer, better class? CTabbedDialog::RegisterViewTypes(); RegisterClass_( CTextTable); // newer, better class? RegisterClass_( CTableKeySingleSelector); // newer, better class? RegisterClass_( CPatternButtonPopupText); // newer, better class? RegisterClass_( LOffscreenView); RegisterClass_( COpenRecentlyEditedPopup ); RegisterClass_( CEditDictionary); RegisterClass_( CEditDictionaryTable); } void CEditorWindow::FinishCreateSelf() { CBrowserWindow::FinishCreateSelf(); // Show/hide toolbars based on preference settings XP_Bool value; PREF_GetBoolPref(Pref_ShowParagraphBar, &value); mToolbarShown[eParagraphBar] = value; ShowOneDragBar(Paragraph_Bar_PaneID, value); PREF_GetBoolPref(Pref_ShowCharacterBar, &value); mToolbarShown[eCharacterBar] = value; ShowOneDragBar(Character_Bar_PaneID, value); } void CEditorWindow::ListenToMessage( MessageT inMessage, void* ioParam ) { switch (inMessage) { case msg_NSCDocTitleChanged: NoteDocTitleChanged((const char*)ioParam); break; default: { if ( inMessage == 'Para' ) { switch (*(long*)ioParam) { case 1: inMessage = cmd_Format_Paragraph_Normal; break; case 2: inMessage = cmd_Format_Paragraph_Head1; break; case 3: inMessage = cmd_Format_Paragraph_Head2; break; case 4: inMessage = cmd_Format_Paragraph_Head3; break; case 5: inMessage = cmd_Format_Paragraph_Head4; break; case 6: inMessage = cmd_Format_Paragraph_Head5; break; case 7: inMessage = cmd_Format_Paragraph_Head6; break; case 8: inMessage = cmd_Format_Paragraph_Address; break; case 9: inMessage = cmd_Format_Paragraph_Formatted; break; case 10: inMessage = cmd_Format_Paragraph_List_Item; break; case 11: inMessage = cmd_Format_Paragraph_Desc_Title; break; case 12: inMessage = cmd_Format_Paragraph_Desc_Text; break; } } else if ( inMessage == 'Size' ) { switch (*(long*)ioParam) { case 1: inMessage = cmd_Format_Font_Size_N2; break; case 2: inMessage = cmd_Format_Font_Size_N1; break; case 3: inMessage = cmd_Format_Font_Size_0; break; case 4: inMessage = cmd_Format_Font_Size_P1; break; case 5: inMessage = cmd_Format_Font_Size_P2; break; case 6: inMessage = cmd_Format_Font_Size_P3; break; case 7: inMessage = cmd_Format_Font_Size_P4; break; } } else if ( inMessage == 'Algn' ) { switch (*(long*)ioParam) { case 1: inMessage = cmd_JustifyLeft; break; case 2: inMessage = cmd_JustifyCenter; break; case 3: inMessage = cmd_JustifyRight; break; } } // GetHTMLView() guaranteed not to fail GetHTMLView()->ObeyCommand( inMessage, ioParam ); break; } } } // EDT_PreCloseCallbackFn static void closeEditorWindowCallback( void* hook ) { CEditorWindow *editorWindow = (CEditorWindow *)hook; if ( editorWindow ) editorWindow->SetPluginDoneClosing(); } Boolean CEditorWindow::ObeyCommand( CommandT inCommand, void *ioParam ) { switch ( inCommand ) { case cmd_NewWindowEditor: CEditorWindow::MakeEditWindow( NULL, NULL ); break; case cmd_ViewSource: // Delegate this to the view. if ( ((CEditView *)GetHTMLView())->IsDoneLoading() ) GetHTMLView()->ObeyCommand(inCommand, ioParam); break; case cmd_Toggle_Paragraph_Toolbar: ToggleDragBar(Paragraph_Bar_PaneID, eParagraphBar, Pref_ShowParagraphBar); break; case cmd_Toggle_Character_Toolbar: ToggleDragBar(Character_Bar_PaneID, eCharacterBar, Pref_ShowCharacterBar); break; case cmd_Close: case cmd_Quit: // we'll just intercept these and then send them on to the default case MWContext *mwcontext = GetWindowContext()->operator MWContext*(); History_entry* newEntry = SHIST_GetCurrent(&mwcontext->hist); CStr255 fileName; if ( newEntry && newEntry->address ) fileName = newEntry->address; if ( ((CEditView *)GetHTMLView())->IsDoneLoading() && EDT_DirtyFlag( *GetWindowContext() ) ) { Select(); // This helps during a quit or "close all" MessageT itemHit = HandleModalDialog( EDITDLG_SAVE_BEFORE_QUIT, fileName, NULL ); if (itemHit == cancel) return true; if (itemHit == ok) { // save if ( !((CEditView *)GetHTMLView())->SaveDocument() ) return true; } EDT_SetDirtyFlag( mwcontext, false ); // we have to do this or else when we quit, we will be asked twice to save } // need to let this work asynchronously; make our own internal loop if ( ((CEditView *)GetHTMLView())->IsDoneLoading() && newEntry && newEntry->address ) { mPluginDoneClosing = false; EDT_PreClose( mwcontext, newEntry->address, closeEditorWindowCallback, this ); do { CFrontApp::GetApplication()->ProcessNextEvent(); } while ( !mPluginDoneClosing ); } // fall through default: { return CBrowserWindow::ObeyCommand (inCommand, ioParam); } } return TRUE; } void CEditorWindow::FindCommandStatus( CommandT inCommand, Boolean& outEnabled, Boolean& outUsesMark, Char16& outMark, Str255 outName ) { short index; outUsesMark = FALSE; outEnabled = false; switch ( inCommand ) { case cmd_ViewSource: // Delegate this to the view. if ( ((CEditView *)GetHTMLView())->IsDoneLoading() ) GetHTMLView()->FindCommandStatus(inCommand, outEnabled, outUsesMark, outMark, outName); break; case cmd_Toggle_Character_Toolbar: outEnabled = ((CEditView *)GetHTMLView())->IsDoneLoading(); index = mToolbarShown[eCharacterBar] ? CEditView::EDITOR_MENU_HIDE_COMP_TOOLBAR : CEditView::EDITOR_MENU_SHOW_COMP_TOOLBAR; ::GetIndString( outName, CEditView::STRPOUND_EDITOR_MENUS, index ); break; case cmd_Toggle_Paragraph_Toolbar: outEnabled = ((CEditView *)GetHTMLView())->IsDoneLoading(); index = mToolbarShown[eParagraphBar] ? CEditView::EDITOR_MENU_HIDE_FORMAT_TOOLBAR : CEditView::EDITOR_MENU_SHOW_FORMAT_TOOLBAR; ::GetIndString( outName, CEditView::STRPOUND_EDITOR_MENUS, index ); break; default: CBrowserWindow::FindCommandStatus(inCommand, outEnabled, outUsesMark, outMark, outName ); } } // Called when we are trying to quit the application Boolean CEditorWindow::AttemptQuitSelf( long inSaveOption ) { MWContext *mwcontext = GetWindowContext()->operator MWContext*(); if ( EDT_DirtyFlag( mwcontext ) ) { History_entry* newEntry = SHIST_GetCurrent(&mwcontext->hist); CStr255 fileName; if ( newEntry && newEntry->address ) fileName = newEntry->address; if ( kAEAsk == inSaveOption ) { MessageT itemHit = HandleModalDialog( EDITDLG_SAVE_BEFORE_QUIT, fileName, NULL ); if ( cancel == itemHit ) return false; } // save if ( !((CEditView *)GetHTMLView())->SaveDocument() ) return false; } return true; } #ifdef PROFILE #pragma profile off #endif