tenfourfox/editor/libeditor/TypeInState.cpp

405 lines
10 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "TypeInState.h"
#include <stddef.h>
#include "mozilla/dom/Selection.h"
#include "mozilla/mozalloc.h"
#include "nsAString.h"
#include "nsDebug.h"
#include "nsEditor.h"
#include "nsError.h"
#include "nsGkAtoms.h"
#include "nsIDOMNode.h"
#include "nsISupportsBase.h"
#include "nsISupportsImpl.h"
#include "nsReadableUtils.h"
#include "nsStringFwd.h"
class nsIAtom;
class nsIDOMDocument;
using namespace mozilla;
using namespace mozilla::dom;
/********************************************************************
* XPCOM cruft
*******************************************************************/
NS_IMPL_CYCLE_COLLECTION(TypeInState, mLastSelectionContainer)
NS_IMPL_CYCLE_COLLECTING_ADDREF(TypeInState)
NS_IMPL_CYCLE_COLLECTING_RELEASE(TypeInState)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TypeInState)
NS_INTERFACE_MAP_ENTRY(nsISelectionListener)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
/********************************************************************
* public methods
*******************************************************************/
TypeInState::TypeInState() :
mSetArray()
,mClearedArray()
,mRelativeFontSize(0)
,mLastSelectionOffset(0)
{
Reset();
}
TypeInState::~TypeInState()
{
// Call Reset() to release any data that may be in
// mClearedArray and mSetArray.
Reset();
}
nsresult
TypeInState::UpdateSelState(Selection* aSelection)
{
NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
if (!aSelection->Collapsed()) {
return NS_OK;
}
return nsEditor::GetStartNodeAndOffset(aSelection, getter_AddRefs(mLastSelectionContainer), &mLastSelectionOffset);
}
NS_IMETHODIMP TypeInState::NotifySelectionChanged(nsIDOMDocument *, nsISelection *aSelection, int16_t)
{
// XXX: Selection currently generates bogus selection changed notifications
// XXX: (bug 140303). It can notify us when the selection hasn't actually
// XXX: changed, and it notifies us more than once for the same change.
// XXX:
// XXX: The following code attempts to work around the bogus notifications,
// XXX: and should probably be removed once bug 140303 is fixed.
// XXX:
// XXX: This code temporarily fixes the problem where clicking the mouse in
// XXX: the same location clears the type-in-state.
RefPtr<Selection> selection = static_cast<Selection*>(aSelection);
if (aSelection) {
int32_t rangeCount = selection->RangeCount();
if (selection->Collapsed() && rangeCount) {
nsCOMPtr<nsIDOMNode> selNode;
int32_t selOffset = 0;
nsresult result =
nsEditor::GetStartNodeAndOffset(selection, getter_AddRefs(selNode),
&selOffset);
NS_ENSURE_SUCCESS(result, result);
if (selNode && selNode == mLastSelectionContainer && selOffset == mLastSelectionOffset)
{
// We got a bogus selection changed notification!
return NS_OK;
}
mLastSelectionContainer = selNode;
mLastSelectionOffset = selOffset;
}
else
{
mLastSelectionContainer = nullptr;
mLastSelectionOffset = 0;
}
}
Reset();
return NS_OK;
}
void TypeInState::Reset()
{
for(uint32_t i = 0, n = mClearedArray.Length(); i < n; i++) {
delete mClearedArray[i];
}
mClearedArray.Clear();
for(uint32_t i = 0, n = mSetArray.Length(); i < n; i++) {
delete mSetArray[i];
}
mSetArray.Clear();
}
void
TypeInState::SetProp(nsIAtom* aProp, const nsAString& aAttr,
const nsAString& aValue)
{
// special case for big/small, these nest
if (nsGkAtoms::big == aProp) {
mRelativeFontSize++;
return;
}
if (nsGkAtoms::small == aProp) {
mRelativeFontSize--;
return;
}
int32_t index;
if (IsPropSet(aProp, aAttr, nullptr, index)) {
// if it's already set, update the value
mSetArray[index]->value = aValue;
return;
}
// Make a new propitem and add it to the list of set properties.
mSetArray.AppendElement(new PropItem(aProp, aAttr, aValue));
// remove it from the list of cleared properties, if we have a match
RemovePropFromClearedList(aProp, aAttr);
}
void
TypeInState::ClearAllProps()
{
// null prop means "all" props
ClearProp(nullptr, EmptyString());
}
void
TypeInState::ClearProp(nsIAtom* aProp, const nsAString& aAttr)
{
// if it's already cleared we are done
if (IsPropCleared(aProp, aAttr)) {
return;
}
// make a new propitem
PropItem* item = new PropItem(aProp, aAttr, EmptyString());
// remove it from the list of set properties, if we have a match
RemovePropFromSetList(aProp, aAttr);
// add it to the list of cleared properties
mClearedArray.AppendElement(item);
}
/***************************************************************************
* TakeClearProperty: hands back next property item on the clear list.
* caller assumes ownership of PropItem and must delete it.
*/
PropItem*
TypeInState::TakeClearProperty()
{
uint32_t count = mClearedArray.Length();
if (!count) {
return nullptr;
}
--count; // indices are zero based
PropItem* propItem = mClearedArray[count];
mClearedArray.RemoveElementAt(count);
return propItem;
}
/***************************************************************************
* TakeSetProperty: hands back next poroperty item on the set list.
* caller assumes ownership of PropItem and must delete it.
*/
PropItem*
TypeInState::TakeSetProperty()
{
uint32_t count = mSetArray.Length();
if (!count) {
return nullptr;
}
count--; // indices are zero based
PropItem* propItem = mSetArray[count];
mSetArray.RemoveElementAt(count);
return propItem;
}
//**************************************************************************
// TakeRelativeFontSize: hands back relative font value, which is then
// cleared out.
int32_t
TypeInState::TakeRelativeFontSize()
{
int32_t relSize = mRelativeFontSize;
mRelativeFontSize = 0;
return relSize;
}
void
TypeInState::GetTypingState(bool &isSet, bool &theSetting, nsIAtom *aProp)
{
GetTypingState(isSet, theSetting, aProp, EmptyString(), nullptr);
}
void
TypeInState::GetTypingState(bool &isSet,
bool &theSetting,
nsIAtom *aProp,
const nsString &aAttr,
nsString *aValue)
{
if (IsPropSet(aProp, aAttr, aValue))
{
isSet = true;
theSetting = true;
}
else if (IsPropCleared(aProp, aAttr))
{
isSet = true;
theSetting = false;
}
else
{
isSet = false;
}
}
/********************************************************************
* protected methods
*******************************************************************/
void
TypeInState::RemovePropFromSetList(nsIAtom* aProp, const nsAString& aAttr)
{
int32_t index;
if (!aProp)
{
// clear _all_ props
for(uint32_t i = 0, n = mSetArray.Length(); i < n; i++) {
delete mSetArray[i];
}
mSetArray.Clear();
mRelativeFontSize=0;
}
else if (FindPropInList(aProp, aAttr, nullptr, mSetArray, index))
{
delete mSetArray[index];
mSetArray.RemoveElementAt(index);
}
}
void
TypeInState::RemovePropFromClearedList(nsIAtom* aProp, const nsAString& aAttr)
{
int32_t index;
if (FindPropInList(aProp, aAttr, nullptr, mClearedArray, index))
{
delete mClearedArray[index];
mClearedArray.RemoveElementAt(index);
}
}
bool TypeInState::IsPropSet(nsIAtom *aProp,
const nsAString& aAttr,
nsAString* outValue)
{
int32_t i;
return IsPropSet(aProp, aAttr, outValue, i);
}
bool TypeInState::IsPropSet(nsIAtom* aProp,
const nsAString& aAttr,
nsAString* outValue,
int32_t& outIndex)
{
// linear search. list should be short.
uint32_t i, count = mSetArray.Length();
for (i=0; i<count; i++)
{
PropItem *item = mSetArray[i];
if ( (item->tag == aProp) &&
(item->attr == aAttr) )
{
if (outValue) *outValue = item->value;
outIndex = i;
return true;
}
}
return false;
}
bool TypeInState::IsPropCleared(nsIAtom* aProp,
const nsAString& aAttr)
{
int32_t i;
return IsPropCleared(aProp, aAttr, i);
}
bool TypeInState::IsPropCleared(nsIAtom* aProp,
const nsAString& aAttr,
int32_t& outIndex)
{
if (FindPropInList(aProp, aAttr, nullptr, mClearedArray, outIndex))
return true;
if (FindPropInList(0, EmptyString(), nullptr, mClearedArray, outIndex))
{
// special case for all props cleared
outIndex = -1;
return true;
}
return false;
}
bool TypeInState::FindPropInList(nsIAtom *aProp,
const nsAString &aAttr,
nsAString *outValue,
nsTArray<PropItem*> &aList,
int32_t &outIndex)
{
// linear search. list should be short.
uint32_t i, count = aList.Length();
for (i=0; i<count; i++)
{
PropItem *item = aList[i];
if ( (item->tag == aProp) &&
(item->attr == aAttr) )
{
if (outValue) *outValue = item->value;
outIndex = i;
return true;
}
}
return false;
}
/********************************************************************
* PropItem: helper struct for TypeInState
*******************************************************************/
PropItem::PropItem() :
tag(nullptr)
,attr()
,value()
{
MOZ_COUNT_CTOR(PropItem);
}
PropItem::PropItem(nsIAtom *aTag, const nsAString &aAttr, const nsAString &aValue) :
tag(aTag)
,attr(aAttr)
,value(aValue)
{
MOZ_COUNT_CTOR(PropItem);
}
PropItem::~PropItem()
{
MOZ_COUNT_DTOR(PropItem);
}