Back to index

lightning-sunbird  0.9+nobinonly
nsDeviceContextMac.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsDeviceContextMac.h"
00040 #include "nsRenderingContextMac.h"
00041 #include "nsDeviceContextSpecX.h"
00042 #include "nsIPrintingContext.h"
00043 #include "nsString.h"
00044 #include "nsHashtable.h"
00045 #include "nsFont.h"
00046 #include <Gestalt.h>
00047 #include <Appearance.h>
00048 #include <TextEncodingConverter.h>
00049 #include <TextCommon.h>
00050 #include <StringCompare.h>
00051 #include <Fonts.h>
00052 #include <Resources.h>
00053 #include <MacWindows.h>
00054 #include <FixMath.h>
00055 #include "nsIPref.h"
00056 #include "nsIServiceManager.h"
00057 #include "nsQuickSort.h"
00058 #include "nsUnicodeMappingUtil.h"
00059 #include "nsCarbonHelpers.h"
00060 #include "nsRegionMac.h"
00061 #include "nsIScreenManager.h"
00062 #include "nsIServiceManager.h"
00063 #include "nsReadableUtils.h"
00064 #include "nsUnicharUtils.h"
00065 
00066 
00067 PRUint32 nsDeviceContextMac::mPixelsPerInch = 96;
00068 PRUint32 nsDeviceContextMac::sNumberOfScreens = 0;
00069 
00070 
00075 nsDeviceContextMac :: nsDeviceContextMac()
00076   : DeviceContextImpl(),
00077     mOldPort(nsnull)
00078 {
00079 }
00080 
00085 nsDeviceContextMac :: ~nsDeviceContextMac()
00086 {
00087 }
00088 
00093 NS_IMETHODIMP nsDeviceContextMac :: Init(nsNativeWidget aNativeWidget)
00094 {
00095   // cache the screen manager service for later
00096   nsresult ignore;
00097   mScreenManager = do_GetService("@mozilla.org/gfx/screenmanager;1", &ignore);
00098   NS_ASSERTION ( mScreenManager, "No screen manager, we're in trouble" );
00099   if ( !mScreenManager )
00100     return NS_ERROR_FAILURE;
00101 
00102   // figure out how many monitors there are.
00103   if ( !sNumberOfScreens )
00104     mScreenManager->GetNumberOfScreens(&sNumberOfScreens);
00105 
00106        // get resolution. Ensure that mPixelsToTwips is integral or we 
00107        // run into serious rounding problems.
00108   double pix_inch = GetScreenResolution();              //Fix2X((**thepix).hRes);
00109   mPixelsToTwips = nscoord(NSIntPointsToTwips(72)/(float)pix_inch);
00110   mTwipsToPixels = 1.0f/mPixelsToTwips;
00111        
00112   return DeviceContextImpl::Init(aNativeWidget);
00113 }
00114 
00115 
00120 NS_IMETHODIMP nsDeviceContextMac :: CreateRenderingContext(nsIRenderingContext *&aContext)
00121 {
00122 #ifdef NS_PRINT_PREVIEW
00123   // Defer to Alt when there is one
00124   if (mAltDC && ((mUseAltDC & kUseAltDCFor_CREATERC_PAINT) || (mUseAltDC & kUseAltDCFor_CREATERC_REFLOW))) {
00125     return mAltDC->CreateRenderingContext(aContext);
00126   }
00127 #endif
00128 
00129   nsRenderingContextMac *pContext;
00130   nsresult              rv;
00131 
00132        pContext = new nsRenderingContextMac();
00133   if (nsnull != pContext) {
00134     NS_ADDREF(pContext);
00135 
00136     CGrafPtr  thePort;
00137        ::GetPort((GrafPtr*)&thePort);
00138 
00139     rv = pContext->Init(this, thePort);
00140   }
00141   else
00142     rv = NS_ERROR_OUT_OF_MEMORY;
00143 
00144   if (NS_OK != rv){
00145     NS_IF_RELEASE(pContext);
00146   }
00147   aContext = pContext;
00148   return rv;
00149 }
00150 
00155 NS_IMETHODIMP nsDeviceContextMac :: SupportsNativeWidgets(PRBool &aSupportsWidgets)
00156 {
00157   //XXX it is very critical that this not lie!! MMP
00158   
00159   // ¥¥¥ VERY IMPORTANT (pinkerton)
00160   // This routine should return true if the widgets behave like Win32
00161   // "windows", that is they paint themselves and the app never knows about
00162   // them or has to send them update events. We were returning false which
00163   // meant that raptor needed to make sure to redraw them. However, if we
00164   // return false, the widgets never get created because the CreateWidget()
00165   // call in nsView never creates the widget. If we return true (which makes
00166   // things work), we are lying because the controls really need those
00167   // precious update/repaint events.
00168   // 
00169   // The situation we need is the following:
00170   // - return false from SupportsNativeWidgets()
00171   // - Create() is called on widgets when the above case is true.
00172   // 
00173   // Raptor currently doesn't work this way and needs to be fixed.
00174   // (please remove this comment when this situation is rectified)
00175   
00176   if( nsnull != mSpec){
00177        aSupportsWidgets = PR_FALSE;
00178   } else {
00179        aSupportsWidgets = PR_TRUE;
00180   }
00181   
00182   //if (nsnull == mSurface)
00183     
00184   //else
00185    //aSupportsWidgets = PR_FALSE;
00186 
00187   return NS_OK;
00188 }
00189 
00190 
00195 NS_IMETHODIMP nsDeviceContextMac :: GetScrollBarDimensions(float &aWidth, float &aHeight) const
00196 {
00197   // XXX Should we push this to widget library
00198   float scale;
00199   GetCanonicalPixelScale(scale);
00200   aWidth = 16 * mDevUnitsToAppUnits * scale;
00201   aHeight = 16 * mDevUnitsToAppUnits * scale;
00202   return NS_OK;
00203 }
00204 
00205 
00209 static bool HasAppearanceManager()
00210 {
00211 
00212 #define APPEARANCE_MIN_VERSION     0x0110        // we require version 1.1
00213        
00214        static bool inited = false;
00215        static bool hasAppearanceManager = false;
00216 
00217        if (inited)
00218               return hasAppearanceManager;
00219        inited = true;
00220 
00221        SInt32 result;
00222        if (::Gestalt(gestaltAppearanceAttr, &result) != noErr)
00223               return false;        // no Appearance Mgr
00224 
00225        if (::Gestalt(gestaltAppearanceVersion, &result) != noErr)
00226               return false;        // still version 1.0
00227 
00228        hasAppearanceManager = (result >= APPEARANCE_MIN_VERSION);
00229 
00230        return hasAppearanceManager;
00231 }
00232 
00233 // helper function to get the system font for a specific script
00234 #define FONTNAME_MAX_UNICHRS sizeof(fontName255) * 2
00235 nsresult 
00236 GetSystemFontForScript(ThemeFontID aFontID, ScriptCode aScriptCode,
00237                        nsAFlatString& aFontName, SInt16& aFontSize,
00238                        Style& aFontStyle)
00239 {
00240   Str255 fontName255;
00241   ::GetThemeFont(aFontID, aScriptCode, fontName255, &aFontSize, &aFontStyle);
00242   if (fontName255[0] == 255) {
00243     NS_WARNING("Too long fong name (> 254 chrs)");
00244     return NS_ERROR_FAILURE;
00245   }
00246   fontName255[fontName255[0]+1] = 0;
00247         
00248   OSStatus err;
00249   // the theme font could contains font name in different encoding. 
00250   // we need to convert them to unicode according to the font's text encoding.
00251 
00252   TECObjectRef converter = 0;
00253   TextEncoding unicodeEncoding = 
00254     ::CreateTextEncoding(kTextEncodingUnicodeDefault, 
00255                          kTextEncodingDefaultVariant,
00256                          kTextEncodingDefaultFormat);
00257                                                               
00258   FMFontFamily fontFamily = ::FMGetFontFamilyFromName(fontName255);;
00259   TextEncoding fontEncoding = 0;
00260   err = ::FMGetFontFamilyTextEncoding(fontFamily, &fontEncoding);
00261 
00262   if (err != noErr) {
00263     NS_WARNING("Could not get the encoding for the font.");
00264     return NS_ERROR_FAILURE;
00265   }
00266   err = ::TECCreateConverter(&converter, fontEncoding, unicodeEncoding);
00267   if (err != noErr) {
00268     NS_WARNING("Could not create the converter.");
00269     return NS_ERROR_FAILURE;
00270   }
00271   PRUnichar unicodeFontName[FONTNAME_MAX_UNICHRS + 1];
00272   ByteCount actualInputLength, actualOutputLength;
00273   err = ::TECConvertText(converter, &fontName255[1], fontName255[0], 
00274                          &actualInputLength, 
00275                          (TextPtr)unicodeFontName,
00276                          FONTNAME_MAX_UNICHRS * sizeof(PRUnichar),
00277                          &actualOutputLength);  
00278   if (err != noErr) {
00279     NS_WARNING("Could not convert the font name.");
00280     return NS_ERROR_FAILURE;
00281   }
00282 
00283   ::TECDisposeConverter(converter);
00284 
00285   unicodeFontName[actualOutputLength / sizeof(PRUnichar)] = PRUnichar('\0');
00286   aFontName = nsDependentString(unicodeFontName);
00287   return NS_OK;
00288 }
00289 
00294 NS_IMETHODIMP nsDeviceContextMac :: GetSystemFont(nsSystemFontID aID, nsFont *aFont) const
00295 {
00296   nsresult status = NS_OK;
00297 
00298   switch (aID) {
00299     //---------
00300     // CSS System Fonts
00301     //
00302     //   Important: don't chage the code below, or make sure to preserve
00303     //   some compatibility with MacIE5 - developers will appreciate.
00304     //   Run the testcase in bug 3371 in quirks mode and strict mode.
00305     //---------
00306         // css2
00307     case eSystemFont_Caption:
00308     case eSystemFont_Icon: 
00309     case eSystemFont_Menu: 
00310     case eSystemFont_MessageBox: 
00311     case eSystemFont_SmallCaption: 
00312     case eSystemFont_StatusBar: 
00313         // css3
00314     case eSystemFont_Window:
00315     case eSystemFont_Document:
00316     case eSystemFont_Workspace:
00317     case eSystemFont_Desktop:
00318     case eSystemFont_Info:
00319     case eSystemFont_Dialog:
00320     case eSystemFont_Button:
00321     case eSystemFont_PullDownMenu:
00322     case eSystemFont_List:
00323     case eSystemFont_Field:
00324         // moz
00325     case eSystemFont_Tooltips:
00326     case eSystemFont_Widget:
00327       float  dev2app;
00328       dev2app = DevUnitsToAppUnits();
00329 
00330       aFont->style       = NS_FONT_STYLE_NORMAL;
00331       aFont->weight      = NS_FONT_WEIGHT_NORMAL;
00332       aFont->decorations = NS_FONT_DECORATION_NONE;
00333 
00334       if (aID == eSystemFont_Window ||
00335           aID == eSystemFont_Document) {
00336             aFont->name.AssignLiteral("sans-serif");
00337             aFont->size = NSToCoordRound(aFont->size * 0.875f); // quick hack
00338       }
00339       else if (HasAppearanceManager())
00340       {
00341         ThemeFontID fontID = kThemeViewsFont;
00342         switch (aID)
00343         {
00344               // css2
00345           case eSystemFont_Caption:       fontID = kThemeSystemFont;         break;
00346           case eSystemFont_Icon:          fontID = kThemeViewsFont;          break;
00347           case eSystemFont_Menu:          fontID = kThemeSystemFont;         break;
00348           case eSystemFont_MessageBox:    fontID = kThemeSmallSystemFont;    break;
00349           case eSystemFont_SmallCaption:  fontID = kThemeSmallEmphasizedSystemFont;  break;
00350           case eSystemFont_StatusBar:     fontID = kThemeSmallSystemFont;    break;
00351               // css3
00352           //case eSystemFont_Window:      = 'sans-serif'
00353           //case eSystemFont_Document:    = 'sans-serif'
00354           case eSystemFont_Workspace:     fontID = kThemeViewsFont;          break;
00355           case eSystemFont_Desktop:       fontID = kThemeViewsFont;          break;
00356           case eSystemFont_Info:          fontID = kThemeViewsFont;          break;
00357           case eSystemFont_Dialog:        fontID = kThemeSystemFont;         break;
00358           case eSystemFont_Button:        fontID = kThemePushButtonFont;     break;
00359           case eSystemFont_PullDownMenu:  fontID = kThemeMenuItemFont;       break;
00360           case eSystemFont_List:          fontID = kThemeSystemFont;         break;
00361           case eSystemFont_Field:         fontID = kThemeApplicationFont;    break;
00362               // moz
00363           case eSystemFont_Tooltips:      fontID = kThemeSmallSystemFont;    break;
00364           case eSystemFont_Widget:        fontID = kThemeSmallSystemFont;    break;
00365         }
00366 
00367         nsAutoString fontName;
00368         SInt16 fontSize;
00369         Style fontStyle;
00370 
00371         ScriptCode sysScript = ::GetScriptManagerVariable (smSysScript);
00372         nsresult rv;
00373         rv = GetSystemFontForScript(fontID, smRoman,
00374                                     fontName, fontSize, fontStyle);
00375         if (NS_FAILED(rv))
00376           fontName = NS_LITERAL_STRING("Lucida Grande");
00377 
00378         if (sysScript != smRoman) {
00379           SInt16 localFontSize;
00380           Style localFontStyle;
00381           nsAutoString localSysFontName;
00382           rv = GetSystemFontForScript(fontID, sysScript,
00383                                       localSysFontName,
00384                                       localFontSize, localFontStyle);
00385           if (NS_SUCCEEDED(rv) && !fontName.Equals(localSysFontName)) {
00386             fontName += NS_LITERAL_STRING(",") + localSysFontName;
00387             fontSize = localFontSize;
00388             fontStyle = localFontStyle;
00389           }
00390         }
00391         aFont->name = fontName;        
00392         aFont->size = NSToCoordRound(float(fontSize) * dev2app);
00393 
00394         if (fontStyle & bold)
00395           aFont->weight = NS_FONT_WEIGHT_BOLD;
00396         if (fontStyle & italic)
00397           aFont->style = NS_FONT_STYLE_ITALIC;
00398         if (fontStyle & underline)
00399           aFont->decorations = NS_FONT_DECORATION_UNDERLINE;
00400       }
00401       else
00402       {
00403         aFont->name.AssignLiteral("geneva");
00404       }
00405       break;
00406 
00407   }
00408 
00409   aFont->systemFont = PR_TRUE;
00410 
00411   return status;
00412 }
00413 
00414 
00419 NS_IMETHODIMP nsDeviceContextMac::GetDepth(PRUint32& aDepth)
00420 {
00421   /*
00422   nsCOMPtr<nsIScreen> screen;
00423   FindScreenForSurface ( getter_AddRefs(screen) );
00424   if ( screen ) {
00425     PRInt32 depth;
00426     screen->GetPixelDepth ( &depth );
00427     aDepth = NS_STATIC_CAST ( PRUint32, depth );
00428   }
00429   else 
00430     aDepth = 1;
00431   */
00432   
00433   // The above seems correct, however, because of the way Mozilla 
00434   // rendering is set upQuickDraw will get confused when
00435   // blitting to a secondary screen with a different bit depth.
00436   // By always returning the bit depth of the primary screen, QD
00437   // can do the proper color mappings.
00438    
00439   if ( !mPrimaryScreen && mScreenManager )
00440     mScreenManager->GetPrimaryScreen ( getter_AddRefs(mPrimaryScreen) );  
00441     
00442   if(!mPrimaryScreen) {
00443     aDepth = 1;
00444     return NS_OK;
00445   }
00446    
00447   PRInt32 depth;
00448   mPrimaryScreen->GetPixelDepth ( &depth );
00449   aDepth = NS_STATIC_CAST ( PRUint32, depth );
00450     
00451   return NS_OK;
00452 }
00453 
00458 NS_IMETHODIMP nsDeviceContextMac :: CheckFontExistence(const nsString& aFontName)
00459 {
00460        short fontNum;
00461        if (GetMacFontNumber(aFontName, fontNum))
00462               return NS_OK;
00463        else
00464               return NS_ERROR_FAILURE;
00465 }
00466 
00467 
00468 //
00469 // FindScreenForSurface
00470 //
00471 // Determines which screen intersects the largest area of the given surface.
00472 //
00473 void
00474 nsDeviceContextMac :: FindScreenForSurface ( nsIScreen** outScreen )
00475 {
00476   // optimize for the case where we only have one monitor.
00477   if ( !mPrimaryScreen && mScreenManager )
00478     mScreenManager->GetPrimaryScreen ( getter_AddRefs(mPrimaryScreen) );  
00479   if ( sNumberOfScreens == 1 ) {
00480     NS_IF_ADDREF(*outScreen = mPrimaryScreen.get());
00481     return;
00482   }
00483   
00484   nsIWidget* widget = reinterpret_cast<nsIWidget*>(mWidget);      // PRAY!
00485   NS_ASSERTION ( widget, "No Widget --> No Window" );
00486   if ( !widget ) {
00487     NS_IF_ADDREF(*outScreen = mPrimaryScreen.get());              // bail out with the main screen just to be safe.
00488     return;
00489   }
00490 
00491 #if !MOZ_WIDGET_COCOA
00492   // we have a widget stashed inside, get a native WindowRef out of it
00493        WindowRef window = reinterpret_cast<WindowRef>(widget->GetNativeData(NS_NATIVE_DISPLAY));
00494 
00495   StPortSetter  setter(window);
00496 
00497   Rect bounds;
00498   ::GetWindowPortBounds ( window, &bounds );
00499 
00500   nsresult rv = NS_OK;
00501   if ( mScreenManager ) {
00502     if ( !(bounds.top || bounds.left || bounds.bottom || bounds.right) ) {
00503       NS_WARNING ( "trying to find screen for sizeless window" );
00504       NS_IF_ADDREF(*outScreen = mPrimaryScreen.get());
00505     }
00506     else {
00507       // convert window bounds to global coordinates
00508       Point topLeft = { bounds.top, bounds.left };
00509       Point bottomRight = { bounds.bottom, bounds.right };
00510       ::LocalToGlobal ( &topLeft );
00511       ::LocalToGlobal ( &bottomRight );
00512       Rect globalWindowBounds = { topLeft.v, topLeft.h, bottomRight.v, bottomRight.h } ;
00513       
00514       mScreenManager->ScreenForRect ( globalWindowBounds.left, globalWindowBounds.top, 
00515                                        globalWindowBounds.bottom - globalWindowBounds.top, 
00516                                        globalWindowBounds.right - globalWindowBounds.left, outScreen );
00517     }
00518   }
00519   else
00520     *outScreen = nsnull;
00521 #else
00522   // in cocoa, we don't have a windowPtr! bail out with the main screen
00523   NS_IF_ADDREF(*outScreen = mPrimaryScreen.get());
00524 #endif
00525   
00526 } // FindScreenForSurface
00527 
00528 
00533 NS_IMETHODIMP nsDeviceContextMac::GetDeviceSurfaceDimensions(PRInt32 & outWidth, PRInt32 & outHeight)
00534 {
00535 #ifdef NS_PRINT_PREVIEW
00536   // Defer to Alt when there is one
00537   if (mAltDC && (mUseAltDC & kUseAltDCFor_SURFACE_DIM)) {
00538     return mAltDC->GetDeviceSurfaceDimensions(outWidth, outHeight);
00539   }
00540 #endif
00541 
00542        if( mSpec ) {
00543          // we have a printer device
00544               outWidth = (mPageRect.right-mPageRect.left)*mDevUnitsToAppUnits;
00545               outHeight = (mPageRect.bottom-mPageRect.top)*mDevUnitsToAppUnits;
00546        }
00547        else {
00548     // we have a screen device. find the screen that the window is on and
00549     // return its dimensions.
00550     nsCOMPtr<nsIScreen> screen;
00551     FindScreenForSurface ( getter_AddRefs(screen) );
00552     if ( screen ) {     
00553       PRInt32 width, height, ignored;
00554       screen->GetRect ( &ignored, &ignored, &width, &height );
00555       
00556       outWidth =  NSToIntRound(width * mDevUnitsToAppUnits);
00557       outHeight =  NSToIntRound(height * mDevUnitsToAppUnits);
00558          }
00559          else {
00560            NS_WARNING ( "No screen for this surface. How odd" );
00561            outHeight = 0;
00562            outWidth = 0;
00563          }
00564        }
00565        
00566        return NS_OK; 
00567 }
00568 
00569 
00573 NS_IMETHODIMP
00574 nsDeviceContextMac::GetRect(nsRect &aRect)
00575 {
00576        if( mSpec ) {
00577          // we have a printer device
00578          aRect.x = 0;
00579          aRect.y = 0;
00580               aRect.width = (mPageRect.right-mPageRect.left)*mDevUnitsToAppUnits;
00581               aRect.height = (mPageRect.bottom-mPageRect.top)*mDevUnitsToAppUnits;
00582        }
00583        else {
00584     // we have a screen device. find the screen that the window is on and
00585     // return its top/left coordinates.
00586     nsCOMPtr<nsIScreen> screen;
00587     FindScreenForSurface ( getter_AddRefs(screen) );
00588     if ( screen ) {
00589       PRInt32 x, y, width, height;
00590       screen->GetRect ( &x, &y, &width, &height );
00591       
00592       aRect.y =  NSToIntRound(y * mDevUnitsToAppUnits);
00593       aRect.x =  NSToIntRound(x * mDevUnitsToAppUnits);
00594       aRect.width =  NSToIntRound(width * mDevUnitsToAppUnits);
00595       aRect.height =  NSToIntRound(height * mDevUnitsToAppUnits);
00596          }
00597          else {
00598            NS_WARNING ( "No screen for this surface. How odd" );
00599            aRect.x = aRect.y = aRect.width = aRect.height = 0;
00600          }
00601        }
00602 
00603   return NS_OK;
00604   
00605 } // GetDeviceTopLeft
00606 
00607 
00611 NS_IMETHODIMP nsDeviceContextMac::GetClientRect(nsRect &aRect)
00612 {
00613        if( mSpec ) {
00614          // we have a printer device
00615          aRect.x = aRect.y = 0;
00616               aRect.width = (mPageRect.right-mPageRect.left)*mDevUnitsToAppUnits;
00617               aRect.height = (mPageRect.bottom-mPageRect.top)*mDevUnitsToAppUnits;
00618        }
00619        else {
00620     // we have a screen device. find the screen that the window is on and
00621     // return its dimensions.
00622     nsCOMPtr<nsIScreen> screen;
00623     FindScreenForSurface ( getter_AddRefs(screen) );
00624     if ( screen ) {      
00625       PRInt32 x, y, width, height;
00626       screen->GetAvailRect ( &x, &y, &width, &height );
00627       
00628       aRect.y =  NSToIntRound(y * mDevUnitsToAppUnits);
00629       aRect.x =  NSToIntRound(x * mDevUnitsToAppUnits);
00630       aRect.width =  NSToIntRound(width * mDevUnitsToAppUnits);
00631       aRect.height =  NSToIntRound(height * mDevUnitsToAppUnits);
00632          }
00633          else {
00634            NS_WARNING ( "No screen for this surface. How odd" );
00635            aRect.x = aRect.y = aRect.width = aRect.height = 0;
00636          }
00637        }
00638        
00639        return NS_OK; 
00640 }
00641 
00642 
00643 #pragma mark -
00644 
00645 
00646 //------------------------------------------------------------------------
00647 
00648 NS_IMETHODIMP nsDeviceContextMac::GetDeviceContextFor(nsIDeviceContextSpec *aDevice,nsIDeviceContext *&aContext)
00649 {
00650     GrafPtr curPort; 
00651     double pix_Inch;
00652     nsDeviceContextMac *macDC;
00653 
00654        aContext = new nsDeviceContextMac();
00655   if(nsnull == aContext){
00656     return NS_ERROR_OUT_OF_MEMORY;
00657   }
00658   NS_ADDREF(aContext);
00659 
00660        macDC = (nsDeviceContextMac*)aContext;
00661        macDC->mSpec = aDevice;
00662        
00663        ::GetPort(&curPort);
00664 
00665     nsCOMPtr<nsIPrintingContext> printingContext = do_QueryInterface(aDevice);
00666     if (printingContext) {
00667         if (NS_FAILED(printingContext->GetPrinterResolution(&pix_Inch)))
00668             pix_Inch = 72.0;
00669         double top, left, bottom, right;
00670         printingContext->GetPageRect(&top, &left, &bottom, &right);
00671         Rect& pageRect = macDC->mPageRect;
00672         pageRect.top = top, pageRect.left = left;
00673         pageRect.bottom = bottom, pageRect.right = right;
00674     }
00675 
00676 
00677        ((nsDeviceContextMac*)aContext)->Init(curPort);
00678 
00679        macDC->mTwipsToPixels = pix_Inch/(float)NSIntPointsToTwips(72);
00680        macDC->mPixelsToTwips = 1.0f/macDC->mTwipsToPixels;
00681     macDC->mAppUnitsToDevUnits = macDC->mTwipsToPixels;
00682     macDC->mDevUnitsToAppUnits = 1.0f / macDC->mAppUnitsToDevUnits;
00683   
00684     macDC->mCPixelScale = macDC->mTwipsToPixels / mTwipsToPixels;
00685 
00686        //((nsDeviceContextMac*)aContext)->Init(this);
00687     return NS_OK;
00688 }
00689 
00690 
00695 NS_IMETHODIMP nsDeviceContextMac::BeginDocument(PRUnichar * aTitle, 
00696                                                 PRUnichar*  aPrintToFileName,
00697                                                 PRInt32     aStartPage, 
00698                                                 PRInt32     aEndPage)
00699 {
00700     nsresult rv = NS_ERROR_FAILURE;
00701     nsCOMPtr<nsIPrintingContext> printingContext = do_QueryInterface(mSpec);
00702     if (printingContext)
00703         rv = printingContext->BeginDocument(aTitle, aPrintToFileName, aStartPage, aEndPage);
00704     return rv;
00705 }
00706 
00707 
00712 NS_IMETHODIMP nsDeviceContextMac::EndDocument(void)
00713 {
00714     nsresult rv = NS_ERROR_FAILURE;
00715     nsCOMPtr<nsIPrintingContext> printingContext = do_QueryInterface(mSpec);
00716     if (printingContext)
00717         rv = printingContext->EndDocument();
00718     return rv;
00719 }
00720 
00725 NS_IMETHODIMP nsDeviceContextMac::AbortDocument(void)
00726 {
00727     return EndDocument();
00728 }
00729 
00730 
00735 NS_IMETHODIMP nsDeviceContextMac::BeginPage(void)
00736 {
00737     nsresult rv = NS_ERROR_FAILURE;
00738     nsCOMPtr<nsIPrintingContext> printingContext = do_QueryInterface(mSpec);
00739     if (printingContext)
00740         rv = printingContext->BeginPage();
00741     return rv;
00742 }
00743 
00744 
00749 NS_IMETHODIMP nsDeviceContextMac::EndPage(void)
00750 {
00751     nsresult rv = NS_ERROR_FAILURE;
00752     nsCOMPtr<nsIPrintingContext> printingContext = do_QueryInterface(mSpec);
00753     if (printingContext)
00754         rv = printingContext->EndPage();
00755     return rv;
00756 }
00757 
00758 
00759 #pragma mark -
00760 
00761 //------------------------------------------------------------------------
00762 
00763 nsHashtable* nsDeviceContextMac :: gFontInfoList = nsnull;
00764 
00765 class FontNameKey : public nsHashKey
00766 {
00767 public:
00768   FontNameKey(const nsString& aString);
00769 
00770   virtual PRUint32 HashCode(void) const;
00771   virtual PRBool Equals(const nsHashKey *aKey) const;
00772   virtual nsHashKey *Clone(void) const;
00773 
00774   nsAutoString  mString;
00775 };
00776 
00777 FontNameKey::FontNameKey(const nsString& aString)
00778 {
00779        mString.Assign(aString);
00780 }
00781 
00782 PRUint32 FontNameKey::HashCode(void) const
00783 {
00784   nsString str;
00785   ToLowerCase(mString, str);
00786   return nsCRT::HashCode(str.get());
00787 }
00788 
00789 PRBool FontNameKey::Equals(const nsHashKey *aKey) const
00790 {
00791   return mString.Equals(((FontNameKey*)aKey)->mString,
00792                         nsCaseInsensitiveStringComparator());
00793 }
00794 
00795 nsHashKey* FontNameKey::Clone(void) const
00796 {
00797   return new FontNameKey(mString);
00798 }
00799 
00800 #pragma mark -
00801 
00806 void nsDeviceContextMac :: InitFontInfoList()
00807 {
00808 
00809        OSStatus err;
00810        if (!gFontInfoList) 
00811        {
00812               gFontInfoList = new nsHashtable();
00813               if (!gFontInfoList)
00814                      return;
00815 
00816         // use the new Font Manager enumeration API.
00817         ATSFontFamilyIterator iter;
00818         err = ::ATSFontFamilyIteratorCreate(kATSFontContextLocal,
00819                      NULL, NULL, // filter and its refcon
00820                      kATSOptionFlagsDefaultScope,
00821                      &iter);
00822         if (err != noErr)
00823             return;
00824         
00825               TextEncoding unicodeEncoding = ::CreateTextEncoding(kTextEncodingUnicodeDefault, 
00826                                                                                                          kTextEncodingDefaultVariant,
00827                                                                                                          kUnicodeUTF8Format);
00828         // enumerate all fonts.
00829         TECObjectRef converter = NULL;
00830         TextEncoding oldFontEncoding = 0;
00831         ATSFontFamilyRef fontFamily;
00832         while (::ATSFontFamilyIteratorNext(iter, &fontFamily) == noErr) {
00833                   // we'd like to use ATSFontFamilyGetName here, but it's ignorant of the
00834         // font encodings, resulting in garbage names for non-western fonts.
00835             Str255 fontName;
00836             err = ::ATSFontFamilyGetQuickDrawName(fontFamily, fontName);
00837             if (err != noErr || fontName[0] == 0 || fontName[1] == '.' || fontName[1] == '%')
00838                 continue;
00839             TextEncoding fontEncoding;
00840             fontEncoding = ::ATSFontFamilyGetEncoding(fontFamily);
00841             if (oldFontEncoding != fontEncoding) {
00842                 oldFontEncoding = fontEncoding;
00843                 if (converter)
00844                     err = ::TECDisposeConverter(converter);
00845                 err = ::TECCreateConverter(&converter, fontEncoding, unicodeEncoding);
00846                 if (err != noErr)
00847                     continue;
00848             }
00849             // convert font name to UNICODE.
00850                      char unicodeFontName[sizeof(fontName)];
00851                      ByteCount actualInputLength, actualOutputLength;
00852                      err = ::TECConvertText(converter, &fontName[1], fontName[0], &actualInputLength, 
00853                                                                       (TextPtr)unicodeFontName , sizeof(unicodeFontName), &actualOutputLength);    
00854                      unicodeFontName[actualOutputLength] = 0;
00855 
00856                      nsString temp = NS_ConvertUTF8toUTF16(nsDependentCString(unicodeFontName));
00857               FontNameKey key(temp);
00858                      gFontInfoList->Put(&key, (void*)::FMGetFontFamilyFromATSFontFamilyRef(fontFamily));
00859         }
00860         if (converter)
00861             err = ::TECDisposeConverter(converter);
00862         err = ::ATSFontFamilyIteratorRelease(&iter);
00863        }
00864 }
00865 
00866 
00867 
00872 bool nsDeviceContextMac :: GetMacFontNumber(const nsString& aFontName, short &aFontNum)
00873 {
00874        //¥TODO?: Maybe we shouldn't call that function so often. If nsFont could store the
00875        //                          fontNum, nsFontMetricsMac::SetFont() wouldn't need to call this at all.
00876        InitFontInfoList();
00877     FontNameKey key(aFontName);
00878        aFontNum = (short) NS_PTR_TO_INT32(gFontInfoList->Get(&key));
00879        return (aFontNum != 0);
00880 }
00881 
00882 
00883 //------------------------------------------------------------------------
00884 // Override to tweak font settings
00885 nsresult nsDeviceContextMac::CreateFontAliasTable()
00886 {
00887   nsresult result = NS_OK;
00888 
00889   if (nsnull == mFontAliasTable) {
00890     mFontAliasTable = new nsHashtable();
00891     if (nsnull != mFontAliasTable)
00892     {
00893                      nsAutoString  fontTimes;              fontTimes.AssignLiteral("Times");
00894                      nsAutoString  fontTimesNewRoman;      fontTimesNewRoman.AssignLiteral("Times New Roman");
00895                      nsAutoString  fontTimesRoman;         fontTimesRoman.AssignLiteral("Times Roman");
00896                      nsAutoString  fontArial;              fontArial.AssignLiteral("Arial");
00897                      nsAutoString  fontHelvetica;          fontHelvetica.AssignLiteral("Helvetica");
00898                      nsAutoString  fontCourier;            fontCourier.AssignLiteral("Courier");
00899                      nsAutoString  fontCourierNew;         fontCourierNew.AssignLiteral("Courier New");
00900                      nsAutoString  fontUnicode;            fontUnicode.AssignLiteral("Unicode");
00901                      nsAutoString  fontBitstreamCyberbit;  fontBitstreamCyberbit.AssignLiteral("Bitstream Cyberbit");
00902                      nsAutoString  fontNullStr;
00903 
00904       AliasFont(fontTimes, fontTimesNewRoman, fontTimesRoman, PR_FALSE);
00905       AliasFont(fontTimesRoman, fontTimesNewRoman, fontTimes, PR_FALSE);
00906       AliasFont(fontTimesNewRoman, fontTimesRoman, fontTimes, PR_FALSE);
00907       AliasFont(fontArial, fontHelvetica, fontNullStr, PR_FALSE);
00908       AliasFont(fontHelvetica, fontArial, fontNullStr, PR_FALSE);
00909       AliasFont(fontCourier, fontCourierNew, fontNullStr, PR_FALSE);         // changed from DeviceContextImpl
00910       AliasFont(fontCourierNew, fontCourier, fontNullStr, PR_FALSE);
00911       AliasFont(fontUnicode, fontBitstreamCyberbit, fontNullStr, PR_FALSE); // XXX ????
00912     }
00913     else {
00914       result = NS_ERROR_OUT_OF_MEMORY;
00915     }
00916   }
00917   return result;
00918 }
00919 
00920 #pragma mark -
00921 
00922 //------------------------------------------------------------------------
00923 //
00924 
00925 static NS_DEFINE_CID(kPrefCID, NS_PREF_CID);
00926 
00931 PRUint32 nsDeviceContextMac::GetScreenResolution()
00932 {
00933        static PRBool initialized = PR_FALSE;
00934        if (initialized)
00935               return mPixelsPerInch;
00936        initialized = PR_TRUE;
00937 
00938     nsresult rv;
00939     nsCOMPtr<nsIPref> prefs(do_GetService(kPrefCID, &rv));
00940     if (NS_SUCCEEDED(rv) && prefs) {
00941               PRInt32 intVal;
00942               if (NS_SUCCEEDED(prefs->GetIntPref("layout.css.dpi", &intVal)) && intVal > 0) {
00943                      mPixelsPerInch = intVal;
00944               }
00945 #if 0
00946 // the code here will ignore the default setting of 96dpi and
00947 // instead force approximately 84dpi. There's no real reason for this
00948 // and we shipped Camino0.7 with 96dpi and got no complaints. As
00949 // a result, I'm removing it, but leaving the code for posterity.
00950               else {
00951                      short hppi, vppi;
00952                      ::ScreenRes(&hppi, &vppi);
00953                      mPixelsPerInch = hppi * 1.17f;
00954               }
00955 #endif
00956        }
00957 
00958        return mPixelsPerInch;
00959 }
00960 
00961 
00962 #pragma mark -
00963 //------------------------------------------------------------------------
00964 nsFontEnumeratorMac::nsFontEnumeratorMac()
00965 {
00966 }
00967 
00968 NS_IMPL_ISUPPORTS1(nsFontEnumeratorMac, nsIFontEnumerator)
00969 
00970 typedef struct EnumerateFamilyInfo
00971 {
00972   PRUnichar** mArray;
00973   int         mIndex;
00974 } EnumerateFamilyInfo;
00975 
00976 typedef struct EnumerateFontInfo
00977 {
00978   PRUnichar** mArray;
00979   int         mIndex;
00980   int         mCount;
00981   ScriptCode  mScript;
00982   nsGenericFontNameType mType;
00983 } EnumerateFontInfo;
00984 
00985 
00986 
00987 static int
00988 CompareFontNames(const void* aArg1, const void* aArg2, void* aClosure)
00989 {
00990   const PRUnichar* str1 = *((const PRUnichar**) aArg1);
00991   const PRUnichar* str2 = *((const PRUnichar**) aArg2);
00992 
00993   // XXX add nsICollation stuff
00994 
00995   return nsCRT::strcmp(str1, str2);
00996 }
00997 static PRBool
00998 EnumerateFamily(nsHashKey *aKey, void *aData, void* closure)
00999 
01000 {
01001   EnumerateFamilyInfo* info = (EnumerateFamilyInfo*) closure;
01002   PRUnichar** array = info->mArray;
01003   int j = info->mIndex;
01004   
01005   
01006   PRUnichar* str = ToNewUnicode(((FontNameKey*)aKey)->mString);
01007   if (!str) {
01008     for (j = j - 1; j >= 0; j--) {
01009       nsMemory::Free(array[j]);
01010     }
01011     info->mIndex = 0;
01012     return PR_FALSE;
01013   }
01014   array[j] = str;
01015   info->mIndex++;
01016 
01017   return PR_TRUE;
01018 }
01019 
01020 NS_IMETHODIMP
01021 nsFontEnumeratorMac::EnumerateAllFonts(PRUint32* aCount, PRUnichar*** aResult)
01022 {
01023   if (aCount) {
01024     *aCount = 0;
01025   }
01026   else {
01027     return NS_ERROR_NULL_POINTER;
01028   }
01029   if (aResult) {
01030     *aResult = nsnull;
01031   }
01032   else {
01033     return NS_ERROR_NULL_POINTER;
01034   }
01035 
01036        nsDeviceContextMac::InitFontInfoList();
01037        nsHashtable* list = nsDeviceContextMac::gFontInfoList;
01038        if(!list) {
01039               return NS_ERROR_FAILURE;
01040        }
01041        PRInt32 items = list->Count();
01042   PRUnichar** array = (PRUnichar**)
01043     nsMemory::Alloc(items * sizeof(PRUnichar*));
01044   if (!array) {
01045     return NS_ERROR_OUT_OF_MEMORY;
01046   }
01047   EnumerateFamilyInfo info = { array, 0 };
01048   list->Enumerate ( EnumerateFamily, &info);
01049   NS_ASSERTION( items == info.mIndex, "didn't get all the fonts");
01050   if (!info.mIndex) {
01051     nsMemory::Free(array);
01052     return NS_ERROR_OUT_OF_MEMORY;
01053   }
01054 
01055   NS_QuickSort(array, info.mIndex, sizeof(PRUnichar*),
01056     CompareFontNames, nsnull);
01057 
01058   *aCount = info.mIndex;
01059   *aResult = array;
01060 
01061   return NS_OK;
01062 }
01063 
01064 static PRBool
01065 EnumerateFont(nsHashKey *aKey, void *aData, void* closure)
01066 
01067 {
01068   EnumerateFontInfo* info = (EnumerateFontInfo*) closure;
01069   PRUnichar** array = info->mArray;
01070   int j = info->mCount;
01071   PRBool match = PR_FALSE;
01072 
01073   // we need to match the cast of FMFontFamily in nsDeviceContextMac :: InitFontInfoList()
01074   FMFontFamily fontFamily = (FMFontFamily) NS_PTR_TO_INT32(aData);
01075   TextEncoding fontEncoding;
01076   OSStatus status = ::FMGetFontFamilyTextEncoding(fontFamily, &fontEncoding);
01077   if (noErr == status) {
01078     ScriptCode script;
01079     status = ::RevertTextEncodingToScriptInfo(fontEncoding, &script, nsnull, nsnull);
01080     match = ((noErr == status) && (script == info->mScript));
01081   }
01082 
01083   if (match) {
01084          PRUnichar* str = ToNewUnicode(((FontNameKey*)aKey)->mString);
01085          if (!str) {
01086            for (j = j - 1; j >= 0; j--) {
01087              nsMemory::Free(array[j]);
01088            }
01089            info->mIndex = 0;
01090            return PR_FALSE;
01091          }
01092          array[j] = str;
01093          info->mCount++;
01094        }
01095        info->mIndex++;
01096   return PR_TRUE;
01097 }
01098 NS_IMETHODIMP
01099 nsFontEnumeratorMac::EnumerateFonts(const char* aLangGroup,
01100   const char* aGeneric, PRUint32* aCount, PRUnichar*** aResult)
01101 {
01102   if ((! aLangGroup) ||( !aGeneric ))
01103        return NS_ERROR_NULL_POINTER;
01104        
01105   if (aCount) {
01106     *aCount = 0;
01107   }
01108   else {
01109     return NS_ERROR_NULL_POINTER;
01110   }
01111   if (aResult) {
01112     *aResult = nsnull;
01113   }
01114   else {
01115     return NS_ERROR_NULL_POINTER;
01116   }
01117 
01118   if ((!strcmp(aLangGroup, "x-unicode")) ||
01119       (!strcmp(aLangGroup, "x-user-def"))) {
01120     return EnumerateAllFonts(aCount, aResult);
01121   }
01122 
01123        nsDeviceContextMac::InitFontInfoList();
01124        nsHashtable* list = nsDeviceContextMac::gFontInfoList;
01125        if(!list) {
01126               return NS_ERROR_FAILURE;
01127        }
01128        PRInt32 items = list->Count();
01129   PRUnichar** array = (PRUnichar**)
01130     nsMemory::Alloc(items * sizeof(PRUnichar*));
01131   if (!array) {
01132     return NS_ERROR_OUT_OF_MEMORY;
01133   }
01134   nsUnicodeMappingUtil* gUtil = nsUnicodeMappingUtil::GetSingleton();
01135        if(!gUtil) {
01136               return NS_ERROR_FAILURE;
01137        }
01138   
01139   nsAutoString GenName; GenName.AssignWithConversion(aGeneric);
01140   EnumerateFontInfo info = { array, 0 , 0, gUtil->MapLangGroupToScriptCode(aLangGroup) ,gUtil->MapGenericFontNameType(GenName) };
01141   list->Enumerate ( EnumerateFont, &info);
01142   if (!info.mIndex) {
01143     nsMemory::Free(array);
01144     return NS_ERROR_OUT_OF_MEMORY;
01145   }
01146 
01147   NS_QuickSort(array, info.mCount, sizeof(PRUnichar*),
01148     CompareFontNames, nsnull);
01149 
01150   *aCount = info.mCount;
01151   *aResult = array;
01152 
01153   return NS_OK;
01154 }
01155 NS_IMETHODIMP
01156 nsFontEnumeratorMac::HaveFontFor(const char* aLangGroup,PRBool* aResult)
01157 {
01158   NS_ENSURE_ARG_POINTER(aLangGroup);
01159   NS_ENSURE_ARG_POINTER(aResult);
01160   *aResult = PR_FALSE;
01161   PRUint32 count;
01162   PRUnichar **ptr;
01163   nsresult res = EnumerateFonts(aLangGroup, "", &count, &ptr);
01164   NS_ENSURE_SUCCESS(res, res);
01165   *aResult = (count > 0);
01166   PRUint32 i;
01167   for(i = 0 ; i < count; i++)
01168        nsMemory::Free(ptr[i]);
01169   nsMemory::Free(ptr);
01170   return NS_OK;
01171 }
01172 
01173 NS_IMETHODIMP
01174 nsFontEnumeratorMac::GetDefaultFont(const char *aLangGroup, 
01175   const char *aGeneric, PRUnichar **aResult)
01176 {
01177   // aLangGroup=null or ""  means any (i.e., don't care)
01178   // aGeneric=null or ""  means any (i.e, don't care)
01179 
01180   NS_ENSURE_ARG_POINTER(aResult);
01181   *aResult = nsnull;
01182 
01183   return NS_OK;
01184 }
01185 
01186 NS_IMETHODIMP
01187 nsFontEnumeratorMac::UpdateFontList(PRBool *updateFontList)
01188 {
01189   *updateFontList = PR_FALSE; // always return false for now
01190   return NS_OK;
01191 }