⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 balloonhelp.cpp

📁 mysee网络直播源代码Mysee Lite是Mysee独立研发的网络视频流媒体播放系统。在应有了P2P技术和一系列先进流媒体技术之后
💻 CPP
📖 第 1 页 / 共 3 页
字号:
{
   m_nMouseMoveTolerance = nTolerance;
}

//
// creates a new balloon window
// Parameters:
//    strTitle    |  Title of balloon
//    strContent  |  Content of balloon
//    ptAnchor    |  point tail of balloon will be "anchor"ed to
//    unOptions   |  One or more of:
//                :     unCLOSE_ON_LBUTTON_UP   |  closes window on WM_LBUTTON_UP
//                :     unCLOSE_ON_RBUTTON_UP   |  closes window on WM_RBUTTON_UP
//                :     unCLOSE_ON_MOUSE_MOVE   |  closes window when user moves mouse past threshhold
//                :     unCLOSE_ON_KEYPRESS     |  closes window on the next keypress message sent to this thread.    (!!! probably not thread safe !!!)
//                :     unDELETE_THIS_ON_CLOSE  |  deletes object when window is closed.  Used by LaunchBalloon(), use with care
//                :     unSHOW_CLOSE_BUTTON     |  shows close button in upper right
//                :     unSHOW_INNER_SHADOW     |  draw inner shadow in balloon
//                :     unSHOW_TOPMOST          |  place balloon above all other windows
//                :     unDISABLE_FADE          |  disable the fade-in/fade-out effects (overrides system and user settings)
//                :     unDISABLE_FADEIN        |  disable the fade-in effect
//                :     unDISABLE_FADEOUT       |  disable the fade-out effect
//    pParentWnd  |  Parent window.  If NULL will be set to AfxGetMainWnd()
//    strURL      |  If not empty, when the balloon is clicked ShellExecute() will
//                |  be called, with strURL passed in.
//    unTimeout   |  If not 0, balloon will automatically close after unTimeout milliseconds.
//    hIcon       |  If not NULL, the icon indicated by hIcon will be displayed at top-left of the balloon.
//
// Returns:
//    TRUE if successful, else FALSE
//
BOOL CBalloonHelp::Create(const CString& strTitle, const CString& strContent, 
               const CPoint& ptAnchor, unsigned int unOptions,
               HWND pParentWnd, //=NULL
               const CString strURL, //= ""
               unsigned int unTimeout, //= 0
               HICON hIcon) //= NULL
{
	m_strContent   = strContent;
	m_ptAnchor     = ptAnchor;
	m_unOptions    = unOptions;
	m_strURL       = strURL;
	m_unTimeout    = unTimeout;

	if ( NULL != hIcon )
		SetIcon(hIcon);
	   
	if (NULL == pParentWnd)
		return FALSE;

	// if no fonts set, use defaults
	if ( NULL == m_pContentFont )
	{
		m_pContentFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
		if (!m_pContentFont)
			return FALSE;
	}

	// title font defaults to bold version of content font
	if ( NULL == m_pTitleFont )
	{
		LOGFONT LogFont;
		GetObject(m_pContentFont, sizeof(LogFont), &LogFont);
		LogFont.lfWeight = FW_BOLD;
		if (!(m_pTitleFont = (HFONT)CreateFontIndirect(&LogFont)))
			return FALSE;
	}

	// check system settings: if fade effects are disabled, disable here too
	BOOL bFade = FALSE;
	::SystemParametersInfo(SPI_GETTOOLTIPANIMATION, 0, &bFade, 0);
	if (bFade)
		::SystemParametersInfo(SPI_GETTOOLTIPFADE, 0, &bFade, 0);
	if (!bFade || NULL == m_fnSetLayeredWindowAttributes)
		m_unOptions |= unDISABLE_FADE;

	// create invisible at arbitrary position; then position, set region, and finally show

	// the idea with WS_EX_TOOLWINDOW is, you can't switch to this using alt+tab
	DWORD dwExStyle = WS_EX_TOOLWINDOW;
    if(!((m_unOptions & unDISABLE_FADE) == unDISABLE_FADE))
        dwExStyle |= WS_EX_LAYERED;

	if ( m_unOptions&unSHOW_TOPMOST )      // make topmost, if requested
		dwExStyle |= WS_EX_TOPMOST;
	if (!__super::Create(pParentWnd, CRect(0, 0, 10, 10), strTitle, WS_POPUP, dwExStyle))
		return FALSE;
	PositionWindow();

	// show, possibly fading
	if(!(m_unOptions & unHIDE_AFTER_CREATED))
		ShowBalloon();

	if ( m_unOptions & unCLOSE_ON_MOUSE_MOVE )
	{
		::GetCursorPos(&m_ptMouseOrig);
		ScreenToClient(&m_ptMouseOrig);
		SetTimer(ID_TIMER_HOTTRACK, 100, NULL);
	}

	if ( (m_unOptions & unCLOSE_ON_LBUTTON_UP) || (m_unOptions & unCLOSE_ON_RBUTTON_UP) )
		SetCapture();

	if ( m_unOptions&unCLOSE_ON_KEYPRESS )
		SetKeyboardHook();

	// show, but don't take focus away from parent (or whatever)
//	ShowWindow(SW_SHOWNOACTIVATE);

	return TRUE;
}

// Wrapper for possibly-existing API call
BOOL CBalloonHelp::SetLayeredWindowAttributes(COLORREF crKey, int nAlpha, DWORD dwFlags)
{
   if ( NULL != m_fnSetLayeredWindowAttributes )
      return (*m_fnSetLayeredWindowAttributes)(m_hWnd, crKey, (BYTE)nAlpha, dwFlags);
   return FALSE;
}

// calculates the area of the screen the balloon falls into
// this determins which direction the tail points
CBalloonHelp::BALLOON_QUADRANT CBalloonHelp::GetBalloonQuadrant()
{
   CRect rectDesktop;
   ::GetWindowRect(::GetDesktopWindow(), &rectDesktop);
   
   if ( m_ptAnchor.y < rectDesktop.Height()/2 )
   {
      if ( m_ptAnchor.x < rectDesktop.Width()/2 )
      {
         return BQ_TOPLEFT;
      }
      else
      {
         return BQ_TOPRIGHT;
      }
   }
   else
   {
      if ( m_ptAnchor.x < rectDesktop.Width()/2 )
      {
         return BQ_BOTTOMLEFT;
      }
      else
      {
         return BQ_BOTTOMRIGHT;
      }
   }

   // unreachable
}

// Calculate the dimensions and draw the balloon header
CSize CBalloonHelp::DrawHeader(HDC pDC, bool bDraw)
{
   CSize sizeHdr(0,0);
   CRect rectClient;
   GetClientRect(&rectClient);   // use this for positioning when drawing
                                 // else if content is wider than title, centering wouldn't work

   // calc & draw icon
   if ( NULL != m_ilIcon )
   {
      int x = 0;
      int y = 0;
      ImageList_GetIconSize(m_ilIcon, &x, &y);
      sizeHdr.cx += x;
      sizeHdr.cy = max(sizeHdr.cy, y);
      ImageList_SetBkColor(m_ilIcon, m_crBackground);
      if (bDraw)
         ImageList_Draw(m_ilIcon, 0, pDC, 0, 0, ILD_NORMAL);//ILD_TRANSPARENT);
      rectClient.left += x;
   }
   // make some space between left icon and title
   rectClient.left += 5;

   // calc & draw close button
   if ( m_unOptions & unSHOW_CLOSE_BUTTON )
   {
      // if something is already in the header (icon) leave space
      if ( sizeHdr.cx > 0 )
         sizeHdr.cx += nTIP_MARGIN;
      sizeHdr.cx += 16;
      sizeHdr.cy = max(sizeHdr.cy, 16);
      if (bDraw)
		 DrawFrameControl(pDC, CRect(rectClient.right-16,0,rectClient.right,16), DFC_CAPTION, DFCS_CAPTIONCLOSE|DFCS_FLAT);
      rectClient.right -= 16;
   }

   // calc title size
   CString strTitle;
   GetWindowText(strTitle);
   if ( !strTitle.IsEmpty() )
   {
      HFONT pOldFont = (HFONT)SelectObject(pDC, m_pTitleFont);

      // if something is already in the header (icon or close button) leave space
      if ( sizeHdr.cx > 0 ) 
         sizeHdr.cx += nTIP_MARGIN;
      CRect rectTitle(0,0,0,0);
	  DrawText(pDC, strTitle, strTitle.GetLength(), &rectTitle, DT_CALCRECT | DT_NOPREFIX | DT_EXPANDTABS | DT_SINGLELINE);
      sizeHdr.cx += rectTitle.Width();
      sizeHdr.cy = max(sizeHdr.cy, rectTitle.Height());

	  //slightly adjust the y position of title to center it vertically with the left icon
	  rectClient.top = (sizeHdr.cy - rectTitle.Height()) /2;
      // draw title
      if ( bDraw )
      {
         SetBkMode(pDC, TRANSPARENT);
         SetTextColor(pDC, m_crForeground);
         DrawText(pDC, strTitle, strTitle.GetLength(), &rectClient,  DT_NOPREFIX  | DT_EXPANDTABS | DT_SINGLELINE);
      }

      // cleanup
      SelectObject(pDC, pOldFont);
   }

   return sizeHdr;
}

// Calculate the dimensions and draw the balloon contents
CSize CBalloonHelp::DrawContent(HDC pDC, int nTop, bool bDraw)
{
   CRect rectContent;
   ::GetClientRect(::GetDesktopWindow(), &rectContent);

   rectContent.top = nTop;

   // limit to half screen width
   rectContent.right -= rectContent.Width()/2;

   // calc size
   HFONT pOldFont = (HFONT)SelectObject(pDC, m_pContentFont);
   if ( !m_strContent.IsEmpty() )
      DrawText(pDC, m_strContent, m_strContent.GetLength(), &rectContent, DT_CALCRECT | DT_LEFT | DT_NOPREFIX | DT_EXPANDTABS | DT_WORDBREAK);
   else
      rectContent.SetRectEmpty();   // don't want to leave half the screen for empty strings ;)
   
   // draw
   if (bDraw)
   {
      SetBkMode(pDC, TRANSPARENT);
      SetTextColor(pDC, m_crForeground);
      DrawText(pDC, m_strContent, m_strContent.GetLength(), &rectContent, DT_LEFT | DT_NOPREFIX | DT_WORDBREAK | DT_EXPANDTABS);
   }

   // cleanup
   SelectObject(pDC, pOldFont);

   return rectContent.Size();
}

// calculates the client size necessary based on title and content
CSize CBalloonHelp::CalcClientSize()
{
   _ASSERTE(m_hWnd);
   HDC hdc = GetDC();
   CSize sizeHeader = CalcHeaderSize(hdc);
   CSize sizeContent = CalcContentSize(hdc);
   ReleaseDC(hdc);

   return CSize(max(sizeHeader.cx,sizeContent.cx), sizeHeader.cy + nTIP_MARGIN + sizeContent.cy);
}

// calculates the size for the entire window based on content size
CSize CBalloonHelp::CalcWindowSize()
{
   CSize size = CalcClientSize();
   size.cx += nTIP_MARGIN*2;
   size.cy += nTIP_TAIL+nTIP_MARGIN*2;
   //size.cx = max(size.cx, nTIP_MARGIN*2+nTIP_TAIL*4);
   return size;
}


// this routine calculates the size and position of the window relative
// to it's anchor point, and moves the window accordingly.  The region is also
// created and set here.
void CBalloonHelp::PositionWindow(bool forceRedraw/*=false*/)
{
	CSize sizeWnd = CalcWindowSize();

	CPoint ptTail[3];
	CPoint ptTopLeft(0, 0);
	CPoint ptBottomRight(sizeWnd.cx, sizeWnd.cy);

	switch (GetBalloonQuadrant())
	{
	case BQ_TOPLEFT:
		ptTopLeft.y = nTIP_TAIL;
		ptTail[0].x = (sizeWnd.cx-nTIP_TAIL)/4 + nTIP_TAIL;
		ptTail[0].y = nTIP_TAIL+1;
		ptTail[2].x = (sizeWnd.cx-nTIP_TAIL)/4;
		ptTail[2].y = ptTail[0].y;
		ptTail[1].x = ptTail[2].x;
		ptTail[1].y = 1;
		break;
	case BQ_TOPRIGHT:
		ptTopLeft.y = nTIP_TAIL;
		ptTail[0].x = (sizeWnd.cx-nTIP_TAIL)/4*3;
		ptTail[0].y = nTIP_TAIL+1;
		ptTail[2].x = (sizeWnd.cx-nTIP_TAIL)/4*3 + nTIP_TAIL;
		ptTail[2].y = ptTail[0].y;
		ptTail[1].x = ptTail[2].x;
		ptTail[1].y = 1;
		break;
	case BQ_BOTTOMLEFT:
		ptBottomRight.y = sizeWnd.cy-nTIP_TAIL;
		ptTail[0].x = (sizeWnd.cx-nTIP_TAIL)/4 + nTIP_TAIL;
		ptTail[0].y = sizeWnd.cy-nTIP_TAIL-2;
		ptTail[2].x = (sizeWnd.cx-nTIP_TAIL)/4;
		ptTail[2].y = ptTail[0].y;
		ptTail[1].x = ptTail[2].x;
		ptTail[1].y = sizeWnd.cy-2;
		break;
	case BQ_BOTTOMRIGHT:
		ptBottomRight.y = sizeWnd.cy-nTIP_TAIL;
		ptTail[0].x = (sizeWnd.cx-nTIP_TAIL)/4*3;
		ptTail[0].y = sizeWnd.cy-nTIP_TAIL-2;
		ptTail[2].x = (sizeWnd.cx-nTIP_TAIL)/4*3 + nTIP_TAIL;
		ptTail[2].y = ptTail[0].y;
		ptTail[1].x = ptTail[2].x;
		ptTail[1].y = sizeWnd.cy-2;
		break;
	}

	// adjust for very narrow balloons
	if ( ptTail[0].x < nTIP_MARGIN )
		ptTail[0].x = nTIP_MARGIN;
	if ( ptTail[0].x > sizeWnd.cx - nTIP_MARGIN )
		ptTail[0].x = sizeWnd.cx - nTIP_MARGIN;
	if ( ptTail[1].x < nTIP_MARGIN )
		ptTail[1].x = nTIP_MARGIN;
	if ( ptTail[1].x > sizeWnd.cx - nTIP_MARGIN )
		ptTail[1].x = sizeWnd.cx - nTIP_MARGIN;
	if ( ptTail[2].x < nTIP_MARGIN )
		ptTail[2].x = nTIP_MARGIN;
	if ( ptTail[2].x > sizeWnd.cx - nTIP_MARGIN )
		ptTail[2].x = sizeWnd.cx - nTIP_MARGIN;

	// get window position
	CPoint ptOffs(m_ptAnchor.x - ptTail[1].x, m_ptAnchor.y - ptTail[1].y);

	// adjust position so all is visible
	CRect rectScreen;
	::GetWindowRect(::GetDesktopWindow(), &rectScreen);
	int nAdjustX = 0;
	int nAdjustY = 0;
	if ( ptOffs.x < 0 )
		nAdjustX = -ptOffs.x;
	else if ( ptOffs.x + sizeWnd.cx >= rectScreen.right )
		nAdjustX = rectScreen.right - (ptOffs.x + sizeWnd.cx);
	if ( ptOffs.y < 0 )
		nAdjustY = -ptOffs.y;
	else if ( ptOffs.y + sizeWnd.cy >= rectScreen.bottom )
		nAdjustY = rectScreen.bottom - (ptOffs.y + sizeWnd.cy);

	// reposition tail
	// uncomment two commented lines below to move entire tail 
	// instead of just anchor point

	//ptTail[0].x -= nAdjustX;
	ptTail[1].x -= nAdjustX;
	//ptTail[2].x -= nAdjustX;
	ptOffs.x    += nAdjustX;
	ptOffs.y    += nAdjustY;

	// place window
	MoveWindow(ptOffs.x, ptOffs.y, sizeWnd.cx, sizeWnd.cy, TRUE);

	// apply region
	BOOL bRegionChanged = TRUE;
	HRGN region;		//the tail of balloon
	HRGN regionRound;	//the body of balloon
	HRGN regionComplete;//the total part
	region = CreatePolygonRgn(&ptTail[0], 3, ALTERNATE);	
	regionRound = CreateRoundRectRgn(ptTopLeft.x,ptTopLeft.y,ptBottomRight.x,ptBottomRight.y,nTIP_MARGIN*2,nTIP_MARGIN*2);
	regionComplete = CreateRectRgn(0,0,1,1);
	CombineRgn(regionComplete, region, regionRound, RGN_OR);

	if ( NULL == m_rgnComplete )
		m_rgnComplete = CreateRectRgn(0, 0, 1, 1);
	else if ( EqualRgn(m_rgnComplete, regionComplete) )
		bRegionChanged = FALSE;
	::CombineRgn(m_rgnComplete, regionComplete, NULL, RGN_COPY);
	   
	SetWindowRgn(regionComplete, TRUE);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -