/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Lars Knoll * Zack Rusin * John C. Griggs * Esben Mose Hansen * Roland Mainz * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include #include "nspr.h" #include "nsIPrefBranch.h" #include "nsIPrefService.h" #include "nsIPrefBranch2.h" #include "nsIServiceManager.h" #include "nsCRT.h" #include "nsDeviceContextQt.h" #include "nsFontMetricsQt.h" #include "nsFont.h" #include "nsGfxCIID.h" #include "nsRenderingContextQt.h" #include "nsDeviceContextSpecQt.h" #ifdef USE_POSTSCRIPT #include "nsGfxPSCID.h" #include "nsIDeviceContextPS.h" #endif /* USE_POSTSCRIPT */ #ifdef USE_XPRINT #include "nsGfxXPrintCID.h" #include "nsIDeviceContextXPrint.h" #endif /* USE_XPRINT */ #include #include #include #include #include #include #include #include #include "nsIScreenManager.h" #include "qtlog.h" #define QCOLOR_TO_NS_RGB(c) \ ((nscolor)NS_RGB(c.red(),c.green(),c.blue())) nscoord nsDeviceContextQt::mDpi = 0; nsDeviceContextQt::nsDeviceContextQt() : DeviceContextImpl() { mTwipsToPixels = 1.0; mPixelsToTwips = 1.0; mDepth = 0 ; mWidthFloat = 0.0f; mHeightFloat = 0.0f; mWidth = -1; mHeight = -1; } nsDeviceContextQt::~nsDeviceContextQt() { nsCOMPtr pbi = do_GetService(NS_PREFSERVICE_CONTRACTID); if (pbi) { pbi->RemoveObserver("layout.css.dpi", this); } } NS_IMETHODIMP nsDeviceContextQt::Init(nsNativeWidget aNativeWidget) { PRBool bCleanUp = PR_FALSE; QWidget *pWidget = nsnull; mWidget = (QWidget*)aNativeWidget; if (mWidget != nsnull) pWidget = mWidget; else { pWidget = new QWidget(); bCleanUp = PR_TRUE; } nsresult ignore; nsCOMPtr sm(do_GetService("@mozilla.org/gfx/screenmanager;1", &ignore)); if (sm) { nsCOMPtr screen; sm->GetPrimaryScreen(getter_AddRefs(screen)); if (screen) { PRInt32 x,y,width,height,depth; screen->GetAvailRect(&x,&y,&width,&height); screen->GetPixelDepth(&depth); mWidthFloat = float(width); mHeightFloat = float(height); mDepth = NS_STATIC_CAST(PRUint32,depth); } } if (!mDpi) { // Set prefVal the value of the preference "layout.css.dpi" // or -1 if we can't get it. // If it's negative, we pretend it's not set. // If it's 0, it means force use of the operating system's logical resolution. // If it's positive, we use it as the logical resolution PRInt32 prefVal = -1; nsCOMPtr prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID)); if (prefBranch) { nsresult res = prefBranch->GetIntPref("layout.css.dpi", &prefVal); if (NS_FAILED(res)) { prefVal = -1; } nsCOMPtr pbi(do_QueryInterface(prefBranch)); pbi->AddObserver("layout.css.dpi", this, PR_FALSE); } SetDPI(prefVal); } else { SetDPI(mDpi); } #ifdef MOZ_LOGGING static PRBool once = PR_TRUE; if (once) { PR_LOG(gQtLogModule, QT_BASIC, ("GFX: dpi=%d t2p=%g p2t=%g depth=%d\n", mDpi,mTwipsToPixels,mPixelsToTwips,mDepth)); once = PR_FALSE; } #endif QStyle &style = pWidget->style(); mScrollbarWidth = mScrollbarHeight = style.pixelMetric(QStyle::PM_ScrollBarExtent); DeviceContextImpl::CommonInit(); if (bCleanUp) delete pWidget; return NS_OK; } NS_IMETHODIMP nsDeviceContextQt::CreateRenderingContext(nsIRenderingContext *&aContext) { nsresult rv; nsDrawingSurfaceQt *surf; QPaintDevice *pDev = nsnull; if (mWidget) pDev = (QPaintDevice*)mWidget; // to call init for this, we need to have a valid nsDrawingSurfaceQt created nsCOMPtr pContext( new nsRenderingContextQt() ); // create the nsDrawingSurfaceQt surf = new nsDrawingSurfaceQt(); if (surf) { //Handled by the nsDrawingSurfaceQt //FIXME: instead of passing it around // create it in the nsDrawingSurface init method QPainter *gc = new QPainter(); // init the nsDrawingSurfaceQt if (pDev) rv = surf->Init(pDev,gc); else rv = surf->Init(gc,10,10,0); if (NS_SUCCEEDED(rv)) // Init the nsRenderingContextQt rv = pContext->Init(this,surf); } else rv = NS_ERROR_OUT_OF_MEMORY; if (NS_SUCCEEDED(rv)) { aContext = pContext; NS_ADDREF(aContext); } return rv; } NS_IMETHODIMP nsDeviceContextQt::CreateRenderingContextInstance(nsIRenderingContext *&aContext) { return CreateRenderingContext(aContext); } NS_IMETHODIMP nsDeviceContextQt::SupportsNativeWidgets(PRBool &aSupportsWidgets) { //XXX it is very critical that this not lie!! MMP // read the comments in the mac code for this // ############## aSupportsWidgets = PR_TRUE; return NS_OK; } NS_IMETHODIMP nsDeviceContextQt::GetScrollBarDimensions(float &aWidth, float &aHeight) const { float scale; GetCanonicalPixelScale(scale); aWidth = mScrollbarWidth * mPixelsToTwips * scale; aHeight = mScrollbarHeight * mPixelsToTwips * scale; return NS_OK; } NS_IMETHODIMP nsDeviceContextQt::GetSystemFont(nsSystemFontID anID, nsFont *aFont) const { nsresult status = NS_OK; switch (anID) { case eSystemFont_Caption: case eSystemFont_Icon: case eSystemFont_Menu: case eSystemFont_MessageBox: case eSystemFont_SmallCaption: case eSystemFont_StatusBar: case eSystemFont_Window: // css3 case eSystemFont_Document: case eSystemFont_Workspace: case eSystemFont_Desktop: case eSystemFont_Info: case eSystemFont_Dialog: case eSystemFont_Button: case eSystemFont_PullDownMenu: case eSystemFont_List: case eSystemFont_Field: case eSystemFont_Tooltips: case eSystemFont_Widget: status = GetSystemFontInfo(aFont); break; } return status; } NS_IMETHODIMP nsDeviceContextQt::CheckFontExistence(const nsString& aFontName) { QString family = QString::fromUcs2(aFontName.get()); QStringList families = QFontDatabase().families(); return families.find(family) != families.end(); } NS_IMETHODIMP nsDeviceContextQt::GetDeviceSurfaceDimensions(PRInt32 &aWidth, PRInt32 &aHeight) { if (-1 == mWidth) mWidth = NSToIntRound(mWidthFloat * mDevUnitsToAppUnits); if (-1 == mHeight) mHeight = NSToIntRound(mHeightFloat * mDevUnitsToAppUnits); aWidth = mWidth; aHeight = mHeight; return NS_OK; } NS_IMETHODIMP nsDeviceContextQt::GetRect(nsRect &aRect) { PRInt32 width,height; nsresult rv; rv = GetDeviceSurfaceDimensions(width,height); aRect.x = 0; aRect.y = 0; aRect.width = width; aRect.height = height; return rv; } NS_IMETHODIMP nsDeviceContextQt::GetClientRect(nsRect &aRect) { return GetRect(aRect); } NS_IMETHODIMP nsDeviceContextQt::GetDeviceContextFor(nsIDeviceContextSpec *aDevice, nsIDeviceContext *&aContext) { nsresult rv; PrintMethod method; nsDeviceContextSpecQt *spec = NS_STATIC_CAST(nsDeviceContextSpecQt *, aDevice); rv = spec->GetPrintMethod(method); if (NS_FAILED(rv)) return rv; #ifdef USE_XPRINT if (method == pmXprint) { // XPRINT static NS_DEFINE_CID(kCDeviceContextXp, NS_DEVICECONTEXTXP_CID); nsCOMPtr dcxp(do_CreateInstance(kCDeviceContextXp, &rv)); NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't create Xp Device context."); if (NS_FAILED(rv)) return rv; rv = dcxp->SetSpec(aDevice); if (NS_FAILED(rv)) return rv; rv = dcxp->InitDeviceContextXP((nsIDeviceContext*)aContext, (nsIDeviceContext*)this); if (NS_FAILED(rv)) return rv; rv = dcxp->QueryInterface(NS_GET_IID(nsIDeviceContext), (void **)&aContext); return rv; } else #endif /* USE_XPRINT */ #ifdef USE_POSTSCRIPT if (method == pmPostScript) { // PostScript // default/PS static NS_DEFINE_CID(kCDeviceContextPS, NS_DEVICECONTEXTPS_CID); // Create a Postscript device context nsCOMPtr dcps(do_CreateInstance(kCDeviceContextPS, &rv)); NS_ASSERTION(NS_SUCCEEDED(rv), "Couldn't create PS Device context."); if (NS_FAILED(rv)) return rv; rv = dcps->SetSpec(aDevice); if (NS_FAILED(rv)) return rv; rv = dcps->InitDeviceContextPS((nsIDeviceContext*)aContext, (nsIDeviceContext*)this); if (NS_FAILED(rv)) return rv; rv = dcps->QueryInterface(NS_GET_IID(nsIDeviceContext), (void **)&aContext); return rv; } #endif /* USE_POSTSCRIPT */ NS_WARNING("no print module created."); return NS_ERROR_UNEXPECTED; } NS_IMETHODIMP nsDeviceContextQt::BeginDocument(PRUnichar * /*aTitle*/, PRUnichar* /*aPrintToFileName*/, PRInt32 /*aStartPage*/, PRInt32 /*aEndPage*/) { return NS_OK; } NS_IMETHODIMP nsDeviceContextQt::EndDocument(void) { return NS_OK; } NS_IMETHODIMP nsDeviceContextQt::BeginPage(void) { return NS_OK; } NS_IMETHODIMP nsDeviceContextQt::EndPage(void) { return NS_OK; } NS_IMETHODIMP nsDeviceContextQt::GetDepth(PRUint32& aDepth) { aDepth = mDepth; return NS_OK; } nsresult nsDeviceContextQt::SetDPI(PRInt32 aDpi) { // Set OSVal to what the operating system thinks the logical resolution is. PRInt32 OSVal = 0; QWidget *pDev = mWidget; if (!pDev) { QWidgetList *wlist = QApplication::allWidgets(); pDev = wlist->first(); qDebug("number of widgets is %d", wlist->count() ); delete wlist; } QPaintDeviceMetrics qPaintMetrics(pDev); OSVal = qPaintMetrics.logicalDpiX(); #ifdef DEBUG if (!pDev) qDebug("nsDeviceContextQt::SetDPI called without widget (find cleaner solution)"); #endif if (aDpi > 0) { // If there's a valid pref value for the logical resolution, // use it. mDpi = aDpi; } else if (aDpi == 0 || OSVal > 96) { // Either if the pref is 0 (force use of OS value) or the OS // value is bigger than 96, use the OS value. mDpi = OSVal; } else { // if we couldn't get the pref or it's negative, and the OS // value is under 96ppi, then use 96. mDpi = 96; } int pt2t = 72; // make p2t a nice round number - this prevents rounding problems mPixelsToTwips = float(NSToIntRound(float(NSIntPointsToTwips(pt2t)) / float(aDpi))); mTwipsToPixels = 1.0f / mPixelsToTwips; // XXX need to reflow all documents return NS_OK; } NS_IMETHODIMP nsDeviceContextQt::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aData) { if (nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) != 0) { // Our local observer only handles pref changes. // Forward everything else to our super class. return DeviceContextImpl::Observe(aSubject, aTopic, aData); } nsCOMPtr prefBranch(do_QueryInterface(aSubject)); NS_ASSERTION(prefBranch, "All pref change observer subjects implement nsIPrefBranch"); nsCAutoString prefName(NS_LossyConvertUCS2toASCII(aData).get()); if (prefName.Equals(NS_LITERAL_CSTRING("layout.css.dpi"))) { PRInt32 dpi; nsresult rv = prefBranch->GetIntPref(prefName.get(), &dpi); if (NS_SUCCEEDED(rv)) SetDPI(dpi); return NS_OK; } else return DeviceContextImpl::Observe(aSubject, aTopic, aData); } nsresult nsDeviceContextQt::GetSystemFontInfo(nsFont* aFont) const { nsresult status = NS_OK; int rawWeight; QFont theFont = QApplication::font(); QFontInfo theFontInfo(theFont); aFont->style = NS_FONT_STYLE_NORMAL; aFont->weight = NS_FONT_WEIGHT_NORMAL; aFont->decorations = NS_FONT_DECORATION_NONE; aFont->name.Assign(theFontInfo.family().ucs2()); if (theFontInfo.bold()) { aFont->weight = NS_FONT_WEIGHT_BOLD; } rawWeight = theFontInfo.pixelSize(); aFont->size = NSIntPixelsToTwips(rawWeight,mPixelsToTwips); if (theFontInfo.italic()) { aFont->style = NS_FONT_STYLE_ITALIC; } if (theFontInfo.underline()) { aFont->decorations = NS_FONT_DECORATION_UNDERLINE; } return (status); }