Backed out bug 313403. (merge)
authorVladimir Vukicevic <vladimir@pobox.com>
Wed, 20 Aug 2008 13:28:49 -0700
changeset 17164 73967cc9e4ee07b66a5a3f9a35958027a51507d5
parent 17162 30da9cae7cf2aa0445c3c7cdcae0aeea0b5710f1 (current diff)
parent 17163 2ee05d1a0f59ff18a6d6f2f6dc0ee9982676ce3b (diff)
child 17165 7e6f8b885113878dc00f2e6527776bc1eb14e527
child 18320 74a9a3453bd9aac11a14e83d8065c647b28f6b77
child 18521 f197b51bbc29a30860e750ee87fd0a047a024f2e
push id1
push usersledru@mozilla.com
push dateThu, 04 Dec 2014 17:57:20 +0000
bugs313403
milestone1.9.1a2pre
Backed out bug 313403. (merge)
widget/src/windows/nsWindow.cpp
--- a/widget/src/windows/nsWindow.cpp
+++ b/widget/src/windows/nsWindow.cpp
@@ -252,16 +252,20 @@ PRInt32 GetWindowsVersion()
     osInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
     // This cast is safe and supposed to be here, don't worry
     ::GetVersionEx((OSVERSIONINFO*)&osInfo);
     version = (osInfo.dwMajorVersion & 0xff) << 8 | (osInfo.dwMinorVersion & 0xff);
   }
   return version;
 }
 
+
+// Pick some random timer ID.  Is there a better way?
+#define NS_FLASH_TIMER_ID 0x011231984
+
 static NS_DEFINE_CID(kCClipboardCID,       NS_CLIPBOARD_CID);
 static NS_DEFINE_IID(kRenderingContextCID, NS_RENDERING_CONTEXT_CID);
 
 // When we build we are currently (11/27/01) setting the WINVER to 0x0400
 // Which means we do not compile in the system resource for the HAND cursor
 // this enables us still define the resource and if it isn't there then we will
 // get our own hand cursor.
 // 32649 is the resource number as defined by WINUSER.H for this cursor
@@ -419,16 +423,129 @@ static PRBool is_vk_down(int vk)
 #define GET_APPCOMMAND_LPARAM(lParam) ((short)(HIWORD(lParam) & ~FAPPCOMMAND_MASK))
 //#define GET_DEVICE_LPARAM(lParam)     ((WORD)(HIWORD(lParam) & FAPPCOMMAND_MASK))
 //#define GET_MOUSEORKEY_LPARAM         GET_DEVICE_LPARAM
 //#define GET_FLAGS_LPARAM(lParam)      (LOWORD(lParam))
 //#define GET_KEYSTATE_LPARAM(lParam)   GET_FLAGS_LPARAM(lParam)
 
 #endif  // #ifndef APPCOMMAND_BROWSER_BACKWARD
 
+/* This object maintains a correlation between attention timers and the
+   windows to which they belong. It's lighter than a hashtable (expected usage
+   is really just one at a time) and allows nsWindow::GetNSWindowPtr
+   to remain private. */
+class nsAttentionTimerMonitor {
+public:
+  nsAttentionTimerMonitor() : mHeadTimer(0) { }
+  ~nsAttentionTimerMonitor() {
+    TimerInfo *current, *next;
+    for (current = mHeadTimer; current; current = next) {
+      next = current->next;
+      delete current;
+    }
+  }
+  void AddTimer(HWND timerWindow, HWND flashWindow, PRInt32 maxFlashCount, UINT timerID) {
+    TimerInfo *info;
+    PRBool    newInfo = PR_FALSE;
+    info = FindInfo(timerWindow);
+    if (!info) {
+      info = new TimerInfo;
+      newInfo = PR_TRUE;
+    }
+    if (info) {
+      info->timerWindow = timerWindow;
+      info->flashWindow = flashWindow;
+      info->maxFlashCount = maxFlashCount;
+      info->flashCount = 0;
+      info->timerID = timerID;
+      info->hasFlashed = PR_FALSE;
+      info->next = 0;
+      if (newInfo)
+        AppendTimer(info);
+    }
+  }
+  HWND GetFlashWindowFor(HWND timerWindow) {
+    TimerInfo *info = FindInfo(timerWindow);
+    return info ? info->flashWindow : 0;
+  }
+  PRInt32 GetMaxFlashCount(HWND timerWindow) {
+    TimerInfo *info = FindInfo(timerWindow);
+    return info ? info->maxFlashCount : -1;
+  }
+  PRInt32 GetFlashCount(HWND timerWindow) {
+    TimerInfo *info = FindInfo(timerWindow);
+    return info ? info->flashCount : -1;
+  }
+  void IncrementFlashCount(HWND timerWindow) {
+    TimerInfo *info = FindInfo(timerWindow);
+    ++(info->flashCount);
+  }
+  void KillTimer(HWND timerWindow) {
+    TimerInfo *info = FindInfo(timerWindow);
+    if (info) {
+      // make sure it's unflashed and kill the timer
+
+      if (info->hasFlashed)
+        ::FlashWindow(info->flashWindow, FALSE);
+
+      ::KillTimer(info->timerWindow, info->timerID);
+      RemoveTimer(info);
+      delete info;
+    }
+  }
+  void SetFlashed(HWND timerWindow) {
+    TimerInfo *info = FindInfo(timerWindow);
+    if (info)
+      info->hasFlashed = PR_TRUE;
+  }
+
+private:
+  struct TimerInfo {
+    HWND       timerWindow,
+               flashWindow;
+    UINT       timerID;
+    PRInt32    maxFlashCount;
+    PRInt32    flashCount;
+    PRBool     hasFlashed;
+    TimerInfo *next;
+  };
+  TimerInfo *FindInfo(HWND timerWindow) {
+    TimerInfo *scan;
+    for (scan = mHeadTimer; scan; scan = scan->next)
+      if (scan->timerWindow == timerWindow)
+        break;
+    return scan;
+  }
+  void AppendTimer(TimerInfo *info) {
+    if (!mHeadTimer)
+      mHeadTimer = info;
+    else {
+      TimerInfo *scan, *last;
+      for (scan = mHeadTimer; scan; scan = scan->next)
+        last = scan;
+      last->next = info;
+    }
+  }
+  void RemoveTimer(TimerInfo *info) {
+    TimerInfo *scan, *last = 0;
+    for (scan = mHeadTimer; scan && scan != info; scan = scan->next)
+      last = scan;
+    if (scan) {
+      if (last)
+        last->next = scan->next;
+      else
+        mHeadTimer = scan->next;
+    }
+  }
+
+  TimerInfo *mHeadTimer;
+};
+
+static nsAttentionTimerMonitor *gAttentionTimerMonitor = 0;
+
 HWND nsWindow::GetTopLevelHWND(HWND aWnd, PRBool aStopOnFirstTopLevel)
 {
   HWND curWnd = aWnd;
   HWND topWnd = NULL;
 
   while (curWnd)
   {
     topWnd = curWnd;
@@ -1311,16 +1428,18 @@ NS_METHOD nsWindow::Destroy()
   }
 
   EnableDragDrop(PR_FALSE);
 
   // destroy the HWND
   if (mWnd) {
     // prevent the widget from causing additional events
     mEventCallback = nsnull;
+    if (gAttentionTimerMonitor)
+      gAttentionTimerMonitor->KillTimer(mWnd);
 
     // if IME is disabled, restore it.
     if (mOldIMC) {
       mOldIMC = ::ImmAssociateContext(mWnd, mOldIMC);
       NS_ASSERTION(!mOldIMC, "Another IMC was associated");
     }
 
     HICON icon;
@@ -7347,42 +7466,78 @@ void nsWindow::GetCompositionWindowPos(H
   cpForm->ptCurrentPos.y = event.theReply.mCursorPosition.y + IME_Y_OFFSET +
                            event.theReply.mCursorPosition.height;
   cpForm->rcArea.left = cpForm->ptCurrentPos.x;
   cpForm->rcArea.top = cpForm->ptCurrentPos.y;
   cpForm->rcArea.right = cpForm->ptCurrentPos.x + event.theReply.mCursorPosition.width;
   cpForm->rcArea.bottom = cpForm->ptCurrentPos.y + event.theReply.mCursorPosition.height;
 }
 
+// This function is called on a timer to do the flashing.  It simply toggles the flash
+// status until the window comes to the foreground.
+static VOID CALLBACK nsGetAttentionTimerFunc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)
+{
+  // flash the window until we're in the foreground.
+  if (::GetForegroundWindow() != hwnd)
+  {
+    // flash the outermost owner
+    HWND flashwnd = gAttentionTimerMonitor->GetFlashWindowFor(hwnd);
+
+    PRInt32 maxFlashCount = gAttentionTimerMonitor->GetMaxFlashCount(hwnd);
+    PRInt32 flashCount = gAttentionTimerMonitor->GetFlashCount(hwnd);
+    if (maxFlashCount > 0) {
+      // We have a max flash count, if we haven't met it yet, flash again.
+      if (flashCount < maxFlashCount) {
+        ::FlashWindow(flashwnd, TRUE);
+        gAttentionTimerMonitor->IncrementFlashCount(hwnd);
+      }
+      else
+        gAttentionTimerMonitor->KillTimer(hwnd);
+    }
+    else {
+      // The caller didn't specify a flash count.
+      ::FlashWindow(flashwnd, TRUE);
+    }
+
+    gAttentionTimerMonitor->SetFlashed(hwnd);
+  }
+  else
+    gAttentionTimerMonitor->KillTimer(hwnd);
+}
+
 // Draw user's attention to this window until it comes to foreground.
 NS_IMETHODIMP
 nsWindow::GetAttention(PRInt32 aCycleCount)
 {
   // Got window?
   if (!mWnd)
     return NS_ERROR_NOT_INITIALIZED;
 
-  // Don't flash if the flash count is 0 or if the 
-  // top level window is already active.
-  if (aCycleCount == 0 || ::GetForegroundWindow() == GetTopLevelHWND(mWnd))
+  // Don't flash if the flash count is 0.
+  if (aCycleCount == 0)
     return NS_OK;
 
-  DWORD defaultCycleCount = 0;
-  ::SystemParametersInfo(SPI_GETFOREGROUNDFLASHCOUNT, 0, &defaultCycleCount, 0);
-  HWND flashWnd = mWnd;
-  while (HWND ownerWnd = ::GetWindow(flashWnd, GW_OWNER))
-    flashWnd = ownerWnd;
-
-  FLASHWINFO flashInfo;
-  ZeroMemory(&flashInfo, sizeof(FLASHWINFO));
-  flashInfo.cbSize = sizeof(FLASHWINFO);
-  flashInfo.hwnd = flashWnd;
-  flashInfo.dwFlags = FLASHW_ALL;
-  flashInfo.uCount = aCycleCount > 0 ? aCycleCount : defaultCycleCount;
-  ::FlashWindowEx(&flashInfo);
+  // timer is on the parentmost window; window to flash is its ownermost
+  HWND timerwnd = GetTopLevelHWND(mWnd);
+  HWND flashwnd = timerwnd;
+  HWND nextwnd;
+  while ((nextwnd = ::GetWindow(flashwnd, GW_OWNER)) != 0)
+    flashwnd = nextwnd;
+
+  // If window is in foreground, no notification is necessary.
+  if (::GetForegroundWindow() != timerwnd) {
+    // kick off a timer that does single flash until the window comes to the foreground
+    if (!gAttentionTimerMonitor)
+      gAttentionTimerMonitor = new nsAttentionTimerMonitor;
+    if (gAttentionTimerMonitor) {
+      gAttentionTimerMonitor->AddTimer(timerwnd, flashwnd, aCycleCount, NS_FLASH_TIMER_ID);
+      ::SetTimer(timerwnd, NS_FLASH_TIMER_ID, GetCaretBlinkTime(), (TIMERPROC)nsGetAttentionTimerFunc);
+    }
+  }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWindow::GetLastInputEventTime(PRUint32& aTime)
 {
   WORD qstatus = HIWORD(GetQueueStatus(QS_INPUT));