зеркало из https://github.com/mozilla/pjs.git
5199 строки
135 KiB
C
5199 строки
135 KiB
C
/* -*- Mode: C++; tab-width: 8; 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.
|
||
*/
|
||
/*
|
||
xfe.c --- other stuff specific to the X front end.
|
||
Created: Jamie Zawinski <jwz@netscape.com>, 22-Jun-94.
|
||
*/
|
||
|
||
#include "rosetta.h"
|
||
#include "mozilla.h"
|
||
#include "altmail.h"
|
||
#include "xfe.h"
|
||
#include "fonts.h"
|
||
#include "felocale.h"
|
||
#include "intl_csi.h"
|
||
#include "selection.h"
|
||
#include "rdf.h"
|
||
#include "private/prpriv.h" /* for PR_GetMonitorEntryCount */
|
||
|
||
#if defined(SMOOTH_PROGRESS)
|
||
#include "progress.h"
|
||
#endif
|
||
|
||
#include <plevent.h> /* for mocha */
|
||
#include <prtypes.h>
|
||
#include <libevent.h>
|
||
#include <xp_list.h>
|
||
|
||
/* Layering support - LO_RefreshArea is called through compositor */
|
||
#include "layers.h"
|
||
|
||
#include <X11/IntrinsicP.h>
|
||
#include <X11/ShellP.h>
|
||
#include <X11/Xatom.h>
|
||
#ifdef DEBUG
|
||
#include <X11/Xmu/Editres.h> /* for editres to work */
|
||
#endif
|
||
|
||
#include <Xfe/Xfe.h> /* for xfe widgets and utilities */
|
||
|
||
#ifdef EDITOR
|
||
#include "xeditor.h"
|
||
#endif /* EDITOR */
|
||
#include <Xm/Label.h>
|
||
|
||
#ifdef AIXV3
|
||
#include <sys/select.h>
|
||
#endif /* AIXV3 */
|
||
|
||
#include <sys/time.h>
|
||
|
||
#include <np.h>
|
||
|
||
#include "msgcom.h"
|
||
#include "secnav.h"
|
||
#include "mozjava.h"
|
||
|
||
#include "libmocha.h"
|
||
#include "libevent.h"
|
||
|
||
#include "prefapi.h"
|
||
#include "NSReg.h"
|
||
#ifdef MOZ_SMARTUPDATE
|
||
#include "softupdt.h"
|
||
#endif
|
||
|
||
#if defined(_HPUX_SOURCE)
|
||
/* I don't know where this is coming from... "ld -y Error" says
|
||
/lib/libPW.a(xlink.o): Error is DATA UNSAT
|
||
/lib/libPW.a(xmsg.o): Error is DATA UNSAT
|
||
*/
|
||
int Error = 0;
|
||
#endif
|
||
|
||
#include "libi18n.h"
|
||
|
||
#include "libimg.h" /* Image Library public API. */
|
||
#include "il_util.h" /* Colormap/colorspace utilities. */
|
||
|
||
/* for XP_GetString() */
|
||
#include <xpgetstr.h>
|
||
|
||
#ifndef NO_WEB_FONTS
|
||
#include "nf.h"
|
||
#include "Mnfrc.h"
|
||
#include "Mnfrf.h"
|
||
#endif
|
||
|
||
#include "XmL/GridP.h"
|
||
#include "XmL/Grid.h"
|
||
|
||
extern int XFE_DOCUMENT_DONE;
|
||
extern int XFE_ERROR_SAVING_OPTIONS;
|
||
extern int XFE_STDERR_DIAGNOSTICS_HAVE_BEEN_TRUNCATED;
|
||
extern int XFE_ERROR_CREATING_PIPE;
|
||
extern int XFE_DISPLAY_FACTORY_INSTALL_COLORMAP_ERROR;
|
||
|
||
extern char *fe_calendar_path;
|
||
extern char *fe_host_on_demand_path;
|
||
extern char *fe_conference_path;
|
||
|
||
extern MWContext *last_documented_xref_context;
|
||
extern LO_Element *last_documented_xref;
|
||
extern LO_AnchorData *last_documented_anchor_data;
|
||
|
||
int fe_WindowCount = 0;
|
||
|
||
/*
|
||
* GLOBALS for new fe_find_scrollbar_sizes mechanism...
|
||
*/
|
||
int fe_ScrollBarW = 0;
|
||
int fe_ScrollBarH = 0;
|
||
Boolean fe_SBW_P = False;
|
||
Boolean fe_SBH_P = False;
|
||
|
||
MWContext *someGlobalContext = NULL;
|
||
static Boolean has_session_mgr= False;
|
||
|
||
#ifdef MOZ_MAIL_NEWS
|
||
MSG_Master* fe_mailMaster = NULL;
|
||
MSG_Master* fe_newsMaster = NULL;
|
||
#endif
|
||
|
||
struct fe_MWContext_cons *fe_all_MWContexts = 0;
|
||
|
||
void fe_delete_cb (Widget, XtPointer, XtPointer);
|
||
|
||
void fe_save_history_timer (XtPointer closure, XtIntervalId *id);
|
||
extern void fe_MakeAddressBookWidgets(Widget shell, MWContext *context);
|
||
|
||
extern void fe_ab_destroy_cb (Widget, XtPointer, XtPointer);
|
||
#ifdef UNIX_LDAP
|
||
extern void fe_ldapsearch_destroy_cb(Widget, XtPointer, XtPointer);
|
||
#endif
|
||
extern XP_Bool fe_ReadLastUserHistory(char **hist_entry_ptr);
|
||
|
||
#define DO_NOT_PASS_LAYER_N_EVENT 1
|
||
#define HANDLE_LEAVE_WIN 1
|
||
|
||
#if DO_NOT_PASS_LAYER_N_EVENT
|
||
extern void
|
||
fe_HTMLViewTooltips(MWContext *context,
|
||
int x, int y, char *alt_text,
|
||
XFE_TipStringCallbackStruct *cb_info);
|
||
#endif
|
||
|
||
/*
|
||
* Keep track of tooltip mapping to avoid conflict with fascist shells
|
||
* that insist on raising themselves - like taskbar and netcaster webtop
|
||
*/
|
||
static Boolean fe_tooltip_is_showing = False;
|
||
|
||
#if XmVersion >= 2000
|
||
#define _XM_OBJECT_AT_POINT XmObjectAtPoint
|
||
#else
|
||
#define _XM_OBJECT_AT_POINT _XmInputInGadget
|
||
#endif
|
||
|
||
void
|
||
fe_url_exit (URL_Struct *url, int status, MWContext *context)
|
||
{
|
||
XP_ASSERT (status == MK_INTERRUPTED ||
|
||
CONTEXT_DATA (context)->active_url_count > 0);
|
||
|
||
if (CONTEXT_DATA (context)->active_url_count > 0)
|
||
CONTEXT_DATA (context)->active_url_count--;
|
||
|
||
/* We should be guarenteed that XFE_AllConnectionsComplete() will be called
|
||
right after this, if active_url_count has just gone to 0. */
|
||
if (status < 0 && url->error_msg)
|
||
{
|
||
FE_Alert (context, url->error_msg);
|
||
}
|
||
#if defined(EDITOR) && defined(EDITOR_UI)
|
||
/*
|
||
* Do stuff specific to the editor
|
||
*/
|
||
if (context->type == MWContextEditor)
|
||
fe_EditorGetUrlExitRoutine(context, url, status);
|
||
#endif
|
||
if (status != MK_CHANGING_CONTEXT)
|
||
{
|
||
NET_FreeURLStruct (url);
|
||
}
|
||
}
|
||
|
||
/* this func is basically XP_FindContextOfType but only returns
|
||
* a top-level non-nethelp browser context with chrome
|
||
*/
|
||
MWContext*
|
||
fe_FindNonCustomBrowserContext(MWContext *context)
|
||
{
|
||
XP_List *context_list;
|
||
int n_context_list;
|
||
int i;
|
||
|
||
/* If our current context has the right type, go there */
|
||
if (context && context->type == MWContextBrowser &&
|
||
!context->is_grid_cell
|
||
&& !CONTEXT_DATA(context)->hasCustomChrome
|
||
&& context->pHelpInfo == NULL
|
||
&& !(context->name != NULL && /* don't use view source either */
|
||
XP_STRNCMP(context->name, "view-source", 11) == 0)) {
|
||
return context;
|
||
}
|
||
|
||
/* otherwise, just get any other Browser context */
|
||
context_list = XP_GetGlobalContextList();
|
||
n_context_list = XP_ListCount(context_list);
|
||
for (i = 1; i <= n_context_list; i++) {
|
||
MWContext * compContext =
|
||
(MWContext *)XP_ListGetObjectNum(context_list, i);
|
||
if (compContext->type == MWContextBrowser &&
|
||
!compContext->is_grid_cell
|
||
&& !CONTEXT_DATA(compContext)->hasCustomChrome
|
||
&& compContext->pHelpInfo == NULL
|
||
&& !(context->name != NULL && /* don't use view source either */
|
||
XP_STRNCMP(context->name, "view-source", 11) == 0)) {
|
||
/*
|
||
* NOTE: make sure that this window is "open"...
|
||
*
|
||
*/
|
||
FE_RaiseWindow(compContext);
|
||
return compContext;
|
||
}
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
int
|
||
FE_GetURL (MWContext *context, URL_Struct *url)
|
||
{
|
||
if (!context)
|
||
return -1;
|
||
|
||
/*
|
||
* Rules lifted from winfe:
|
||
*
|
||
* The only circumstance in which we load a URL
|
||
* into Editor when called from XP code is during
|
||
* Publishing: we have "files to post". In general, the
|
||
* editor does not use fe_GetURL(). Editor BE has special
|
||
* calls that deal with the special pushups an editor context
|
||
* needs. This code delegates geturl() requests made of an editor
|
||
* to a browser context. This will happen for help, nethelp,
|
||
* about:, etc...djw
|
||
*/
|
||
if (EDT_IS_EDITOR(context) && url->files_to_post == NULL) {
|
||
|
||
MWContext* real_context;
|
||
|
||
real_context = fe_FindNonCustomBrowserContext(context);
|
||
|
||
if (!real_context) {
|
||
real_context = FE_MakeNewWindow(context, NULL, NULL, NULL);
|
||
}
|
||
|
||
context = real_context;
|
||
}
|
||
|
||
return fe_GetURL (context, url, FALSE);
|
||
}
|
||
|
||
|
||
extern int XFE_DesktopTypeTranslate(const char*,char**,int);
|
||
|
||
extern Boolean
|
||
xfe_NewWindowRequired(MWContext *context, const char *url)
|
||
{
|
||
#ifdef MOZ_MAIL_NEWS
|
||
MSG_Pane *msgPane = MSG_FindPaneOfContext(context, MSG_MESSAGEPANE);
|
||
MSG_Pane *threadPane = MSG_FindPaneOfContext(context, MSG_THREADPANE);
|
||
MSG_Pane *folderPane = MSG_FindPaneOfContext(context, MSG_FOLDERPANE);
|
||
#endif
|
||
if (!context)
|
||
return True;
|
||
|
||
#ifdef MOZ_MAIL_NEWS
|
||
if (folderPane && MSG_RequiresNewsWindow(url))
|
||
return False;
|
||
#endif
|
||
|
||
return True;
|
||
}
|
||
|
||
/* View the URL in the given context.
|
||
A pointer to url_string is not retained.
|
||
Returns the status of NET_GetURL().
|
||
*/
|
||
int
|
||
fe_GetURL (MWContext *context, URL_Struct *url, Boolean skip_get_url)
|
||
{
|
||
int32 x, y;
|
||
LO_Element *e;
|
||
History_entry *he;
|
||
MWContext *new_context = NULL;
|
||
char *new_address=NULL;
|
||
URL_Struct *desktop_url=NULL;
|
||
Boolean needNewWindow = False;
|
||
/*
|
||
* See if the URL points to a local file which is a Netscape desktop
|
||
* file type. If so, extract the url from the file and create a new
|
||
* URL_Struct. (added for desktop dnd integration - Alastair.)
|
||
*/
|
||
|
||
if (XFE_DesktopTypeTranslate(url->address,&new_address,TRUE)) {
|
||
if (new_address && XP_STRLEN(new_address)>0) {
|
||
desktop_url=NET_CreateURLStruct (new_address,NET_DONT_RELOAD);
|
||
if (desktop_url) {
|
||
NET_FreeURLStruct(url);
|
||
url=desktop_url;
|
||
}
|
||
}
|
||
}
|
||
|
||
#ifdef JAVA
|
||
/*
|
||
** This hack is to get around the crash when we have an
|
||
** auto-config proxy installed, and we come up in the mail
|
||
** window and get a message with an applet on it:
|
||
*/
|
||
if (context->type == MWContextJava) {
|
||
context = NSN_JavaContextToRealContext(context);
|
||
}
|
||
#endif
|
||
|
||
#ifdef MOZ_MAIL_NEWS
|
||
/* If this URL requires a particular kind of window, and this is not
|
||
that kind of window, then we need to find or create one.
|
||
*/
|
||
if (!skip_get_url &&
|
||
#ifdef EDITOR
|
||
!(context->is_editor) &&
|
||
#endif
|
||
#if 1
|
||
(needNewWindow = MSG_NewWindowRequiredForURL(context, url)) &&
|
||
#else
|
||
(needNewWindow = MSG_NewWindowRequired(context, url->address)) &&
|
||
#endif
|
||
XP_STRCMP(url->address, "search-libmsg:"))
|
||
{
|
||
|
||
XP_ASSERT (!MSG_NewWindowProhibited (context, url->address));
|
||
|
||
if ( context->type != MWContextSearch
|
||
&& context->type != MWContextSearchLdap
|
||
/* Skip for Nethelp which will create its
|
||
* own new window if it is needed */
|
||
&& NET_URL_Type(url->address) != NETHELP_TYPE_URL )
|
||
|
||
{
|
||
|
||
if ( MSG_RequiresBrowserWindow(url->address) )
|
||
{
|
||
new_context = fe_FindNonCustomBrowserContext(context);
|
||
}
|
||
|
||
if (!new_context )
|
||
{
|
||
context = fe_MakeWindow (XtParent (CONTEXT_WIDGET (context)), context,
|
||
url, NULL, MWContextBrowser, FALSE);
|
||
return (context ? 0 : -1);
|
||
}
|
||
else
|
||
{
|
||
/* If we find a new browser context, use it to display URL
|
||
*/
|
||
if (context != new_context) {
|
||
/* If we have picked an existing context that isn't this
|
||
* one in which to display this document, make sure that
|
||
* context is uniconified and raised first.
|
||
*/
|
||
|
||
/* Popup the shell first, so that we gurantee its realized
|
||
*/
|
||
XtPopup(CONTEXT_WIDGET(new_context),XtGrabNone);
|
||
|
||
/* Force the window to the front and de-iconify if needed
|
||
*/
|
||
XMapRaised(XtDisplay(CONTEXT_WIDGET(new_context)),
|
||
XtWindow(CONTEXT_WIDGET(new_context)));
|
||
}
|
||
context = new_context;
|
||
}
|
||
}
|
||
}
|
||
#endif /* MOZ_MAIL_NEWS */
|
||
|
||
e = LO_XYToNearestElement (context,
|
||
CONTEXT_DATA (context)->document_x,
|
||
CONTEXT_DATA (context)->document_y,
|
||
NULL);
|
||
|
||
he = SHIST_GetCurrent (&context->hist);
|
||
|
||
if (e)
|
||
SHIST_SetPositionOfCurrentDoc (&context->hist, e->lo_any.ele_id);
|
||
|
||
/* LOU: don't do this any more since Layout will do it
|
||
* for you once the page starts loading
|
||
*
|
||
* Here is why we have to do this for now...
|
||
*
|
||
* | This is important feedback on this new "feature" (or misfeature as
|
||
* | it may turn out).
|
||
*
|
||
* If this is the feature where the front end no longer calls
|
||
* XP_InterruptContext() inside fe_GetURL(), but only when data from
|
||
* the new URL arrives, then I think I understand what's going on.
|
||
*
|
||
* | I didn't expect the images that are currently transfering to interfere
|
||
* | significantly with the TCP connection setup,
|
||
*
|
||
* They won't interfere within TCP or IP modules on intervening routers.
|
||
* ISDN signalling guarantees perfect scheduling of the D-channel; modem
|
||
* link layers are fair.
|
||
*
|
||
* The problem is this: before, when fe_GetURL() called XP_InterruptContext(),
|
||
* the connections for the old document were closed immediately. Those TCP
|
||
* closes turned into packets with the FIN flag set on the wire, which went to
|
||
* the server over the fairly-scheduled link. The client moved into TCP state
|
||
* FIN_WAIT_1, indicating that it had sent a FIN but needed an ACK, and that
|
||
* the server had not yet closed its half of the connection. As soon as the
|
||
* next TCP segment for this connection arrived at the client, the client TCP
|
||
* noticed that the user process had closed, so it aborted the connection by
|
||
* sending a RST.
|
||
*
|
||
* Since the change to the front ends, it appears the old connections don't
|
||
* (all) get closed until data on the new connection arrives. The data for
|
||
* those old connections that are not yet closed will still be queued in the
|
||
* server's TCP, and sent as the client ACKs previous data and updates the
|
||
* server's window. There is no way for an HTTP daemon to cancel the write
|
||
* or send syscalls that enqueued this data, once the syscall has returned.
|
||
* Only a RST from the client for each old connection will cause the server
|
||
* to kill its enqueued data.
|
||
*
|
||
* I doubt there is much data in the fairly narrow pipe between server and
|
||
* client. Because the pipe is narrow, the server TCP has not opened its
|
||
* congestion window, and there cannot be much data in flight. The problem
|
||
* is lack of abort because of lack of close() when the new URL is clicked.
|
||
*
|
||
* | Perhaps there is some TCP magic that we can do
|
||
* | to cause the new stream to have absolute priority over other TCP
|
||
* | streams.
|
||
*
|
||
* No such magic, nor any need for it in this scenario.
|
||
*
|
||
* | I could also pause all the transfering connections while
|
||
* | we are waiting.
|
||
*
|
||
* Only a close, which guarantees an abort when more old data arrives, will
|
||
* do the trick. Anything else requires server mods as well as client.
|
||
*/
|
||
if (!skip_get_url &&
|
||
XP_STRNCMP("view-source:", url->address, 12) != 0) {
|
||
XP_InterruptContext (context);
|
||
}
|
||
|
||
if (CONTEXT_DATA (context)->refresh_url_timer)
|
||
{
|
||
XtRemoveTimeOut (CONTEXT_DATA (context)->refresh_url_timer);
|
||
CONTEXT_DATA (context)->refresh_url_timer = 0;
|
||
}
|
||
if (CONTEXT_DATA (context)->refresh_url_timer_url)
|
||
{
|
||
free (CONTEXT_DATA (context)->refresh_url_timer_url);
|
||
CONTEXT_DATA (context)->refresh_url_timer_url = 0;
|
||
}
|
||
|
||
if (url && XP_FindNamedAnchor (context, url, &x, &y))
|
||
{
|
||
char *temp;
|
||
URL_Struct *urlcp;
|
||
|
||
fe_ScrollTo (context, 0, y);
|
||
|
||
if (! CONTEXT_DATA (context)->is_resizing) {
|
||
fe_SetURLString (context, url);
|
||
}
|
||
|
||
/* Create URL from prev history entry to preserve security, etc. */
|
||
urlcp = SHIST_CreateURLStructFromHistoryEntry(context, he);
|
||
|
||
/* Swap addresses. */
|
||
temp = url->address;
|
||
url->address = urlcp->address;
|
||
urlcp->address = temp;
|
||
|
||
/* set history_num correctly */
|
||
urlcp->history_num = url->history_num;
|
||
|
||
/* Free old URL, and reassign. */
|
||
NET_FreeURLStruct(url);
|
||
url = urlcp;
|
||
NET_FreeURLStruct (url);
|
||
fe_RefreshAllAnchors ();
|
||
return 0;
|
||
}
|
||
else
|
||
{
|
||
#ifdef EDITOR
|
||
/*
|
||
* We have to pass "about:" docs through the old way,
|
||
* cause there's all this magic that happens! We do trap
|
||
* these in fe_EditorNew() so the user will not be allowed
|
||
* to edit them, but *we* do GetURLs on them as well.
|
||
*/
|
||
if (EDT_IS_EDITOR(context) && strncasecmp(url->address, "about:", 6)!= 0)
|
||
return fe_GetSecondaryURL (context, url, FO_EDIT, 0,
|
||
skip_get_url);
|
||
else
|
||
#endif
|
||
return fe_GetSecondaryURL (context, url, FO_CACHE_AND_PRESENT, 0,
|
||
skip_get_url);
|
||
}
|
||
}
|
||
|
||
|
||
/* Start loading a URL other than the main one (ie, an embedded image.)
|
||
A pointer to url_string is not retained.
|
||
Returns the status of NET_GetURL().
|
||
*/
|
||
int
|
||
fe_GetSecondaryURL (MWContext *context,
|
||
URL_Struct *url_struct,
|
||
int output_format,
|
||
void *call_data, Boolean skip_get_url)
|
||
{
|
||
/* The URL will be freed when libnet calls the fe_url_exit() callback. */
|
||
|
||
/* If the URL_Struct already has fe_data, try to preserve it. (HTTP publishing in the
|
||
editor, EDT_PublishFileTo(), uses it.) */
|
||
if (call_data) {
|
||
XP_ASSERT(!url_struct->fe_data);
|
||
url_struct->fe_data = call_data;
|
||
}
|
||
/* else leave url_struct->fe_data alone */
|
||
|
||
|
||
CONTEXT_DATA (context)->active_url_count++;
|
||
if (CONTEXT_DATA (context)->active_url_count == 1)
|
||
{
|
||
CONTEXT_DATA (context)->clicking_blocked = True;
|
||
|
||
if (context->is_grid_cell)
|
||
{
|
||
MWContext *top_context = context;
|
||
while ((top_context->grid_parent)&&
|
||
(top_context->grid_parent->is_grid_cell))
|
||
{
|
||
top_context = top_context->grid_parent;
|
||
}
|
||
#if !defined(SMOOTH_PROGRESS)
|
||
fe_StartProgressGraph (top_context->grid_parent);
|
||
#endif
|
||
}
|
||
else
|
||
{
|
||
#if !defined(SMOOTH_PROGRESS)
|
||
fe_StartProgressGraph (context);
|
||
#endif
|
||
}
|
||
|
||
#if defined(SMOOTH_PROGRESS)
|
||
PM_EnsureProgressManager(context);
|
||
#endif
|
||
|
||
fe_SetCursor (context, False);
|
||
|
||
/* Enable stop here (uses active_url_count)
|
||
* This makes the stop button active during
|
||
* host lookup and initial connection.
|
||
*/
|
||
FE_UpdateStopState(context);
|
||
}
|
||
|
||
if (skip_get_url)
|
||
{
|
||
return(0);
|
||
}
|
||
|
||
return NET_GetURL (url_struct, output_format, context, fe_url_exit);
|
||
}
|
||
|
||
static XtInputId fe_fds_to_XtInputIds [FD_SETSIZE] = { 0, };
|
||
|
||
static void
|
||
fe_stream_callback (void *closure, int *source, XtInputId *id)
|
||
{
|
||
#ifdef QUANTIFY
|
||
quantify_start_recording_data();
|
||
#endif /* QUANTIFY */
|
||
|
||
#ifdef QUANTIFY
|
||
quantify_stop_recording_data();
|
||
#endif /* QUANTIFY */
|
||
}
|
||
|
||
static void
|
||
fe_add_input (int fd, int mask)
|
||
{
|
||
if (fd < 0 || fd >= countof (fe_fds_to_XtInputIds))
|
||
abort ();
|
||
|
||
LOCK_FDSET();
|
||
|
||
#ifdef UNIX_ASYNC_DNS
|
||
if (fe_UseAsyncDNS() && fe_fds_to_XtInputIds [fd]) {
|
||
/* If we're already selecting input on this fd, don't select it again
|
||
and lose our pointer to the XtInput object.. This shouldn't happen,
|
||
but netlib does this when async DNS lookups are happening. (This
|
||
will lose if the `mask' arg has changed on two consecutive calls
|
||
without an intervening call to `fe_remove_input', but that doesn't
|
||
happen.) -- jwz, 9-Jan-97.
|
||
*/
|
||
goto DONE;
|
||
}
|
||
#endif
|
||
|
||
fe_fds_to_XtInputIds [fd] = XtAppAddInput (fe_XtAppContext, fd,
|
||
(XtPointer) mask,
|
||
fe_stream_callback, 0);
|
||
#ifdef JAVA
|
||
if (PR_CurrentThread() != mozilla_thread) {
|
||
/*
|
||
** Sometimes a non-mozilla thread will be using the netlib to fetch
|
||
** data. Because mozilla is the only thread that runs the netlib
|
||
** "select" code, we need to be able to kick mozilla and wake it up
|
||
** when the select set has changed.
|
||
**
|
||
** A way to do this would be to have mozilla stop calling select
|
||
** and instead manually manipulate the idle' thread's select set,
|
||
** but there is yet to be an NSPR interface at that level.
|
||
*/
|
||
PR_PostEvent(mozilla_event_queue, NULL);
|
||
}
|
||
#endif /* JAVA */
|
||
|
||
#ifdef UNIX_ASYNC_DNS
|
||
DONE:
|
||
#endif
|
||
UNLOCK_FDSET();
|
||
}
|
||
|
||
static void
|
||
fe_remove_input (int fd)
|
||
{
|
||
if (fd < 0 || fd >= countof (fe_fds_to_XtInputIds))
|
||
return; /* was abort() -- */
|
||
|
||
LOCK_FDSET();
|
||
|
||
if (fe_fds_to_XtInputIds [fd] != 0) {
|
||
XtRemoveInput (fe_fds_to_XtInputIds [fd]);
|
||
fe_fds_to_XtInputIds [fd] = 0;
|
||
}
|
||
|
||
UNLOCK_FDSET();
|
||
}
|
||
|
||
void
|
||
FE_SetReadSelect (MWContext *context, int fd)
|
||
{
|
||
fe_add_input (fd, XtInputReadMask | XtInputExceptMask);
|
||
}
|
||
|
||
void
|
||
FE_SetConnectSelect (MWContext *context, int fd)
|
||
{
|
||
fe_add_input (fd, (XtInputWriteMask | XtInputExceptMask));
|
||
}
|
||
|
||
void
|
||
FE_ClearReadSelect (MWContext *context, int fd)
|
||
{
|
||
fe_remove_input (fd);
|
||
}
|
||
|
||
void
|
||
FE_ClearConnectSelect (MWContext *context, int fd)
|
||
{
|
||
FE_ClearReadSelect (context, fd);
|
||
}
|
||
|
||
void
|
||
FE_ClearFileReadSelect (MWContext *context, int fd)
|
||
{
|
||
FE_ClearReadSelect (context, fd);
|
||
}
|
||
|
||
void
|
||
FE_SetFileReadSelect (MWContext *context, int fd)
|
||
{
|
||
FE_SetReadSelect (context, fd);
|
||
}
|
||
|
||
|
||
/* making contexts and windows */
|
||
|
||
static unsigned int
|
||
default_char_width (int charset, fe_Font font)
|
||
{
|
||
XCharStruct overall;
|
||
int ascent, descent;
|
||
FE_TEXT_EXTENTS(charset, font, "n", 1, &ascent, &descent, &overall);
|
||
return overall.width;
|
||
}
|
||
|
||
|
||
static void fe_pick_visual_and_colormap (Widget toplevel,
|
||
MWContext *new_context);
|
||
void fe_get_context_resources (MWContext *context);
|
||
void fe_get_final_context_resources (MWContext *context);
|
||
|
||
MWContext *
|
||
fe_MakeWindow(Widget toplevel, MWContext *context_to_copy,
|
||
URL_Struct *url, char *window_name, MWContextType type,
|
||
Boolean skip_get_url)
|
||
{
|
||
return fe_MakeNewWindow(toplevel, context_to_copy, url, window_name, type,
|
||
skip_get_url, NULL);
|
||
}
|
||
|
||
static void
|
||
fe_position_download_context(MWContext *context)
|
||
{
|
||
MWContext *active_context = NULL;
|
||
Widget shell = CONTEXT_WIDGET(context);
|
||
|
||
XP_ASSERT(context->type == MWContextSaveToDisk);
|
||
|
||
/* Lets position this ourselves. If window manager interactive placement
|
||
is on and this context doesn't exist for more than a few microsecs,
|
||
then the user would see this as a outline, place it somewhere but
|
||
wouldn't see a window at all.
|
||
*/
|
||
if (fe_all_MWContexts->next)
|
||
active_context = fe_all_MWContexts->next->context;
|
||
if (active_context) {
|
||
WMShellWidget wmshell = (WMShellWidget) shell;
|
||
Widget parent = CONTEXT_WIDGET(active_context);
|
||
Screen* screen = XtScreen (parent);
|
||
Dimension screen_width = WidthOfScreen (screen);
|
||
Dimension screen_height = HeightOfScreen (screen);
|
||
Dimension parent_width = 0;
|
||
Dimension parent_height = 0;
|
||
Dimension child_width = 0;
|
||
Dimension child_height = 0;
|
||
Position x;
|
||
Position y;
|
||
XSizeHints size_hints;
|
||
|
||
XtRealizeWidget (shell); /* to cause its size to be computed */
|
||
|
||
XtVaGetValues(shell,
|
||
XtNwidth, &child_width, XtNheight, &child_height, 0);
|
||
XtVaGetValues (parent,
|
||
XtNwidth, &parent_width, XtNheight, &parent_height, 0);
|
||
|
||
x = (((Position)parent_width) - ((Position)child_width)) / 2;
|
||
y = (((Position)parent_height) - ((Position)child_height)) / 2;
|
||
XtTranslateCoords (parent, x, y, &x, &y);
|
||
|
||
if ((Dimension) (x + child_width) > screen_width)
|
||
x = screen_width - child_width;
|
||
if (x < 0)
|
||
x = 0;
|
||
|
||
if ((Dimension) (y + child_height) > screen_height)
|
||
y = screen_height - child_height;
|
||
if (y < 0)
|
||
y = 0;
|
||
|
||
XtVaSetValues (shell, XtNx, x, XtNy, y, 0);
|
||
|
||
/* Horrific kludge */
|
||
wmshell->wm.size_hints.flags &= (~PPosition);
|
||
wmshell->wm.size_hints.flags |= USPosition;
|
||
if (XGetNormalHints (XtDisplay(shell), XtWindow(shell), &size_hints)) {
|
||
size_hints.x = wmshell->wm.size_hints.x;
|
||
size_hints.y = wmshell->wm.size_hints.y;
|
||
size_hints.flags &= (~PPosition);
|
||
size_hints.flags |= USPosition;
|
||
XSetNormalHints (XtDisplay(shell), XtWindow(shell), &size_hints);
|
||
}
|
||
}
|
||
}
|
||
|
||
void
|
||
fe_find_scrollbar_sizes(MWContext *context)
|
||
{
|
||
/* Figure out how much space the horizontal & vertical scrollbars take up.
|
||
* It's basically impossible to determine this before creating them...
|
||
*/
|
||
if (CONTEXT_DATA(context)->scrolled) {
|
||
Boolean hscrollP = XtIsManaged(CONTEXT_DATA (context)->hscroll);
|
||
Boolean vscrollP = XtIsManaged(CONTEXT_DATA (context)->vscroll);
|
||
Dimension w1 = 0, w2 = 0;
|
||
Dimension h1 = 0, h2 = 0;
|
||
|
||
XtVaGetValues (CONTEXT_DATA (context)->drawing_area,
|
||
XmNwidth, &w2,
|
||
XmNheight, &h2,
|
||
0);
|
||
|
||
/* Vertical ScrollBar dimensions...
|
||
*
|
||
*/
|
||
if (!fe_SBW_P) {
|
||
XtVaGetValues (CONTEXT_DATA (context)->scrolled,
|
||
XmNwidth, &w1, 0);
|
||
if (vscrollP) {
|
||
fe_ScrollBarW = w1 - w2;
|
||
fe_SBW_P = True;
|
||
}
|
||
else {
|
||
/*
|
||
* MAGIC Number... [ default value ]
|
||
*/
|
||
fe_ScrollBarW = 15;
|
||
}
|
||
}
|
||
CONTEXT_DATA (context)->sb_w = fe_ScrollBarW;
|
||
CONTEXT_DATA (context)->scrolled_width = (unsigned long) w2;
|
||
|
||
/* Horizontal ScrollBar dimensions...
|
||
*
|
||
*/
|
||
if (!fe_SBH_P) {
|
||
if (hscrollP) {
|
||
XtVaGetValues (CONTEXT_DATA (context)->scrolled,
|
||
XmNheight, &h1, 0);
|
||
fe_ScrollBarH = h1 - h2;
|
||
fe_SBH_P = True;
|
||
}
|
||
else {
|
||
/*
|
||
* MAGIC Number... [ default value ]
|
||
*/
|
||
fe_ScrollBarH = 15;
|
||
}
|
||
}
|
||
CONTEXT_DATA (context)->sb_h = fe_ScrollBarH;
|
||
CONTEXT_DATA (context)->scrolled_height = (unsigned long) h2;
|
||
}
|
||
}
|
||
|
||
|
||
void
|
||
fe_load_default_font(MWContext *context)
|
||
{
|
||
fe_Font font;
|
||
int ascent;
|
||
int descent;
|
||
int16 charset;
|
||
|
||
charset = CS_LATIN1;
|
||
font = fe_LoadFontFromFace(context, NULL, &charset, 0, 3, 0);
|
||
if (!font)
|
||
{
|
||
CONTEXT_DATA (context)->line_height = 17;
|
||
return;
|
||
}
|
||
FE_FONT_EXTENTS(CS_LATIN1, font, &ascent, &descent);
|
||
CONTEXT_DATA (context)->line_height = ascent + descent;
|
||
fe_DoneWithFont(font);
|
||
}
|
||
|
||
static void
|
||
fe_focus_notify_eh (Widget w, XtPointer closure, XEvent *ev, Boolean *cont)
|
||
{
|
||
MWContext *context = (MWContext *) closure;
|
||
JSEvent *event;
|
||
|
||
TRACEMSG (("fe_focus_notify_eh\n"));
|
||
switch (ev->type) {
|
||
case FocusIn:
|
||
TRACEMSG (("focus in\n"));
|
||
fe_MochaFocusNotify(context, NULL);
|
||
break;
|
||
case FocusOut:
|
||
TRACEMSG (("focus out\n"));
|
||
fe_MochaBlurNotify(context, NULL);
|
||
break;
|
||
}
|
||
}
|
||
|
||
void
|
||
fe_map_notify_eh (Widget w, XtPointer closure, XEvent *ev, Boolean *cont)
|
||
{
|
||
#ifdef JAVA
|
||
MWContext *context = (MWContext *) closure;
|
||
switch (ev->type) {
|
||
case MapNotify:
|
||
LJ_UniconifyApplets(context);
|
||
break;
|
||
case UnmapNotify:
|
||
LJ_IconifyApplets(context);
|
||
break;
|
||
}
|
||
#endif /* JAVA */
|
||
}
|
||
|
||
void
|
||
fe_copy_context_settings(MWContext *to, MWContext *from)
|
||
{
|
||
/* notyet fe_ContextData* dto; */
|
||
|
||
if ((NULL==to) || (NULL==from))
|
||
return;
|
||
|
||
/* set default doc_csid */
|
||
if ((NULL!=to->fe.data) && (NULL!=from->fe.data)) {
|
||
to->fe.data->xfe_doc_csid = from->fe.data->xfe_doc_csid;
|
||
}
|
||
|
||
#ifdef notyet
|
||
dto = CONTEXT_DATA(to);
|
||
if (from) {
|
||
fe_ContextData* dfrom = CONTEXT_DATA(from);
|
||
dto->show_url_p = dfrom->show_url_p;
|
||
dto->show_toolbar_p = dfrom->show_toolbar_p;
|
||
dto->show_toolbar_icons_p = dfrom->show_toolbar_icons_p;
|
||
dto->show_toolbar_text_p = dfrom->show_toolbar_text_p;
|
||
#ifdef EDITOR
|
||
dto->show_character_toolbar_p = dfrom->show_character_toolbar_p;
|
||
dto->show_paragraph_toolbar_p = dfrom->show_paragraph_toolbar_p;
|
||
#endif
|
||
dto->show_directory_buttons_p = dfrom->show_directory_buttons_p;
|
||
dto->show_menubar_p = dfrom->show_menubar_p;
|
||
dto->show_bottom_status_bar_p = dfrom->show_bottom_status_bar_p;
|
||
HG21989
|
||
dto->autoload_images_p = dfrom->autoload_images_p;
|
||
dto->loading_images_p = False;
|
||
dto->looping_images_p = False;
|
||
dto->delayed_images_p = dfrom->delayed_images_p;
|
||
dto->force_load_images = 0;
|
||
dto->fancy_ftp_p = dfrom->fancy_ftp_p;
|
||
dto->xfe_doc_csid = dfrom->xfe_doc_csid;
|
||
dto->XpixelsPerPoint = dfrom->XpixelsPerPoint;
|
||
dto->YpixelsPerPoint = dfrom->YpixelsPerPoint;
|
||
}
|
||
else {
|
||
dto->show_url_p = fe_globalPrefs.show_url_p;
|
||
dto->show_toolbar_p = fe_globalPrefs.show_toolbar_p;
|
||
#ifdef EDITOR
|
||
dto->show_character_toolbar_p = fe_globalPrefs.editor_character_toolbar;
|
||
dto->show_paragraph_toolbar_p = fe_globalPrefs.editor_paragraph_toolbar;
|
||
#endif
|
||
dto->show_toolbar_icons_p = fe_globalPrefs.toolbar_icons_p;
|
||
dto->show_toolbar_text_p = fe_globalPrefs.toolbar_text_p;
|
||
dto->show_directory_buttons_p = fe_globalPrefs.show_directory_buttons_p;
|
||
dto->show_menubar_p = fe_globalPrefs.show_menubar_p;
|
||
dto->show_bottom_status_bar_p = fe_globalPrefs.show_bottom_status_bar_p;
|
||
HG12976
|
||
dto->autoload_images_p = fe_globalPrefs.autoload_images_p;
|
||
dto->loading_images_p = False;
|
||
dto->looping_images_p = False;
|
||
dto->delayed_images_p = False;
|
||
dto->force_load_images = 0;
|
||
dto->fancy_ftp_p = fe_globalPrefs.fancy_ftp_p;
|
||
dto->xfe_doc_csid = INTL_DefaultDocCharSetID(NULL);
|
||
/* XpixelsPerPoint and YpixelsPerPoint are caculated in
|
||
* XFE_Frame::initializeMWContext.
|
||
*/
|
||
}
|
||
#endif /* notyet */
|
||
|
||
}
|
||
|
||
/* #### mailnews.c */
|
||
extern void fe_set_compose_wrap_state(MWContext *context, XP_Bool wrap_p);
|
||
|
||
/* XXX - "temporary" routine until we pass fe_drawable's straight from
|
||
layout to FE drawing functions */
|
||
void
|
||
XFE_SetDrawable(MWContext *context, CL_Drawable *drawable)
|
||
{
|
||
fe_Drawable *fe_drawable;
|
||
if (! drawable)
|
||
return;
|
||
|
||
fe_drawable = (fe_Drawable*)CL_GetDrawableClientData(drawable);
|
||
CONTEXT_DATA(context)->drawable = fe_drawable;
|
||
}
|
||
|
||
/* Callback to set the XY offset for all drawing into this drawable */
|
||
static void
|
||
fe_set_drawable_origin(CL_Drawable *drawable, int32 x_origin, int32 y_origin)
|
||
{
|
||
fe_Drawable *fe_drawable =
|
||
(fe_Drawable*)CL_GetDrawableClientData(drawable);
|
||
fe_drawable->x_origin = x_origin;
|
||
fe_drawable->y_origin = y_origin;
|
||
}
|
||
|
||
/* Callback to set the clip region for all drawing calls */
|
||
static void
|
||
fe_set_drawable_clip(CL_Drawable *drawable, FE_Region clip_region)
|
||
{
|
||
fe_Drawable *fe_drawable =
|
||
(fe_Drawable*)CL_GetDrawableClientData(drawable);
|
||
fe_drawable->clip_region = clip_region;
|
||
}
|
||
|
||
/* Callback not necessary, but may help to locate errors */
|
||
static void
|
||
fe_restore_drawable_clip(CL_Drawable *drawable)
|
||
{
|
||
fe_Drawable *fe_drawable =
|
||
(fe_Drawable*)CL_GetDrawableClientData(drawable);
|
||
fe_drawable->clip_region = NULL;
|
||
}
|
||
|
||
/* XXX - Works faster if we don't define this, but seems to introduce bugs */
|
||
#define USE_REGION_FOR_COPY
|
||
|
||
#ifndef USE_REGION_FOR_COPY
|
||
|
||
static Drawable fe_copy_src, fe_copy_dst;
|
||
static GC fe_copy_gc;
|
||
|
||
static void
|
||
fe_copy_rect_func(void *empty, XP_Rect *rect)
|
||
{
|
||
XCopyArea (fe_display, fe_copy_src, fe_copy_dst, fe_copy_gc,
|
||
rect->left, rect->top,
|
||
rect->right - rect->left, rect->bottom - rect->top,
|
||
rect->left, rect->top);
|
||
}
|
||
#endif
|
||
|
||
static void
|
||
fe_copy_pixels(CL_Drawable *drawable_src,
|
||
CL_Drawable *drawable_dst,
|
||
FE_Region region)
|
||
{
|
||
XP_Rect bbox;
|
||
XGCValues gcv;
|
||
GC gc;
|
||
fe_Drawable *fe_drawable_dst =
|
||
(fe_Drawable*)CL_GetDrawableClientData(drawable_dst);
|
||
fe_Drawable *fe_drawable_src =
|
||
(fe_Drawable*)CL_GetDrawableClientData(drawable_src);
|
||
|
||
Drawable src = fe_drawable_src->xdrawable;
|
||
Drawable dst = fe_drawable_dst->xdrawable;
|
||
|
||
/* FIXME - for simple regions, it may be faster to iterate over
|
||
rectangles rather than using clipping regions */
|
||
memset (&gcv, ~0, sizeof (gcv));
|
||
gcv.function = GXcopy;
|
||
|
||
#ifdef USE_REGION_FOR_COPY
|
||
gc = fe_GetGCfromDW (fe_display, dst, GCFunction, &gcv, region);
|
||
|
||
FE_GetRegionBoundingBox(region, &bbox);
|
||
XCopyArea (fe_display, src, dst, gc, bbox.left, bbox.top,
|
||
bbox.right - bbox.left, bbox.bottom - bbox.top,
|
||
bbox.left, bbox.top);
|
||
#else
|
||
fe_copy_gc = fe_GetGCfromDW (fe_display, dst, GCFunction, &gcv, NULL);
|
||
fe_copy_src = src;
|
||
fe_copy_dst = dst;
|
||
FE_ForEachRectInRegion(region,
|
||
(FE_RectInRegionFunc)fe_copy_rect_func, NULL);
|
||
#endif
|
||
}
|
||
|
||
/* There is only one backing store pixmap shared among all windows */
|
||
static Pixmap fe_backing_store_pixmap = 0;
|
||
|
||
/* We use a serial number to compare pixmaps rather than the X Pixmap
|
||
handle itself, in case the server allocates a pixmap with the same
|
||
handle as a pixmap that we've deallocated. */
|
||
static int fe_backing_store_pixmap_serial_num = 0;
|
||
|
||
/* Current lock owner for backing store */
|
||
static fe_Drawable *backing_store_owner = NULL;
|
||
static int backing_store_width = 0;
|
||
static int backing_store_height = 0;
|
||
static int backing_store_refcount = 0;
|
||
static int backing_store_depth;
|
||
|
||
static void
|
||
fe_destroy_backing_store(CL_Drawable *drawable)
|
||
{
|
||
XFreePixmap(fe_display, fe_backing_store_pixmap);
|
||
backing_store_owner = NULL;
|
||
fe_backing_store_pixmap = 0;
|
||
backing_store_width = 0;
|
||
backing_store_height = 0;
|
||
}
|
||
|
||
/* Function that's called to indicate that the drawable will be used.
|
||
No other drawable calls will be made until we InitDrawable. */
|
||
static void
|
||
fe_init_drawable(CL_Drawable *drawable)
|
||
{
|
||
backing_store_refcount++;
|
||
}
|
||
|
||
/* Function that's called to indicate that we're temporarily done with
|
||
the drawable. The drawable won't be used until we call InitDrawable
|
||
again. */
|
||
static void
|
||
fe_relinquish_drawable(CL_Drawable *drawable)
|
||
{
|
||
XP_ASSERT(backing_store_refcount > 0);
|
||
backing_store_refcount--;
|
||
|
||
if (backing_store_refcount == 0)
|
||
fe_destroy_backing_store(drawable);
|
||
}
|
||
|
||
static PRBool
|
||
fe_lock_drawable(CL_Drawable *drawable, CL_DrawableState new_state)
|
||
{
|
||
fe_Drawable *prior_backing_store_owner;
|
||
fe_Drawable *fe_drawable = (fe_Drawable *)CL_GetDrawableClientData(drawable);
|
||
if (new_state == CL_UNLOCK_DRAWABLE)
|
||
return PR_TRUE;
|
||
|
||
XP_ASSERT(backing_store_refcount > 0);
|
||
|
||
if (!fe_backing_store_pixmap)
|
||
return PR_FALSE;
|
||
|
||
prior_backing_store_owner = backing_store_owner;
|
||
|
||
/* Check to see if we're the last one to use this drawable.
|
||
If not, someone else might have modified the bits, since the
|
||
last time we wrote to them using this drawable. */
|
||
if (new_state & CL_LOCK_DRAWABLE_FOR_READ) {
|
||
if (prior_backing_store_owner != fe_drawable)
|
||
return PR_FALSE;
|
||
|
||
/* The pixmap could have changed since the last time this
|
||
drawable was used due to a resize of the backing store, even
|
||
though no one else has drawn to it. */
|
||
if (fe_drawable->xdrawable_serial_num !=
|
||
fe_backing_store_pixmap_serial_num) {
|
||
return PR_FALSE;
|
||
}
|
||
}
|
||
|
||
backing_store_owner = fe_drawable;
|
||
|
||
fe_drawable->xdrawable = fe_backing_store_pixmap;
|
||
fe_drawable->xdrawable_serial_num = fe_backing_store_pixmap_serial_num;
|
||
|
||
return PR_TRUE;
|
||
}
|
||
|
||
static void
|
||
fe_set_drawable_dimensions(CL_Drawable *drawable, uint32 width, uint32 height)
|
||
{
|
||
Window backing_store_window;
|
||
struct fe_MWContext_cons *cntx;
|
||
fe_Drawable *fe_drawable =
|
||
(fe_Drawable*)CL_GetDrawableClientData(drawable);
|
||
|
||
XP_ASSERT(backing_store_refcount > 0);
|
||
if ((width > backing_store_width) || (height > backing_store_height)) {
|
||
|
||
/* The backing store only gets larger, not smaller. */
|
||
if (width < backing_store_width)
|
||
width = backing_store_width;
|
||
if (height < backing_store_height)
|
||
height = backing_store_height;
|
||
|
||
if (fe_backing_store_pixmap) {
|
||
XFreePixmap(fe_display, fe_backing_store_pixmap);
|
||
backing_store_owner = NULL;
|
||
}
|
||
|
||
fe_backing_store_pixmap_serial_num++;
|
||
|
||
/* Grab a window for use with XCreatePixmap. X doesn't care about
|
||
what window we use, as long as it's on the same screen that the
|
||
pixmap will be drawn on. */
|
||
backing_store_window = 0;
|
||
for (cntx = fe_all_MWContexts; cntx; cntx = cntx->next) {
|
||
if (CONTEXT_DATA (cntx->context)->drawing_area != NULL) {
|
||
backing_store_window =
|
||
XtWindow (CONTEXT_DATA (cntx->context)->drawing_area);
|
||
if (backing_store_window)
|
||
break;
|
||
}
|
||
}
|
||
if (!backing_store_window) {
|
||
XP_ASSERT(0);
|
||
return;
|
||
}
|
||
|
||
fe_backing_store_pixmap = XCreatePixmap (fe_display,
|
||
backing_store_window,
|
||
width, height,
|
||
backing_store_depth);
|
||
backing_store_width = width;
|
||
backing_store_height = height;
|
||
}
|
||
fe_drawable->xdrawable = fe_backing_store_pixmap;
|
||
}
|
||
|
||
static
|
||
CL_DrawableVTable window_drawable_vtable = {
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
fe_set_drawable_origin,
|
||
NULL,
|
||
fe_set_drawable_clip,
|
||
fe_restore_drawable_clip,
|
||
fe_copy_pixels,
|
||
NULL
|
||
};
|
||
|
||
static
|
||
CL_DrawableVTable backing_store_drawable_vtable = {
|
||
fe_lock_drawable,
|
||
fe_init_drawable,
|
||
fe_relinquish_drawable,
|
||
NULL,
|
||
fe_set_drawable_origin,
|
||
NULL,
|
||
fe_set_drawable_clip,
|
||
fe_restore_drawable_clip,
|
||
fe_copy_pixels,
|
||
fe_set_drawable_dimensions
|
||
};
|
||
|
||
CL_Compositor *
|
||
fe_create_compositor(MWContext *context)
|
||
{
|
||
int32 comp_width, comp_height;
|
||
CL_Drawable *cl_window_drawable, *cl_backing_store_drawable;
|
||
CL_Compositor *compositor;
|
||
fe_Drawable *window_drawable, *backing_store_drawable;
|
||
Window window;
|
||
|
||
Visual *visual;
|
||
|
||
window = XtWindow(CONTEXT_DATA(context)->drawing_area);
|
||
visual = fe_globalData.default_visual;
|
||
backing_store_depth = fe_VisualDepth (fe_display, visual);
|
||
|
||
/* Create a new compositor and its default layers */
|
||
comp_width = CONTEXT_DATA(context)->scrolled_width;
|
||
comp_height = CONTEXT_DATA(context)->scrolled_height;
|
||
|
||
if (CONTEXT_DATA(context)->vscroll &&
|
||
XtIsManaged(CONTEXT_DATA(context)->vscroll))
|
||
comp_width -= CONTEXT_DATA(context)->sb_w;
|
||
if (CONTEXT_DATA(context)->hscroll &&
|
||
XtIsManaged(CONTEXT_DATA(context)->hscroll))
|
||
comp_height -= CONTEXT_DATA(context)->sb_h;
|
||
|
||
window_drawable = XP_NEW_ZAP(fe_Drawable);
|
||
if (!window_drawable)
|
||
return NULL;
|
||
window_drawable->xdrawable = window;
|
||
|
||
/* Create backing store drawable, but don't create pixmap
|
||
until fe_set_drawable_dimensions() is called from the
|
||
compositor */
|
||
backing_store_drawable = XP_NEW_ZAP(fe_Drawable);
|
||
if (!backing_store_drawable)
|
||
return NULL;
|
||
|
||
/* Create XP handle to window's HTML view for compositor */
|
||
cl_window_drawable = CL_NewDrawable(comp_width, comp_height,
|
||
CL_WINDOW, &window_drawable_vtable,
|
||
(void*)window_drawable);
|
||
|
||
/* Create XP handle to backing store for compositor */
|
||
cl_backing_store_drawable = CL_NewDrawable(comp_width, comp_height,
|
||
CL_BACKING_STORE,
|
||
&backing_store_drawable_vtable,
|
||
(void*)backing_store_drawable);
|
||
|
||
compositor = CL_NewCompositor(cl_window_drawable, cl_backing_store_drawable,
|
||
0, 0, comp_width, comp_height, 15);
|
||
|
||
/* Set reasonable default drawable */
|
||
CONTEXT_DATA (context)->drawable = window_drawable;
|
||
|
||
return compositor;
|
||
}
|
||
|
||
/* Create and initialize the Image Library JMC callback interface.
|
||
Also create an IL_GroupContext for the current context. */
|
||
XP_Bool
|
||
fe_init_image_callbacks(MWContext *context)
|
||
{
|
||
IL_GroupContext *img_cx;
|
||
IMGCB* img_cb;
|
||
JMCException *exc = NULL;
|
||
|
||
if (!context->img_cx) {
|
||
PRBool observer_added_p;
|
||
|
||
img_cb = IMGCBFactory_Create(&exc); /* JMC Module */
|
||
if (exc) {
|
||
JMC_DELETE_EXCEPTION(&exc); /* XXXM12N Should really return
|
||
exception */
|
||
return FALSE;
|
||
}
|
||
|
||
/* Create an Image Group Context. IL_NewGroupContext augments the
|
||
reference count for the JMC callback interface. The opaque argument
|
||
to IL_NewGroupContext is the Front End's display context, which will
|
||
be passed back to all the Image Library's FE callbacks. */
|
||
img_cx = IL_NewGroupContext((void*)context, (IMGCBIF *)img_cb);
|
||
|
||
/* Attach the IL_GroupContext to the FE's display context. */
|
||
context->img_cx = img_cx;
|
||
|
||
/* Add an image group observer to the IL_GroupContext. */
|
||
observer_added_p = IL_AddGroupObserver(img_cx, fe_ImageGroupObserver,
|
||
(void *)context);
|
||
}
|
||
return TRUE;
|
||
}
|
||
|
||
/* in src/context_funcs.cpp */
|
||
extern MWContext *xfe2_MakeNewWindow(Widget, MWContext*, URL_Struct*,
|
||
char *,MWContextType, Boolean, Chrome*);
|
||
|
||
MWContext *
|
||
fe_MakeNewWindow(Widget toplevel, MWContext *context_to_copy,
|
||
URL_Struct *url, char *window_name, MWContextType type,
|
||
Boolean skip_get_url, Chrome *decor)
|
||
{
|
||
MWContext* context;
|
||
struct fe_MWContext_cons *cons;
|
||
fe_ContextData *fec;
|
||
Widget shell;
|
||
char *shell_name;
|
||
|
||
int delete_response;
|
||
Boolean allow_resize;
|
||
Boolean is_modal;
|
||
Boolean context_displays_html_p = False;
|
||
Arg av[20];
|
||
int ac;
|
||
int16 new_charset;
|
||
|
||
new_charset = CS_DEFAULT;
|
||
|
||
/* Fix type */
|
||
if (url && (type != MWContextSaveToDisk) && (type != MWContextBookmarks) &&
|
||
(type != MWContextAddressBook) && (type != MWContextDialog) &&
|
||
(type != MWContextEditor)) {
|
||
#ifdef MOZ_MAIL_NEWS
|
||
if (MSG_RequiresMailWindow (url->address) || MSG_RequiresNewsWindow(url->address))
|
||
type = MWContextMail;
|
||
else if (MSG_RequiresBrowserWindow (url->address)) {
|
||
#else
|
||
{
|
||
#endif
|
||
type = MWContextBrowser;
|
||
}
|
||
}
|
||
|
||
return xfe2_MakeNewWindow(toplevel, context_to_copy, url, window_name,
|
||
type, skip_get_url, decor);
|
||
}
|
||
|
||
|
||
MWContext *
|
||
FE_MakeNewWindow(MWContext *old_context,
|
||
URL_Struct *url,
|
||
char *window_name,
|
||
Chrome *chrome)
|
||
{
|
||
MWContext * new_context;
|
||
MWContextType type = MWContextBrowser;
|
||
|
||
if ( (old_context == NULL)
|
||
|| (CONTEXT_WIDGET (old_context) == NULL) )
|
||
return(NULL);
|
||
|
||
if (chrome && chrome->type != 0)
|
||
type = chrome->type;
|
||
|
||
/*
|
||
* Dependent Windows
|
||
*/
|
||
if (chrome && chrome->dependent)
|
||
{
|
||
/*
|
||
* Ensure that a dependent list exists, returning NULL
|
||
* if one cannot be created.
|
||
*/
|
||
if (old_context->js_dependent_list == 0)
|
||
{
|
||
XP_List * list;
|
||
list = XP_ListNew();
|
||
if (!list)
|
||
return NULL;
|
||
old_context->js_dependent_list = list;
|
||
}
|
||
}
|
||
|
||
new_context = fe_MakeNewWindow (XtParent(CONTEXT_WIDGET (old_context)),
|
||
old_context, url,
|
||
window_name, type, (url == NULL), chrome);
|
||
|
||
if (chrome && chrome->dependent)
|
||
{
|
||
/* parent knows about dependent */
|
||
XP_ListAddObject(old_context->js_dependent_list, new_context);
|
||
/* dependent knows about parent */
|
||
new_context->js_parent = old_context;
|
||
}
|
||
|
||
return(new_context);
|
||
}
|
||
|
||
MWContext *
|
||
FE_MakeBlankWindow(MWContext *old_context, URL_Struct *url, char *window_name)
|
||
{
|
||
MWContext *new_context;
|
||
|
||
if ((old_context == NULL)||(CONTEXT_WIDGET (old_context) == NULL))
|
||
{
|
||
return(NULL);
|
||
}
|
||
|
||
new_context = fe_MakeWindow (
|
||
XtParent(CONTEXT_WIDGET (old_context)),
|
||
old_context, NULL, window_name, MWContextBrowser, TRUE);
|
||
return(new_context);
|
||
}
|
||
|
||
|
||
void
|
||
FE_SetWindowLoading(MWContext *context, URL_Struct *url,
|
||
Net_GetUrlExitFunc **exit_func_p)
|
||
{
|
||
if ((context != NULL)&&(url != NULL))
|
||
{
|
||
fe_GetURL (context, url, TRUE);
|
||
*exit_func_p = (Net_GetUrlExitFunc *)fe_url_exit;
|
||
}
|
||
}
|
||
|
||
|
||
Boolean
|
||
fe_IsGridParent (MWContext *context)
|
||
{
|
||
return (context->grid_children && XP_ListTopObject (context->grid_children));
|
||
}
|
||
|
||
|
||
MWContext *
|
||
fe_GetFocusGridOfContext(MWContext *context)
|
||
{
|
||
MWContext *child;
|
||
int i = 1;
|
||
|
||
if (context == NULL)
|
||
return 0;
|
||
|
||
/* grid_children keeps a list of html frames...
|
||
it's possible that it is null when there is no html frames created */
|
||
if ( !context->grid_children) return 0;
|
||
|
||
while ((child = (MWContext*)XP_ListGetObjectNum (context->grid_children,
|
||
i++))) {
|
||
if (CONTEXT_DATA (child)->focus_grid)
|
||
return child;
|
||
|
||
if (fe_IsGridParent (child)) {
|
||
child = fe_GetFocusGridOfContext (child);
|
||
if (child != NULL)
|
||
return child;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
void
|
||
fe_MochaFocusNotify (MWContext *context, LO_Element *element)
|
||
{
|
||
JSEvent *event;
|
||
event = XP_NEW_ZAP(JSEvent);
|
||
event->type = EVENT_FOCUS;
|
||
ET_SendEvent (context, element, event, NULL, NULL);
|
||
}
|
||
|
||
void
|
||
fe_MochaBlurNotify (MWContext *context, LO_Element *element)
|
||
{
|
||
JSEvent *event;
|
||
event = XP_NEW_ZAP(JSEvent);
|
||
event->type = EVENT_BLUR;
|
||
ET_SendEvent (context, element, event, NULL, NULL);
|
||
}
|
||
|
||
void
|
||
fe_SetGridFocus (MWContext *context)
|
||
{
|
||
Widget w;
|
||
MWContext *top, *focus_grid;
|
||
Dimension border_width;
|
||
|
||
if (context == NULL)
|
||
return;
|
||
|
||
/* focus the new guy */
|
||
fe_MochaFocusNotify (context, NULL);
|
||
|
||
top = XP_GetNonGridContext (context);
|
||
if (top == NULL)
|
||
return;
|
||
|
||
if ((focus_grid = fe_GetFocusGridOfContext (top))) {
|
||
/* blur the previous guy */
|
||
fe_MochaBlurNotify (focus_grid, NULL);
|
||
|
||
CONTEXT_DATA (focus_grid)->focus_grid = False;
|
||
w = CONTEXT_DATA (focus_grid)->main_pane;
|
||
XtVaGetValues (w, XmNborderWidth, &border_width, 0);
|
||
if (border_width)
|
||
XtVaSetValues (w, XmNborderColor,
|
||
(CONTEXT_DATA (context)->default_bg_pixel), 0);
|
||
}
|
||
|
||
/* Then indicate which cell has focus */
|
||
TRACEMSG (("context: 0x%x has focus\n", context));
|
||
|
||
CONTEXT_DATA (context)->focus_grid = True;
|
||
w = CONTEXT_DATA (context)->main_pane;
|
||
|
||
XtVaGetValues (w, XmNborderWidth, &border_width, 0);
|
||
if (border_width)
|
||
XtVaSetValues (w, XmNborderColor,
|
||
CONTEXT_DATA (context)->default_fg_pixel, 0);
|
||
|
||
XFE_SetDocTitle (context, 0);
|
||
}
|
||
|
||
MWContext *
|
||
#ifdef XP_UNIX
|
||
FE_MakeGridWindow (MWContext *old_context, void *hist_list, void *history,
|
||
int32 x, int32 y,
|
||
#else
|
||
FE_MakeGridWindow (MWContext *old_context, void *history, int32 x, int32 y,
|
||
#endif /* XP_UNIX */
|
||
int32 width, int32 height, char *url_str, char *window_name,
|
||
int8 scrolling, NET_ReloadMethod force_reload, Bool no_edge)
|
||
{
|
||
Widget parent = CONTEXT_DATA (old_context)->drawing_area;
|
||
MWContext *context = XP_NewContext();
|
||
struct fe_MWContext_cons *cons = (struct fe_MWContext_cons *)
|
||
malloc (sizeof (struct fe_MWContext_cons));
|
||
fe_ContextData *fec = (fe_ContextData *) calloc (sizeof (fe_ContextData), 1);
|
||
History_entry *he = (History_entry *)history;
|
||
URL_Struct *url = NULL;
|
||
|
||
CONTEXT_DATA (context) = fec;
|
||
|
||
/* add the layout function pointers
|
||
*/
|
||
context->funcs = fe_BuildDisplayFunctionTable();
|
||
context->convertPixX = context->convertPixY = 1;
|
||
context->is_grid_cell = TRUE;
|
||
context->grid_parent = old_context;
|
||
|
||
/* New field added by putterman for increase/decrease font */
|
||
context->fontScalingPercentage = old_context->fontScalingPercentage;
|
||
|
||
cons->context = context;
|
||
cons->next = fe_all_MWContexts;
|
||
fe_all_MWContexts = cons;
|
||
|
||
/* pixelsPerPoint: display-specific information needed by the back end
|
||
* when converting style sheet length units between points and pixels.
|
||
*/
|
||
context->XpixelsPerPoint = old_context->XpixelsPerPoint;
|
||
context->YpixelsPerPoint = old_context->YpixelsPerPoint;
|
||
|
||
SHIST_InitSession (context); /* Initialize the history library. */
|
||
#ifdef XP_UNIX
|
||
if (hist_list != NULL)
|
||
{
|
||
context->hist.list_ptr = hist_list;
|
||
}
|
||
else
|
||
{
|
||
SHIST_AddDocument(context, he);
|
||
}
|
||
#else
|
||
SHIST_AddDocument(context, he);
|
||
#endif /* XP_UNIX */
|
||
|
||
if (he)
|
||
url = SHIST_CreateURLStructFromHistoryEntry (context, he);
|
||
else
|
||
url = NET_CreateURLStruct (url_str, NET_DONT_RELOAD);
|
||
|
||
if (url) {
|
||
MWContext *top = XP_GetNonGridContext(old_context);
|
||
History_entry *h = SHIST_GetCurrent (&top->hist);
|
||
|
||
url->force_reload = force_reload;
|
||
|
||
/* Set the referer field in the url to the document that refered to the
|
||
* grid parent. New function fe_GetURLForReferral() might be used
|
||
* here, brendan says this is probably Ok. -mcafee
|
||
*/
|
||
if (h && h->referer)
|
||
url->referer = strdup(h->referer);
|
||
}
|
||
|
||
if (window_name)
|
||
{
|
||
context->name = strdup (window_name);
|
||
}
|
||
XP_AddContextToList (context);
|
||
|
||
if (old_context)
|
||
{
|
||
CONTEXT_DATA (context)->autoload_images_p =
|
||
CONTEXT_DATA (old_context)->autoload_images_p;
|
||
CONTEXT_DATA (context)->loading_images_p = False;
|
||
CONTEXT_DATA (context)->looping_images_p = False;
|
||
CONTEXT_DATA (context)->delayed_images_p =
|
||
CONTEXT_DATA (old_context)->delayed_images_p;
|
||
CONTEXT_DATA (context)->force_load_images = 0;
|
||
CONTEXT_DATA (context)->fancy_ftp_p =
|
||
CONTEXT_DATA (old_context)->fancy_ftp_p;
|
||
CONTEXT_DATA (context)->xfe_doc_csid =
|
||
CONTEXT_DATA (old_context)->xfe_doc_csid;
|
||
}
|
||
CONTEXT_WIDGET (context) = CONTEXT_WIDGET (old_context);
|
||
CONTEXT_DATA (context)->backdrop_pixmap = (Pixmap) ~0;
|
||
CONTEXT_DATA (context)->grid_scrolling = scrolling;
|
||
|
||
/* Set the image library callbacks */
|
||
CONTEXT_DATA(context)->DisplayPixmap = (DisplayPixmapPtr)NULL/* fe_DisplayPixmap */;
|
||
CONTEXT_DATA(context)->NewPixmap = (NewPixmapPtr)NULL;
|
||
|
||
/* FRAMES_HAVE_THEIR_OWN_COLORMAP was an unfinished
|
||
experiment by kevina. */
|
||
#ifdef FRAMES_HAVE_THEIR_OWN_COLORMAP
|
||
/* We have to go through this to get the toplevel widget */
|
||
{
|
||
MWContext *top_context = XP_GetNonGridContext(context);
|
||
|
||
fe_pick_visual_and_colormap (XtParent(CONTEXT_WIDGET (top_context)),
|
||
context);
|
||
}
|
||
#else
|
||
/* Inherit colormap from our parent */
|
||
CONTEXT_DATA(context)->colormap = CONTEXT_DATA(old_context)->colormap;
|
||
#endif
|
||
|
||
#ifdef FRAMES_HAVE_THEIR_OWN_COLORMAP
|
||
/* XXXM12N Create and initialize the Image Library JMC callback
|
||
interface. Also create a new IL_GroupContext for this window.*/
|
||
if (!fe_init_image_callbacks(context))
|
||
{
|
||
return NULL;
|
||
}
|
||
fe_InitColormap(context);
|
||
#endif
|
||
|
||
XtGetApplicationResources (CONTEXT_WIDGET (old_context),
|
||
(XtPointer) CONTEXT_DATA (context),
|
||
fe_Resources, fe_ResourcesSize,
|
||
0, 0);
|
||
|
||
/* CONTEXT_DATA (context)->main_pane = parent; */
|
||
|
||
/*
|
||
* set the default coloring correctly into the new context.
|
||
*/
|
||
{
|
||
Pixel unused_select_pixel;
|
||
XmGetColors (XtScreen (parent),
|
||
fe_cmap(context),
|
||
CONTEXT_DATA (context)->default_bg_pixel,
|
||
&(CONTEXT_DATA (context)->fg_pixel),
|
||
&(CONTEXT_DATA (context)->top_shadow_pixel),
|
||
&(CONTEXT_DATA (context)->bottom_shadow_pixel),
|
||
&unused_select_pixel);
|
||
}
|
||
|
||
/* ### Create a form widget to parent the scroller.
|
||
*
|
||
* This might keep the scroller from becoming smaller than
|
||
* the cell size when there are no scrollbars.
|
||
*/
|
||
|
||
{
|
||
Arg av [50];
|
||
int ac;
|
||
Widget pane, mainw, scroller;
|
||
int border_width = 0;
|
||
|
||
if (no_edge)
|
||
border_width = 0;
|
||
else
|
||
border_width = 2;
|
||
|
||
ac = 0;
|
||
XtSetArg (av[ac], XmNx, (Position)x); ac++;
|
||
XtSetArg (av[ac], XmNy, (Position)y); ac++;
|
||
XtSetArg (av[ac], XmNwidth, (Dimension)width - 2*border_width); ac++;
|
||
XtSetArg (av[ac], XmNheight, (Dimension)height - 2*border_width); ac++;
|
||
XtSetArg (av[ac], XmNborderWidth, border_width); ac++;
|
||
mainw = XmCreateForm (parent, "form", av, ac);
|
||
|
||
ac = 0;
|
||
XtSetArg (av[ac], XmNborderWidth, 0); ac++;
|
||
XtSetArg (av[ac], XmNmarginWidth, 0); ac++;
|
||
XtSetArg (av[ac], XmNmarginHeight, 0); ac++;
|
||
XtSetArg (av[ac], XmNborderColor,
|
||
CONTEXT_DATA (context)->default_bg_pixel); ac++;
|
||
pane = XmCreatePanedWindow (mainw, "pane", av, ac);
|
||
|
||
XtVaSetValues (pane,
|
||
XmNtopAttachment, XmATTACH_FORM,
|
||
XmNbottomAttachment, XmATTACH_FORM,
|
||
XmNleftAttachment, XmATTACH_FORM,
|
||
XmNrightAttachment, XmATTACH_FORM,
|
||
0);
|
||
|
||
/* The actual work area */
|
||
scroller = fe_MakeScrolledWindow (context, pane, "scroller");
|
||
XtVaSetValues (CONTEXT_DATA (context)->scrolled,
|
||
XmNborderWidth, 0, 0);
|
||
|
||
XtManageChild (scroller);
|
||
XtManageChild (pane);
|
||
XtManageChild (mainw);
|
||
|
||
CONTEXT_DATA (context)->main_pane = mainw;
|
||
}
|
||
|
||
fe_load_default_font (context);
|
||
fe_get_context_resources (context); /* Do other resource db hackery. */
|
||
|
||
/* FIXME - This is flagrantly wasteful of backing store memory.
|
||
Contexts which are not leaves in the FRAMESET hierarchy don't
|
||
need any backing store or compositor. */
|
||
context->compositor = fe_create_compositor(context);
|
||
|
||
/* Figure out how much space the horizontal and vertical scrollbars take up.
|
||
It's basically impossible to determine this before creating them...
|
||
*/
|
||
{
|
||
Dimension w1 = 0, w2 = 0, h1 = 0, h2 = 0;
|
||
|
||
XtManageChild (CONTEXT_DATA (context)->hscroll);
|
||
XtManageChild (CONTEXT_DATA (context)->vscroll);
|
||
XtVaGetValues (CONTEXT_DATA (context)->drawing_area,
|
||
XmNwidth, &w1,
|
||
XmNheight, &h1,
|
||
0);
|
||
|
||
XtUnmanageChild (CONTEXT_DATA (context)->hscroll);
|
||
XtUnmanageChild (CONTEXT_DATA (context)->vscroll);
|
||
XtVaGetValues (CONTEXT_DATA (context)->drawing_area,
|
||
XmNwidth, &w2,
|
||
XmNheight, &h2,
|
||
0);
|
||
|
||
CONTEXT_DATA (context)->sb_w = w2 - w1;
|
||
CONTEXT_DATA (context)->sb_h = h2 - h1;
|
||
|
||
/* Now that we know, we don't need to leave them managed. */
|
||
}
|
||
|
||
XtVaSetValues (CONTEXT_DATA (context)->scrolled, XmNinitialFocus,
|
||
CONTEXT_DATA (context)->drawing_area, 0);
|
||
|
||
fe_SetGridFocus (context); /* Give this grid focus */
|
||
fe_InitScrolling (context); /* big voodoo */
|
||
|
||
/* XXXM12N Create and initialize the Image Library JMC callback
|
||
interface. Also create a new IL_GroupContext for this window.*/
|
||
if (!context->img_cx)
|
||
if (!fe_init_image_callbacks(context))
|
||
{
|
||
return NULL;
|
||
}
|
||
|
||
fe_InitColormap (context);
|
||
|
||
if (url)
|
||
{
|
||
/* #### This might not be right, or there might be more that needs
|
||
to be done... Note that url->history_num is bogus for the new
|
||
context. url->position_tag might also be context-specific. */
|
||
#ifdef XP_UNIX
|
||
/*
|
||
* I believe that if we are restoring from a history entry,
|
||
* we don't want to clear this saved data.
|
||
*/
|
||
if (!he)
|
||
{
|
||
XP_MEMSET (&url->savedData, 0, sizeof (SHIST_SavedData));
|
||
}
|
||
#else
|
||
XP_MEMSET (&url->savedData, 0, sizeof (SHIST_SavedData));
|
||
#endif /* XP_UNIX */
|
||
fe_GetURL (context, url, FALSE);
|
||
}
|
||
|
||
XFE_SetDocTitle (context, 0);
|
||
CONTEXT_DATA (context)->are_scrollbars_active = True;
|
||
|
||
return(context);
|
||
}
|
||
|
||
void
|
||
FE_RestructureGridWindow (MWContext *context, int32 x, int32 y,
|
||
int32 width, int32 height)
|
||
{
|
||
Widget mainw;
|
||
|
||
/*
|
||
* This comes from blank frames. Maybe we should clear them
|
||
* before we return?
|
||
*/
|
||
if (!context ) return;
|
||
|
||
/*
|
||
* Basically we just set the new position and dimensions onto the
|
||
* parent of the drawing area. X will do the rest for us.
|
||
* Because of the window gravity side effects of guffaws scrolling
|
||
* The drawing area won't move with its parent unless we temporarily
|
||
* turn off guffaws.
|
||
*/
|
||
mainw = CONTEXT_DATA (context)->main_pane;
|
||
fe_SetGuffaw(context, FALSE);
|
||
XtVaSetValues (mainw,
|
||
XmNx, (Position)x,
|
||
XmNy, (Position)y,
|
||
XmNwidth, (Dimension)width - 4, /* Adjust for focus border */
|
||
XmNheight, (Dimension)height - 4,
|
||
0);
|
||
fe_SetGuffaw(context, TRUE);
|
||
}
|
||
|
||
/* Required to make custom colormap installation work.
|
||
Really, not *every* context has a private colormap.
|
||
Trivial contexts like address book, bookmarks, etc.
|
||
share a colormap. */
|
||
#define EVERY_CONTEXT_HAS_PRIVATE_COLORMAP
|
||
|
||
static void
|
||
fe_pick_visual_and_colormap (Widget toplevel, MWContext *new_context)
|
||
{
|
||
Screen *screen = XtScreen (toplevel);
|
||
Display *dpy = XtDisplay (toplevel);
|
||
Colormap cmap;
|
||
Visual *v;
|
||
fe_colormap *colormap;
|
||
|
||
v = fe_globalData.default_visual;
|
||
if (!v)
|
||
{
|
||
String str = 0;
|
||
/* "*visualID" is special for a number of reasons... */
|
||
static XtResource res = { "visualID", "VisualID",
|
||
XtRString, sizeof (String),
|
||
0, XtRString, "default" };
|
||
XtGetSubresources (toplevel, &str, (char *) fe_progname, "TopLevelShell",
|
||
&res, 1, 0, 0);
|
||
v = fe_ParseVisual (screen, str);
|
||
fe_globalData.default_visual = v;
|
||
}
|
||
|
||
{
|
||
String str = 0;
|
||
static XtResource res = { "installColormap", XtCString, XtRString,
|
||
sizeof (String), 0, XtRString, "guess" };
|
||
XtGetApplicationResources (toplevel, &str, &res, 1, 0, 0);
|
||
if (!str || !*str || !XP_STRCASECMP(str, "guess"))
|
||
{
|
||
/* But everybody lies about this value */
|
||
char *vendor = XServerVendor (XtDisplay (toplevel));
|
||
fe_globalData.always_install_cmap =
|
||
!strcmp (vendor, "Silicon Graphics");
|
||
}
|
||
else if (!XP_STRCASECMP(str, "yes") || !XP_STRCASECMP(str, "true"))
|
||
fe_globalData.always_install_cmap = True;
|
||
else if (!XP_STRCASECMP(str, "no") || !XP_STRCASECMP(str, "false"))
|
||
fe_globalData.always_install_cmap = False;
|
||
else
|
||
{
|
||
fprintf (stderr,
|
||
XP_GetString(XFE_DISPLAY_FACTORY_INSTALL_COLORMAP_ERROR),
|
||
fe_progname, str);
|
||
fe_globalData.always_install_cmap = False;
|
||
}
|
||
}
|
||
|
||
/* Don't allow colormap flashing on a deep display */
|
||
if (v != DefaultVisualOfScreen (screen))
|
||
fe_globalData.always_install_cmap = True;
|
||
|
||
if (!fe_globalData.default_colormap)
|
||
{
|
||
cmap = DefaultColormapOfScreen (screen);
|
||
fe_globalData.default_colormap =
|
||
fe_NewColormap(screen, DefaultVisualOfScreen (screen), cmap, False);
|
||
}
|
||
|
||
colormap = fe_globalData.common_colormap;
|
||
|
||
if (!colormap)
|
||
{
|
||
if (fe_globalData.always_install_cmap)
|
||
{
|
||
/* Create a colormap for "simple" contexts
|
||
like bookmarks, address book, etc */
|
||
cmap = XCreateColormap (dpy, RootWindowOfScreen (screen),
|
||
v, AllocNone);
|
||
colormap = fe_NewColormap(screen, v, cmap, True);
|
||
}
|
||
else
|
||
{
|
||
/* Use the default colormap for all contexts. */
|
||
colormap = fe_globalData.default_colormap;
|
||
}
|
||
fe_globalData.common_colormap = colormap;
|
||
}
|
||
|
||
|
||
#ifdef EVERY_CONTEXT_HAS_PRIVATE_COLORMAP
|
||
if (fe_globalData.always_install_cmap)
|
||
{
|
||
/* Even when installing "private" colormaps for every window,
|
||
"simple" contexts, which have fixed color composition, share
|
||
a single colormap. */
|
||
MWContextType type = new_context->type;
|
||
if ((type == MWContextBrowser) ||
|
||
(type == MWContextEditor) ||
|
||
(type == MWContextNews) ||
|
||
(type == MWContextMail))
|
||
{
|
||
cmap = XCreateColormap (dpy, RootWindowOfScreen (screen),
|
||
v, AllocNone);
|
||
colormap = fe_NewColormap(screen, v, cmap, True);
|
||
}
|
||
}
|
||
#endif /* !EVERY_CONTEXT_HAS_PRIVATE_COLORMAP */
|
||
|
||
CONTEXT_DATA (new_context)->colormap = colormap;
|
||
}
|
||
|
||
void
|
||
fe_InitializeGlobalResources (Widget toplevel)
|
||
{
|
||
XtGetApplicationResources (toplevel,
|
||
(XtPointer) &fe_globalData,
|
||
fe_GlobalResources, fe_GlobalResourcesSize,
|
||
0, 0);
|
||
|
||
/*
|
||
* And then there was Sun. Try to detect losing olwm,
|
||
* and default to mono desktop icons.
|
||
*/
|
||
if (fe_globalData.wm_icon_policy == NULL) { /* not set */
|
||
if (XfeIsOpenLookRunning(toplevel))
|
||
fe_globalData.wm_icon_policy = "mono";
|
||
else
|
||
fe_globalData.wm_icon_policy = "color";
|
||
}
|
||
}
|
||
|
||
|
||
/* This initializes resources which must be set up BEFORE the widget is
|
||
realized or managed (sizes and things). */
|
||
void
|
||
fe_get_context_resources (MWContext *context)
|
||
{
|
||
fe_ContextData *fec = CONTEXT_DATA (context);
|
||
if (fec->drawing_area) {
|
||
XtVaGetValues (fec->drawing_area,
|
||
XmNbackground, &fec->bg_pixel, 0);
|
||
} /* else??? ### */
|
||
|
||
/* If the selection colors ended up mapping to the same pixel values,
|
||
invert them. */
|
||
if (CONTEXT_DATA (context)->select_fg_pixel ==
|
||
CONTEXT_DATA (context)->fg_pixel &&
|
||
CONTEXT_DATA (context)->select_bg_pixel ==
|
||
CONTEXT_DATA (context)->bg_pixel)
|
||
{
|
||
CONTEXT_DATA (context)->select_fg_pixel =
|
||
CONTEXT_DATA (context)->bg_pixel;
|
||
CONTEXT_DATA (context)->select_bg_pixel =
|
||
CONTEXT_DATA (context)->fg_pixel;
|
||
}
|
||
|
||
/* Tell layout about the default colors and background.
|
||
*/
|
||
{
|
||
XColor c[5];
|
||
c[0].pixel = CONTEXT_DATA (context)->link_pixel;
|
||
c[1].pixel = CONTEXT_DATA (context)->vlink_pixel;
|
||
c[2].pixel = CONTEXT_DATA (context)->alink_pixel;
|
||
c[3].pixel = CONTEXT_DATA (context)->default_fg_pixel;
|
||
c[4].pixel = CONTEXT_DATA (context)->default_bg_pixel;
|
||
XQueryColors (XtDisplay (CONTEXT_WIDGET (context)),
|
||
fe_cmap(context),
|
||
c, 5);
|
||
LO_SetDefaultColor (LO_COLOR_LINK,
|
||
c[0].red >> 8, c[0].green >> 8, c[0].blue >> 8);
|
||
LO_SetDefaultColor (LO_COLOR_VLINK,
|
||
c[1].red >> 8, c[1].green >> 8, c[1].blue >> 8);
|
||
LO_SetDefaultColor (LO_COLOR_ALINK,
|
||
c[2].red >> 8, c[2].green >> 8, c[2].blue >> 8);
|
||
LO_SetDefaultColor (LO_COLOR_FG,
|
||
c[3].red >> 8, c[3].green >> 8, c[3].blue >> 8);
|
||
LO_SetDefaultColor (LO_COLOR_BG,
|
||
c[4].red >> 8, c[4].green >> 8, c[4].blue >> 8);
|
||
|
||
if (CONTEXT_DATA (context)->default_background_image &&
|
||
*CONTEXT_DATA (context)->default_background_image)
|
||
{
|
||
char *bi = CONTEXT_DATA (context)->default_background_image;
|
||
if (bi[0] == '/')
|
||
{
|
||
char *s = (char *) malloc (XP_STRLEN(bi) + 6);
|
||
strcpy (s, "file:");
|
||
strcat (s, bi);
|
||
LO_SetDefaultBackdrop (s);
|
||
free (s);
|
||
}
|
||
else
|
||
{
|
||
LO_SetDefaultBackdrop (bi);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
void
|
||
fe_set_scrolled_default_size(MWContext *context)
|
||
{
|
||
/* Set the default size of the scrolling area based on the size of the
|
||
default font. (This can be overridden by a -geometry argument.)
|
||
*/
|
||
int16 charset = CS_LATIN1;
|
||
fe_Font font = fe_LoadFontFromFace(context, NULL, &charset, 0, 3, 0);
|
||
Widget scrolled = XtNameToWidget (CONTEXT_WIDGET (context), "*scroller");
|
||
unsigned int cw = (font ? default_char_width (CS_LATIN1, font) : 12);
|
||
/* So just add 10% or so and hope it all fits. */
|
||
Dimension w = (cw * 90);
|
||
Dimension h = w;
|
||
|
||
if (context->type == MWContextMessageComposition ) {
|
||
/*
|
||
* NOTE: let's try to pick a smaller MailCompose window...
|
||
*/
|
||
h = ( cw * 50 );
|
||
}
|
||
|
||
if (scrolled) {
|
||
Dimension max_height = HeightOfScreen (XtScreen (scrolled));
|
||
Dimension pseudo_max_height = 0;
|
||
|
||
if (context->type == MWContextMessageComposition ) {
|
||
pseudo_max_height = max_height * 0.50;
|
||
}
|
||
/* EDITOR & BROWSER...
|
||
*
|
||
* We don't want the default window size to be bigger than the screen.
|
||
* So don't make the default height of the scrolling area be more than
|
||
* 90% of the height of the screen. This is pretty pseudo-nebulous,
|
||
* but again, getting exact numbers here is a royal pain.
|
||
*/
|
||
else {
|
||
pseudo_max_height = max_height * 0.70;
|
||
}
|
||
|
||
if (h > pseudo_max_height)
|
||
h = pseudo_max_height;
|
||
|
||
XtVaSetValues (scrolled, XmNwidth, w, XmNheight, h, 0);
|
||
}
|
||
fe_DoneWithFont(font);
|
||
}
|
||
|
||
|
||
void
|
||
fe_save_history_timer (XtPointer closure, XtIntervalId *id)
|
||
{
|
||
Boolean init_only_p = (Boolean) ((int) closure);
|
||
if (! init_only_p) {
|
||
GH_SaveGlobalHistory ();
|
||
NET_WriteCacheFAT (0, False);
|
||
NET_SaveCookies(NULL);
|
||
}
|
||
/* Re-add the timer. */
|
||
fe_globalData.save_history_id =
|
||
XtAppAddTimeOut (fe_XtAppContext,
|
||
fe_globalData.save_history_interval * 1000,
|
||
fe_save_history_timer, (XtPointer) ((int) False));
|
||
}
|
||
|
||
|
||
static void
|
||
fe_refresh_url_timer (XtPointer closure, XtIntervalId *id)
|
||
{
|
||
MWContext *context = (MWContext *) closure;
|
||
URL_Struct *url;
|
||
|
||
CONTEXT_DATA (context)->refresh_url_timer = 0; /* clear */
|
||
|
||
XP_ASSERT (CONTEXT_DATA (context)->refresh_url_timer_url);
|
||
|
||
if (! CONTEXT_DATA (context)->refresh_url_timer_url)
|
||
return;
|
||
url = NET_CreateURLStruct (CONTEXT_DATA (context)->refresh_url_timer_url,
|
||
CONTEXT_DATA (context)->url_refresh_force_reload);
|
||
url->force_reload = CONTEXT_DATA (context)->url_refresh_force_reload ;
|
||
fe_GetURL (context, url, FALSE);
|
||
}
|
||
|
||
void
|
||
FE_SetRefreshURLTimer (MWContext *context, URL_Struct *URL_s)
|
||
{
|
||
if(context->type != MWContextBrowser)
|
||
return;
|
||
|
||
if (CONTEXT_DATA (context)->refresh_url_timer)
|
||
XtRemoveTimeOut (CONTEXT_DATA (context)->refresh_url_timer);
|
||
if (CONTEXT_DATA (context)->refresh_url_timer_url)
|
||
free (CONTEXT_DATA (context)->refresh_url_timer_url);
|
||
CONTEXT_DATA (context)->refresh_url_timer = 0;
|
||
CONTEXT_DATA (context)->refresh_url_timer_secs = URL_s->refresh;
|
||
CONTEXT_DATA (context)->refresh_url_timer_url = strdup (URL_s->refresh_url);
|
||
|
||
/* Place the force reload method into the fe data. We can't access this
|
||
* info in the fe_refresh_url_timer timeout */
|
||
if(URL_s->dont_cache)
|
||
CONTEXT_DATA (context)->url_refresh_force_reload = NET_SUPER_RELOAD;
|
||
else
|
||
CONTEXT_DATA (context)->url_refresh_force_reload = URL_s->force_reload;
|
||
|
||
if (URL_s->refresh <= 0)
|
||
fe_refresh_url_timer ((XtPointer) context, 0);
|
||
else
|
||
CONTEXT_DATA (context)->refresh_url_timer =
|
||
XtAppAddTimeOut (fe_XtAppContext, URL_s->refresh * 1000,
|
||
fe_refresh_url_timer, (XtPointer) context);
|
||
}
|
||
|
||
|
||
/* This initializes resources which must be set up AFTER the widget is
|
||
realized (meaning we need a window of the correct depth.) */
|
||
void
|
||
fe_get_final_context_resources (MWContext *context)
|
||
{
|
||
static Boolean guffaws_done = False;
|
||
|
||
fe_InitIcons (context, MSG_BIFF_Unknown);
|
||
|
||
if (!guffaws_done)
|
||
{
|
||
Widget widget = CONTEXT_DATA (context)->drawing_area;
|
||
if (widget) {
|
||
if (! XtIsRealized (widget)) abort ();
|
||
guffaws_done = True;
|
||
fe_globalData.fe_guffaw_scroll =
|
||
fe_WindowGravityWorks (CONTEXT_WIDGET(context), widget);
|
||
}
|
||
}
|
||
}
|
||
|
||
void
|
||
fe_DestroySaveToDiskContext(MWContext *context)
|
||
{
|
||
fe_delete_cb (0, (XtPointer) context, 0);
|
||
}
|
||
|
||
void fe_cleanup_tooltips(MWContext *context);
|
||
|
||
void
|
||
fe_DestroyContext_part2(void *c)
|
||
{
|
||
MWContext *context = (MWContext*)c;
|
||
|
||
/* Destroy the compositor associated with the context. */
|
||
if (context->compositor)
|
||
{
|
||
CL_DestroyCompositor(context->compositor);
|
||
context->compositor = NULL;
|
||
}
|
||
|
||
#ifdef MOZ_MAIL_NEWS
|
||
MimeDestroyContextData(context);
|
||
#endif
|
||
|
||
if (context->title) free (context->title);
|
||
context->title = 0;
|
||
|
||
/* If a synchronous url dialog is up, dont free the context. Once the
|
||
* synchronous url dialog is over, we will free it.
|
||
*/
|
||
if (fe_IsContextProtected(context)) {
|
||
CONTEXT_DATA(context)->destroyed = 1;
|
||
}
|
||
else {
|
||
free (CONTEXT_DATA (context));
|
||
free (context);
|
||
}
|
||
}
|
||
|
||
void
|
||
fe_DestroyContext (MWContext *context)
|
||
{
|
||
PRBool observer_removed_p;
|
||
fe_ContextData* d = CONTEXT_DATA(context);
|
||
Widget w = CONTEXT_WIDGET (context);
|
||
struct fe_MWContext_cons *rest, *prev;
|
||
|
||
/* Fix for bug #29631 */
|
||
if (context == last_documented_xref_context)
|
||
{
|
||
last_documented_xref_context = 0;
|
||
last_documented_xref = 0;
|
||
last_documented_anchor_data = 0;
|
||
}
|
||
|
||
/* This is a hack. If the mailcompose window is going away and
|
||
* a tooltip was still there (or) was armed (a timer was set for it)
|
||
* for a widget in the mailcompose window, then the destroying
|
||
* mailcompose context would cause a core dump when the tooltip
|
||
* timer hits. This could happen to a javascript window with toolbars
|
||
* too if it is being closed on a timer.
|
||
*
|
||
* In this critical time of 3.x ship, we are fixing this by
|
||
* always killing any tooltip that was present anywhere and
|
||
* remove the timer for any armed tooltop anywhere in the
|
||
* navigator when any context is going away.
|
||
*----
|
||
* The proper thing to do would be
|
||
* - to check if the fe_tool_tips_widget is a child of the
|
||
* CONTEXT_WIDGET(context) and if it is, then cleanup the tooltip.
|
||
*/
|
||
fe_cleanup_tooltips(context);
|
||
|
||
|
||
if (context->is_grid_cell)
|
||
CONTEXT_DATA (context)->being_destroyed = True;
|
||
|
||
#ifdef EDITOR
|
||
if (context->type == MWContextEditor)
|
||
fe_EditorCleanup(context);
|
||
#endif /*EDITOR*/
|
||
|
||
if (context->is_grid_cell)
|
||
w = d->main_pane;
|
||
XP_InterruptContext (context);
|
||
if (d->refresh_url_timer)
|
||
XtRemoveTimeOut (d->refresh_url_timer);
|
||
|
||
/* Progress takes a context, removing timeout */
|
||
if (CONTEXT_DATA (context)->thermo_timer_id)
|
||
{
|
||
XtRemoveTimeOut (CONTEXT_DATA (context)->thermo_timer_id);
|
||
|
||
CONTEXT_DATA (context)->thermo_timer_id = 0;
|
||
}
|
||
|
||
if (d->refresh_url_timer_url)
|
||
free (d->refresh_url_timer_url);
|
||
fe_DisposeColormap(context);
|
||
|
||
/*
|
||
** We have to destroy the layout before calling XtUnmanageChild so that
|
||
** we have a chance to reparent the applet windows to a safe
|
||
** place. Otherwise they'll get destroyed.
|
||
*/
|
||
fe_DestroyLayoutData (context);
|
||
XtUnmanageChild (w);
|
||
|
||
if (context->color_space) {
|
||
IL_ReleaseColorSpace(context->color_space);
|
||
context->color_space = NULL;
|
||
}
|
||
|
||
/* Destroy the image group context after removing the image group
|
||
observer. */
|
||
observer_removed_p =
|
||
IL_RemoveGroupObserver(context->img_cx, fe_ImageGroupObserver,
|
||
(void *)context);
|
||
IL_DestroyGroupContext(context->img_cx);
|
||
context->img_cx = NULL;
|
||
|
||
/* Fix for bug #29631 */
|
||
if (context == last_documented_xref_context)
|
||
{
|
||
last_documented_xref_context = 0;
|
||
last_documented_xref = 0;
|
||
last_documented_anchor_data = 0;
|
||
}
|
||
|
||
fe_StopProgressGraph (context);
|
||
fe_FindReset (context);
|
||
SHIST_EndSession (context);
|
||
fe_DisownSelection (context, 0, True);
|
||
fe_DisownSelection (context, 0, False);
|
||
|
||
if (! context->is_grid_cell) {
|
||
if (context->type == MWContextSaveToDisk)
|
||
XtRemoveCallback (w, XtNdestroyCallback, fe_AbortCallback, context);
|
||
else
|
||
XtRemoveCallback (w, XtNdestroyCallback, fe_delete_cb, context);
|
||
|
||
XtRemoveEventHandler (w, StructureNotifyMask, False, fe_map_notify_eh,
|
||
context);
|
||
|
||
if (context->type == MWContextDialog) {
|
||
XtRemoveEventHandler (w, FocusChangeMask, False, fe_focus_notify_eh,
|
||
context);
|
||
}
|
||
}
|
||
XtDestroyWidget (w);
|
||
|
||
if (CONTEXT_DATA (context)->ftd) free (CONTEXT_DATA (context)->ftd);
|
||
if (CONTEXT_DATA (context)->sd) free (CONTEXT_DATA (context)->sd);
|
||
if (CONTEXT_DATA(context)->find_data)
|
||
XP_FREE(CONTEXT_DATA(context)->find_data);
|
||
|
||
for (prev = 0, rest = fe_all_MWContexts;
|
||
rest;
|
||
prev = rest, rest = rest->next)
|
||
if (rest->context == context)
|
||
break;
|
||
if (! rest) abort ();
|
||
if (prev)
|
||
prev->next = rest->next;
|
||
else
|
||
fe_all_MWContexts = rest->next;
|
||
free (rest);
|
||
|
||
|
||
/* Some window disappears. So we need to recreate windows menu of all
|
||
contexts availables. Mark so. */
|
||
for( rest=fe_all_MWContexts; rest; rest=rest->next )
|
||
CONTEXT_DATA(rest->context)->windows_menu_up_to_date_p = False;
|
||
|
||
#ifdef JAVA
|
||
LJ_DiscardEventsForContext(context);
|
||
#endif /* JAVA */
|
||
|
||
XP_RemoveContextFromList(context);
|
||
|
||
ET_RemoveWindowContext(context, fe_DestroyContext_part2, context);
|
||
}
|
||
|
||
|
||
/* This is called any time the user performs an action.
|
||
It reorders the list of contexts so that the most recently
|
||
used one is at the front.
|
||
*/
|
||
void
|
||
fe_UserActivity (MWContext *context)
|
||
{
|
||
struct fe_MWContext_cons *rest, *prev;
|
||
|
||
for (prev = 0, rest = fe_all_MWContexts;
|
||
rest;
|
||
prev = rest, rest = rest->next)
|
||
if (rest->context == context)
|
||
break;
|
||
if (! rest) abort (); /* not found?? */
|
||
|
||
/* the new and the last are the same, we're done */
|
||
if (context == fe_all_MWContexts->context)
|
||
return;
|
||
|
||
if (context->is_grid_cell) fe_SetGridFocus (context);
|
||
|
||
if (! prev) return; /* it was already first. */
|
||
|
||
prev->next = rest->next;
|
||
rest->next = fe_all_MWContexts;
|
||
fe_all_MWContexts = rest;
|
||
}
|
||
|
||
|
||
void
|
||
fe_RefreshAllAnchors ()
|
||
{
|
||
struct fe_MWContext_cons *rest;
|
||
for (rest = fe_all_MWContexts; rest; rest = rest->next)
|
||
LO_RefreshAnchors (rest->context);
|
||
}
|
||
|
||
/*
|
||
* XXX Need to make all contexts be deleted through this. - dp
|
||
*/
|
||
void
|
||
fe_delete_cb (Widget widget, XtPointer closure, XtPointer call_data)
|
||
{
|
||
MWContext *context = (MWContext *) closure;
|
||
|
||
if (fe_WindowCount == 1)
|
||
{
|
||
/* Unmap the window right away to give feedback that a delete
|
||
is in progress. */
|
||
Widget widget = CONTEXT_WIDGET(context);
|
||
Window window = (widget ? XtWindow(widget) : 0);
|
||
if (window)
|
||
XUnmapWindow (XtDisplay(widget), window);
|
||
|
||
/* Now save files and exit. */
|
||
fe_Exit (0);
|
||
}
|
||
else
|
||
{
|
||
if ( someGlobalContext == context )
|
||
{
|
||
fe_DestroyContext (context);
|
||
fe_WindowCount--;
|
||
|
||
someGlobalContext = XP_FindContextOfType(NULL, MWContextBrowser);
|
||
if (!someGlobalContext)
|
||
someGlobalContext = XP_FindContextOfType(NULL, MWContextMail);
|
||
if (!someGlobalContext)
|
||
someGlobalContext = XP_FindContextOfType(NULL, MWContextNews);
|
||
if (!someGlobalContext)
|
||
someGlobalContext = fe_all_MWContexts->context;
|
||
|
||
XmAddWMProtocols(CONTEXT_WIDGET(someGlobalContext),
|
||
&WM_SAVE_YOURSELF,1);
|
||
XmAddWMProtocolCallback(CONTEXT_WIDGET(someGlobalContext),
|
||
WM_SAVE_YOURSELF,
|
||
fe_wm_save_self_cb, (XtPointer)someGlobalContext);
|
||
}
|
||
else
|
||
{
|
||
fe_DestroyContext (context);
|
||
fe_WindowCount--;
|
||
|
||
}
|
||
|
||
if (fe_WindowCount <= 0)
|
||
abort ();
|
||
}
|
||
}
|
||
|
||
MWContext *
|
||
fe_WidgetToMWContext (Widget widget)
|
||
{
|
||
struct fe_MWContext_cons* rest;
|
||
struct fe_MWContext_cons* prev;
|
||
MWContext* context;
|
||
Widget transient_for;
|
||
|
||
for (;;) {
|
||
|
||
/*
|
||
* Find the toplevel shell.
|
||
*/
|
||
while (widget != NULL && !XtIsWMShell(widget))
|
||
widget = XtParent(widget);
|
||
|
||
if (widget == NULL) /* doom */
|
||
break;
|
||
|
||
/*
|
||
* Walk over the list of contexts. For each context,
|
||
* get the shell, and compare with the widget's shell.
|
||
*/
|
||
rest = fe_all_MWContexts;
|
||
prev = 0;
|
||
for (; rest != NULL; prev = rest, rest = rest->next) {
|
||
context = rest->context;
|
||
if (context != NULL && CONTEXT_DATA(context) != NULL) {
|
||
Widget mainw = CONTEXT_WIDGET(context);
|
||
Widget parent;
|
||
|
||
if (mainw == NULL) /* paranoia */
|
||
continue;
|
||
|
||
parent = XtParent(mainw);
|
||
|
||
/*
|
||
* The old version of this routine allowed
|
||
* for both of these matches, so keep the semantics...
|
||
*/
|
||
if (mainw == widget || parent == widget) { /* match */
|
||
/*
|
||
* Push this context to top of list, so this
|
||
* search is faster next time.
|
||
*/
|
||
if (fe_all_MWContexts != rest) {
|
||
prev->next = rest->next;
|
||
rest->next = fe_all_MWContexts;
|
||
fe_all_MWContexts = rest;
|
||
}
|
||
return context;
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* No match. Hmmmm, what if the shell is a transient,
|
||
* let's try the shell it's transient for.
|
||
*/
|
||
if (XtIsSubclass(widget, transientShellWidgetClass)) {
|
||
XtVaGetValues(widget, XmNtransientFor, &transient_for, 0);
|
||
widget = transient_for;
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
|
||
/* There is no parent -- hope the caller can deal with a null */
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* Now that we have grids, you can't just walk up to the parent shell
|
||
* to find the context for a widget. We are assuming here that the
|
||
* motion event was always delivered to the drawingarea widget, I hope
|
||
* that is correct --ejb
|
||
*/
|
||
MWContext *
|
||
fe_MotionWidgetToMWContext (Widget widget)
|
||
{
|
||
struct fe_MWContext_cons *rest;
|
||
for (rest = fe_all_MWContexts; rest; rest = rest->next) {
|
||
if (CONTEXT_DATA (rest->context)->drawing_area == widget)
|
||
return rest->context;
|
||
}
|
||
/* There is no parent -- hope the caller can deal with a null */
|
||
return 0;
|
||
}
|
||
|
||
|
||
/* fe_MimimalNoUICleanup
|
||
*
|
||
* This does a cleanup of the only the absolute essential stuff.
|
||
* - saves bookmarks, addressbook
|
||
* - saves global history
|
||
* - saves cookies
|
||
* (since all these saves are protected by flags anyway, we wont endup saving
|
||
* again if all these happened before.)
|
||
* - remove lock file if existent
|
||
*
|
||
* This will be called at the following points:
|
||
* 1. when the x server dies [x_fatal_error_handler()]
|
||
* 2. when window manages says 'SAVE_YOURSELF'
|
||
* 3. at_exit_handler()
|
||
*/
|
||
void
|
||
fe_MinimalNoUICleanup()
|
||
{
|
||
PREF_SavePrefFile();
|
||
|
||
#ifdef JAVA
|
||
NR_ShutdownRegistry();
|
||
#endif
|
||
#ifdef MOZ_SMARTUPDATE
|
||
SU_Shutdown();
|
||
#endif
|
||
|
||
RDF_Shutdown();
|
||
GH_SaveGlobalHistory ();
|
||
NET_SaveCookies(NULL);
|
||
AltMailExit();
|
||
}
|
||
|
||
/* If there is whitespace at the beginning or end of string, removes it.
|
||
The string is modified in place.
|
||
*/
|
||
char *
|
||
fe_StringTrim (char *string)
|
||
{
|
||
char *orig = string;
|
||
char *new;
|
||
if (! string) return 0;
|
||
new = string + strlen (string) - 1;
|
||
while (new >= string && XP_IS_SPACE (new [0]))
|
||
*new-- = 0;
|
||
new = string;
|
||
while (XP_IS_SPACE (*new))
|
||
new++;
|
||
if (new == string)
|
||
return string;
|
||
while (*new)
|
||
*string++ = *new++;
|
||
*string = 0;
|
||
return orig;
|
||
}
|
||
|
||
/*
|
||
* fe_StrEndsWith(char *s, char *endstr)
|
||
*
|
||
* returns TRUE if string 's' ends with string 'endstr'
|
||
* else returns FALSE
|
||
*/
|
||
XP_Bool
|
||
fe_StrEndsWith(char *s, char *endstr)
|
||
{
|
||
int l, lend;
|
||
XP_Bool retval = FALSE;
|
||
|
||
if (!endstr)
|
||
/* All strings ends in NULL */
|
||
return(TRUE);
|
||
if (!s)
|
||
/* NULL strings will never have endstr at its end */
|
||
return(FALSE);
|
||
|
||
lend = strlen(endstr);
|
||
l = strlen(s);
|
||
if (l >= lend && !strcmp(s+l-lend, endstr))
|
||
retval = TRUE;
|
||
return (retval);
|
||
}
|
||
|
||
char *
|
||
fe_Basename (char *s)
|
||
{
|
||
int len;
|
||
char *p;
|
||
|
||
if (!s) return (NULL);
|
||
len = strlen(s);
|
||
p = &s[len-1];
|
||
|
||
while(--len > 0 && *p != '/') p--;
|
||
if (*p == '/') p++;
|
||
return (p);
|
||
}
|
||
|
||
|
||
#if defined(MOZ_MAIL_NEWS) || defined(MOZ_MAIL_COMPOSE)
|
||
|
||
/* Mail stuff */
|
||
|
||
const char *
|
||
FE_UsersMailAddress (void)
|
||
{
|
||
static char *cached_uid = 0;
|
||
char *uid, *name;
|
||
if (fe_globalPrefs.email_address && *fe_globalPrefs.email_address)
|
||
{
|
||
if (cached_uid) free (cached_uid);
|
||
cached_uid = 0;
|
||
return fe_globalPrefs.email_address;
|
||
}
|
||
else if (cached_uid)
|
||
{
|
||
return cached_uid;
|
||
}
|
||
else
|
||
{
|
||
fe_DefaultUserInfo (&uid, &name, False);
|
||
free (name);
|
||
cached_uid = uid;
|
||
return uid;
|
||
}
|
||
}
|
||
|
||
|
||
const char *
|
||
FE_UsersRealMailAddress (void)
|
||
{
|
||
static char *cached_uid = 0;
|
||
if (cached_uid)
|
||
{
|
||
return cached_uid;
|
||
}
|
||
else
|
||
{
|
||
char *uid, *name;
|
||
fe_DefaultUserInfo (&uid, &name, True);
|
||
free (name);
|
||
cached_uid = uid;
|
||
return uid;
|
||
}
|
||
}
|
||
|
||
|
||
const char *
|
||
FE_UsersFullName (void)
|
||
{
|
||
static char *cached_name = 0;
|
||
char *uid, *name;
|
||
if (fe_globalPrefs.real_name && *fe_globalPrefs.real_name)
|
||
{
|
||
if (cached_name) free (cached_name);
|
||
cached_name = 0;
|
||
return fe_globalPrefs.real_name;
|
||
}
|
||
else if (cached_name)
|
||
{
|
||
return cached_name;
|
||
}
|
||
else
|
||
{
|
||
fe_DefaultUserInfo (&uid, &name, False);
|
||
free (uid);
|
||
cached_name = name;
|
||
return name;
|
||
}
|
||
}
|
||
|
||
|
||
const char *
|
||
FE_UsersOrganization (void)
|
||
{
|
||
static char *cached_org = 0;
|
||
if (cached_org)
|
||
free (cached_org);
|
||
cached_org = strdup (fe_globalPrefs.organization
|
||
? fe_globalPrefs.organization
|
||
: "");
|
||
return cached_org;
|
||
}
|
||
|
||
const char *
|
||
FE_UsersSignature (void)
|
||
{
|
||
static char *signature = NULL;
|
||
XP_File file;
|
||
time_t sig_date = 0;
|
||
|
||
if (signature)
|
||
XP_FREE (signature);
|
||
|
||
file = XP_FileOpen (fe_globalPrefs.signature_file,
|
||
xpSignature, XP_FILE_READ);
|
||
|
||
if (file)
|
||
{
|
||
struct stat st;
|
||
char buf [1024];
|
||
char *s = buf;
|
||
|
||
int left = sizeof (buf) - 2;
|
||
int size;
|
||
*s = 0;
|
||
|
||
if (!fstat (fileno (file), &st))
|
||
sig_date = st.st_mtime;
|
||
|
||
while ((size = XP_FileRead (s, left, file)) && left > 0)
|
||
{
|
||
left -= size;
|
||
s += size;
|
||
}
|
||
|
||
*s = 0;
|
||
|
||
/* take off all trailing whitespace */
|
||
s--;
|
||
while (s >= buf && isspace (*s))
|
||
*s-- = 0;
|
||
/* terminate with a single newline. */
|
||
s++;
|
||
*s++ = '\n';
|
||
*s++ = 0;
|
||
XP_FileClose (file);
|
||
if ( !strcmp (buf, "\n"))
|
||
signature = NULL;
|
||
else
|
||
signature = strdup (buf);
|
||
}
|
||
else
|
||
signature = NULL;
|
||
|
||
/* The signature file date has changed - check the contents of the file
|
||
again, and save that date to the preferences file so that it is checked
|
||
only when the file changes, even if Netscape has been restarted in the
|
||
meantime. */
|
||
if (fe_globalPrefs.signature_date != sig_date)
|
||
{
|
||
MWContext *context =
|
||
XP_FindContextOfType(0, MWContextMessageComposition);
|
||
if (!context) context = fe_all_MWContexts->context;
|
||
MISC_ValidateSignature (context, signature);
|
||
fe_globalPrefs.signature_date = sig_date;
|
||
|
||
if (!XFE_SavePrefs ((char *) fe_globalData.user_prefs_file,
|
||
&fe_globalPrefs))
|
||
fe_perror (context, XP_GetString( XFE_ERROR_SAVING_OPTIONS ) );
|
||
}
|
||
|
||
return signature;
|
||
}
|
||
|
||
#endif /* MOZ_MAIL_NEWS || MOZ_MAIL_COMPOSE */
|
||
|
||
|
||
int32
|
||
FE_GetContextID (MWContext * window_id)
|
||
{
|
||
return((int32) window_id);
|
||
}
|
||
|
||
XP_Bool
|
||
XFE_UseFancyFTP (MWContext *context)
|
||
{
|
||
return CONTEXT_DATA (context)->fancy_ftp_p;
|
||
}
|
||
|
||
XP_Bool
|
||
XFE_UseFancyNewsgroupListing (MWContext * window_id)
|
||
{
|
||
return False;
|
||
}
|
||
|
||
/* FE_ShowAllNewsArticles
|
||
*
|
||
* Return true if the user wants to see all newsgroup
|
||
* articles and not have the number restricted by
|
||
* .newsrc entries
|
||
*/
|
||
XP_Bool
|
||
XFE_ShowAllNewsArticles (MWContext *window_id)
|
||
{
|
||
return(FALSE); /* temporary LJM */
|
||
}
|
||
|
||
int
|
||
XFE_FileSortMethod (MWContext * window_id)
|
||
{
|
||
return (SORT_BY_NAME);
|
||
}
|
||
|
||
int16
|
||
INTL_DefaultDocCharSetID(MWContext *cxt)
|
||
{
|
||
int16 csid;
|
||
|
||
if (cxt)
|
||
{
|
||
INTL_CharSetInfo csi = LO_GetDocumentCharacterSetInfo(cxt);
|
||
if (INTL_GetCSIDocCSID(csi))
|
||
{
|
||
csid = INTL_GetCSIDocCSID(csi);
|
||
}
|
||
else if (cxt->fe.data && cxt->fe.data->xfe_doc_csid)
|
||
{
|
||
csid = cxt->fe.data->xfe_doc_csid;
|
||
}
|
||
else
|
||
{
|
||
csid = fe_globalPrefs.doc_csid;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
csid = fe_globalPrefs.doc_csid;
|
||
}
|
||
|
||
return csid;
|
||
}
|
||
|
||
char *
|
||
INTL_ResourceCharSet(void)
|
||
{
|
||
return fe_LocaleCharSetName;
|
||
}
|
||
|
||
|
||
INTLCharSetID
|
||
FE_GetCharSetID(INTL_CharSetID_Selector selector)
|
||
{
|
||
INTLCharSetID charsetID = CS_DEFAULT;
|
||
|
||
switch (selector)
|
||
{
|
||
case INTL_FileNameCsidSel:
|
||
case INTL_DefaultTextWidgetCsidSel:
|
||
case INTL_MenuCsidSel:
|
||
charsetID = (INTLCharSetID) fe_LocaleCharSetID;
|
||
break;
|
||
case INTL_OldBookmarkCsidSel:
|
||
charsetID = (INTLCharSetID) INTL_DocToWinCharSetID(fe_LocaleCharSetID);
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
XP_ASSERT(CS_DEFAULT != charsetID);
|
||
|
||
return charsetID;
|
||
}
|
||
void
|
||
INTL_Relayout(MWContext *pContext)
|
||
{
|
||
if(XP_IsContextBusy(pContext) == FALSE)
|
||
{
|
||
fe_ReLayout(pContext, NET_DONT_RELOAD);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
typedef struct fe_timeout {
|
||
TimeoutCallbackFunction func;
|
||
void* closure;
|
||
XtIntervalId timer;
|
||
uint32 serial_num;
|
||
struct fe_timeout *next;
|
||
} fe_timeout;
|
||
|
||
static uint32 fe_timeout_serial_num = 0; /* Unique token for each timeout */
|
||
static fe_timeout *fe_TimeoutList = NULL;
|
||
|
||
static Bool
|
||
remove_timeout_from_list(uint32 serial_num, Bool clear_timeout)
|
||
{
|
||
fe_timeout **p, *t;
|
||
|
||
p = &fe_TimeoutList;
|
||
while ((t = *p)) {
|
||
if (t->serial_num == serial_num) {
|
||
*p = t->next;
|
||
if (clear_timeout)
|
||
XtRemoveTimeOut(t->timer);
|
||
XP_FREE(t);
|
||
return TRUE;
|
||
}
|
||
p = &t->next;
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
static void
|
||
fe_do_timeout(XtPointer p, XtIntervalId* id)
|
||
{
|
||
fe_timeout* timer = (fe_timeout*) p;
|
||
XP_ASSERT(timer->timer == *id);
|
||
(*timer->func)(timer->closure);
|
||
if (!remove_timeout_from_list(timer->serial_num, FALSE)) {
|
||
XP_ASSERT(0);
|
||
}
|
||
}
|
||
|
||
void*
|
||
FE_SetTimeout(TimeoutCallbackFunction func, void* closure, uint32 msecs)
|
||
{
|
||
fe_timeout* timer;
|
||
timer = XP_NEW(fe_timeout);
|
||
if (!timer) return NULL;
|
||
timer->func = func;
|
||
timer->closure = closure;
|
||
timer->timer = XtAppAddTimeOut(fe_XtAppContext, msecs, fe_do_timeout, timer);
|
||
timer->serial_num = ++fe_timeout_serial_num;
|
||
timer->next = fe_TimeoutList;
|
||
fe_TimeoutList = timer;
|
||
return (void*)fe_timeout_serial_num;
|
||
}
|
||
|
||
void
|
||
FE_ClearTimeout(void* timer_id)
|
||
{
|
||
remove_timeout_from_list((uint32)timer_id, TRUE);
|
||
}
|
||
|
||
char *
|
||
FE_GetCipherPrefs(void)
|
||
{
|
||
if (fe_globalPrefs.cipher == NULL)
|
||
return NULL;
|
||
return(strdup(fe_globalPrefs.cipher));
|
||
}
|
||
|
||
void
|
||
FE_SetCipherPrefs(MWContext *context, char *cipher)
|
||
{
|
||
if (fe_globalPrefs.cipher) {
|
||
if (!strcmp(fe_globalPrefs.cipher, cipher))
|
||
return;
|
||
XP_FREE(fe_globalPrefs.cipher);
|
||
}
|
||
fe_globalPrefs.cipher = strdup(cipher);
|
||
if (!XFE_SavePrefs ((char *) fe_globalData.user_prefs_file, &fe_globalPrefs))
|
||
{
|
||
if (context == NULL) {
|
||
MWContext *someContext;
|
||
/* Type to find a context */
|
||
someContext = XP_FindContextOfType(NULL, MWContextBrowser);
|
||
if (!someContext)
|
||
someContext = XP_FindContextOfType(NULL, MWContextMail);
|
||
if (!someContext)
|
||
someContext = XP_FindContextOfType(NULL, MWContextNews);
|
||
if (!someContext)
|
||
someContext = fe_all_MWContexts->context;
|
||
context = someContext;
|
||
}
|
||
if (context != NULL)
|
||
fe_perror (context, XP_GetString( XFE_ERROR_SAVING_OPTIONS));
|
||
}
|
||
}
|
||
|
||
|
||
/************************
|
||
* File status routines *
|
||
************************/
|
||
/*
|
||
* File changes since last seen.
|
||
* For error case of file not present, it returns FALSE and doesn't change
|
||
* return value new_mtime.
|
||
* If file has changed, returns TRUE and new_mtime if not null is updated
|
||
* to the new modified time.
|
||
* If file has not changed, return FALSE and doesn't change new_mtime.
|
||
*/
|
||
XP_Bool
|
||
fe_isFileChanged(char *name, time_t mtime, time_t *new_mtime)
|
||
{
|
||
XP_StatStruct st;
|
||
XP_Bool ret = FALSE;
|
||
|
||
if (name && *name && !stat(name, &st))
|
||
if (st.st_mtime != mtime) ret = TRUE;
|
||
|
||
if (ret && new_mtime)
|
||
*new_mtime = st.st_mtime;
|
||
|
||
return (ret);
|
||
}
|
||
|
||
/*
|
||
* File exists
|
||
*/
|
||
Boolean
|
||
fe_isFileExist(char *name)
|
||
{
|
||
XP_StatStruct st;
|
||
if (!name || !*name) return (False);
|
||
if (!stat (name, &st))
|
||
return (True);
|
||
else
|
||
return (False);
|
||
}
|
||
|
||
/*
|
||
* File exists and is readable.
|
||
*/
|
||
Boolean
|
||
fe_isFileReadable(char *name)
|
||
{
|
||
FILE *fp;
|
||
if (!name || !*name) return (False);
|
||
fp = fopen(name, "r");
|
||
if (fp) {
|
||
fclose(fp);
|
||
return (True);
|
||
}
|
||
else
|
||
return (False);
|
||
}
|
||
|
||
/*
|
||
* File is a directory
|
||
*/
|
||
Boolean
|
||
fe_isDir(char *name)
|
||
{
|
||
XP_StatStruct st;
|
||
if (!name || !*name) return (False);
|
||
if (!stat (name, &st) && S_ISDIR(st.st_mode))
|
||
return (True);
|
||
else
|
||
return (False);
|
||
}
|
||
|
||
|
||
/*
|
||
* Layering support. LO_RefreshArea is called through compositor.
|
||
*/
|
||
|
||
void
|
||
fe_RefreshArea(MWContext *context, int32 x, int32 y,
|
||
uint32 width, uint32 height)
|
||
{
|
||
if(context->compositor)
|
||
{
|
||
XP_Rect rect;
|
||
|
||
rect.left = x;
|
||
rect.top = y;
|
||
rect.right = x + width;
|
||
rect.bottom = y + height;
|
||
CL_UpdateDocumentRect((context)->compositor, &rect, PR_TRUE);
|
||
}
|
||
#ifdef EDITOR
|
||
if (EDT_IS_EDITOR(context))
|
||
fe_EditorRefreshArea(context, x, y, width, height);
|
||
#endif /*EDITOR*/
|
||
}
|
||
|
||
void
|
||
fe_RefreshAreaRequest(MWContext *context, int32 x, int32 y,
|
||
uint32 width, uint32 height)
|
||
{
|
||
if(context->compositor) {
|
||
XP_Rect rect;
|
||
|
||
rect.left = x;
|
||
rect.top = y;
|
||
rect.right = x + width;
|
||
rect.bottom = y + height;
|
||
CL_UpdateDocumentRect((context)->compositor, &rect, PR_FALSE);
|
||
}
|
||
}
|
||
|
||
extern void fe_HTMLViewDoPopup (MWContext *context, CL_Layer *layer,
|
||
CL_Event *layer_event);
|
||
extern void fe_HTMLDragSetLayer(CL_Layer *layer);
|
||
|
||
/* Handle events on a layer-specific basis. */
|
||
PRBool FE_HandleLayerEvent(MWContext *context, CL_Layer *layer,
|
||
CL_Event *layer_event)
|
||
{
|
||
PRBool handled_event_p = TRUE;
|
||
Boolean synthesized_event_p = FALSE;
|
||
fe_EventStruct *fe_event = (fe_EventStruct *)layer_event->fe_event;
|
||
XEvent *event = NULL;
|
||
fe_MouseActionEnum mouse_action = FE_INVALID_MOUSE_ACTION;
|
||
|
||
if (fe_event)
|
||
{
|
||
mouse_action = fe_event->mouse_action;
|
||
}
|
||
else
|
||
{
|
||
/* This means that we have a synthesized event, so fill
|
||
in the FE part. */
|
||
|
||
int32 layer_x_offset, layer_y_offset;
|
||
|
||
synthesized_event_p = TRUE;
|
||
fe_event = XP_NEW_ZAP(fe_EventStruct);
|
||
XP_ASSERT(fe_event);
|
||
|
||
/* Create an XEvent. */
|
||
event = XP_NEW_ZAP(XEvent);
|
||
XP_ASSERT(event);
|
||
|
||
layer_x_offset = CL_GetLayerXOffset(layer);
|
||
layer_y_offset = CL_GetLayerYOffset(layer);
|
||
|
||
/* XXX This part of the event synthesis code is currently based
|
||
on the mouse bindings specified in the default resource file.
|
||
We need to eventually change it so that it will work with any
|
||
set of mouse bindings that the user specifies. */
|
||
|
||
switch (layer_event->type)
|
||
{
|
||
case CL_EVENT_MOUSE_BUTTON_DOWN:
|
||
event->type = ButtonPress;
|
||
event->xbutton.x = layer_event->x + layer_x_offset -
|
||
CONTEXT_DATA (context)->document_x;
|
||
event->xbutton.y = layer_event->y + layer_y_offset -
|
||
CONTEXT_DATA (context)->document_y;
|
||
event->xbutton.time =
|
||
XtLastTimestampProcessed(XtDisplay(CONTEXT_WIDGET(context)));
|
||
|
||
switch (layer_event->which)
|
||
{
|
||
case 1: /* Left mouse. */
|
||
event->xbutton.button = 1;
|
||
mouse_action = FE_ARM_LINK;
|
||
break;
|
||
|
||
case 2: /* Right mouse. */
|
||
event->xbutton.button = 3;
|
||
mouse_action = FE_POPUP_MENU;
|
||
break;
|
||
|
||
default:
|
||
XP_ASSERT(0);
|
||
break;
|
||
}
|
||
break;
|
||
|
||
case CL_EVENT_MOUSE_BUTTON_UP:
|
||
event->type = ButtonRelease;
|
||
event->xbutton.x = layer_event->x + layer_x_offset -
|
||
CONTEXT_DATA (context)->document_x;
|
||
event->xbutton.y = layer_event->y + layer_y_offset -
|
||
CONTEXT_DATA (context)->document_y;
|
||
event->xbutton.time =
|
||
XtLastTimestampProcessed(XtDisplay(CONTEXT_WIDGET(context)));
|
||
|
||
switch (layer_event->which)
|
||
{
|
||
case 1: /* Left mouse. */
|
||
event->xbutton.button = 1;
|
||
mouse_action = FE_ACTIVATE_LINK;
|
||
break;
|
||
|
||
default:
|
||
XP_ASSERT(0);
|
||
break;
|
||
}
|
||
break;
|
||
|
||
case CL_EVENT_MOUSE_MOVE:
|
||
event->type = MotionNotify;
|
||
event->xmotion.x = layer_event->x + layer_x_offset -
|
||
CONTEXT_DATA (context)->document_x;
|
||
event->xmotion.y = layer_event->y + layer_y_offset -
|
||
CONTEXT_DATA (context)->document_y;
|
||
event->xmotion.time =
|
||
XtLastTimestampProcessed(XtDisplay(CONTEXT_WIDGET(context)));
|
||
|
||
switch (layer_event->which)
|
||
{
|
||
case 0: /* No button. */
|
||
mouse_action = FE_DESCRIBE_LINK;
|
||
break;
|
||
|
||
case 1: /* Left mouse. */
|
||
event->xmotion.state = Button1Mask;
|
||
mouse_action = FE_DISARM_LINK_IF_MOVED;
|
||
break;
|
||
|
||
case 2: /* Right mouse. */
|
||
event->xmotion.state = Button3Mask;
|
||
mouse_action = FE_DISARM_LINK_IF_MOVED;
|
||
break;
|
||
|
||
default:
|
||
XP_ASSERT(0);
|
||
break;
|
||
}
|
||
break;
|
||
|
||
case CL_EVENT_MOUSE_ENTER:
|
||
case CL_EVENT_MOUSE_LEAVE:
|
||
case CL_EVENT_KEY_FOCUS_GAINED:
|
||
case CL_EVENT_KEY_FOCUS_LOST:
|
||
if (synthesized_event_p) {
|
||
XP_FREE(event);
|
||
XP_FREE(fe_event);
|
||
}
|
||
return FALSE;
|
||
|
||
default:
|
||
XP_ASSERT(0);
|
||
break;
|
||
}
|
||
|
||
#ifdef LAYERS_FULL_FE_EVENT
|
||
fe_event->event = event;
|
||
fe_event->av = NULL;
|
||
fe_event->ac = NULL;
|
||
fe_event->mouse_action = mouse_action;
|
||
#else
|
||
fe_event_stuff(context,fe_event,event,0,0,mouse_action);
|
||
#endif
|
||
|
||
layer_event->fe_event = fe_event;
|
||
|
||
}
|
||
|
||
switch (mouse_action)
|
||
{
|
||
/* These correspond to
|
||
layer_event->type = CL_EVENT_MOUSE_BUTTON_DOWN */
|
||
|
||
case FE_ARM_LINK:
|
||
/* hack to allow Motif drag and drop to catch Layer event. */
|
||
fe_HTMLDragSetLayer(layer);
|
||
|
||
fe_arm_link_action_for_layer(context, layer, layer_event);
|
||
break;
|
||
|
||
case FE_EXTEND_SELECTION:
|
||
fe_extend_selection_action_for_layer(context, layer,
|
||
layer_event);
|
||
break;
|
||
|
||
case FE_POPUP_MENU:
|
||
fe_HTMLViewDoPopup (context, layer, layer_event);
|
||
break;
|
||
|
||
/* These correspond to
|
||
layer_event->type = CL_EVENT_MOUSE_BUTTON_UP */
|
||
|
||
case FE_ACTIVATE_LINK:
|
||
fe_activate_link_action_for_layer(context, layer,
|
||
layer_event);
|
||
break;
|
||
|
||
case FE_DISARM_LINK:
|
||
#ifdef LAYERS_SEPARATE_DISARM
|
||
fe_disarm_link_action_for_layer(context, layer,
|
||
layer_event);
|
||
#else
|
||
#ifdef DEBUG
|
||
printf("FE_HandleLayerEvent(): unexpected FE_DISARM_LINK\n");
|
||
#endif
|
||
#endif
|
||
break;
|
||
|
||
/* These correspond to
|
||
layer_event->type = CL_EVENT_MOUSE_MOVE */
|
||
|
||
case FE_DESCRIBE_LINK:
|
||
fe_describe_link_action_for_layer(context, layer,
|
||
layer_event);
|
||
break;
|
||
|
||
case FE_DISARM_LINK_IF_MOVED:
|
||
fe_disarm_link_if_moved_action_for_layer(context, layer,
|
||
layer_event);
|
||
break;
|
||
|
||
/* No actions corresponding to
|
||
layer_event->type = CL_EVENT_MOUSE_BUTTON_MULTI_CLICK */
|
||
|
||
case FE_KEY_UP:
|
||
fe_key_up_in_text_action_for_layer(context, layer,
|
||
layer_event);
|
||
break;
|
||
|
||
case FE_KEY_DOWN:
|
||
fe_key_down_in_text_action_for_layer(context, layer,
|
||
layer_event);
|
||
break;
|
||
|
||
default:
|
||
XP_ASSERT(0);
|
||
break;
|
||
}
|
||
|
||
if (synthesized_event_p)
|
||
{
|
||
if (mouse_action == FE_ACTIVATE_LINK)
|
||
{
|
||
/* disarm is now triggered by activate, instead of
|
||
* by the mouseclick, so calling
|
||
* fe_activate_link_action_for_layer() above triggered
|
||
* the disarm already. -- francis
|
||
*/
|
||
#ifdef LAYERS_SEPARATE_DISARM
|
||
fe_disarm_link_action_for_layer(context, layer,
|
||
layer_event);
|
||
#endif
|
||
}
|
||
XP_FREE(event);
|
||
XP_FREE(fe_event);
|
||
}
|
||
|
||
return handled_event_p;
|
||
}
|
||
|
||
/* XXX - For now, unix does not have windowless plugins */
|
||
PRBool FE_HandleEmbedEvent(MWContext *context, LO_EmbedStruct *embed,
|
||
CL_Event *event)
|
||
{
|
||
return PR_FALSE;
|
||
}
|
||
|
||
|
||
/* the purpose of the following code is to provide the backend
|
||
* a way to get temp files associated with each ldap server.
|
||
* these files should have unique names, meaning for each different
|
||
* ldap server, a different file is used. at the same time, if the
|
||
* backend queries with the same ldap server, the same file name should
|
||
* be returned (because we want to allow user to continue from previous
|
||
* searches)
|
||
*
|
||
* anything with ifdef _XP_TMP_FILENAME_FOR_LDAP_ in it needs to be
|
||
* implemented. right now we are just returning hardcoded names to test
|
||
* the ldap features.
|
||
*/
|
||
|
||
/*
|
||
#define _XP_TMP_FILENAME_FOR_LDAP_
|
||
*/
|
||
|
||
char* fe_GetLDAPTmpFile(char *name) {
|
||
char* home = getenv("HOME");
|
||
static char tmp[1024];
|
||
char *configdir;
|
||
|
||
if (!home) home = "";
|
||
if (!name) return NULL;
|
||
|
||
configdir = fe_GetConfigDirFilename("");
|
||
if (configdir)
|
||
{
|
||
strncpy(tmp, configdir, sizeof(tmp));
|
||
free(configdir);
|
||
} else snprintf(tmp, sizeof(tmp), "%.900s/%s/", home, MOZ_USER_DIR);
|
||
|
||
#ifdef _XP_TMP_FILENAME_FOR_LDAP_
|
||
/* we need to write this */
|
||
/* what we need: look for the temp name associated with the
|
||
* ldap server specified in the array "name".
|
||
* if we find it, we return it, if not, we create
|
||
* a new tmp file name and "remember" that it is associated
|
||
* with this particular ldap server - benjie */
|
||
|
||
/* here we look for it */
|
||
|
||
if (HA_I_FOUND_IT) {
|
||
return THE_FILE_NAME;
|
||
} else {
|
||
/* we create a new one */
|
||
char *ldapfile=NULL;
|
||
strcat(tmp,"ldapXXXXXX");
|
||
ldapfile = mktemp(tmp);
|
||
if (!ldapfile) return null;
|
||
else {
|
||
PR_snprintf(tmp, sizeof (tmp),"%s.nab",ldapfile);
|
||
/ok, now we save the temp file name*/
|
||
|
||
/* saving temp file name associated with the ldap server here */
|
||
|
||
return tmp;
|
||
}
|
||
}
|
||
return NULL;
|
||
#else
|
||
if (strcmp(name,"scrappy")==0) {
|
||
strcat(tmp,"nsldap.nab\0");
|
||
return tmp;
|
||
} else if (strcmp(name,"umich")==0) {
|
||
strcat(tmp,"umich.nab\0");
|
||
return tmp;
|
||
} else return NULL;
|
||
#endif
|
||
}
|
||
|
||
|
||
/* this should be called from the backend (XP_FileName) */
|
||
/* for now it is only be called from fe */
|
||
char* FE_GetFileName(char *name, XP_FileType type) {
|
||
switch(type) {
|
||
case xpAddrBook:
|
||
return fe_GetLDAPTmpFile(name);
|
||
default:
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Walks over a tree, calling mappee callback on each widget.
|
||
*/
|
||
XtPointer
|
||
fe_WidgetTreeWalk(Widget widget, fe_WidgetTreeWalkMappee callback,
|
||
XtPointer data)
|
||
{
|
||
Arg av[8];
|
||
Cardinal ac;
|
||
Widget* children;
|
||
Cardinal nchildren;
|
||
Cardinal i;
|
||
XtPointer rv;
|
||
|
||
if (widget == NULL || callback == NULL)
|
||
return 0;
|
||
|
||
if (XtIsSubclass(widget, compositeWidgetClass)) {
|
||
|
||
ac = 0;
|
||
XtSetArg(av[ac], XmNchildren, &children); ac++;
|
||
XtSetArg(av[ac], XmNnumChildren, &nchildren); ac++;
|
||
XtGetValues(widget, av, ac);
|
||
|
||
for (i = 0; i < nchildren; i++) {
|
||
rv = fe_WidgetTreeWalk(children[i], callback, data);
|
||
if (rv != 0)
|
||
return rv;
|
||
}
|
||
}
|
||
|
||
return (callback)(widget, data);
|
||
}
|
||
|
||
/*
|
||
* fe_WidgetTreeWalkChildren
|
||
*
|
||
* Intension here is to call the mappee callback for all children in the
|
||
* tree taking into account the cascade menus.
|
||
*/
|
||
XtPointer
|
||
fe_WidgetTreeWalkChildren(Widget widget, fe_WidgetTreeWalkMappee callback,
|
||
XtPointer closure)
|
||
{
|
||
Widget *buttons = 0, menu = 0;
|
||
Cardinal nbuttons = 0;
|
||
int i;
|
||
XtPointer ret = 0;
|
||
XtVaGetValues (widget, XmNchildren, &buttons, XmNnumChildren, &nbuttons, 0);
|
||
for (i = 0; ret == 0 && i < nbuttons; i++)
|
||
{
|
||
Widget item = buttons[i];
|
||
if (XmIsToggleButton(item) || XmIsToggleButtonGadget(item) ||
|
||
XmIsPushButton(item) || XmIsPushButtonGadget(item) ||
|
||
XmIsCascadeButton(item) || XmIsCascadeButtonGadget(item))
|
||
ret = (callback) (item, closure);
|
||
if (ret != 0) break;
|
||
if (XmIsCascadeButton(item) || XmIsCascadeButtonGadget(item)) {
|
||
XtVaGetValues (item, XmNsubMenuId, &menu, 0);
|
||
if (menu)
|
||
ret = fe_WidgetTreeWalkChildren(menu, callback, closure);
|
||
}
|
||
}
|
||
return(ret);
|
||
}
|
||
|
||
|
||
static XtPointer
|
||
fe_find_widget_mappee(Widget widget, XtPointer data)
|
||
{
|
||
char* match_name = (char*)data;
|
||
char* name = XtName(widget);
|
||
|
||
if (strcmp(name, match_name) == 0) {
|
||
return (XtPointer) widget; /* non-zero, will force termination of walk */
|
||
} else {
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
Widget
|
||
fe_FindWidget(Widget top, char* name)
|
||
{
|
||
XtPointer rv;
|
||
|
||
rv = fe_WidgetTreeWalk(top, fe_find_widget_mappee, (XtPointer)name);
|
||
|
||
return (Widget)rv;
|
||
}
|
||
|
||
/*
|
||
* Tool tips.
|
||
*/
|
||
static Widget fe_tool_tips_widget; /* current hot widget */
|
||
static XtIntervalId fe_tool_tips_timer_id; /* timer id for non-movement */
|
||
static Widget fe_tool_tips_shell; /* posted tips shell */
|
||
|
||
/*
|
||
* New tool tip code.
|
||
*/
|
||
typedef struct TipInfo {
|
||
struct TipInfo* m_next;
|
||
XtPointer m_key;
|
||
union {
|
||
/* if callback == GADGET_DUMMY, gadgets is valid */
|
||
XtCallbackRec callback_rec;
|
||
struct TipInfo* gadgets;
|
||
} u;
|
||
int x, y; /* mouse pos */
|
||
XP_Bool rePosition;
|
||
} TipInfo;
|
||
|
||
#define m_gadgets u.gadgets
|
||
#define m_callback u.callback_rec.callback
|
||
#define m_closure u.callback_rec.closure
|
||
|
||
static TipInfo*
|
||
TipInfoNew(XtPointer key, XtCallbackProc callback, XtPointer closure)
|
||
{
|
||
TipInfo* info;
|
||
|
||
info = XP_NEW(TipInfo);
|
||
|
||
info->m_key = key;
|
||
info->m_callback = callback;
|
||
info->m_closure = closure;
|
||
info->m_next = NULL;
|
||
info->x = -10;
|
||
info->y = -10;
|
||
info->rePosition = False;
|
||
return info;
|
||
}
|
||
|
||
static void
|
||
TipInfoDelete(TipInfo* info)
|
||
{
|
||
XP_FREE(info);
|
||
}
|
||
|
||
static TipInfo*
|
||
TipInfoListInsert(TipInfo** list, TipInfo* info)
|
||
{
|
||
info->m_next = *list;
|
||
*list = info;
|
||
|
||
return info;
|
||
}
|
||
|
||
static void
|
||
TipInfoListDelete(TipInfo* info)
|
||
{
|
||
TipInfo* next;
|
||
while (info != NULL) {
|
||
next = info->m_next;
|
||
XP_FREE(info);
|
||
info = next;
|
||
}
|
||
}
|
||
|
||
static TipInfo*
|
||
TipInfoListFind(TipInfo* head, XtPointer key)
|
||
{
|
||
TipInfo* foo;
|
||
|
||
for (foo = head; foo != NULL; foo = foo->m_next) {
|
||
if (foo->m_key == key)
|
||
break;
|
||
}
|
||
|
||
return foo;
|
||
}
|
||
|
||
static TipInfo*
|
||
TipInfoListRemove(TipInfo** list, XtPointer key)
|
||
{
|
||
TipInfo* info = *list;
|
||
TipInfo* prev = NULL;
|
||
|
||
for (; info != NULL; prev = info, info = info->m_next) {
|
||
if (info->m_key == key) {
|
||
if (prev != NULL) {
|
||
prev->m_next = info->m_next;
|
||
} else {
|
||
*list = info->m_next;
|
||
}
|
||
TipInfoDelete(info);
|
||
break;
|
||
}
|
||
}
|
||
|
||
return *list;
|
||
}
|
||
|
||
static TipInfo* tips_manager_list;
|
||
#ifdef USE_TIP_WIDGET_LIST
|
||
static TipInfo* tips_widget_list;
|
||
#endif
|
||
|
||
static void
|
||
tip_dummy_manager_cb(Widget widget, XtPointer closure, XtPointer cb) {}
|
||
|
||
#define TIPINFO_IS_MANAGER(i) ((i)->m_callback == tip_dummy_manager_cb)
|
||
|
||
/*
|
||
* When there is no callback info, there is no need to allocate
|
||
* memory to save it. Use this as a dummy callback info.
|
||
*/
|
||
static TipInfo tips_no_callback_info; /* must be 0s */
|
||
|
||
static void
|
||
tips_widget_death_cb(Widget widget, XtPointer closure, XtPointer cb)
|
||
{
|
||
TipInfo* info = (TipInfo*)closure;
|
||
|
||
XtRemoveCallback(widget, XmNdestroyCallback, tips_widget_death_cb, info);
|
||
if (TIPINFO_IS_MANAGER(info)) {
|
||
TipInfoListDelete(info->m_gadgets);
|
||
TipInfoListRemove(&tips_manager_list, (XtPointer)info);
|
||
}
|
||
#ifdef USE_TIP_WIDGET_LIST
|
||
else
|
||
{
|
||
TipInfoListRemove(&tips_widget_list, info);
|
||
}
|
||
#endif
|
||
if (info != &tips_no_callback_info)
|
||
TipInfoDelete(info);
|
||
|
||
if (fe_tool_tips_widget == widget)
|
||
fe_tool_tips_widget = NULL;
|
||
}
|
||
|
||
static TipInfo*
|
||
TipInfoNewManager(Widget manager)
|
||
{
|
||
TipInfo* info = TipInfoNew((XtPointer)manager, tip_dummy_manager_cb, NULL);
|
||
|
||
XtAddCallback(manager, XmNdestroyCallback, tips_widget_death_cb, info);
|
||
|
||
return info;
|
||
}
|
||
|
||
static TipInfo*
|
||
TipInfoNewWidget(Widget widget, XtCallbackProc callback, XtPointer closure)
|
||
{
|
||
TipInfo* info;
|
||
|
||
if (callback != NULL) {
|
||
info = TipInfoNew((XtPointer)widget, callback, closure);
|
||
#ifdef USE_TIP_WIDGET_LIST
|
||
TipInfoListInsert(&tips_widget_list, info);
|
||
#endif
|
||
XtAddCallback(widget, XmNdestroyCallback, tips_widget_death_cb, info);
|
||
} else {
|
||
info = &tips_no_callback_info;
|
||
}
|
||
return info;
|
||
}
|
||
|
||
void
|
||
fe_cleanup_tooltips(MWContext *context)
|
||
{
|
||
fe_tool_tips_widget = NULL;
|
||
|
||
/*
|
||
* Stage two? Any event should zap that.
|
||
*/
|
||
if (fe_tool_tips_shell) {
|
||
XtDestroyWidget(fe_tool_tips_shell);
|
||
fe_tool_tips_shell = NULL;
|
||
|
||
/* Mark the tooltips not showing */
|
||
fe_tooltip_is_showing = False;
|
||
}
|
||
|
||
/*
|
||
* Stage one?
|
||
*/
|
||
if (fe_tool_tips_timer_id) {
|
||
XtRemoveTimeOut(fe_tool_tips_timer_id);
|
||
fe_tool_tips_timer_id = 0;
|
||
}
|
||
}
|
||
|
||
static XP_Bool
|
||
fe_display_docString(MWContext* context,
|
||
Widget widget, TipInfo* info, XEvent* event,
|
||
Boolean erase)
|
||
{
|
||
char *s;
|
||
|
||
if (!erase) {
|
||
|
||
s = NULL;
|
||
|
||
/*
|
||
* Do callback so user can change the string.
|
||
*/
|
||
if (info->m_callback != NULL) {
|
||
XFE_TipStringCallbackStruct cb_info;
|
||
|
||
cb_info.reason = XFE_DOCSTRING;
|
||
cb_info.event = event;
|
||
cb_info.string = &s;
|
||
/* Tao
|
||
*/
|
||
cb_info.x = info->x;
|
||
cb_info.y = info->y;
|
||
|
||
(*info->m_callback)(widget, info->m_closure, &cb_info);
|
||
}
|
||
|
||
if (s == NULL)
|
||
{
|
||
s = XfeSubResourceGetWidgetStringValue(widget,
|
||
"documentationString",
|
||
"DocumentationString");
|
||
}
|
||
|
||
} else { /* erasing */
|
||
s = "";
|
||
}
|
||
|
||
#ifdef DEBUG
|
||
if (s == NULL) {
|
||
static char buf[128];
|
||
s = buf;
|
||
PR_snprintf(s, sizeof(buf),"Debug: no documentationString resource for widget %s",
|
||
XtName(widget));
|
||
}
|
||
#endif /*DEBUG*/
|
||
|
||
if (context == NULL || s == NULL)
|
||
return False;
|
||
|
||
XFE_Progress(context, s);
|
||
|
||
return True;
|
||
}
|
||
|
||
static void
|
||
fe_tooltips_display_stage_one(Widget widget, XEvent* event,
|
||
TipInfo* info, Boolean begin)
|
||
{
|
||
MWContext* context = fe_WidgetToMWContext(widget);
|
||
if (context)
|
||
fe_display_docString(context, widget, info, event, !begin);
|
||
}
|
||
|
||
static Widget
|
||
fe_tooltip_create_effects(Widget parent, char* name, char* string)
|
||
{
|
||
Widget shell;
|
||
Widget label;
|
||
Visual *v = 0;
|
||
Colormap cmap = 0;
|
||
Cardinal depth = 0;
|
||
XmFontList fontList;
|
||
XmString xm_string;
|
||
|
||
shell = parent;
|
||
while (XtParent(shell) && !XtIsShell(shell)) {
|
||
shell = XtParent(shell);
|
||
}
|
||
|
||
if (shell == NULL || XtParent(shell) == NULL)
|
||
return NULL;
|
||
|
||
XtVaGetValues(shell, XtNvisual, &v, XtNcolormap, &cmap,
|
||
XtNdepth, &depth, 0);
|
||
|
||
XtVaGetValues(parent, XmNfontList, &fontList, NULL);
|
||
|
||
shell = XtVaCreateWidget(name,
|
||
overrideShellWidgetClass,
|
||
XtParent(shell), /* the app */
|
||
XmNvisual, v,
|
||
XmNcolormap, cmap,
|
||
XmNdepth, depth,
|
||
XmNborderWidth, 1,
|
||
NULL);
|
||
|
||
xm_string = XmStringCreateLocalized(string);
|
||
|
||
label = XtVaCreateManagedWidget("tipLabel",
|
||
xmLabelWidgetClass,
|
||
shell,
|
||
XmNlabelType, XmSTRING,
|
||
XmNlabelString, xm_string,
|
||
NULL);
|
||
XmStringFree(xm_string);
|
||
XtManageChild(label);
|
||
|
||
return label;
|
||
}
|
||
|
||
static Widget
|
||
fe_tooltips_display_stage_two(Widget widget, TipInfo* info)
|
||
{
|
||
Widget parent;
|
||
Widget label;
|
||
Dimension width;
|
||
Dimension border_width;
|
||
Dimension height;
|
||
Position x_root;
|
||
Position y_root;
|
||
Position y_root_orig;
|
||
Screen* screen;
|
||
Position x_info = 0;
|
||
Position y_info = 0;
|
||
|
||
char* s = XfeSubResourceGetWidgetStringValue(widget,
|
||
"tipString",
|
||
"TipString");
|
||
|
||
/*
|
||
* Do callback so user can change the string.
|
||
*/
|
||
if (info->m_callback != NULL) {
|
||
XFE_TipStringCallbackStruct cb_info;
|
||
XAnyEvent any_event;
|
||
|
||
any_event.type = -1;
|
||
any_event.serial = 0;
|
||
any_event.send_event = False;
|
||
any_event.display = XtDisplay(widget);
|
||
any_event.window = XtWindow(widget);
|
||
|
||
cb_info.reason = XFE_TIPSTRING;
|
||
cb_info.event = (XEvent*)&any_event;
|
||
cb_info.string = &s;
|
||
|
||
/* Tao
|
||
*/
|
||
cb_info.x = info->x;
|
||
cb_info.y = info->y;
|
||
|
||
(*info->m_callback)(widget, info->m_closure, &cb_info);
|
||
|
||
/* Tao
|
||
*/
|
||
x_info = cb_info.x;
|
||
y_info = cb_info.y;
|
||
}
|
||
|
||
#ifdef DEBUG
|
||
if (s == NULL &&
|
||
!(info->rePosition &&
|
||
x_info >= 0 &&
|
||
y_info >= 0)) {/* prompt debug tooltip iff it is not a grid/html view
|
||
*/
|
||
static char buf[256];
|
||
s = buf;
|
||
PR_snprintf(s, sizeof(buf), "Debug: no tipString resource for widget %s\n"
|
||
"This message only appears in a DEBUG build",
|
||
XtName(widget));
|
||
}
|
||
#endif /*DEBUG*/
|
||
|
||
if (s == NULL || !XP_STRLEN(s))
|
||
return NULL;
|
||
|
||
parent = XtParent(widget);
|
||
|
||
label = fe_tooltip_create_effects(parent, "tipShell", s);
|
||
if (label == NULL)
|
||
return NULL;
|
||
|
||
parent = XtParent(label);
|
||
|
||
/* Tao: compute x, y only when x_info && y_info is not set
|
||
*/
|
||
/* francis: yes, but do that before making sure it fits on the screen */
|
||
|
||
XtVaGetValues(widget, XmNwidth, &width, XmNheight, &height, 0);
|
||
XtTranslateCoords(widget, 0, 0, &x_root, &y_root_orig);
|
||
x_root += (width/2); /* positon in center of button */
|
||
y_root = y_root_orig + height + 5;
|
||
|
||
/* moved here by francis; formerly right before XtVaSetValues(), below */
|
||
/* Tao
|
||
*/
|
||
if (info->rePosition == True &&
|
||
x_info >= 0 &&
|
||
y_info >= 0) {
|
||
XtTranslateCoords(widget, x_info, y_info, &x_root, &y_root);
|
||
}/* if */
|
||
|
||
/*
|
||
* Make sure it fits on screen.
|
||
*/
|
||
XtVaGetValues(parent, XmNborderWidth, &border_width, 0);
|
||
XtVaGetValues(label, XmNwidth, &width, XmNheight, &height, 0);
|
||
screen = XtScreen(label);
|
||
|
||
height += (2*border_width);
|
||
width += (2*border_width);
|
||
|
||
if (x_root + width > WidthOfScreen(screen))
|
||
x_root = WidthOfScreen(screen) - width;
|
||
else if (x_root < 0)
|
||
x_root = 0;
|
||
if (y_root + height > HeightOfScreen(screen))
|
||
y_root = y_root_orig - height - 5;
|
||
else if (y_root < 0)
|
||
y_root = 0;
|
||
|
||
XtVaSetValues(parent, XmNx, x_root, XmNy, y_root, 0);
|
||
|
||
/*
|
||
* Make sure the user cannot shoot themselves with a random
|
||
* geometry spec. No more attack of the killer tomatoes...djw
|
||
*/
|
||
{
|
||
char buf[16];
|
||
PR_snprintf(buf, sizeof(buf), "%dx%d", width, height);
|
||
XtVaSetValues(parent, XmNwidth, width, XmNheight, height,
|
||
XmNgeometry, buf, 0);
|
||
}
|
||
|
||
/* Mark the tooltips showing */
|
||
fe_tooltip_is_showing = True;
|
||
|
||
XtPopup(parent, XtGrabNone);
|
||
|
||
return parent;
|
||
}
|
||
|
||
static void
|
||
fe_tooltips_stage_two_timeout(XtPointer closure, XtIntervalId *id)
|
||
{
|
||
TipInfo* info = (TipInfo*)closure;
|
||
Widget widget = fe_tool_tips_widget;
|
||
Widget shell;
|
||
|
||
/* Clear the timer id that we store in the context as once the timeout
|
||
* has triggered (that is why we are here), the timeout is automatically
|
||
* removed. Else our event handler will go and remove the timeout again.
|
||
*/
|
||
fe_tool_tips_timer_id = 0;
|
||
|
||
if (fe_tool_tips_shell == NULL && widget != NULL) {
|
||
shell = fe_tooltips_display_stage_two(widget, info);
|
||
fe_tool_tips_shell = shell;
|
||
}
|
||
|
||
}
|
||
|
||
static void
|
||
tip_dummy_demo_cb(Widget widget, XtPointer c, XtPointer d) {}
|
||
|
||
typedef struct {
|
||
MWContext *context;
|
||
#if DO_NOT_PASS_LAYER_N_EVENT
|
||
char *alt_text;
|
||
int x;
|
||
int y;
|
||
#else
|
||
CL_Layer *layer;
|
||
CL_Event *layer_event;
|
||
#endif
|
||
} HTMLTipData_t;
|
||
|
||
static Widget
|
||
fe_HTMLTips_display_stage_two(Widget widget, HTMLTipData_t* info)
|
||
{
|
||
Widget parent;
|
||
Widget label;
|
||
Dimension width;
|
||
Dimension border_width;
|
||
Dimension height;
|
||
Position x_root;
|
||
Position y_root;
|
||
Position y_root_orig;
|
||
Screen* screen;
|
||
Position x_info;
|
||
Position y_info;
|
||
char* s = NULL;
|
||
|
||
if (!info)
|
||
return NULL;
|
||
|
||
|
||
/*
|
||
* Do callback so user can change the string.
|
||
*/
|
||
{
|
||
XFE_TipStringCallbackStruct cb_info;
|
||
cb_info.reason = XFE_TIPSTRING;
|
||
cb_info.event = (XEvent*) NULL;
|
||
cb_info.string = &s;
|
||
|
||
cb_info.x = -10;
|
||
cb_info.y = -10;
|
||
#if DO_NOT_PASS_LAYER_N_EVENT
|
||
fe_HTMLViewTooltips(info->context, info->x, info->y, info->alt_text,
|
||
&cb_info);
|
||
#else
|
||
fe_HTMLViewTooltips(info->context, info->layer, info->layer_event,
|
||
&cb_info);
|
||
#endif
|
||
XP_FREEIF(info);
|
||
|
||
x_info = cb_info.x;
|
||
y_info = cb_info.y;
|
||
}
|
||
if (s == NULL || !XP_STRLEN(s))
|
||
return NULL;
|
||
|
||
parent = XtParent(widget);
|
||
|
||
label = fe_tooltip_create_effects(parent, "tipShell", s);
|
||
if (label == NULL)
|
||
return NULL;
|
||
|
||
parent = XtParent(label);
|
||
|
||
/* Tao: compute x, y only when x_info && y_info is not set
|
||
*/
|
||
/* francis: yes, but do that before making sure it fits on the screen */
|
||
XtVaGetValues(widget, XmNwidth, &width, XmNheight, &height, 0);
|
||
XtTranslateCoords(widget, 0, 0, &x_root, &y_root_orig);
|
||
x_root += (width/2); /* positon in center of button */
|
||
y_root = y_root_orig + height + 5;
|
||
|
||
/* moved here by francis; formerly right before XtVaSetValues(), below */
|
||
if (x_info >= 0 &&
|
||
y_info >= 0) {
|
||
XtTranslateCoords(widget, x_info, y_info, &x_root, &y_root);
|
||
}/* if */
|
||
/*
|
||
* Make sure it fits on screen.
|
||
*/
|
||
XtVaGetValues(parent, XmNborderWidth, &border_width, 0);
|
||
XtVaGetValues(label, XmNwidth, &width, XmNheight, &height, 0);
|
||
screen = XtScreen(label);
|
||
|
||
height += (2*border_width);
|
||
width += (2*border_width);
|
||
|
||
if (x_root + width > WidthOfScreen(screen))
|
||
x_root = WidthOfScreen(screen) - width;
|
||
else if (x_root < 0)
|
||
x_root = 0;
|
||
if (y_root + height > HeightOfScreen(screen))
|
||
y_root = y_root_orig - height - 5;
|
||
else if (y_root < 0)
|
||
y_root = 0;
|
||
|
||
XtVaSetValues(parent, XmNx, x_root, XmNy, y_root, 0);
|
||
|
||
/*
|
||
* Make sure the user cannot shoot themselves with a random
|
||
* geometry spec. No more attack of the killer tomatoes...djw
|
||
*/
|
||
{
|
||
char buf[16];
|
||
PR_snprintf(buf, sizeof(buf),"%dx%d", width, height);
|
||
XtVaSetValues(parent, XmNwidth, width, XmNheight, height,
|
||
XmNgeometry, buf, 0);
|
||
}
|
||
|
||
/* Mark the tooltips showing */
|
||
fe_tooltip_is_showing = True;
|
||
|
||
XtPopup(parent, XtGrabNone);
|
||
|
||
/* free s ; from XP_STRDUP
|
||
*/
|
||
XP_FREEIF(s);
|
||
return parent;
|
||
}
|
||
|
||
static void
|
||
fe_HTMLTips_stage_two_timeout(XtPointer closure, XtIntervalId *id)
|
||
{
|
||
Widget widget = fe_tool_tips_widget;
|
||
Widget shell;
|
||
HTMLTipData_t *tipInfo = (HTMLTipData_t *) closure;
|
||
|
||
/* Clear the timer id that we store in the context as once the timeout
|
||
* has triggered (that is why we are here), the timeout is automatically
|
||
* removed. Else our event handler will go and remove the timeout again.
|
||
*/
|
||
fe_tool_tips_timer_id = 0;
|
||
|
||
if (fe_tool_tips_shell == NULL && widget != NULL) {
|
||
shell = fe_HTMLTips_display_stage_two(widget, tipInfo);
|
||
fe_tool_tips_shell = shell;
|
||
}
|
||
|
||
}
|
||
|
||
extern void
|
||
fe_HTMLViewTooltipsEH(MWContext *context, CL_Layer *layer,
|
||
CL_Event *layer_event, int state)
|
||
{
|
||
Widget widget = CONTEXT_DATA(context)->drawing_area;
|
||
Boolean enter;
|
||
Boolean leave;
|
||
Boolean tips_enabled;
|
||
|
||
enter = FALSE;
|
||
leave = FALSE;
|
||
if (state == 2 || state == 3) {
|
||
enter = TRUE;
|
||
leave = TRUE;
|
||
} /* if */
|
||
else if (state == 1) {
|
||
enter = TRUE;
|
||
leave = FALSE;
|
||
}/* else if */
|
||
else if (state == 4) {
|
||
enter = FALSE;
|
||
leave = TRUE;
|
||
|
||
}/* else if */
|
||
else { /* clicks, pops, squeaks, and other non-motionals */
|
||
enter = FALSE;
|
||
leave = FALSE;
|
||
}/* else */
|
||
|
||
/*
|
||
* Stage two? Any event should zap that.
|
||
*/
|
||
if (fe_tool_tips_shell) {
|
||
/* Mark the tooltips not showing */
|
||
fe_tooltip_is_showing = False;
|
||
XtDestroyWidget(fe_tool_tips_shell);
|
||
fe_tool_tips_shell = NULL;
|
||
}/* if */
|
||
|
||
/*
|
||
* Stage one?
|
||
*/
|
||
if (fe_tool_tips_timer_id) {
|
||
XtRemoveTimeOut(fe_tool_tips_timer_id);
|
||
fe_tool_tips_timer_id = 0;
|
||
}/* if */
|
||
|
||
if (leave == TRUE) {
|
||
fe_tool_tips_widget = NULL;
|
||
}/* if */
|
||
|
||
if (enter == TRUE) {
|
||
tips_enabled = fe_globalPrefs.toolbar_tips_p;
|
||
if (tips_enabled) {
|
||
HTMLTipData_t *tipData =
|
||
(HTMLTipData_t *) XP_CALLOC(1, sizeof(HTMLTipData_t));
|
||
#if DO_NOT_PASS_LAYER_N_EVENT
|
||
LO_Element *le = NULL;
|
||
int x = layer_event->x,
|
||
y = layer_event->y;
|
||
|
||
le = LO_XYToElement(context, x, y, layer);
|
||
if (le &&
|
||
le->type == LO_IMAGE &&
|
||
le->lo_image.alt &&
|
||
le->lo_image.alt_len ) {
|
||
/* do not set time out unless there is an alt_text to display
|
||
*/
|
||
|
||
/* to be free by the caller
|
||
*/
|
||
tipData->alt_text = XP_STRDUP((char *)le->lo_image.alt);
|
||
tipData->x = x;
|
||
tipData->y = y;
|
||
tipData->context = context;
|
||
fe_tool_tips_timer_id =
|
||
XtAppAddTimeOut(fe_XtAppContext,
|
||
500, /* whatever */
|
||
fe_HTMLTips_stage_two_timeout,
|
||
tipData);
|
||
}/* if */
|
||
else
|
||
XP_FREEIF(tipData);
|
||
#else
|
||
|
||
/* fix BSR in XFE_HTMLView::tipCB; freed there
|
||
*/
|
||
CL_Event *dup = (CL_Event *) XP_CALLOC(1, sizeof(CL_Event));
|
||
dup->x = layer_event->x;
|
||
dup->y = layer_event->y;
|
||
|
||
tipData->layer_event = dup;
|
||
tipData->context = context;
|
||
tipData->layer = layer;
|
||
fe_tool_tips_timer_id =
|
||
XtAppAddTimeOut(fe_XtAppContext,
|
||
500, /* whatever */
|
||
fe_HTMLTips_stage_two_timeout,
|
||
tipData);
|
||
#endif
|
||
}/* if tips_enabled */
|
||
fe_tool_tips_widget = widget;
|
||
}/* if */
|
||
}
|
||
|
||
#if HANDLE_LEAVE_WIN
|
||
/* HTMLView tooltip eventhanler
|
||
*/
|
||
static void
|
||
fe_HTMLViewTooltips_eh(Widget widget, XtPointer closure, XEvent *event,
|
||
Boolean *continue_to_dispatch)
|
||
{
|
||
MWContext *context = (MWContext *) closure;
|
||
if (widget &&
|
||
context &&
|
||
CONTEXT_DATA(context)&&
|
||
event &&
|
||
widget == CONTEXT_DATA(context)->drawing_area &&
|
||
XfeIsAlive(widget)) {
|
||
switch (event->type) {
|
||
case EnterNotify:
|
||
/* As djw suggested: we treat a window enter as a leave
|
||
*/
|
||
fe_HTMLViewTooltipsEH(context,
|
||
(CL_Layer *)NULL,
|
||
(CL_Event *)NULL, 4);
|
||
break;
|
||
|
||
case LeaveNotify:
|
||
fe_HTMLViewTooltipsEH(context,
|
||
(CL_Layer *)NULL,
|
||
(CL_Event *)NULL, 4);
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}/* switch */
|
||
}/* if */
|
||
}
|
||
|
||
extern void fe_Add_HTMLViewTooltips_eh(MWContext *context)
|
||
{
|
||
if (context &&
|
||
CONTEXT_DATA(context)->drawing_area) {
|
||
XtAddEventHandler(CONTEXT_DATA(context)->drawing_area,
|
||
EnterWindowMask | LeaveWindowMask,
|
||
FALSE,
|
||
fe_HTMLViewTooltips_eh,
|
||
context);
|
||
}/* if */
|
||
}
|
||
|
||
extern void fe_Remove_HTMLViewTooltips_eh(MWContext *context)
|
||
{
|
||
if (context &&
|
||
CONTEXT_DATA(context)->drawing_area) {
|
||
/* send leave to pop down the tooltip
|
||
*/
|
||
fe_HTMLViewTooltipsEH(context,
|
||
(CL_Layer *)NULL,
|
||
(CL_Event *)NULL, 4);
|
||
|
||
XtRemoveEventHandler(CONTEXT_DATA(context)->drawing_area,
|
||
EnterWindowMask | LeaveWindowMask,
|
||
FALSE,
|
||
fe_HTMLViewTooltips_eh,
|
||
context);
|
||
}/* if */
|
||
}
|
||
#endif /* HANDLE_LEAVE_WIN */
|
||
|
||
static void
|
||
fe_tooltips_event_handler(Widget widget, XtPointer closure, XEvent* event,
|
||
Boolean* keep_going)
|
||
{
|
||
TipInfo* info = (TipInfo*)closure;
|
||
Boolean enter;
|
||
Boolean leave;
|
||
Widget child;
|
||
Boolean demo;
|
||
Boolean tips_enabled;
|
||
|
||
Boolean isGridWidget = XtIsSubclass(widget, xmlGridWidgetClass);
|
||
int x = event->xbutton.x;
|
||
int y = event->xbutton.y;
|
||
unsigned char rowtype;
|
||
unsigned char coltype;
|
||
int row;
|
||
int column;
|
||
static int m_lastRow = -2;
|
||
static int m_lastCol = -2;
|
||
static Boolean m_inGrid = False;
|
||
static unsigned char m_lastRowtype = XmALL_TYPES;
|
||
static unsigned char m_lastColtype = XmALL_TYPES;
|
||
|
||
*keep_going = TRUE;
|
||
|
||
if (info != NULL && info->m_callback == tip_dummy_demo_cb) {
|
||
demo = TRUE;
|
||
} else {
|
||
demo = FALSE;
|
||
}
|
||
|
||
if (event->type == MotionNotify) {
|
||
enter = FALSE;
|
||
leave = FALSE;
|
||
|
||
if (info != NULL && info->m_callback == tip_dummy_manager_cb) {
|
||
TipInfo* ginfo;
|
||
|
||
child = (Widget)_XM_OBJECT_AT_POINT(widget,
|
||
event->xmotion.x,
|
||
event->xmotion.y);
|
||
|
||
if (child != NULL
|
||
&&
|
||
(ginfo = TipInfoListFind(info->m_gadgets,
|
||
(XtPointer)child)) != NULL) {
|
||
info = ginfo;
|
||
widget = child;
|
||
if (fe_tool_tips_widget == NULL) {
|
||
leave = FALSE;
|
||
enter = TRUE;
|
||
} else if (fe_tool_tips_widget != widget) {
|
||
leave = TRUE;
|
||
enter = TRUE;
|
||
}
|
||
} else {
|
||
if (fe_tool_tips_widget != NULL) {
|
||
leave = TRUE;
|
||
enter = FALSE;
|
||
}
|
||
}
|
||
} else { /* motion in non-manager widget */
|
||
|
||
/* Ignore this case if we're dragging. This fixes
|
||
tooltips for the ProxyIcon class. (Otherwise
|
||
tooltips persist during the drag, bleah.) CCM */
|
||
|
||
if(!(event->xmotion.state & Button1Mask)) {
|
||
if (isGridWidget) {
|
||
XmLGridXYToCellTracking(widget,
|
||
x, y, /* input only args. */
|
||
&m_inGrid, /* input/output args. */
|
||
&m_lastRow, &m_lastCol,
|
||
&m_lastRowtype, &m_lastColtype,
|
||
&(info->x), &(info->y), /* output only args. */
|
||
&enter,
|
||
&leave); /* output only args. */
|
||
|
||
}/* if Grid */
|
||
else if (fe_tool_tips_widget == NULL) {
|
||
leave = FALSE;
|
||
enter = TRUE;
|
||
} else if (fe_tool_tips_widget != widget) {
|
||
leave = TRUE;
|
||
enter = TRUE;
|
||
}
|
||
}
|
||
|
||
if (leave == FALSE && enter == FALSE) /* motion */
|
||
return;
|
||
}
|
||
} else if (event->type == EnterNotify) {
|
||
enter = TRUE;
|
||
leave = FALSE;
|
||
if (isGridWidget) {
|
||
XmLGridXYToCellTracking(widget,
|
||
x, y, /* input only args. */
|
||
&m_inGrid, /* input/output args. */
|
||
&m_lastRow, &m_lastCol,
|
||
&m_lastRowtype, &m_lastColtype,
|
||
&(info->x), &(info->y), /* output only args. */
|
||
&enter,
|
||
&leave); /* output only args. */
|
||
info->rePosition = True;
|
||
}/* if */
|
||
} else if (event->type == LeaveNotify) {
|
||
enter = FALSE;
|
||
leave = TRUE;
|
||
if (isGridWidget) {
|
||
m_inGrid = False;
|
||
m_lastRow = m_lastCol = -2;
|
||
|
||
info->x = info->y = -10;
|
||
info->rePosition = True;
|
||
}/* if */
|
||
} else { /* clicks, pops, squeaks, and other non-motionals */
|
||
enter = FALSE;
|
||
leave = FALSE;
|
||
}/* else */
|
||
|
||
/*
|
||
* Stage two? Any event should zap that.
|
||
*/
|
||
if (fe_tool_tips_shell) {
|
||
/* Mark the tooltips not showing */
|
||
fe_tooltip_is_showing = False;
|
||
XtDestroyWidget(fe_tool_tips_shell);
|
||
fe_tool_tips_shell = NULL;
|
||
}
|
||
|
||
/*
|
||
* Stage one?
|
||
*/
|
||
if (fe_tool_tips_timer_id) {
|
||
XtRemoveTimeOut(fe_tool_tips_timer_id);
|
||
fe_tool_tips_timer_id = 0;
|
||
}
|
||
|
||
if (leave == TRUE) {
|
||
if (!isGridWidget)
|
||
/* outliner does not stage_one doc
|
||
*/
|
||
fe_tooltips_display_stage_one(widget,
|
||
event, info, FALSE); /* clear */
|
||
fe_tool_tips_widget = NULL;
|
||
}
|
||
|
||
if (enter == TRUE) {
|
||
if (demo) {
|
||
tips_enabled = XmToggleButtonGetState(widget);
|
||
} else {
|
||
tips_enabled = fe_globalPrefs.toolbar_tips_p;
|
||
}
|
||
|
||
if (tips_enabled) {
|
||
fe_tool_tips_timer_id = XtAppAddTimeOut(fe_XtAppContext,
|
||
500, /* whatever */
|
||
fe_tooltips_stage_two_timeout,
|
||
info);
|
||
}
|
||
if (!isGridWidget)
|
||
/* outliner does not stage_one doc
|
||
*/
|
||
fe_tooltips_display_stage_one(widget, event, info, TRUE); /* msg */
|
||
fe_tool_tips_widget = widget;
|
||
}
|
||
}
|
||
|
||
#define TT_EVENT_MASK (PointerMotionMask|EnterWindowMask|LeaveWindowMask| \
|
||
ButtonPressMask|ButtonReleaseMask| \
|
||
KeyPressMask|KeyReleaseMask)
|
||
|
||
void
|
||
fe_AddTipStringCallback(Widget widget,
|
||
XtCallbackProc callback, XtPointer closure)
|
||
{
|
||
TipInfo* info;
|
||
Boolean install = False;
|
||
EventMask mask;
|
||
|
||
if (XtIsSubclass(widget, xmGadgetClass)) {
|
||
|
||
Widget manager;
|
||
TipInfo* ginfo;
|
||
|
||
manager = XtParent(widget);
|
||
|
||
if ((info = TipInfoListFind(tips_manager_list,
|
||
(XtPointer)manager)) == NULL) {
|
||
info = TipInfoNewManager(manager);
|
||
install = True;
|
||
}
|
||
|
||
ginfo = TipInfoListFind(info->m_gadgets, (XtPointer)widget);
|
||
|
||
if (ginfo != NULL) {
|
||
/* print a warning */
|
||
return;
|
||
} else {
|
||
/* create and insert in managers list */
|
||
ginfo = TipInfoNew((XtPointer)widget, callback, closure);
|
||
TipInfoListInsert(&info->m_gadgets, ginfo);
|
||
}
|
||
|
||
widget = manager;
|
||
mask = TT_EVENT_MASK;
|
||
} else {
|
||
|
||
if (callback != NULL) {
|
||
#ifdef USE_WIDGET_LIST
|
||
info = TipInfoListFind(tips_widget_list, widget);
|
||
if (info != NULL) {
|
||
/* print a warning */
|
||
return;
|
||
}
|
||
#endif
|
||
info = TipInfoNewWidget(widget, callback, closure);
|
||
} else {
|
||
info = &tips_no_callback_info;
|
||
}
|
||
|
||
install = True;
|
||
mask = TT_EVENT_MASK;
|
||
}
|
||
|
||
if (!install)
|
||
return;
|
||
|
||
XtRemoveEventHandler(widget,
|
||
mask,
|
||
FALSE,
|
||
fe_tooltips_event_handler,
|
||
info);
|
||
XtInsertEventHandler(widget,
|
||
mask,
|
||
FALSE,
|
||
fe_tooltips_event_handler,
|
||
info,
|
||
XtListHead);
|
||
}
|
||
|
||
static Boolean
|
||
fe_manager_tt_default_check(Widget widget)
|
||
{
|
||
return (XtIsSubclass(widget, xmPushButtonGadgetClass)
|
||
||
|
||
XtIsSubclass(widget, xmToggleButtonGadgetClass)
|
||
||
|
||
XtIsSubclass(widget, xmCascadeButtonGadgetClass));
|
||
}
|
||
|
||
void
|
||
fe_ManagerAddGadgetToolTips(Widget manager, fe_ToolTipGadgetCheckProc do_class)
|
||
{
|
||
Widget* children;
|
||
Cardinal nchildren;
|
||
unsigned n;
|
||
|
||
/*
|
||
* Now that we keep tipped gadgets on a list, we can use the check
|
||
* routine to determien what to install on the manager.
|
||
*/
|
||
if (!do_class)
|
||
do_class = fe_manager_tt_default_check;
|
||
|
||
XtVaGetValues(manager,
|
||
XmNchildren, &children,
|
||
XmNnumChildren, &nchildren,
|
||
0);
|
||
|
||
for (n = 0; n < nchildren; n++) {
|
||
if ((*do_class)(children[n])) {
|
||
fe_AddTipStringCallback(children[n], NULL, NULL);
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Document String Only */
|
||
static void fe_docString_hascb_disarm_cb(Widget, XtPointer, XtPointer);
|
||
static void fe_docString_hascb_arm_cb(Widget, XtPointer, XtPointer);
|
||
static void fe_docString_disarm_cb(Widget, XtPointer, XtPointer);
|
||
static void fe_docString_arm_cb(Widget, XtPointer, XtPointer);
|
||
|
||
/*
|
||
* These callbacks used if the caller provides no callback.
|
||
* These guys do not allocate memory, they pass the context around
|
||
* as the closure.
|
||
*/
|
||
static void
|
||
fe_docString_disarm_cb(Widget w, XtPointer closure, XtPointer call_data)
|
||
{
|
||
MWContext* context = (MWContext*)closure;
|
||
XmPushButtonCallbackStruct* cbs = (XmPushButtonCallbackStruct*)call_data;
|
||
TipInfo* info = &tips_no_callback_info;
|
||
|
||
fe_display_docString(context, w, info, cbs->event, TRUE);
|
||
}
|
||
|
||
static void
|
||
fe_docString_arm_cb(Widget w, XtPointer closure, XtPointer call_data)
|
||
{
|
||
MWContext* context = (MWContext*)closure;
|
||
XmPushButtonCallbackStruct* cbs = (XmPushButtonCallbackStruct*)call_data;
|
||
TipInfo* info = &tips_no_callback_info;
|
||
|
||
if (!fe_display_docString(context, w, info, cbs->event, FALSE)) {
|
||
/* Arm failed to get any string. Dont waste time from now on for
|
||
* trying to show the docString everytime we arm this widget.
|
||
*/
|
||
XtRemoveCallback(w, XmNarmCallback, fe_docString_arm_cb, context);
|
||
XtRemoveCallback(w, XmNdisarmCallback, fe_docString_disarm_cb, context);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* These callbacks used if the caller provides a callback.
|
||
* These guys do allocate memory, they store the context in the
|
||
* info, and pass the info around as the closure.
|
||
*/
|
||
static void
|
||
tips_button_death_cb(Widget widget, XtPointer closure, XtPointer cb_data)
|
||
{
|
||
TipInfo* info = (TipInfo*)closure;
|
||
XtRemoveCallback(widget, XmNdestroyCallback, tips_button_death_cb, info);
|
||
TipInfoDelete(info);
|
||
}
|
||
|
||
static void
|
||
fe_docString_hascb_disarm_cb(Widget w, XtPointer closure, XtPointer call_data)
|
||
{
|
||
TipInfo* info = (TipInfo*) closure;
|
||
MWContext* context = (MWContext*)info->m_key;
|
||
XmPushButtonCallbackStruct* cbs = (XmPushButtonCallbackStruct*)call_data;
|
||
fe_display_docString(context, w, info, cbs->event, TRUE);
|
||
}
|
||
|
||
static void
|
||
fe_docString_hascb_arm_cb(Widget w, XtPointer closure, XtPointer call_data)
|
||
{
|
||
TipInfo* info = (TipInfo*)closure;
|
||
MWContext* context = (MWContext*)info->m_key;
|
||
XmPushButtonCallbackStruct* cbs = (XmPushButtonCallbackStruct*)call_data;
|
||
|
||
if (!fe_display_docString(context, w, info, cbs->event, FALSE)) {
|
||
/* Arm failed to get any string. Dont waste time from now on for
|
||
* trying to show the docString everytime we arm this widget.
|
||
*/
|
||
XtRemoveCallback(w, XmNarmCallback, fe_docString_hascb_arm_cb, context);
|
||
XtRemoveCallback(w, XmNdisarmCallback,
|
||
fe_docString_hascb_disarm_cb, context);
|
||
tips_button_death_cb(w, (XtPointer)info, call_data);
|
||
}
|
||
}
|
||
|
||
void
|
||
fe_ButtonAddDocStringCallback(MWContext* context, Widget widget,
|
||
XtCallbackProc callback, XtPointer closure)
|
||
{
|
||
TipInfo* info = NULL;
|
||
|
||
if (callback != NULL) {
|
||
info = TipInfoNew((XtPointer)widget, callback, closure);
|
||
info->m_key = (XtPointer)context;
|
||
XtAddCallback(widget, XmNdestroyCallback, tips_button_death_cb, info);
|
||
XtAddCallback(widget, XmNarmCallback, fe_docString_hascb_arm_cb, info);
|
||
XtAddCallback(widget, XmNdisarmCallback,
|
||
fe_docString_hascb_disarm_cb, info);
|
||
} else {
|
||
XtAddCallback(widget, XmNarmCallback, fe_docString_arm_cb, context);
|
||
/* Add the disarm callback to erase the document string */
|
||
XtAddCallback(widget, XmNdisarmCallback, fe_docString_disarm_cb, context);
|
||
}
|
||
}
|
||
|
||
void
|
||
fe_WidgetAddDocumentString(MWContext *context, Widget widget)
|
||
{
|
||
fe_ButtonAddDocStringCallback(context, widget, NULL, NULL);
|
||
}
|
||
|
||
|
||
void
|
||
fe_WidgetAddToolTips(Widget widget)
|
||
{
|
||
fe_AddTipStringCallback(widget, NULL, NULL);
|
||
}
|
||
|
||
Widget
|
||
fe_CreateToolTipsDemoToggle(Widget parent, char* name, Arg* args, Cardinal n)
|
||
{
|
||
Widget widget;
|
||
|
||
widget = XmCreateToggleButton(parent, name, args, n);
|
||
|
||
fe_AddTipStringCallback(widget, tip_dummy_demo_cb, NULL);
|
||
|
||
return widget;
|
||
}
|
||
|
||
Boolean
|
||
fe_ManagerCheckGadgetToolTips(Widget manager, fe_ToolTipGadgetCheckProc check)
|
||
{
|
||
Cardinal nchildren;
|
||
WidgetList children;
|
||
Widget widget;
|
||
int i;
|
||
|
||
if (!check)
|
||
check = fe_manager_tt_default_check;
|
||
|
||
XtVaGetValues(manager,
|
||
XmNchildren, &children, XmNnumChildren, &nchildren, 0);
|
||
|
||
for (i = 0; i < nchildren; i++) {
|
||
widget = children[i];
|
||
if ((*check)(widget) == TRUE)
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
Boolean
|
||
fe_ContextHasPopups(MWContext* context)
|
||
{
|
||
Widget shell = CONTEXT_WIDGET(context);
|
||
|
||
return (XfeNumPopups(shell) > 0);
|
||
}
|
||
|
||
/*
|
||
* Motif 1.2 core dumps if right/left arrow keys are pressed while an option
|
||
* menu is active. So always use fe_CreateOptionMenu() instead of
|
||
* XmCreateOptionMenu(). This will create one and set the traversal off
|
||
* on the popup submenu.
|
||
* - dp
|
||
*/
|
||
Widget
|
||
fe_CreateOptionMenu(Widget parent, char* name, Arg* p_argv, Cardinal p_argc)
|
||
{
|
||
Widget menu = (Widget )NULL;
|
||
Widget submenu = (Widget )NULL;
|
||
|
||
menu = XmCreateOptionMenu(parent, name, p_argv, p_argc);
|
||
|
||
XtVaGetValues(menu, XmNsubMenuId, &submenu, 0);
|
||
if (submenu)
|
||
XtVaSetValues(submenu, XmNtraversalOn, False, 0);
|
||
|
||
return menu;
|
||
}
|
||
|
||
|
||
/*
|
||
* fe_ProtectContext()
|
||
*
|
||
* Sets the dont_free_context_memory variable in the CONTEXT_DATA. This
|
||
* will prevent the memory for the context and CONTEXT_DATA to be freed
|
||
* even if the context was destroyed.
|
||
*/
|
||
void
|
||
fe_ProtectContext(MWContext *context)
|
||
{
|
||
unsigned char del = XmDO_NOTHING;
|
||
|
||
if (!CONTEXT_DATA(context)->dont_free_context_memory)
|
||
{
|
||
/* This is the first person trying to protect this context.
|
||
* Dont allow the user to destroy this context using the windowmanger
|
||
* delete menu item.
|
||
*/
|
||
XtVaGetValues(CONTEXT_WIDGET(context), XmNdeleteResponse, &del, 0);
|
||
CONTEXT_DATA(context)->delete_response = del;
|
||
XtVaSetValues(CONTEXT_WIDGET(context),
|
||
XmNdeleteResponse, XmDO_NOTHING, 0);
|
||
}
|
||
CONTEXT_DATA(context)->dont_free_context_memory++;
|
||
}
|
||
|
||
/*
|
||
* fe_UnProtectContext()
|
||
*
|
||
* Undo what fe_ProtectContext() does. Unsets dont_free_context_memory
|
||
* variable in the context_data.
|
||
*/
|
||
void
|
||
fe_UnProtectContext(MWContext *context)
|
||
{
|
||
XP_ASSERT(CONTEXT_DATA(context)->dont_free_context_memory);
|
||
if (CONTEXT_DATA(context)->dont_free_context_memory)
|
||
{
|
||
CONTEXT_DATA(context)->dont_free_context_memory--;
|
||
if (!CONTEXT_DATA(context)->dont_free_context_memory) {
|
||
/* This is the last person unprotecting this context.
|
||
* Set the delete_response to what it was before.
|
||
*/
|
||
XtVaSetValues(CONTEXT_WIDGET(context), XmNdeleteResponse,
|
||
CONTEXT_DATA(context)->delete_response, 0);
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* fe_IsContextProtected()
|
||
*
|
||
* Return the protection state of the context.
|
||
*/
|
||
Boolean
|
||
fe_IsContextProtected(MWContext *context)
|
||
{
|
||
return (CONTEXT_DATA(context)->dont_free_context_memory);
|
||
}
|
||
|
||
/*
|
||
* fe_IsContextDestroyed()
|
||
*
|
||
* Return if the context was destroyed. This is valid only for protected
|
||
* contexts.
|
||
*/
|
||
Boolean
|
||
fe_IsContextDestroyed(MWContext *context)
|
||
{
|
||
return (CONTEXT_DATA(context)->destroyed);
|
||
}
|
||
|
||
/* Recurses over children of context, returning True if any context is
|
||
stoppable. */
|
||
Boolean
|
||
fe_IsContextLooping(MWContext *context)
|
||
{
|
||
int i = 1;
|
||
MWContext *child;
|
||
|
||
if (!context)
|
||
return False;
|
||
|
||
if (CONTEXT_DATA(context)->looping_images_p)
|
||
return True;
|
||
|
||
while ((child = (MWContext*)XP_ListGetObjectNum (context->grid_children,
|
||
i++)))
|
||
if (fe_IsContextLooping(child))
|
||
return True;
|
||
|
||
return False;
|
||
}
|
||
|
||
|
||
/* Recurses over children of context, returning True if any context is
|
||
stoppable. */
|
||
static Boolean
|
||
fe_is_context_stoppable_recurse(MWContext *context)
|
||
{
|
||
int i = 1;
|
||
MWContext *child;
|
||
|
||
if (!context)
|
||
return False;
|
||
|
||
if ((CONTEXT_DATA(context)->loading_images_p &&
|
||
CONTEXT_DATA(context)->autoload_images_p) ||
|
||
CONTEXT_DATA(context)->looping_images_p)
|
||
return True;
|
||
|
||
while ((child = (MWContext*)XP_ListGetObjectNum (context->grid_children,
|
||
i++)))
|
||
if (fe_is_context_stoppable_recurse(child))
|
||
return True;
|
||
|
||
return False;
|
||
}
|
||
|
||
|
||
/* Returns True if this is a context whose activity can be stopped. */
|
||
Boolean
|
||
fe_IsContextStoppable(MWContext *context)
|
||
{
|
||
/* XP_IsContextStoppable checks for mocha threads, too. */
|
||
return (fe_is_context_stoppable_recurse(context) ||
|
||
XP_IsContextStoppable(context));
|
||
}
|
||
|
||
|
||
static Widget ToplevelWidget = NULL;
|
||
|
||
|
||
/*
|
||
* FE_SetToplevelWidget
|
||
*/
|
||
void
|
||
FE_SetToplevelWidget(Widget toplevel)
|
||
{
|
||
ToplevelWidget = toplevel;
|
||
}
|
||
|
||
|
||
/*
|
||
* FE_GetToplevelWidget
|
||
*/
|
||
Widget
|
||
FE_GetToplevelWidget(void)
|
||
{
|
||
return ToplevelWidget;
|
||
}
|
||
|
||
Time
|
||
fe_GetTimeFromEvent(XEvent* event)
|
||
{
|
||
Time time;
|
||
|
||
if (event->type == KeyPress || event->type == KeyRelease)
|
||
time = event->xkey.time;
|
||
else if (event->type == ButtonPress || event->type == ButtonRelease)
|
||
time = event->xbutton.time;
|
||
else
|
||
time = XtLastTimestampProcessed(event->xany.display);
|
||
|
||
return time;
|
||
}
|
||
|
||
/*
|
||
* Check if Conference is installed
|
||
*/
|
||
XP_Bool
|
||
fe_IsConferenceInstalled()
|
||
{
|
||
/* Note: will use XP registry to check if conference
|
||
* is installed when the installer is ready.
|
||
* For now, just check if we can find conference...
|
||
*/
|
||
|
||
return (fe_conference_path != NULL);
|
||
}
|
||
|
||
/*
|
||
* Check if Calendar is installed
|
||
*/
|
||
XP_Bool
|
||
fe_IsCalendarInstalled()
|
||
{
|
||
/* Note: will use XP registry to check if the Calendar component
|
||
* is installed when the installer is ready.
|
||
* For now, just check if we can find ctime...
|
||
*/
|
||
|
||
return (fe_calendar_path != NULL);
|
||
}
|
||
|
||
/*
|
||
* Check if Host on Demand is installed
|
||
*/
|
||
XP_Bool
|
||
fe_IsHostOnDemandInstalled()
|
||
{
|
||
/* Note: will use XP registry to check if the HOD component
|
||
* is installed when the installer is ready.
|
||
*/
|
||
|
||
return (fe_host_on_demand_path != NULL);
|
||
}
|
||
/*
|
||
* Check if polaris is installed
|
||
*/
|
||
XP_Bool
|
||
fe_IsPolarisInstalled()
|
||
{
|
||
return (fe_IsCalendarInstalled() || fe_IsHostOnDemandInstalled());
|
||
}
|
||
|
||
|
||
/*
|
||
* fe_IsEditorDisabled
|
||
*/
|
||
XP_Bool
|
||
fe_IsEditorDisabled(void)
|
||
{
|
||
XP_Bool disabled = FALSE;
|
||
|
||
PREF_GetBoolPref("browser.editor.disabled", &disabled);
|
||
|
||
return disabled;
|
||
}
|
||
|
||
|
||
/*
|
||
* Return the URL struct for the brower startup page
|
||
*/
|
||
URL_Struct *
|
||
fe_GetBrowserStartupUrlStruct()
|
||
{
|
||
char *bufp = NULL;
|
||
char *hist_entry = 0;
|
||
URL_Struct *url = 0;
|
||
|
||
if (fe_globalPrefs.browser_startup_page == BROWSER_STARTUP_HOME &&
|
||
fe_globalPrefs.home_document &&
|
||
*fe_globalPrefs.home_document) {
|
||
bufp = XP_STRDUP(fe_globalPrefs.home_document);
|
||
}
|
||
else if (fe_globalPrefs.browser_startup_page == BROWSER_STARTUP_LAST &&
|
||
fe_ReadLastUserHistory(&hist_entry)) {
|
||
bufp = hist_entry;
|
||
}
|
||
url = NET_CreateURLStruct(bufp, FALSE);
|
||
if (bufp) XP_FREE(bufp);
|
||
return url;
|
||
}
|
||
|
||
|
||
/*
|
||
* Get a context that represents the background root window. This is used
|
||
* at init time to find a context we can use to prompt for passwords,
|
||
* confirm or alert before any main window contexts get initialized.
|
||
*/
|
||
MWContext *
|
||
FE_GetInitContext(void)
|
||
{
|
||
static MWContext *rootcx = NULL;
|
||
MWContext *m_context = NULL;
|
||
fe_ContextData *fec;
|
||
struct fe_MWContext_cons *cons;
|
||
MWContextType type;
|
||
Widget m_widget;
|
||
LO_Color *color;
|
||
|
||
if (rootcx) return rootcx;
|
||
|
||
type = MWContextDialog; /* XXX should be it's own type */
|
||
|
||
m_context = XP_NewContext();
|
||
if (m_context == NULL) return NULL;
|
||
|
||
fec = XP_NEW_ZAP (fe_ContextData);
|
||
if (fec == NULL) {
|
||
XP_DELETE(m_context);
|
||
return NULL;
|
||
}
|
||
|
||
m_context->type = type;
|
||
m_context->is_editor = False;
|
||
|
||
CONTEXT_DATA (m_context) = fec;
|
||
|
||
/* get cmap.... Set to NULL for nowXXXXX */
|
||
CONTEXT_DATA (m_context)->colormap = NULL;
|
||
|
||
m_widget = FE_GetToplevelWidget();
|
||
CONTEXT_WIDGET (m_context) = m_widget;
|
||
|
||
fe_InitRemoteServer (XtDisplay (m_widget));
|
||
|
||
/* add the layout function pointers */
|
||
m_context->funcs = fe_BuildDisplayFunctionTable();
|
||
m_context->convertPixX = m_context->convertPixY = 1;
|
||
m_context->is_grid_cell = FALSE;
|
||
m_context->grid_parent = NULL;
|
||
|
||
/* set the XFE default Document Character set */
|
||
CONTEXT_DATA(m_context)->xfe_doc_csid = fe_globalPrefs.doc_csid;
|
||
|
||
#ifdef notdef
|
||
fe_InitIconColors(m_context);
|
||
|
||
fe_LicenseDialog (m_context);
|
||
#endif
|
||
|
||
XtGetApplicationResources (m_widget,
|
||
(XtPointer) CONTEXT_DATA (m_context),
|
||
fe_Resources, fe_ResourcesSize,
|
||
0, 0);
|
||
|
||
#ifdef notdef
|
||
/* Use colors from prefs */
|
||
color = &fe_globalPrefs.links_color;
|
||
CONTEXT_DATA(m_context)->link_pixel =
|
||
fe_GetPixel(m_context, color->red, color->green, color->blue);
|
||
|
||
color = &fe_globalPrefs.vlinks_color;
|
||
CONTEXT_DATA(m_context)->vlink_pixel =
|
||
fe_GetPixel(m_context, color->red, color->green, color->blue);
|
||
|
||
color = &fe_globalPrefs.text_color;
|
||
CONTEXT_DATA(m_context)->default_fg_pixel =
|
||
fe_GetPixel(m_context, color->red, color->green, color->blue);
|
||
|
||
color = &fe_globalPrefs.background_color;
|
||
CONTEXT_DATA(m_context)->default_bg_pixel =
|
||
fe_GetPixel(m_context, color->red, color->green, color->blue);
|
||
|
||
/*
|
||
* set the default coloring correctly into the new context.
|
||
*/
|
||
{
|
||
Pixel unused_select_pixel;
|
||
XmGetColors (XtScreen (m_widget),
|
||
fe_cmap(m_context),
|
||
CONTEXT_DATA (m_context)->default_bg_pixel,
|
||
&(CONTEXT_DATA (m_context)->fg_pixel),
|
||
&(CONTEXT_DATA (m_context)->top_shadow_pixel),
|
||
&(CONTEXT_DATA (m_context)->bottom_shadow_pixel),
|
||
&unused_select_pixel);
|
||
}
|
||
#endif
|
||
rootcx = m_context;
|
||
return rootcx;
|
||
}
|
||
|
||
void fe_CacheWindowOffset(MWContext *context, int32 sx, int32 sy)
|
||
{
|
||
CONTEXT_DATA(context)->cachedPos.x=sx;
|
||
CONTEXT_DATA(context)->cachedPos.y=sy;
|
||
}
|
||
|
||
/*
|
||
* Keep track of tooltip mapping to avoid conflict with fascist shells
|
||
* that insist on raising themselves - like taskbar and netcaster webtop
|
||
*/
|
||
Boolean fe_ToolTipIsShowing(void)
|
||
{
|
||
return fe_tooltip_is_showing;
|
||
}
|
||
|
||
#ifdef MOZ_MAIL_NEWS
|
||
MSG_IMAPUpgradeType FE_PromptIMAPSubscriptionUpgrade(MWContext *context,
|
||
const char *hostName)
|
||
{
|
||
return fe_promptIMAPSubscriptionUpgrade(context,hostName);
|
||
}
|
||
#endif
|
||
|
||
/* Get URL for referral if there is one. */
|
||
char *fe_GetURLForReferral(History_entry *he)
|
||
{
|
||
if (!he)
|
||
return NULL;
|
||
|
||
/* Origin URL takes precedence over address */
|
||
#ifdef DEBUG_mcafee /* Waiting for norris checkin */
|
||
if (he && he->origin_url)
|
||
return XP_STRDUP (he->origin_url);
|
||
else
|
||
#endif
|
||
if (he && he->address)
|
||
return XP_STRDUP (he->address);
|
||
else
|
||
return NULL;
|
||
}
|
||
|
||
/*
|
||
* FEU_StayingAlive
|
||
*/
|
||
void
|
||
FEU_StayingAlive(void)
|
||
{
|
||
XtInputMask pending;
|
||
|
||
/* Service timers first */
|
||
if ( (pending = XtAppPending(fe_XtAppContext)) ) {
|
||
if ( pending & XtIMTimer ) {
|
||
XtAppProcessEvent(fe_XtAppContext, XtIMTimer);
|
||
} else {
|
||
XtAppProcessEvent(fe_XtAppContext, pending);
|
||
}
|
||
}
|
||
|
||
/* Service network connections */
|
||
NET_ProcessNet(0, NET_EVERYTIME_TYPE);
|
||
}
|