/* -*- 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. */ /* TextEditorView.cpp - anything that has a text editable area in it. Crreated: Dora Hsu , Sept-30-96. */ #include "TextEditorView.h" #include "SpellHandler.h" #include #include "libi18n.h" #include "felocale.h" #include "xfe.h" // for CONTEXT_DATA #include "Xfe/Xfe.h" #ifdef DEBUG_editor #define XDEBUG(x) x #else #define XDEBUG(x) #endif extern "C" void fe_mail_text_modify_cb (Widget, XtPointer, XtPointer); extern "C" XtPointer fe_GetFont(MWContext *context, int sizeNum, int fontmask); extern "C" unsigned char * fe_ConvertToLocaleEncoding(int16 charset, unsigned char *str); MenuSpec XFE_TextEditorView::popupmenu_spec[] = { { xfeCmdCut, PUSHBUTTON }, { xfeCmdCopy, PUSHBUTTON }, { xfeCmdPaste, PUSHBUTTON }, { xfeCmdPasteAsQuoted, PUSHBUTTON }, MENU_SEPARATOR, { xfeCmdQuoteOriginalText, PUSHBUTTON }, {NULL} }; const char *XFE_TextEditorView::textFocusIn = "XFE_TextEditorView::textFocusIn"; XFE_TextEditorView::XFE_TextEditorView(XFE_Component* toplevel_component, XFE_View *parent_view, MSG_Pane *p, MWContext *context) : XFE_MNView(toplevel_component, parent_view, context, p) { m_popup = NULL; } void XFE_TextEditorView::createWidgets(Widget parentW, XP_Bool wrap_p) { int ac; Arg av[20]; Widget textW; MWContext *context = getParent()->getContext(); XmFontList fontList; Widget form; fontList = (XmFontList)fe_GetFont (context, 3, LO_FONT_FIXED); form = XmCreateForm(parentW, "plainTextForm", NULL, 0 ); ac = 0; XtSetArg(av[ac], XmNtopOffset, 5); ac++; XtSetArg (av[ac], XmNeditMode, XmMULTI_LINE_EDIT); ac++; XtSetArg (av[ac], XmNfontList, fontList); ac++; XtSetArg (av[ac], XmNscrollHorizontal, !wrap_p); ac++; XtSetArg (av[ac], XmNwordWrap, wrap_p); ac++; XtSetArg (av[ac], XmNtopAttachment, XmATTACH_FORM); ac++; XtSetArg (av[ac], XmNleftAttachment, XmATTACH_FORM); ac++; XtSetArg (av[ac], XmNbottomAttachment, XmATTACH_FORM); ac++; XtSetArg (av[ac], XmNrightAttachment, XmATTACH_FORM); ac++; textW = XmCreateScrolledText(parentW, "mailto_bodyText", av, ac); XtAddCallback (textW, XmNmodifyVerifyCallback, fe_mail_text_modify_cb, getParent()->getContext()); // NOTE... [ we need this for the ComposeView focus mechanism... // XtAddCallback(textW, XmNfocusCallback, textFocusCallback, this); // setBaseWidget(form); XtManageChild(textW); // This is a hack...NEED here... // Otherwise, we cannot swap the text widget upon wrap on/off. CONTEXT_DATA(m_contextData)->mcBodyText = textW; } void XFE_TextEditorView::textFocus() { Widget textW = CONTEXT_DATA(m_contextData)->mcBodyText; getToplevel()->notifyInterested(XFE_TextEditorView::textFocusIn, textW); } void XFE_TextEditorView::textFocusCallback(Widget, XtPointer clientData, XtPointer) { XFE_TextEditorView *obj = (XFE_TextEditorView*)clientData; obj->textFocus(); } void XFE_TextEditorView::setFont(XmFontList fontList) { int ac; Arg av[20]; Widget textW = CONTEXT_DATA(m_contextData)->mcBodyText; ac = 0; XtSetArg (av[ac], XmNfontList, fontList); ac++; XtSetValues(textW, av, ac); if (fe_LocaleCharSetID & MULTIBYTE) { Dimension columns; XtVaGetValues(textW, XmNcolumns, &columns, NULL); XtVaSetValues(textW, XmNcolumns, (columns + 1) / 2, NULL); } } void XFE_TextEditorView::setComposeWrapState(XP_Bool wrap_p) { Boolean old_wrap, new_wrap, old_scroll, new_scroll; Widget text, parent, gramp; MWContext *context = getParent()->getContext(); XP_ASSERT(context->type == MWContextMessageComposition); XP_ASSERT(CONTEXT_DATA(context)->mcBodyText); text = CONTEXT_DATA(context)->mcBodyText; if (!text) return; parent = XtParent(text); gramp = XtParent(parent); XtVaGetValues(text, XmNwordWrap, &old_wrap, XmNscrollHorizontal, &old_scroll, 0); new_wrap = wrap_p; new_scroll = !wrap_p; CONTEXT_DATA (context)->compose_wrap_lines_p = wrap_p; if (new_wrap == old_wrap && new_scroll == old_scroll) return; { String body = 0; XmTextPosition cursor = 0; unsigned char top = 0, bot = 0, left = 0, right = 0; Widget topw = 0, botw = 0, leftw = 0, rightw = 0; Arg av [20]; int ac = 0; XmFontList font_list = 0; /* #### warning much of this is cloned from mozilla.c (fe_create_composition_widgets) */ XtVaGetValues (text, XmNcursorPosition, &cursor, XmNfontList, &font_list, 0); body = fe_GetTextField(text); XtVaGetValues (parent, XmNtopAttachment, &top, XmNtopWidget, &topw, XmNbottomAttachment, &bot, XmNbottomWidget, &botw, XmNleftAttachment, &left, XmNleftWidget, &leftw, XmNrightAttachment, &right, XmNrightWidget, &rightw, 0); XtUnmanageChild(parent); XtDestroyWidget(parent); CONTEXT_DATA(context)->mcBodyText = text = parent = 0; XtSetArg (av[ac], XmNeditable, True); ac++; XtSetArg (av[ac], XmNcursorPositionVisible, True); ac++; XtSetArg (av[ac], XmNeditMode, XmMULTI_LINE_EDIT); ac++; XtSetArg (av[ac], XmNscrollVertical, TRUE); ac++; XtSetArg (av[ac], XmNscrollHorizontal, new_scroll); ac++; XtSetArg (av[ac], XmNwordWrap, new_wrap); ac++; XtSetArg (av[ac], XmNfontList, font_list); ac++; text = XmCreateScrolledText (gramp, "mailto_bodyText", av, ac); parent = XtParent(text); // This is really a hack since we changed the widget in the // middle of displaying fe_HackTranslations(m_contextData, text); if (fe_LocaleCharSetID & MULTIBYTE) { Dimension columns; XtVaGetValues(text, XmNcolumns, &columns, NULL); XtVaSetValues(text, XmNcolumns, (columns + 1) / 2, NULL); } /* Warning -- this assumes no widgets are attached to this one (which is the normal state of affairs, since this is the widget that should grow.) */ XtVaSetValues (parent, XmNtopAttachment, top, XmNtopWidget, topw, XmNbottomAttachment, bot, XmNbottomWidget, botw, XmNleftAttachment, left, XmNleftWidget, leftw, XmNrightAttachment, right, XmNrightWidget, rightw, 0); XtVaSetValues (text, XmNfontList, font_list, XmNcursorPosition, cursor, XmNwordWrap, new_wrap, 0); fe_SetTextField(text, (body ? body : "")); if (body) free(body); /* Put this callback back so that the "you haven't typed anything do you really want to send?" hack still works with this new text widget #### cloned from dialogs.c (FE_InitializeMailCompositionContext) */ XtAddCallback (text, XmNmodifyVerifyCallback, fe_mail_text_modify_cb, context); // NOTE... [ we need this for the ComposeView focus mechanism... // XtAddCallback(text, XmNfocusCallback, textFocusCallback, this); CONTEXT_DATA(context)->mcBodyText = text; XtManageChild(text); /* just in case it didn't take... */ XtVaSetValues (text, XmNcursorPosition, cursor, 0); /* Move focus back there. */ XmProcessTraversal (text, XmTRAVERSE_CURRENT); } } void XFE_TextEditorView::doCommand(CommandType cmd, void */*calldata*/, XFE_CommandInfo* info) { XDEBUG( printf ("in XFE_TextEditorView::doCommand(%s)\n", Command::getString(cmd));) Widget destW = XmGetDestination(XtDisplay(getBaseWidget())); Widget focusW= XmGetFocusWidget(getBaseWidget()); if ( (destW == CONTEXT_DATA(m_contextData)->mcBodyText || focusW == CONTEXT_DATA(m_contextData)->mcBodyText)) { // Now we are sure the focus is in the text area, // We should perform those commands that are operable in the area if (cmd == xfeCmdDelete) { XEvent *event = info->event; XtCallActionProc (CONTEXT_DATA(m_contextData)->mcBodyText, "delete-next-character", event, NULL, 0 ); } else if ( cmd == xfeCmdSelectAll ) { Widget text = CONTEXT_DATA(m_contextData)->mcBodyText; XmTextSetSelection(text, 0, XmTextGetLastPosition(text), info->event->xkey.time); } else if ( cmd == xfeCmdSpellCheck ) { xfe_TextSpellCheck(m_contextData); } #if 0 else if ( cmd == xfeCmdFindInObject ) { // Future work here return; } else if ( cmd == xfeCmdFindAgain ) { // Future work here return; } #endif } #if 0 //On XFE Compose Window, we don't do Popup anymore...as 4.x Spec indicated. // There are problem to deal with popup when view swaps... // This is a XFE framework problem... if ( cmd == xfeCmdShowPopup ) { // May need to find out if it's an HTML view or not XEvent *event = info->event; if (m_popup) delete m_popup; m_popup = new XFE_PopupMenu( (XFE_Frame *) getToplevel(), XfeAncestorFindApplicationShell(getToplevel()->getBaseWidget())); m_popup->addMenuSpec(popupmenu_spec); m_popup->position (event); m_popup->show(); } #endif } Boolean XFE_TextEditorView::isCommandEnabled(CommandType cmd, void */*calldata*/, XFE_CommandInfo*) { XDEBUG( printf ("in XFE_TextEditorView::isCommandEnabled(%s)\n", Command::getString(cmd));) Widget destW = XmGetDestination(XtDisplay(getBaseWidget())); Widget focusW= XmGetFocusWidget(getBaseWidget()); if ( cmd == xfeCmdShowPopup ) return True; else if (cmd == xfeCmdSpellCheck) { return xfe_SpellCheckerAvailable(m_contextData); } else if ( (destW == CONTEXT_DATA(m_contextData)->mcBodyText || focusW == CONTEXT_DATA(m_contextData)->mcBodyText)) { // Now we are sure the focus is in the mcBodyText Area, // we should turn on/off commands accordingly for this area if ( (cmd == xfeCmdDelete) || (cmd == xfeCmdSelectAll) ) return True; } XDEBUG( printf ("leaving XFE_TextEditorView::isCommandEnabled()\n");) return False; } Boolean XFE_TextEditorView::handlesCommand(CommandType command, void */*calldata*/, XFE_CommandInfo*) { XDEBUG( printf ("in XFE_TextEditorView::handlesCommand(%s)\n", Command::getString(command));) Widget destW = XmGetDestination(XtDisplay(getBaseWidget())); Widget focusW= XmGetFocusWidget(getBaseWidget()); if (command == xfeCmdGetNewMessages || command == xfeCmdAddNewsgroup || command == xfeCmdDelete || command == xfeCmdShowPopup || command == xfeCmdSpellCheck || command == xfeCmdDelete || command == xfeCmdSelectAll ) { return True; } XDEBUG( printf ("leaving XFE_TextEditorView::handlesCommand(%s)\n", Command::getString(command));) return False; } char* XFE_TextEditorView::getPlainText() { // This method suppose to get the text message in the widget // and return in a char string block return NULL; } // ---- These methods might need to move up to be XFE_EDITABLE ---------- void XFE_TextEditorView::insertMessageCompositionText( const char* text, XP_Bool leaveCursorAtBeginning, XP_Bool /*isHTML*/ ) { MWContext* context = getParent()->getContext(); XmTextPosition pos = 0, newpos = 0; unsigned char *loc; Widget bodyTextW= CONTEXT_DATA(m_contextData)->mcBodyText; XtRemoveCallback(bodyTextW, XmNmodifyVerifyCallback, fe_mail_text_modify_cb, getParent()->getContext()); XtVaGetValues(bodyTextW, XmNcursorPosition, &pos, 0); loc = fe_ConvertToLocaleEncoding(INTL_DefaultWinCharSetID(context), (unsigned char *) text); XmTextInsert(bodyTextW, pos, (char*) loc); if (((char *) loc) != text) { XP_FREE(loc); } XtVaGetValues(bodyTextW, XmNcursorPosition, &newpos, 0); if (leaveCursorAtBeginning) { XtVaSetValues(bodyTextW, XmNcursorPosition, pos, 0); } else if (pos == newpos) { /* On some motif (eg. AIX), text insertion point is not moved after * inserted text. We depend on that here. * * WARNING: XXX I18N watch. The strlen might not be the right i18n way. */ newpos = pos+strlen(text); XtVaSetValues(bodyTextW, XmNcursorPosition, newpos, 0); } XtAddCallback (bodyTextW, XmNmodifyVerifyCallback, fe_mail_text_modify_cb, getParent()->getContext()); } void XFE_TextEditorView::getMessageBody( char **pBody, uint32 *body_size, MSG_FontCode **font_changes) { MWContext *context = getParent()->getContext(); Dimension columns = 0; char *loc; unsigned char *tmp; Widget bodyTextW= CONTEXT_DATA(m_contextData)->mcBodyText; *pBody = 0; loc = 0; tmp = 0; XtVaGetValues (bodyTextW, XmNvalue, &loc, XmNcolumns, &columns, 0); if (fe_LocaleCharSetID & MULTIBYTE) { columns *= 2; } if (loc) { tmp = fe_ConvertFromLocaleEncoding(INTL_DefaultWinCharSetID(context), (unsigned char *) loc); } if (tmp) { #ifdef WRAP_EDITOR_TO_WINDOW_WIDTH /* Old way: always wrap to window width */ if (columns <= 0) columns = 79; *pBody = (char *) XP_WordWrap(INTL_DefaultWinCharSetID(context), tmp, columns, 1 /* look for '>' */); #else /* New way: obey the ``Wrap Lines to the new length specified by user'' toggle. */ if (CONTEXT_DATA(context)->compose_wrap_lines_p) { if (fe_globalPrefs.msg_wrap_length >0) columns = fe_globalPrefs.msg_wrap_length; else columns = 72; *pBody = (char *) XP_WordWrap(INTL_DefaultWinCharSetID(context), tmp, columns, 1 /* look for '>' */); } else { /* Else, don't wrap it at all. */ *pBody = (char*)tmp; tmp = 0; } #endif /* New way. */ if (loc != (char *) tmp) { XP_FREE(tmp); } } *body_size = (*pBody ? strlen(*pBody) : 0); *font_changes = 0; } void XFE_TextEditorView::doneWithMessageBody(char* pBody) { Widget bodyTextW= CONTEXT_DATA(m_contextData)->mcBodyText; char *loc; XtVaGetValues(bodyTextW, XmNvalue, &loc, 0); if (pBody != loc) { XP_FREE(pBody); } } Boolean XFE_TextEditorView::isModified() { return (!(CONTEXT_DATA(m_contextData)->mcCitedAndUnedited) && CONTEXT_DATA(m_contextData)->mcEdited); }