• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_hover_state.h"
6#import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.h"
7
8@interface BookmarkBarFolderHoverState(Private)
9- (void)setHoverState:(HoverState)state;
10- (void)closeBookmarkFolderOnHoverButton:(BookmarkButton*)button;
11- (void)openBookmarkFolderOnHoverButton:(BookmarkButton*)button;
12@end
13
14@implementation BookmarkBarFolderHoverState
15
16- (id)init {
17  if ((self = [super init])) {
18    hoverState_ = kHoverStateClosed;
19  }
20  return self;
21}
22
23- (NSDragOperation)draggingEnteredButton:(BookmarkButton*)button {
24  if ([button isFolder]) {
25    if (hoverButton_ == button) {
26      // CASE A: hoverButton_ == button implies we've dragged over
27      // the same folder so no need to open or close anything new.
28    } else if (hoverButton_ &&
29               hoverButton_ != button) {
30      // CASE B: we have a hoverButton_ but it is different from the new button.
31      // This implies we've dragged over a new folder, so we'll close the old
32      // and open the new.
33      // Note that we only schedule the open or close if we have no other tasks
34      // currently pending.
35
36      if (hoverState_ == kHoverStateOpen) {
37        // Close the old.
38        [self scheduleCloseBookmarkFolderOnHoverButton];
39      } else if (hoverState_ == kHoverStateClosed) {
40        // Open the new.
41        [self scheduleOpenBookmarkFolderOnHoverButton:button];
42      }
43    } else if (!hoverButton_) {
44      // CASE C: we don't have a current hoverButton_ but we have dragged onto
45      // a new folder so we open the new one.
46      [self scheduleOpenBookmarkFolderOnHoverButton:button];
47    }
48  } else if (!button) {
49    if (hoverButton_) {
50      // CASE D: We have a hoverButton_ but we've moved onto an area that
51      // requires no hover.  We close the hoverButton_ in this case.  This
52      // means cancelling if the open is pending (i.e. |kHoverStateOpening|)
53      // or closing if we don't alrealy have once in progress.
54
55      // Intiate close only if we have not already done so.
56      if (hoverState_ == kHoverStateOpening) {
57        // Cancel the pending open.
58        [self cancelPendingOpenBookmarkFolderOnHoverButton];
59      } else if (hoverState_ != kHoverStateClosing) {
60        // Schedule the close.
61        [self scheduleCloseBookmarkFolderOnHoverButton];
62      }
63    } else {
64      // CASE E: We have neither a hoverButton_ nor a new button that requires
65      // a hover.  In this case we do nothing.
66    }
67  }
68
69  return NSDragOperationMove;
70}
71
72- (void)draggingExited {
73  if (hoverButton_) {
74    if (hoverState_ == kHoverStateOpening) {
75      [self cancelPendingOpenBookmarkFolderOnHoverButton];
76    } else if (hoverState_ == kHoverStateClosing) {
77      [self cancelPendingCloseBookmarkFolderOnHoverButton];
78    }
79  }
80}
81
82// Schedule close of hover button.  Transition to kHoverStateClosing state.
83- (void)scheduleCloseBookmarkFolderOnHoverButton {
84  DCHECK(hoverButton_);
85  [self setHoverState:kHoverStateClosing];
86  [self performSelector:@selector(closeBookmarkFolderOnHoverButton:)
87             withObject:hoverButton_
88             afterDelay:bookmarks::kDragHoverCloseDelay
89                inModes:[NSArray arrayWithObject:NSRunLoopCommonModes]];
90}
91
92// Cancel pending hover close.  Transition to kHoverStateOpen state.
93- (void)cancelPendingCloseBookmarkFolderOnHoverButton {
94  [self setHoverState:kHoverStateOpen];
95  [NSObject
96      cancelPreviousPerformRequestsWithTarget:self
97      selector:@selector(closeBookmarkFolderOnHoverButton:)
98      object:hoverButton_];
99}
100
101// Schedule open of hover button.  Transition to kHoverStateOpening state.
102- (void)scheduleOpenBookmarkFolderOnHoverButton:(BookmarkButton*)button {
103  DCHECK(button);
104  hoverButton_.reset([button retain]);
105  [self setHoverState:kHoverStateOpening];
106  [self performSelector:@selector(openBookmarkFolderOnHoverButton:)
107             withObject:hoverButton_
108             afterDelay:bookmarks::kDragHoverOpenDelay
109                inModes:[NSArray arrayWithObject:NSRunLoopCommonModes]];
110}
111
112// Cancel pending hover open.  Transition to kHoverStateClosed state.
113- (void)cancelPendingOpenBookmarkFolderOnHoverButton {
114  [self setHoverState:kHoverStateClosed];
115  [NSObject
116      cancelPreviousPerformRequestsWithTarget:self
117      selector:@selector(openBookmarkFolderOnHoverButton:)
118      object:hoverButton_];
119  hoverButton_.reset();
120}
121
122// Hover button accessor.  For testing only.
123- (BookmarkButton*)hoverButton {
124  return hoverButton_;
125}
126
127// Hover state accessor.  For testing only.
128- (HoverState)hoverState {
129  return hoverState_;
130}
131
132// This method encodes the rules of our |hoverButton_| state machine.  Only
133// specific state transitions are allowable (encoded in the DCHECK).
134// Note that there is no state for simultaneously opening and closing.  A
135// pending open must complete before scheduling a close, and vice versa.  And
136// it is not possible to make a transition directly from open to closed, and
137// vice versa.
138- (void)setHoverState:(HoverState)state {
139  DCHECK(
140    (hoverState_ == kHoverStateClosed && state == kHoverStateOpening) ||
141    (hoverState_ == kHoverStateOpening && state == kHoverStateClosed) ||
142    (hoverState_ == kHoverStateOpening && state == kHoverStateOpen) ||
143    (hoverState_ == kHoverStateOpen && state == kHoverStateClosing) ||
144    (hoverState_ == kHoverStateClosing && state == kHoverStateOpen) ||
145    (hoverState_ == kHoverStateClosing && state == kHoverStateClosed)
146  ) << "bad transition: old = " << hoverState_ << " new = " << state;
147
148  hoverState_ = state;
149}
150
151// Called after a delay to close a previously hover-opened folder.
152// Note: this method is not meant to be invoked directly, only through
153// a delayed call to |scheduleCloseBookmarkFolderOnHoverButton:|.
154- (void)closeBookmarkFolderOnHoverButton:(BookmarkButton*)button {
155  [NSObject
156      cancelPreviousPerformRequestsWithTarget:self
157      selector:@selector(closeBookmarkFolderOnHoverButton:)
158      object:hoverButton_];
159  [self setHoverState:kHoverStateClosed];
160  [[button target] closeBookmarkFolder:button];
161  hoverButton_.reset();
162}
163
164// Called after a delay to open a new hover folder.
165// Note: this method is not meant to be invoked directly, only through
166// a delayed call to |scheduleOpenBookmarkFolderOnHoverButton:|.
167- (void)openBookmarkFolderOnHoverButton:(BookmarkButton*)button {
168  [self setHoverState:kHoverStateOpen];
169  [[button target] performSelector:@selector(openBookmarkFolderFromButton:)
170      withObject:button];
171}
172
173@end
174