- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / bookmarks / bookmark_bar_folder_hover_state.mm
1 // Copyright (c) 2011 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