зеркало из https://github.com/mozilla/pjs.git
512 строки
15 KiB
C++
512 строки
15 KiB
C++
/* -*- 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 <dora@netscape.com>, Sept-30-96.
|
|
*/
|
|
|
|
|
|
|
|
#include "TextEditorView.h"
|
|
#include "SpellHandler.h"
|
|
#include <Xm/Text.h>
|
|
#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);
|
|
}
|
|
|
|
|
|
|
|
|