NCBI C++ ToolKit
sticky_tooltip_handler.cpp
Go to the documentation of this file.

Go to the SVN repository for this file.

1 /* $Id: sticky_tooltip_handler.cpp 46553 2021-07-06 14:46:51Z shkeda $
2  * ===========================================================================
3  *
4  * PUBLIC DOMAIN NOTICE
5  * National Center for Biotechnology Information
6  *
7  * This software/database is a "United States Government Work" under the
8  * terms of the United States Copyright Act. It was written as part of
9  * the author's official duties as a United States Government employee and
10  * thus cannot be copyrighted. This software/database is freely available
11  * to the public for use. The National Library of Medicine and the U.S.
12  * Government have not placed any restriction on its use or reproduction.
13  *
14  * Although all reasonable efforts have been taken to ensure the accuracy
15  * and reliability of the software and data, the NLM and the U.S.
16  * Government do not and cannot warrant the performance or results that
17  * may be obtained by using this software or data. The NLM and the U.S.
18  * Government disclaim all warranties, express or implied, including
19  * warranties of performance, merchantability or fitness for any particular
20  * purpose.
21  *
22  * Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Authors: Bob Falk
27  *
28  * File Description:
29  *
30  */
31 
32 #include <ncbi_pch.hpp>
33 
34 #include <corelib/ncbidbg.hpp>
35 
37 
40 
44 #include <gui/utils/command.hpp>
45 
46 #include <wx/settings.h>
47 #include <wx/window.h>
48 #include <wx/app.h>
49 
51 
52 
53 #define TTMGR_TIMER_ID 62210
54 #define TTMGR_TIMER_CLEAR_UNPINNED 62211
55 
56 #if defined(__WXOSX_CARBON__) || defined(__WXMAC_CARBON__)
57 std::vector<std::pair<wxWindow*,IStickyTooltipHandlerHost::CWindowRef> >
58  IStickyTooltipHandlerHost::sm_Windows;
59 #endif
60 
62 #if defined(__WXOSX_CARBON__) || defined(__WXMAC_CARBON__)
63 : m_TipGroupWinRef(NULL)
64 #endif
65 {
66  m_ServiceLocator = NULL;
67 }
68 
70 {
71 }
72 
74 {
76 }
77 
79 {
80  CFloatingFrame* ff = NULL;
81  wxFrame* f = NULL;
82 
83  for( wxWindow* p = TTHH_GetWindow(); p != NULL; p = p->GetParent() ){
84  if( f == NULL ){
85  f = dynamic_cast<wxFrame*>(p);
86  }
87  if( ff == NULL ){
88  ff = dynamic_cast<CFloatingFrame*>(p);
89  }
90  }
91 
92  _ASSERT( (f!=NULL) || (ff!=NULL) );
93 
94  if( (ff!=NULL) && (f!=ff) ){
95  f = ff;
96  }
97 
98  return f;
99 }
100 
102 {
103  wxFrame* f = GetUnderlyingFrame();
104  if( f == NULL ) return false;
105 
106  return (dynamic_cast<CFloatingFrame*>(f) != NULL);
107 }
108 
110 {
111 // On mac Carbon, tooltip windows do not jump to front. And raising
112 // other windows causes some focus issues (need to click in
113 // your current window to get focus back at times)
114 #if defined(__WXOSX_CARBON__) || defined(__WXMAC_CARBON__)
115  if (IsFloatingWindow())
116  return;
117 
118  if (m_ServiceLocator != NULL) {
119  IWindowManagerService* win_srv =
121 
122  if (win_srv!=NULL) {
123  // Bring all floating windows to the front, and then set the focus back
124  // since raising those windows will change focus.
126  TTHH_GetWindow()->SetFocus();
127  }
128  }
129 #endif
130 }
131 
133 {
134  if (m_ServiceLocator != NULL) {
135  IWindowManagerService* win_srv =
137 
138  if (win_srv!=NULL) {
139  return (win_srv->IsDragging());
140  }
141  }
142 
143  return false;
144 }
145 
146 
147 
149 {
150 #if defined(__WXOSX_CARBON__) || defined(__WXMAC_CARBON__)
151  if (TTHH_GetWindow() == NULL ) {
152  return;
153  }
154 
155  wxFrame* f = GetUnderlyingFrame();
156  std::vector<std::pair<wxWindow*,CWindowRef> >::iterator iter;
157 
158  WindowGroupRef view_ref;
159  WindowGroupRef top_ref;
160 
161  for (iter=sm_Windows.begin(); iter != sm_Windows.end(); ++iter) {
162  if ( (*iter).first == (wxWindow*)f ) {
163  break;
164  }
165  }
166 
167  bool floater = IsFloatingWindow();
168 
169  if (iter != sm_Windows.end()) {
170  m_TipGroupWinRef = (*iter).second.m_Ref;
171  return;
172  }
173 
174  WindowGroupRef group_class = NULL;
175  if (floater) {
176  group_class = GetWindowGroupOfClass(kFloatingWindowClass);
177  //_TRACE("Setting window class to floating");
178  }
179  else {
180  group_class = GetWindowGroupOfClass(kDocumentWindowClass);
181  //_TRACE("Setting window class to document ");
182  }
183 
184 
185  CreateWindowGroup( kWindowGroupAttrSelectAsLayer |
186  kWindowGroupAttrHideOnCollapse, &m_TipGroupWinRef );
187  CreateWindowGroup( kWindowGroupAttrSelectAsLayer|
188  kWindowGroupAttrHideOnCollapse, &view_ref );
189  CreateWindowGroup( kWindowGroupAttrSelectAsLayer |
190  kWindowGroupAttrHideOnCollapse, &top_ref );
191 
192  SetWindowGroupParent(m_TipGroupWinRef, top_ref);
193  SetWindowGroupParent(view_ref, m_TipGroupWinRef);
194  SetWindowGroupParent(top_ref, group_class);
195 
196  SetWindowGroup((WindowRef)f->MacGetTopLevelWindowRef(), view_ref);
197 
198  sm_Windows.push_back(std::pair<wxWindow*, CWindowRef>((wxWindow*)f,CWindowRef(m_TipGroupWinRef, dock_count)));
199 #endif
200 }
201 
202 
203 /*****************************************************************************/
204 /*****************************************************************************/
205 
206 
207 BEGIN_EVENT_TABLE(CStickyTooltipHandler, wxTimer)
211  EVT_LEAVE_WINDOW(CStickyTooltipHandler::OnLeaveWindow)
216 
221 
229 
230 
231 
233 : m_Host(NULL)
234 , m_UnpinnedTip(NULL)
235 , m_ActiveTip(NULL)
236 , m_Timer(this, TTMGR_TIMER_ID)
237 , m_SuspendCount(0)
238 , m_TimerClearUnpinned(this, TTMGR_TIMER_CLEAR_UNPINNED)
239 , m_MouseMoveThreshold(2.0f)
240 , m_UnpinnedMouseMoveThreshold(4.0f)
241 , m_ClearUnpinnedDelay(250)
242 , m_PopupDistance(0.22f)
243 // Base popup delay according to what is typical for individual platforms.
244 // Currently cannot be configured.
245 #if defined(NCBI_OS_MSWIN)
246 , m_TipPopupDelay(500)
247 #elif defined(NCBI_OS_MAC)
248 , m_TipPopupDelay(1000)
249 #elif defined(NCBI_OS_UNIX)
250 // default for GTK
251 , m_TipPopupDelay(500)
252 #else
253 // In case we compile something else..
254 , m_TipPopupDelay(500)
255 #endif
256 {
257  RegisterCommands();
258 }
259 
261 {
262  unsigned int i;
263 
264  if (NULL != m_UnpinnedTip) {
265  m_UnpinnedTip->Show(false);
266  m_UnpinnedTip->Destroy();
268  }
269 
270  for (i=0; i<m_ToolTips.size(); ++i) {
271  m_ToolTips[i]->Show(false);
272  m_ToolTips[i]->Destroy();
273  }
274  m_ToolTips.clear();
275 
276  for (i=0; i<m_SuspendedTips.size(); ++i) {
277  m_SuspendedTips[i]->Destroy();
278  }
279  m_SuspendedTips.clear();
280 
281  UpdateTips();
282 
283  m_Timer.Stop();
284 }
285 
287 {
288  static bool initialized = false;
289  if (initialized)
290  return;
291  initialized = true;
292 
294  cmd_reg.RegisterCommand(eCmdCopyTipText, "Copy All", "Copy All", "", "");
295  cmd_reg.RegisterCommand(eCmdSelectTipText, "Enable Select", "Enable Select", "", "", "", "", wxITEM_CHECK);
296  cmd_reg.RegisterCommand(eCmdCopySelectedTipText, "Copy Selected", "Copy Selected", "", "");
297 
298 }
299 
301 {
302  std::vector<CTooltipFrame*>::iterator iter;
303 
304  // Only add tips for items that do not already have tips open
305  for (iter=m_ToolTips.begin(); iter!=m_ToolTips.end(); ++iter) {
306  if (*iter == tt)
307  break;
308  }
309 
310  if (iter == m_ToolTips.end()) {
311  m_ToolTips.push_back(tt);
312 
313  // The timer lets us bring forward the tooltip the mouse is on
314  if (m_ToolTips.size() == 1)
315  m_Timer.Start(200);
316  }
317 
318  UpdateTips();
319 }
320 
322 {
323  // Only one unpinned tip at a time (others should auto-close).
324  ClearUnpinned();
325 
326  m_UnpinnedTip = tt;
327 
328  wxCommandEvent evt(wxEVT_COMMAND_BUTTON_CLICKED, eCmdTipAdded);
329  evt.SetEventObject(m_UnpinnedTip);
330  m_Host->TTHH_GetWindow()->GetEventHandler()->ProcessEvent(evt);
331 
332  UpdateTips();
333 }
334 
336 {
337  _ASSERT(tt != NULL && tt == m_UnpinnedTip);
338 
339  // Move a tip from being the upinned tip to the list of pinned tips
340  m_UnpinnedTip->SetPinned(true);
343 }
344 
346 {
347  std::vector<CTooltipFrame*>::iterator iter;
348 
349  iter = std::find(m_ToolTips.begin(), m_ToolTips.end(), tt);
350 
351  _ASSERT(iter != m_ToolTips.end());
352 
353  // should already be clear, but just in case:
354  ClearUnpinned();
355 
356  // User has pressed unpin-button on tip, so move it to unpinned
357  (*iter)->SetPinned(false);
358  if (m_ActiveTip == *iter)
359  m_ActiveTip = NULL;
360  m_UnpinnedTip = *iter;
361  m_ToolTips.erase(iter);
362  if (m_ToolTips.size() == 0)
363  m_Timer.Stop();
364  UpdateTips();
365 
366  // refresh pane to remove the pinned tip anchor
367  wxCommandEvent evt(wxEVT_COMMAND_BUTTON_CLICKED, eCmdTipInactive);
368  evt.SetEventObject(m_UnpinnedTip);
369  m_Host->TTHH_GetWindow()->GetEventHandler()->ProcessEvent(evt);
370 }
371 
372 void CStickyTooltipHandler::ParentMove(const wxPoint&screen_pos)
373 {
374  std::vector<CTooltipFrame*>::iterator iter;
375 
376  // Move all tips based on new window position
377  for (iter=m_ToolTips.begin(); iter!=m_ToolTips.end(); ++iter) {
378  (*iter)->UpdateScreenPosition(screen_pos);
379  }
380 
381  UpdateTips();
382 }
383 
385 {
386  std::vector<CTooltipFrame*>::iterator iter;
387  wxRect combined_rect;
388 
389  // Parent shown (maybe because tip selected) so show all tips
390  for (iter=m_ToolTips.begin(); iter!=m_ToolTips.end(); ++iter) {
391  (*iter)->Show(show);
392 
393  // When a notebook page is displayed, its tips must be checked against
394  // bounds that could have changed while the page was hidden. Pass 'true'
395  // to indicate window may currently be hidden, but is about to show.
396  if (show) {
397  (*iter)->FitToWindow(true);
398  }
399 
400  combined_rect.Union((*iter)->GetRect());
401  }
402 
403  // Clear any unpinned tips before we hide (should generally not be there anyway if
404  // mouse if off active window...)
405  ClearUnpinned();
406 
407  m_Host->RaiseOverlappingWindows(combined_rect);
408 
409  UpdateTips();
410 }
411 
413 {
414  ParentMove(m_Host->TTHH_GetWindow()->GetScreenPosition());
415  wxRect combined_rect;
416 
417  // Parent window resized, make sure all toolips fit in resized parent
418  std::vector<CTooltipFrame*>::iterator iter;
419  for (iter=m_ToolTips.begin(); iter!=m_ToolTips.end(); ++iter) {
420  (*iter)->FitToWindow();
421  combined_rect.Union((*iter)->GetRect());
422  }
423 
424  m_Host->RaiseOverlappingWindows(combined_rect);
425 
426  UpdateTips();
427 }
428 
429 // could I use EVT_ENTER_WINDOW to do this?
430 void CStickyTooltipHandler::OnTimer(wxTimerEvent& event)
431 {
432  wxPoint m = wxGetMousePosition();
433  if (event.GetId() == TTMGR_TIMER_CLEAR_UNPINNED) {
434  if (NULL != m_UnpinnedTip) {
435  if (!m_UnpinnedTip->GetScreenRect().Contains(m)) {
436  ClearUnpinned();
437  // Check for a new tip at the new position right away:
438  wxPoint mouse_pos =
439  m - m_Host->TTHH_GetWindow()->GetScreenPosition();
440  CheckForNewTip(mouse_pos);
441  }
442  }
443  }
444  else if (event.GetId() == TTMGR_TIMER_ID && m_ToolTips.size() > 0) {
445  std::vector<CTooltipFrame*>::iterator iter;
446 
447  // If a popup menu is displayed, or the mouse has been captured for
448  // some reason (zooming, panning, marquee select..) don't activate
449  // any tooltips we happen to be passing over.
451  wxWindow::GetCapture() != NULL)
452  return;
453 
454  if (m_Host->IsDragging())
455  return;
456 
457  // If mouse was on a tip at last check return (don't switch the tip).
458  // If it has moved from that tip, fit the tip back to the window.
459  if (NULL != m_ActiveTip) {
460  // don't switch focus away from tip if the tip has its own popup menu
461  // displayed.
463  return;
464 
465  // If the mouse has moved away from the tip, make it inactive:
466  if (!m_ActiveTip->GetScreenRect().Contains(m) &&
467  m_ActiveTip->IsShown()) {
468 
469  // Send event to host window so it can un-highlight
470  // the glyph/element belonging to this tooltip
471  wxCommandEvent evt(wxEVT_COMMAND_BUTTON_CLICKED, eCmdTipInactive);
472  evt.SetEventObject(m_ActiveTip);
473  m_Host->TTHH_GetWindow()->GetEventHandler()->ProcessEvent(evt);
474 
476 
477  // If the tooltip has a url, and the user launches a browser, bringing the
478  // other overlapping windows to front will also put gbench in front
479  // of the launched browser, which is bad. Now if the tip is in front
480  // of another gbench window, this 'hack' will allow it to stay in front
481  // of that window until the user clicks on it, resizes the window etc.
482  if (m_ActiveTip->LaunchedBrowser()) {
484  }
485  else {
487  }
488 
489  m_ActiveTip = NULL;
490  }
491  else {
492  return;
493  }
494  }
495 
496  // This tool tip manager window has focus if it, any of its parents
497  // or any of the tool tips on the window have focus. Do not
498  // pop tool tips to the front if the window does not have focus.
499  bool has_focus = false;
500  wxWindow* w = m_Host->TTHH_GetWindow();
501  wxWindow* focus_win = wxWindow::FindFocus();
502 
503  if (focus_win == NULL) {
504  return;
505  }
506 
507  // Check if this (tooltip-supporting) window is a child of the current
508  // focus window. Do not go past a CFloatingFrame in the parent-hierachy,
509  // because that is a top-level window and if the current focus window
510  // is the main window, all (floating) frames are its children. (this way
511  // only widgets docked in the main window will be included as children)
512  bool top = false;
513  while (!has_focus && !top) {
514  if (focus_win == w) {
515  has_focus = true;
516  }
517  w = w->GetParent();
518  if (w == NULL ||
519  wxString(w->GetClassInfo()->GetClassName()) == wxT("CFloatingFrame"))
520  top = true;
521  }
522 
523  // If a parent of the this (tooltip) windows doesn't have focus, check to
524  // see if one of the window's tooltips has focus (that also would make it
525  // the active window). Also, If there is an unpinned tip and the mouse
526  // is inside of it, do not try to switch to another tip
527  if (m_UnpinnedTip != NULL) {
528  if (m_UnpinnedTip->GetScreenRect().Contains(m))
529  return;
530  has_focus = checkChildFocus(focus_win, m_UnpinnedTip);
531  }
532 
533  for (iter=m_ToolTips.begin(); iter!=m_ToolTips.end() && !has_focus;
534  ++iter) {
535  has_focus = checkChildFocus(focus_win, *iter);
536  }
537 
538  // This window, including sub-windows and enclosing frame(s),
539  // does not have focus
540  if (!has_focus) {
541  return;
542  }
543 
544  bool found = false;
545 
546  // See if the mouse has moved inside a different tip
547  for (iter=m_ToolTips.begin(); iter!=m_ToolTips.end() && !found; ++iter) {
548  wxRect r = (*iter)->GetScreenRect();
549  if (r.Contains(m) && (*iter)->IsShown()) {
550  m_ActiveTip = *iter;
552 #ifdef __WXOSX_COCOA__
555 #else
556  m_ActiveTip->Raise();
557 #endif
558  // If the user has entered another tooltip, that is (also) cause for
559  // clearing the unpinned tip (if any) since if the user had wanted
560  // to keep it, they would pin it before going to another tip
561  ClearUnpinned();
562 
563  // Send command to host window so it can highlight
564  // the screen element that matches this tip
565  wxCommandEvent evt(wxEVT_COMMAND_BUTTON_CLICKED, eCmdTipActive);
566  evt.SetEventObject(m_ActiveTip);
567  m_Host->TTHH_GetWindow()->GetEventHandler()->ProcessEvent(evt);
568 
569 #ifdef NCBI_OS_LINUX
570  // without this on GTK, we may wind up with a NULL focus, which
571  // prevents subsequent updates from working.
572  m_ActiveTip->SetFocus();
573 #endif
574  found = true;
575  }
576  }
577  }
578 }
579 
581 {
582  ParentShow(false);
583  ClearUnpinned();
584  m_ActiveTip = NULL;
585 
586  // Suspend may be called more than once prior to ReCreate so don't
587  // erase suspended tips
588  m_SuspendedTips.insert(m_SuspendedTips.end(), m_ToolTips.begin(),
589  m_ToolTips.end());
590 
591  ++m_SuspendCount;
592 
593  m_ToolTips.clear();
594 
595  UpdateTips();
596 }
597 
599 {
600  std::vector<CTooltipFrame*>::iterator iter;
601 
602  --m_SuspendCount;
603  //if (m_SuspendCount > 0)
604  // return;
605 
606  // If there are suspended tips there should be no active ones. If you
607  // call InitWindowGroups, all tip windows Must be re-created (on MAC)
608  if (m_SuspendedTips.size() > 0)
609  _ASSERT(m_ToolTips.size() == 0);
610 
611  m_Host->InitWindowGroup(dock_count);
612 
613  // Some windows (such as in notebook) may be in hidden when drag/drop
614  // is completed - we do this to keep the tooltips of those tabs hidden
615  // as well.
616  bool is_visible = m_Host->TTHH_GetWindow()->IsShownOnScreen();
617 
618  // Create a new copy of each tip, based on content and position of
619  // suspended tips, then delete suspended tips (if we don't do this,
620  // parent window hierarchy is no longer correct and tips behave funny).
621  for (iter=m_SuspendedTips.begin(); iter!=m_SuspendedTips.end(); ++iter)
622  {
623  CTooltipFrame* tt = new CTooltipFrame( (*iter)->GetParent()
624  ,(*iter)->GetName()
625  ,(*iter)->GetParent()->GetScreenPosition() + (*iter)->GetPos()
626  ,wxDefaultSize
627  ,(*iter)->GetTipInfo()
628 #if defined(__WXOSX_CARBON__) || defined(__WXMAC_CARBON__)
629  ,m_Host->GetWindowGroup()
630 #endif
631  );
632  tt->SetTargetSize( (*iter)->GetTargetSize() );
633  tt->Show(is_visible);
634  tt->SetPinned(true);
635  tt->SetLinkEventHandler(this);
636 
637  // Set pin graphic
638  wxCommandEvent dummy_event;
639  tt->OnPinButton(dummy_event);
640 
641  tt->FitToWindow();
642  m_Host->RaiseOverlappingWindows(tt->GetRect());
643  (*iter)->Destroy();
644  AddTip(tt);
645  }
646 
647  m_SuspendedTips.clear();
648 
649  UpdateTips();
650 }
651 
652 // On Mac frames other than the main app can appear in front of
653 // their tool tips when they get focus. (but that is handled in
654 // activate now)
656 {
657 }
658 
660 {
661  if (NULL != m_UnpinnedTip) {
662  // Send event to host window so it can un-highlight
663  // the glyph/elelemnt if it is currently active
664  wxCommandEvent evt(wxEVT_COMMAND_BUTTON_CLICKED, eCmdTipInactive);
665  evt.SetEventObject(m_UnpinnedTip);
666  m_Host->TTHH_GetWindow()->GetEventHandler()->ProcessEvent(evt);
667 
668  // Separate command for keeping track of when tooltips are deleted
669  // (The eCmdTipInactive tracks whether tip has focus, not whether it
670  // has been removed).
671  wxCommandEvent evt2(wxEVT_COMMAND_BUTTON_CLICKED, eCmdTipRemoved);
672  evt2.SetEventObject(m_UnpinnedTip);
673  m_Host->TTHH_GetWindow()->GetEventHandler()->ProcessEvent(evt2);
674 
675  m_UnpinnedTip->Show(false);
676  m_UnpinnedTip->Destroy();
678 
679  m_TimerClearUnpinned.Stop();
680 
681  UpdateTips();
682  }
683 }
684 void CStickyTooltipHandler::OnSize(wxSizeEvent& event)
685 {
686  Resize();
687  event.Skip();
688 }
689 
690 // Timer update function for tip manager. The timer to call this is
691 // kicked off when we see a tip applies to the current mouse location,
692 // and this creates the tip if the mouse is still close enough that
693 // the tip is still 'needed'.
695 {
696  wxPoint p = wxGetMousePosition();
697  wxPoint delta = m_TipInitPos - p;
698 
699  wxRect screen_rect = m_Host->TTHH_GetWindow()->GetScreenRect();
700 
701  // Don't pop up a tool tip if the cursor has moved outside the window
702  if (!screen_rect.Contains(p))
703  return;
704 
705  // Don't pop up a tool tip if the right-click menu has come up.
707  return;
708 
709  wxPoint unused_pt;
710  wxWindow* w = m_Host->TTHH_GetWindow();
711  wxWindow* mouse_win = ::wxFindWindowAtPointer(unused_pt);
712 
713  if (w != mouse_win)
714  return;
715 
716  // make sure timer stops since it prevents new tips from coming up (really
717  // should not be a problem)
718  Stop();
719 
720  // If mouse moved too far to allow a tip to be displayed, check again.
721  // This is needed because standard check only happens on mouse motion,
722  // and mouse may have stopped while on the glyph that needs a tooltip.
723  if (delta.x*delta.x + delta.y*delta.y >
725  wxPoint mouse_pos = p - m_Host->TTHH_GetWindow()->GetScreenPosition();
726  CheckForNewTip(mouse_pos);
727  return;
728  }
729 
730  // Only display tip if the host window is still displayed
731  // (window could change in short period since timer started)
732  if (m_Host->TTHH_GetWindow()->IsShownOnScreen() ) {
733  // Get the info for the tooltip. In theory, this could be a different
734  // tip than was identified in CheckForNewTip(), since the cursor
735  // could have moved by up to 'm_MouseMoveThreshold'. It could also return
736  // an empty tip, in which case we display nothing.
737  wxSize pt(1,1);
738  wxRect area(p - m_Host->TTHH_GetWindow()->GetScreenPosition(), pt);
739  CTooltipInfo next_tip = m_Host->TTHH_GetTooltip(area);
740  if (next_tip.GetTipID() != "") {
741  int cursor_y = wxSystemSettings::GetMetric(wxSYS_CURSOR_Y);
742  int cursor_x = wxSystemSettings::GetMetric(wxSYS_CURSOR_X);
743 
744  if (p.y + cursor_y/3 > screen_rect.GetBottom())
745  cursor_y = 0;
746  if (p.x + cursor_x/3 > screen_rect.GetRight())
747  cursor_x = 0;
748 
750  ,wxT("wxWidgets Child Frame")
751 #if defined(__WXOSX_CARBON__) || defined(__WXMAC_CARBON__) || defined(__WXOSX_COCOA__)
752  ,wxPoint(p.x+4,p.y+4)
753 #else
754  ,wxPoint(p.x+(int)(((float)cursor_x)*m_PopupDistance),
755  p.y+(int)(((float)cursor_y)*m_PopupDistance))
756 #endif
757  ,wxDefaultSize
758  ,next_tip
759 #if defined(__WXOSX_CARBON__) || defined(__WXMAC_CARBON__)
760  ,m_Host->GetWindowGroup()
761 #endif
762  );
763  tt->Show(true);
764  tt->SetLinkEventHandler(this);
765  AddUnpinned(tt);
766 
767 #ifdef __WXGTK__
768  // GTK keeps sending mouse-move events to the (underlying) window
769  // until lmouseup, even though the mouse is in the newly created
770  // tooltip window. This *looks* like the mouse has moved away from
771  // the tip which closes the tip window. Give the new window focus
772  // right away. GTK only.
773 
774  tt->SetFocus();
775 #endif
776 
777  tt->SetInitialMousePos(wxGetMousePosition());
778  }
779  }
780 }
781 
783 {
784  try {
785  // If there is already an unpinned tip up, don't start the process of displaying
786  // a new tip. Let the user move the mouse (outside the borders of the unpinned
787  // tip) to get the unpinned tip to go away first.
788  if (NULL != m_UnpinnedTip) {
789  // Mouse is moving in window with an unpinned tip. Set a timer
790  // to remove the tip (unless the mouse gets inside the tip which
791  // will prevent the removal from happening). Set a tiny distance
792  // threshold before starting the timer to keep the tips from being
793  // too "sensitive".
794  if (!m_TimerClearUnpinned.IsRunning()) {
795  wxPoint m = wxGetMousePosition();
796  wxPoint delta = m_UnpinnedTip->GetInitialMousePos() - m;
797  if (delta.x*delta.x + delta.y*delta.y >
800  }
801  }
802 
803  return;
804  }
805 
806  // See if a tooltip applies to this position. Note that (in order to avoid
807  // network delays) the returned id may not be consistent for tooltips
808  // that are not already displayed.
809  string need_id = m_Host->TTHH_NeedTooltip(pos);
810 
811  bool tip_displayed = false;
812  // With sticky tips, some may already be displayed. Don't display
813  // multiple windows for the same tip
814  for (unsigned int i = 0; i < m_ToolTips.size(); ++i) {
815  if (m_ToolTips[i]->GetTipInfo().GetTipID() == need_id) {
816  tip_displayed = true;
817  m_ToolTips[i]->SetElementActive(true);
818  }
819  else {
820  m_ToolTips[i]->SetElementActive(false);
821  }
822  }
823 
824  // If no tip currently displayed and one is needed, and we haven't set the
825  // timer to display the next tip alredy, start a new tip
826  if (!tip_displayed &&
827  need_id != "" &&
828  !IsRunning()) {
829 
830  // Show tip after short delay, but do not show if mouse moves in
831  // meantime. Keeps tips from popping up too quickly.
832  m_TipInitPos = wxGetMousePosition();
833  Start(m_TipPopupDelay, wxTIMER_ONE_SHOT);
834  }
835  } NCBI_CATCH("CheckForNewTip");
836 }
837 
838 void CStickyTooltipHandler::OnMotion(wxMouseEvent& event)
839 {
840  CheckForNewTip(event.GetPosition());
841  event.Skip();
842 }
843 
844 
845 // Do this for middle button too. Suppresses tooltips when right
846 // and middle button are being used.
848 {
849  ClearUnpinned();
850 
851  // If a timer was initiated to display a new tip,
852  // this kills the timer (and tip)
853  Stop();
854  event.Skip();
855 }
856 
857 void CStickyTooltipHandler::OnMouseWheel(wxMouseEvent& event)
858 {
859  CheckForNewTip(event.GetPosition());
860  event.Skip();
861 }
862 
863 
864 // If mouse leaves the window, clear away any unpinned tips
865 void CStickyTooltipHandler::OnLeaveWindow(wxMouseEvent& event)
866 {
867  // Don't unpin tip if mouse is still within the window area, since
868  // it is most likely just on one of this windows own tooltips.
869  wxRect screen_rect = m_Host->TTHH_GetWindow()->GetScreenRect();
870 
871  // Don't pop up a tool tip if the cursor has moved outside the window
872  wxPoint p = wxGetMousePosition();
873  if (screen_rect.Contains(p)) {
874  event.Skip();
875  return;
876  }
877 
878  ClearUnpinned();
879 
880  // If a timer was initiated to display a new tip,
881  // this kills the timer (and tip)
882  Stop();
883 
884  event.Skip();
885 }
886 
887 void CStickyTooltipHandler::OnPinTip(wxCommandEvent& event)
888 {
889  // Need the tool tip window for the event - we stored its pointer in
890  // the button's (event objects) client data when the button was created
891  wxEvtHandler* obj = dynamic_cast<wxEvtHandler*>(event.GetEventObject());
892  if (obj != NULL) {
893  CTooltipFrame* f = static_cast<CTooltipFrame*>(obj->GetClientData());
894 
895  if (f != NULL) {
896  if (f->IsPinned())
897  UnpinPinned(f);
898  else
899  PinUnpinned(f);
900 
901  // Call event function directly rather than
902  // have tip respond to event since we want this event
903  // processing (in in STHandler) to happen first.
904  f->OnPinButton(event);
905  }
906  }
907 }
908 
909 void CStickyTooltipHandler::OnTipRelease(wxCommandEvent& event)
910 {
911  // Tip would still be the active tip on release, so we shouldn't
912  // need to clip it to the window (wait until mouse leaves window)
913  /*
914  if (event.GetEventObject() != NULL) {
915  CTooltipFrame* tt = dynamic_cast<CTooltipFrame*>(event.GetEventObject());
916  if (tt != NULL) {
917  tt->FitToWindow();
918  m_Host->RaiseOverlappingWindows(tt->GetRect());
919  }
920  }
921  */
922 }
923 
924 void CStickyTooltipHandler::OnTipMove(wxCommandEvent& event)
925 {
926  UpdateTips();
927  wxCommandEvent evt(wxEVT_COMMAND_BUTTON_CLICKED, eCmdTipActive);
928  evt.SetEventObject(m_ActiveTip);
929  m_Host->TTHH_GetWindow()->GetEventHandler()->ProcessEvent(evt);
930 }
931 
932 void CStickyTooltipHandler::OnWindowMoveCmd(wxCommandEvent& event)
933 {
934 // In Cocoa the frames are attached to the parents such that
935 // moving the parent moves the child. (NSWindow: addChildWindow:ordered:)
936 // Programtically moving the widget in this case should have no effect
937 // so it mostly works if we don't call ParentMove (under __WXOSX_COCOA__)
938 // BUT if you shake the widow hard enough (with the mouse) on cocoa,
939 // the tip can 'fall off' and this fixes that, so we leave it in.
940 //#ifndef __WXOSX_COCOA__
941  ParentMove(m_Host->TTHH_GetWindow()->GetScreenPosition());
942 //#endif
943 }
944 
945 void CStickyTooltipHandler::OnSuspendCmd(wxCommandEvent& event)
946 {
947  Suspend();
948 }
949 
950 void CStickyTooltipHandler::OnReCreateCmd(wxCommandEvent& event)
951 {
952  ReCreate(0);
953 }
954 
956 {
957  // Currently does same thing as recreate, but I left this in
958  // in case we need to handle docking events differently at
959  // some point)
960  ReCreate(event.GetInt());
961 }
962 
963 void CStickyTooltipHandler::OnActivateCmd(wxCommandEvent& event)
964 {
965  // Only Mac (Carbon) - tooltips will fall behind main window on occasion,
966  // esp. when a window is undocked from main window and user
967  // subsequently clicks on main window title bar (activates).
968 
969 #if defined(__WXOSX_CARBON__) || defined(__WXMAC_CARBON__)
970  wxWindow* w = m_Host->TTHH_GetWindow();
971  bool shown = true;
972  while (w != NULL && shown) {
973  //_TRACE(w->GetClassInfo()->GetClassName());
974 
975  if (!w->IsShown())
976  shown = false;
977  w = w->GetParent();
978  }
979 
980  if (shown) {
981  //_TRACE("Reshowing on activate.");
982  ParentShow(false);
983  ParentShow(true);
984  }
985 #endif
986 }
987 
988 void CStickyTooltipHandler::OnWindowShowCmd(wxCommandEvent& event)
989 {
990  ParentShow(true);
991 }
992 
993 void CStickyTooltipHandler::OnWindowHideCmd(wxCommandEvent& event)
994 {
995  ParentShow(false);
996 }
997 
998 
999 bool CStickyTooltipHandler::checkChildFocus(wxWindow* fwin, wxWindow* checkwin)
1000 {
1001  // Return true if any child of fwin currently has focus
1002  if (checkwin==fwin) return true;
1003  for (unsigned int i=0; i<checkwin->GetChildren().size(); ++i) {
1004  bool b = checkChildFocus(fwin, checkwin->GetChildren()[i]);
1005  if (b) return true;
1006  }
1007 
1008  return false;
1009 }
1010 
1012 {
1013  if (m_Host != NULL) {
1014  std::vector<IStickyTooltipHandlerHost::TipLocation> tips;
1015 
1016  std::vector<CTooltipFrame*> tip_windows = m_ToolTips;
1017  if (m_UnpinnedTip != NULL)
1018  tip_windows.push_back(m_UnpinnedTip);
1019 
1020  for (size_t i=0; i<tip_windows.size(); ++i) {
1021  wxPoint pos = tip_windows[i]->GetPos();
1022  wxSize sz = tip_windows[i]->GetSize();
1023  wxSize win_sz = m_Host->TTHH_GetWindow()->GetSize();
1024 
1026 
1027  tl.TipRect.Init(pos.x,
1028  win_sz.GetHeight() - (pos.y + sz.GetHeight()),
1029  pos.x + sz.GetWidth(),
1030  win_sz.GetHeight()-pos.y);
1031  tl.TipID = tip_windows[i]->GetTipInfo().GetTipID();
1032  tips.push_back(tl);
1033  }
1034 
1035  m_Host->x_SetCurrentTips(tips);
1036  }
1037 }
1038 
CStickyTooltipHandler : Class that manages all the tooltips for a specific window.
void OnTipRelease(wxCommandEvent &event)
void CheckForNewTip(wxPoint pos)
Checks if a new tip should be popped up for the current location.
IStickyTooltipHandlerHost * m_Host
Host window for tips.
bool checkChildFocus(wxWindow *fwin, wxWindow *checkwin)
virtual void ParentShow(bool show)
Called when parent is unhidden to show all tips.
void OnLeaveWindow(wxMouseEvent &event)
virtual void AddUnpinned(CTooltipFrame *tt)
Add an unpinned tip to the window.
int m_TipPopupDelay
Delay between seeing the need for a tip and displaying it.
void UpdateTips()
Any command that updates tip position should use this to keep the list of user-accessible tip informa...
int m_SuspendCount
Tracks suspend count for window for debugging.
virtual void ParentMove(const wxPoint &screen_pos)
Move all the tooltip windows along with the parent.
int m_ClearUnpinnedDelay
When the mouse starts moving after a tip pops up, this is the delay until the tip is removed (mouse m...
float m_UnpinnedMouseMoveThreshold
Mouse move threshold for disabling tip after it pops up.
void OnWindowShowCmd(wxCommandEvent &event)
float m_MouseMoveThreshold
Mouse move threshold for disabling tip before it pops up.
virtual void OnTimer(wxTimerEvent &event)
Determines which tip mouse is on (if any) and brings it to front.
void OnRightMouseDown(wxMouseEvent &event)
CTooltipFrame * m_ActiveTip
If the mouse pointer is inside the boundries of a tip,that tip is m_ActiveTip.
wxTimer m_TimerClearUnpinned
Timer to clear away unpinned tip if mouse starts moving.
void OnPinTip(wxCommandEvent &event)
virtual void OnSetFocus()
Currently does nothing.
virtual void PinUnpinned(CTooltipFrame *tt)
Take the current upinned tip and pin it (add it to m_ToolTips)
virtual void ClearUnpinned()
Delete the current unpinned tip (if any)
virtual void Suspend()
Hides tips and then copies them from m_ToolTips to m_SuspendedTips - called before docking starts.
void OnSize(wxSizeEvent &event)
void OnWindowMoveCmd(wxCommandEvent &event)
void OnMotion(wxMouseEvent &event)
Commands propogated from parent windows to initiate move, resize, docking (suspend/recreate),...
void OnSuspendCmd(wxCommandEvent &event)
void OnActivateCmd(wxCommandEvent &event)
float m_PopupDistance
Distance from tip that the tooltip pops up.
wxPoint m_TipInitPos
position of mouse when need for new tip indicated
virtual void UnpinPinned(CTooltipFrame *tt)
Remove a tip from m_ToolTips, and set m_UnpinnedTip to the tip.
void OnWindowHideCmd(wxCommandEvent &event)
virtual void ReCreate(int dock_count=0)
Recreates suspended tips (old tips are deleted).
wxTimer m_Timer
Timer to watch mouse and change active tip as mouse moves.
std::vector< CTooltipFrame * > m_ToolTips
All tooltips for current window (m_Host)
virtual void AddTip(CTooltipFrame *tt)
Add tool tip to current window.
void OnTipMove(wxCommandEvent &event)
std::vector< CTooltipFrame * > m_SuspendedTips
Hidden tooltips (tips are all hidden when window is being (un)docked)
void OnReCreateCmd(wxCommandEvent &event)
virtual void Resize()
Clips all tip windows to parent window.
CTooltipFrame * m_UnpinnedTip
At a given time, only 1 tip can be unpinned.
void OnMouseWheel(wxMouseEvent &event)
void OnMainWindowDockCmd(wxCommandEvent &event)
The tooltip window that displays tip information.
void SetLinkEventHandler(ILinkEventHandler *linkEventHandler)
Pointer to the links event hanlder.
wxPoint GetInitialMousePos() const
virtual bool Show(bool show=true)
Override Show from wxWindow to add/remove frame from its parent window (on mac cocoa)
void SetPinned(bool b)
Set flag to indicate if tip is pinned or not.
bool LaunchedBrowser() const
Return true if a browser has been launchced fromt this tip.
virtual void OnPinButton(wxCommandEvent &evt)
When the pin/unpin button is pressed.
void SetLaunchedFalse()
When the tip is no longer active, this is called to remove the 'launched' property.
void FitToWindow(bool about_to_show=false)
Clip tip to parent window.
bool TipPopupMenuDisplayed() const
Return true if this tip windows on internal popup menu is displayed.
void SetToDefaultSize()
Set tip window to its full (unclipped) size.
void SetInitialMousePos(wxPoint p)
void SetTargetSize(wxSize s)
Set the full (unclipped) size of the tip.
Contents of a single tool tip including display text and an ID that will indicate which underlying vi...
std::string GetTipID() const
CUICommandRegistry is a centralized registry where all application commands should be registered.
Definition: ui_command.hpp:146
static CUICommandRegistry & GetInstance()
the main instance associated with the application
Definition: ui_command.cpp:176
int RegisterCommand(CUICommand *cmd)
assumes ownership of the given object returns a command id (useful when registry is used for auto id ...
Definition: ui_command.cpp:198
virtual string TTHH_NeedTooltip(const wxPoint &)
Return id of the underlying element to dispaly a tooltip, otherwise "".
virtual CTooltipInfo TTHH_GetTooltip(const wxRect &)
Return the contents to be displayed in the tool tip.
bool IsFloatingWindow()
Return true if the hosting window is ultimately enclosed in a CFloatingFrame class (meaning its in a ...
virtual wxWindow * TTHH_GetWindow()
Return the pointer to the underlying window.
virtual bool TTHH_PopupMenuDisplayed()
Return true if underlying window is currently displaying a popup menu.
void TTHH_Init()
Init calls virtual functions so can't be called from ctor.
wxFrame * GetUnderlyingFrame()
Return the undering wxFrame parent for a wxWindow object.
IServiceLocator * m_ServiceLocator
Needed to find other views.
void InitWindowGroup(int dock_count=0)
Mac only - create or return window group for this window.
bool IsDragging()
Retruns true if Any window is currently being dragged.
void RaiseOverlappingWindows(wxRect r)
If the tip window is docked in the main window (not a CFloatingFrame) this raises all the CFloatingFr...
void x_SetCurrentTips(std::vector< TipLocation > &tips)
Sets information on current tips that can be queried by the user.
IWindowManagerService Window Manager Service provides access to Window Manager functionality.
GUI command routing and handling framework.
static FILE * f
Definition: readconf.c:23
#define NULL
Definition: ncbistd.hpp:225
#define NCBI_CATCH(message)
Catch CExceptions as well This macro is deprecated - use *_X or *_XX variant instead of it.
Definition: ncbiexpt.hpp:580
virtual void RaiseFloatingWindowsInZOrder()=0
Raise all floating windows to preserve/refresh z-order.
CIRef< T > GetServiceByType()
retrieves a typed reference to a service, the name of C++ type is used as the name of the service.
Definition: service.hpp:91
virtual bool IsDragging() const =0
Returns true if a drag operation is currently in progress.
void Init()
Definition: glrect.hpp:62
@ eCmdParentHide
tool tip parent window shown
Definition: command.hpp:123
@ eCmdReleaseTip
User clicked somewhere on a tip.
Definition: command.hpp:129
@ eCmdCopySelectedTipText
User selected option to make text selectable.
Definition: command.hpp:135
@ eCmdParentMove
Definition: command.hpp:117
@ eCmdMoveTip
Left mouse (or equiv) up inside tip area.
Definition: command.hpp:130
@ eCmdParentActivate
tool tip cmd indicating a main-window docking update
Definition: command.hpp:121
@ eCmdCopyTipText
Mouse left a tip (remove glyph highlight)
Definition: command.hpp:133
@ eCmdSelectTipText
User selected option to copy (all) tip text.
Definition: command.hpp:134
@ eCmdReCreate
tool tip parent window about to dock
Definition: command.hpp:119
@ eCmdTipRemoved
Event fired when a new tip added.
Definition: command.hpp:137
@ eCmdSuspend
tool tip window parent moves
Definition: command.hpp:118
@ eCmdParentShow
tool tip parent window activated
Definition: command.hpp:122
@ eCmdTipActive
User is interactively moving a tool tip.
Definition: command.hpp:131
@ eCmdTipAdded
Copy currently selected tip text to clipboard.
Definition: command.hpp:136
@ eCmdDockMainWindow
tool tip parent window finished docking
Definition: command.hpp:120
@ eCmdTipInactive
Mouse entered a tip (highlight matching glyph)
Definition: command.hpp:132
@ eCmdPinTip
tool tip parent window hidden
Definition: command.hpp:124
#define END_NCBI_SCOPE
End previously defined NCBI scope.
Definition: ncbistl.hpp:103
#define BEGIN_NCBI_SCOPE
Define ncbi namespace.
Definition: ncbistl.hpp:100
END_EVENT_TABLE()
int i
#define wxT(x)
Definition: muParser.cpp:41
const struct ncbi::grid::netcache::search::fields::SIZE size
NCBI C++ auxiliary debug macros.
Int4 delta(size_t dimension_, const Int4 *score_)
double r(size_t dimension_, const Int4 *score_, const double *prob_, double theta_)
#define TTMGR_TIMER_ID
#define TTMGR_TIMER_CLEAR_UNPINNED
#define _ASSERT
Modified on Fri Sep 20 14:58:21 2024 by modify_doxy.py rev. 669887