Font prefs shouldn't limit choices + some optimizations of the 'user perception' by lazily building the drop down font lists while there, b=142511, r=gerv, sr=blizzard, a=asa

This commit is contained in:
rbs%maths.uq.edu.au 2003-05-21 01:14:33 +00:00
Родитель 8a265bc366
Коммит ffb05f59ba
6 изменённых файлов: 261 добавлений и 223 удалений

Просмотреть файл

@ -5137,47 +5137,6 @@ CompareFontNames(const void* aArg1, const void* aArg2, void* aClosure)
return nsCRT::strcmp(str1, str2);
}
NS_IMETHODIMP
nsFontEnumeratorWin::EnumerateAllFonts(PRUint32* aCount, PRUnichar*** aResult)
{
NS_ENSURE_ARG_POINTER(aCount);
NS_ENSURE_ARG_POINTER(aResult);
*aCount = 0;
*aResult = nsnull;
if (!gInitializedFontEnumerator) {
if (!InitializeFontEnumerator()) {
return NS_ERROR_FAILURE;
}
}
int count = nsFontMetricsWin::gGlobalFonts->Count();
PRUnichar** array = (PRUnichar**)nsMemory::Alloc(count * sizeof(PRUnichar*));
if (!array) {
return NS_ERROR_OUT_OF_MEMORY;
}
for (int i = 0; i < count; ++i) {
nsGlobalFont* font = (nsGlobalFont*)nsFontMetricsWin::gGlobalFonts->ElementAt(i);
PRUnichar* str = ToNewUnicode(font->name);
if (!str) {
for (i = i - 1; i >= 0; --i) {
nsMemory::Free(array[i]);
}
nsMemory::Free(array);
return NS_ERROR_OUT_OF_MEMORY;
}
array[i] = str;
}
NS_QuickSort(array, count, sizeof(PRUnichar*), CompareFontNames, nsnull);
*aCount = count;
*aResult = array;
return NS_OK;
}
static int
SignatureMatchesLangGroup(FONTSIGNATURE* aSignature,
const char* aLangGroup)
@ -5201,17 +5160,8 @@ SignatureMatchesLangGroup(FONTSIGNATURE* aSignature,
}
static int
FontMatchesGenericType(nsGlobalFont* aFont, const char* aGeneric,
const char* aLangGroup)
FontMatchesGenericType(nsGlobalFont* aFont, const char* aGeneric)
{
if (!strcmp(aLangGroup, "ja")) return 1;
if (!strcmp(aLangGroup, "zh-TW")) return 1;
if (!strcmp(aLangGroup, "zh-CN")) return 1;
if (!strcmp(aLangGroup, "ko")) return 1;
if (!strcmp(aLangGroup, "th")) return 1;
if (!strcmp(aLangGroup, "he")) return 1;
if (!strcmp(aLangGroup, "ar")) return 1;
switch (aFont->logFont.lfPitchAndFamily & 0xF0) {
case FF_DONTCARE: return 0;
case FF_ROMAN: return !strcmp(aGeneric, "serif");
@ -5224,21 +5174,23 @@ FontMatchesGenericType(nsGlobalFont* aFont, const char* aGeneric,
return 0;
}
NS_IMETHODIMP
nsFontEnumeratorWin::EnumerateFonts(const char* aLangGroup,
static nsresult
EnumerateMatchingFonts(const char* aLangGroup,
const char* aGeneric, PRUint32* aCount, PRUnichar*** aResult)
{
NS_ENSURE_ARG_POINTER(aLangGroup);
NS_ENSURE_ARG_POINTER(aGeneric);
// aLangGroup=null or "" means any (i.e., don't care)
// aGeneric=null or "" means any (i.e, don't care)
NS_ENSURE_ARG_POINTER(aCount);
NS_ENSURE_ARG_POINTER(aResult);
*aCount = 0;
*aResult = nsnull;
if ((!strcmp(aLangGroup, "x-unicode")) ||
(!strcmp(aLangGroup, "x-user-def"))) {
return EnumerateAllFonts(aCount, aResult);
if (aLangGroup && *aLangGroup &&
(!strcmp(aLangGroup, "x-unicode") ||
!strcmp(aLangGroup, "x-user-def"))) {
return EnumerateMatchingFonts(nsnull, nsnull, aCount, aResult);
}
if (!gInitializedFontEnumerator) {
@ -5255,8 +5207,14 @@ nsFontEnumeratorWin::EnumerateFonts(const char* aLangGroup,
int j = 0;
for (int i = 0; i < count; ++i) {
nsGlobalFont* font = (nsGlobalFont*)nsFontMetricsWin::gGlobalFonts->ElementAt(i);
if (SignatureMatchesLangGroup(&font->signature, aLangGroup) &&
FontMatchesGenericType(font, aGeneric, aLangGroup)) {
PRBool accept = PR_TRUE;
if (aLangGroup && *aLangGroup) {
accept = SignatureMatchesLangGroup(&font->signature, aLangGroup);
}
if (accept && aGeneric && *aGeneric) {
accept = FontMatchesGenericType(font, aGeneric);
}
if (accept) {
PRUnichar* str = ToNewUnicode(font->name);
if (!str) {
for (j = j - 1; j >= 0; --j) {
@ -5278,6 +5236,19 @@ nsFontEnumeratorWin::EnumerateFonts(const char* aLangGroup,
return NS_OK;
}
NS_IMETHODIMP
nsFontEnumeratorWin::EnumerateAllFonts(PRUint32* aCount, PRUnichar*** aResult)
{
return EnumerateMatchingFonts(nsnull, nsnull, aCount, aResult);
}
NS_IMETHODIMP
nsFontEnumeratorWin::EnumerateFonts(const char* aLangGroup,
const char* aGeneric, PRUint32* aCount, PRUnichar*** aResult)
{
return EnumerateMatchingFonts(aLangGroup, aGeneric, aCount, aResult);
}
NS_IMETHODIMP
nsFontEnumeratorWin::HaveFontFor(const char* aLangGroup, PRBool* aResult)
{

Просмотреть файл

@ -40,7 +40,7 @@
====================================================================== */
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
@namespace url("http://www.w3.org/1999/xhtml");
@namespace html url("http://www.w3.org/1999/xhtml");
/* ::::: Themes ::::: */
@ -78,6 +78,12 @@
width: 4em;
}
.prefpanel-font-list {
-moz-box-flex: 1;
}
/* ::::: Mouse Wheel ::::: */
#mouseWheelMode {
width: 9em;
}

Просмотреть файл

@ -40,7 +40,7 @@
====================================================================== */
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
@namespace url("http://www.w3.org/1999/xhtml");
@namespace html url("http://www.w3.org/1999/xhtml");
/* ::::: Themes ::::: */
@ -78,6 +78,12 @@
width: 4em;
}
.prefpanel-font-list {
-moz-box-flex: 1;
}
/* ::::: Mouse Wheel ::::: */
#mouseWheelMode {
width: 9em;
}

Просмотреть файл

@ -39,10 +39,6 @@
try
{
var fontList = Components.classes["@mozilla.org/gfx/fontlist;1"].createInstance();
if (fontList)
fontList = fontList.QueryInterface(Components.interfaces.nsIFontList);
var pref = Components.classes["@mozilla.org/preferences;1"].getService( Components.interfaces.nsIPref );
}
catch(e)
@ -50,6 +46,8 @@ catch(e)
dump("failed to get font list or pref object: "+e+" in pref-fonts.js\n");
}
var fontEnumerator = null;
var globalFonts = null;
var fontTypes = ["serif", "sans-serif", "cursive", "fantasy", "monospace"];
var variableSize, fixedSize, minSize, languageList;
var languageData = [];
@ -216,6 +214,17 @@ function Startup()
}
}
function getFontEnumerator()
{
if (!fontEnumerator)
{
fontEnumerator = Components.classes["@mozilla.org/gfx/fontenumerator;1"]
.createInstance()
.QueryInterface(Components.interfaces.nsIFontEnumerator);
}
return fontEnumerator;
}
function listElement( aListID )
{
this.listElement = document.getElementById( aListID );
@ -230,47 +239,148 @@ listElement.prototype =
this.listElement.removeChild( this.listElement.firstChild );
},
appendString:
function ( aString )
{
var menuItemNode = document.createElement( "menuitem" );
if( menuItemNode )
{
menuItemNode.setAttribute( "label", aString );
this.listElement.firstChild.appendChild( menuItemNode );
}
},
appendFontNames:
function ( aDataObject )
{
function ( aLanguage, aFontType )
{
var i;
var count = { value: 0 };
var fonts = getFontEnumerator().EnumerateFonts( aLanguage, aFontType, count );
var popupNode = document.createElement( "menupopup" );
var strDefaultFontFace = "";
var fontName;
while (aDataObject.hasMoreElements()) {
fontName = aDataObject.getNext();
fontName = fontName.QueryInterface(Components.interfaces.nsISupportsString);
var fontNameStr = fontName.toString();
if (strDefaultFontFace == "")
strDefaultFontFace = fontNameStr;
var itemNode = document.createElement( "menuitem" );
itemNode.setAttribute( "value", fontNameStr );
itemNode.setAttribute( "label", fontNameStr );
popupNode.appendChild( itemNode );
}
if (strDefaultFontFace != "") {
this.listElement.removeAttribute( "disabled" );
} else {
this.listElement.setAttribute( "value", strDefaultFontFace );
this.listElement.setAttribute( "label",
gPrefutilitiesBundle.getString("nofontsforlang") );
this.listElement.setAttribute( "disabled", "true" );
}
// always put the default system font at the front of the list
var itemNode = document.createElement( "menuitem" );
itemNode.setAttribute( "value", "" ); // special blank value
itemNode.setAttribute( "label", gPrefutilitiesBundle.getString("systemDefault") );
popupNode.appendChild( itemNode );
var separatorNode = null;
if (fonts.length > 0)
{
separatorNode = document.createElement( "menuseparator" );
popupNode.appendChild( separatorNode );
for (i = 0; i < fonts.length; i++)
{
itemNode = document.createElement( "menuitem" );
itemNode.setAttribute( "value", fonts[i] );
itemNode.setAttribute( "label", fonts[i] );
popupNode.appendChild( itemNode );
}
}
// get all the fonts to complete the font lists
if (!globalFonts)
{
globalFonts = getFontEnumerator().EnumerateAllFonts( count );
}
// since the lists are sorted, we can get unique entries by just walking
// both lists linearly side-by-side, skipping those values already in
// the popup list
if (globalFonts.length > fonts.length)
{
var menuItem = separatorNode ? separatorNode.nextSibling : null;
var menuValue = menuItem ? menuItem.getAttribute( "value" ) : null;
separatorNode = document.createElement( "menuseparator" );
popupNode.appendChild( separatorNode );
for (i = 0; i < globalFonts.length; i++)
{
if (globalFonts[i] != menuValue)
{
itemNode = document.createElement( "menuitem" );
itemNode.setAttribute( "value", globalFonts[i] );
itemNode.setAttribute( "label", globalFonts[i] );
popupNode.appendChild( itemNode );
}
else
{
menuItem = menuItem.nextSibling;
menuValue = menuItem ? menuItem.getAttribute( "value" ) : null;
}
}
}
this.listElement.appendChild( popupNode );
return strDefaultFontFace;
return popupNode.firstChild;
}
};
function lazyAppendFontNames( i )
{
// schedule the build of the next font list
if (i+1 < fontTypes.length)
{
window.setTimeout(lazyAppendFontNames, 100, i+1);
}
// now build and populate the fonts for the requested font type
var firstItem = null;
var selectElement = new listElement( fontTypes[i] );
selectElement.clearList();
try
{
firstItem = selectElement.appendFontNames( languageList.value, fontTypes[i] );
}
catch(e) {
dump("pref-fonts.js: " + e + "\nFailed to build the font list for " + fontTypes[i] + "\n");
return;
}
// now set the selected font item for the drop down list
if (!firstItem)
return; // nothing to select, so no need to bother
// the first item returned by default is our last resort fall-back
var selectedItem = firstItem;
if( languageList.value in languageData )
{
// data exists for this language, pre-select items based on this information
var dataVal = languageData[languageList.value].types[fontTypes[i]];
if (!dataVal.length) // special blank means [System Default]
{
selectedItem = firstItem;
}
else
{
var dataEls = selectElement.listElement.getElementsByAttribute( "value", dataVal );
selectedItem = dataEls.length ? dataEls[0] : firstItem;
}
}
else
{
try
{
var fontPrefString = "font.name." + fontTypes[i] + "." + languageList.value;
var selectVal = parent.hPrefWindow.pref.CopyUnicharPref( fontPrefString );
var dataEls = selectElement.listElement.getElementsByAttribute( "value", selectVal );
// we need to honor name-list in case name is unavailable
if (!dataEls.length) {
var fontListPrefString = "font.name-list." + fontTypes[i] + "." + languageList.value;
var nameList = parent.hPrefWindow.pref.CopyUnicharPref( fontListPrefString );
var fontNames = nameList.split(",");
var stripWhitespace = /^\s*(.*)\s*$/;
for (j = 0; j < fontNames.length; j++) {
selectVal = fontNames[j].replace(stripWhitespace, "$1");
dataEls = selectElement.listElement.getElementsByAttribute("value", selectVal);
if (dataEls.length)
break; // exit loop if we find one
}
}
selectedItem = dataEls.length ? dataEls[0] : firstItem;
}
catch(e) {
selectedItem = firstItem;
}
}
selectElement.listElement.selectedItem = selectedItem;
selectElement.listElement.removeAttribute( "disabled" );
}
function saveFontPrefs()
{
// if saveState function is available, assume can call it.
@ -297,8 +407,30 @@ function saveFontPrefs()
catch(e)
{
}
if( currValue != dataObject.languageData[language].types[type] )
pref.SetUnicharPref( fontPrefString, dataObject.languageData[language].types[type] );
var dataValue = dataObject.languageData[language].types[type];
if( currValue != dataValue )
{
if (dataValue)
{
pref.SetUnicharPref( fontPrefString, dataValue );
}
else
{
// A font name can't be blank. The special blank means [System Default].
// Unset the pref entirely, letting Gfx to decide. GfxXft will use what
// Xft says, whereas GfxWin and others will use the built-in settings
// that are shipped for font.name and font.name-list.
try
{
// ClearUserPref throws an exception...
pref.ClearUserPref( fontPrefString );
}
catch(e)
{
}
}
}
}
var variableSizePref = "font.size.variable." + language;
var fixedSizePref = "font.size.fixed." + language;
@ -392,123 +524,45 @@ function selectLanguage()
if( !currentLanguage )
currentLanguage = languageList.value;
else if( currentLanguage == languageList.value )
return; // same as before, nothing changed
// lazily populate the successive font lists at 100ms intervals.
// (Note: the third parameter to setTimeout() is going to be
// passed as argument to the callback function.)
window.setTimeout(lazyAppendFontNames, 100, 0);
// in the meantime, disable the menu lists
for( var i = 0; i < fontTypes.length; i++ )
{
// build and populate the font list for the newly chosen font type
var fontEnumerator = fontList.availableFonts(languageList.value, fontTypes[i]);
var selectElement = new listElement( fontTypes[i] );
selectElement.clearList();
var strDefaultFontFace = selectElement.appendFontNames(fontEnumerator);
//the first font face name returned by the enumerator is our last resort
var defaultListSelection = selectElement.listElement.getElementsByAttribute( "value", strDefaultFontFace)[0];
var selectedItem;
//fall-back initialization values (first font face list entry)
defaultListSelection = strDefaultFontFace ? selectElement.listElement.getElementsByAttribute( "value", strDefaultFontFace)[0] : null;
if( languageList.value in languageData )
{
// data exists for this language, pre-select items based on this information
var dataElements = selectElement.listElement.getElementsByAttribute( "value", languageData[languageList.value].types[fontTypes[i]] );
selectedItem = dataElements.length ? dataElements[0] : defaultListSelection;
minSizeSelect(languageData[languageList.value].minSize);
if (strDefaultFontFace)
{
selectElement.listElement.selectedItem = selectedItem;
variableSize.removeAttribute("disabled");
fixedSize.removeAttribute("disabled");
minSize.removeAttribute("disabled");
variableSize.selectedItem = variableSize.getElementsByAttribute( "value", languageData[languageList.value].variableSize )[0];
fixedSize.selectedItem = fixedSize.getElementsByAttribute( "value", languageData[languageList.value].fixedSize )[0];
}
else
{
variableSize.setAttribute("disabled","true");
fixedSize.setAttribute("disabled","true");
minSize.setAttribute("disabled","true");
}
}
else
{
if (strDefaultFontFace) {
//initialze pref panel only if font faces are available for this language family
var selectVal;
try {
var fontPrefString = "font.name." + fontTypes[i] + "." + languageList.value;
selectVal = parent.hPrefWindow.pref.CopyUnicharPref( fontPrefString );
var dataEls = selectElement.listElement.getElementsByAttribute( "value", selectVal );
// we need to honor name-list in case name is unavailable
if (!dataEls.length) {
var fontListPrefString = "font.name-list." + fontTypes[i] + "." + languageList.value;
var nameList = parent.hPrefWindow.pref.CopyUnicharPref( fontListPrefString );
var fontNames = nameList.split(",");
var stripWhitespace = /^\s*(.*)\s*$/;
for (j = 0; j < fontNames.length; j++) {
selectVal = fontNames[j].replace(stripWhitespace, "$1");
dataEls = selectElement.listElement.getElementsByAttribute("value", selectVal);
if (dataEls.length)
break; // exit loop if we find one
}
}
selectedItem = dataEls.length ? dataEls[0] : defaultListSelection;
if (!dataEls.length)
selectedVal = strDefaultFontFace;
}
catch(e) {
//always initialize: fall-back to default values
selectVal = strDefaultFontFace;
selectedItem = defaultListSelection;
}
selectElement.listElement.selectedItem = selectedItem;
variableSize.removeAttribute("disabled");
fixedSize.removeAttribute("disabled");
minSize.removeAttribute("disabled");
try {
var variableSizePref = "font.size.variable." + languageList.value;
var fixedSizePref = "font.size.fixed." + languageList.value;
var sizeVarVal = parent.hPrefWindow.pref.GetIntPref( variableSizePref );
var sizeFixedVal = parent.hPrefWindow.pref.GetIntPref( fixedSizePref );
variableSize.selectedItem = variableSize.getElementsByAttribute( "value", sizeVarVal )[0];
fixedSize.selectedItem = fixedSize.getElementsByAttribute( "value", sizeFixedVal )[0];
}
catch(e) {
//font size lists can simply deafult to the first entry
}
var minSizeVal = 0;
try {
var minSizePref = "font.minimum-size." + languageList.value;
minSizeVal = pref.GetIntPref( minSizePref );
}
catch(e) {}
minSizeSelect( minSizeVal );
}
else
{
//disable otherwise
variableSize.setAttribute("disabled","true");
fixedSize.setAttribute("disabled","true");
minSize.setAttribute("disabled","true");
minSizeSelect(0);
}
}
var listElement = document.getElementById( fontTypes[i] );
listElement.setAttribute( "value", "" );
listElement.setAttribute( "label", "" );
listElement.setAttribute( "disabled", "true" );
}
// and set the font sizes
try
{
var variableSizePref = "font.size.variable." + languageList.value;
var sizeVarVal = parent.hPrefWindow.pref.GetIntPref( variableSizePref );
variableSize.selectedItem = variableSize.getElementsByAttribute( "value", sizeVarVal )[0];
var fixedSizePref = "font.size.fixed." + languageList.value;
var sizeFixedVal = parent.hPrefWindow.pref.GetIntPref( fixedSizePref );
fixedSize.selectedItem = fixedSize.getElementsByAttribute( "value", sizeFixedVal )[0];
}
catch(e) { } // font size lists can simply default to the first entry
var minSizeVal = 0;
try
{
var minSizePref = "font.minimum-size." + languageList.value;
minSizeVal = pref.GetIntPref( minSizePref );
}
catch(e) { }
minSizeSelect( minSizeVal );
currentLanguage = languageList.value;
}

Просмотреть файл

@ -21,6 +21,7 @@
Contributor(s):
-->
<?xml-stylesheet href="chrome://communicator/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://communicator/skin/prefpanels.css" type="text/css"?>
<!DOCTYPE page SYSTEM "chrome://communicator/locale/pref/pref-fonts.dtd" >
<page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
@ -142,7 +143,7 @@
accesskey="&serif.accesskey;"
control="serif"/>
</hbox>
<menulist id="serif" flex="1" style="width: 0px;">
<menulist id="serif" class="prefpanel-font-list">
<menupopup/>
</menulist>
<spacer/>
@ -153,7 +154,7 @@
accesskey="&sans-serif.accesskey;"
control="sans-serif"/>
</hbox>
<menulist id="sans-serif" flex="1" style="width: 0px;">
<menulist id="sans-serif" class="prefpanel-font-list">
<menupopup/>
</menulist>
<spacer/>
@ -164,7 +165,7 @@
accesskey="&cursive.accesskey;"
control="cursive"/>
</hbox>
<menulist id="cursive">
<menulist id="cursive" class="prefpanel-font-list">
<menupopup/>
</menulist>
<spacer/>
@ -175,7 +176,7 @@
accesskey="&fantasy.accesskey;"
control="fantasy"/>
</hbox>
<menulist id="fantasy">
<menulist id="fantasy" class="prefpanel-font-list">
<menupopup/>
</menulist>
<spacer/>
@ -186,8 +187,7 @@
accesskey="&monospace.accesskey;"
control="monospace"/>
</hbox>
<menulist id="monospace"
flex="1" style="width: 0px;" crop="right">
<menulist id="monospace" class="prefpanel-font-list">
<menupopup/>
</menulist>
<menulist id="sizeMono">

Просмотреть файл

@ -3,7 +3,6 @@ choosehomepage=Choose Home Page
choosefile=Choose a file
viewrow=View...
hiderow=Hide
nofontsforlang=No fonts available for this language
# Themes preferences panel inserts the name of the selected
# theme into the %theme_name% segment
@ -19,3 +18,5 @@ prefSaveFailedAlert=Failed to save the preferences file. Any preference changes
prefSaveFailedTitle=Save Error
groupIsSet=-- Home Page Group is Set --
systemDefault=[System Default]