1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
|
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "chrome/browser/cocoa/bookmark_button.h"
#include "base/logging.h"
#import "base/scoped_nsobject.h"
#include "chrome/browser/bookmarks/bookmark_model.h"
#import "chrome/browser/cocoa/bookmark_button_cell.h"
#import "chrome/browser/cocoa/browser_window_controller.h"
#import "chrome/browser/cocoa/view_id_util.h"
#include "chrome/browser/metrics/user_metrics.h"
// The opacity of the bookmark button drag image.
static const CGFloat kDragImageOpacity = 0.7;
namespace bookmark_button {
NSString* const kPulseBookmarkButtonNotification =
@"PulseBookmarkButtonNotification";
NSString* const kBookmarkKey = @"BookmarkKey";
NSString* const kBookmarkPulseFlagKey = @"BookmarkPulseFlagKey";
};
@interface BookmarkButton(Private)
// Make a drag image for the button.
- (NSImage*)dragImage;
@end // @interface BookmarkButton(Private)
@implementation BookmarkButton
@synthesize delegate = delegate_;
- (id)initWithFrame:(NSRect)frameRect {
// BookmarkButton's ViewID may be changed to VIEW_ID_OTHER_BOOKMARKS in
// BookmarkBarController, so we can't just override -viewID method to return
// it.
if ((self = [super initWithFrame:frameRect]))
view_id_util::SetID(self, VIEW_ID_BOOKMARK_BAR_ELEMENT);
return self;
}
- (void)dealloc {
if ([[self cell] respondsToSelector:@selector(safelyStopPulsing)])
[[self cell] safelyStopPulsing];
view_id_util::UnsetID(self);
[super dealloc];
}
- (const BookmarkNode*)bookmarkNode {
return [[self cell] bookmarkNode];
}
- (BOOL)isFolder {
const BookmarkNode* node = [self bookmarkNode];
return (node && node->is_folder());
}
- (BOOL)isEmpty {
return [self bookmarkNode] ? NO : YES;
}
- (void)setIsContinuousPulsing:(BOOL)flag {
[[self cell] setIsContinuousPulsing:flag];
}
- (BOOL)isContinuousPulsing {
return [[self cell] isContinuousPulsing];
}
- (NSPoint)screenLocationForRemoveAnimation {
NSPoint point;
if (dragPending_) {
// Use the position of the mouse in the drag image as the location.
point = dragEndScreenLocation_;
point.x += dragMouseOffset_.x;
if ([self isFlipped]) {
point.y += [self bounds].size.height - dragMouseOffset_.y;
} else {
point.y += dragMouseOffset_.y;
}
} else {
// Use the middle of this button as the location.
NSRect bounds = [self bounds];
point = NSMakePoint(NSMidX(bounds), NSMidY(bounds));
point = [self convertPoint:point toView:nil];
point = [[self window] convertBaseToScreen:point];
}
return point;
}
// By default, NSButton ignores middle-clicks.
// But we want them.
- (void)otherMouseUp:(NSEvent*)event {
[self performClick:self];
}
// Overridden from DraggableButton.
- (void)beginDrag:(NSEvent*)event {
// Don't allow a drag of the empty node.
// The empty node is a placeholder for "(empty)", to be revisited.
if ([self isEmpty])
return;
if ([self delegate]) {
// Ask our delegate to fill the pasteboard for us.
NSPasteboard* pboard = [NSPasteboard pasteboardWithName:NSDragPboard];
[[self delegate] fillPasteboard:pboard forDragOfButton:self];
// At the moment, moving bookmarks causes their buttons (like me!)
// to be destroyed and rebuilt. Make sure we don't go away while on
// the stack.
[self retain];
// Lock bar visibility, forcing the overlay to stay visible if we are in
// fullscreen mode.
if ([[self delegate] dragShouldLockBarVisibility]) {
DCHECK(!visibilityDelegate_);
NSWindow* window = [[self delegate] browserWindow];
visibilityDelegate_ =
[BrowserWindowController browserWindowControllerForWindow:window];
[visibilityDelegate_ lockBarVisibilityForOwner:self
withAnimation:NO
delay:NO];
}
const BookmarkNode* node = [self bookmarkNode];
const BookmarkNode* parent = node ? node->GetParent() : NULL;
BOOL isWithinFolder = parent && parent->type() == BookmarkNode::FOLDER;
UserMetrics::RecordAction(UserMetricsAction(
isWithinFolder ? "BookmarkBarFolder_DragStart" :
"BookmarkBar_DragStart"));
dragMouseOffset_ = [self convertPointFromBase:[event locationInWindow]];
dragPending_ = YES;
CGFloat yAt = [self bounds].size.height;
NSSize dragOffset = NSMakeSize(0.0, 0.0);
[self dragImage:[self dragImage] at:NSMakePoint(0, yAt) offset:dragOffset
event:event pasteboard:pboard source:self slideBack:YES];
// And we're done.
dragPending_ = NO;
[self autorelease];
} else {
// Avoid blowing up, but we really shouldn't get here.
NOTREACHED();
}
}
// Overridden to release bar visibility.
- (void)endDrag {
// visibilityDelegate_ can be nil if we're detached, and that's fine.
[visibilityDelegate_ releaseBarVisibilityForOwner:self
withAnimation:YES
delay:YES];
visibilityDelegate_ = nil;
[super endDrag];
}
- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal {
NSDragOperation operation = NSDragOperationCopy;
if (isLocal) {
operation |= NSDragOperationMove;
}
if ([delegate_ canDragBookmarkButtonToTrash:self]) {
operation |= NSDragOperationDelete;
}
return operation;
}
- (void)draggedImage:(NSImage *)anImage
endedAt:(NSPoint)aPoint
operation:(NSDragOperation)operation {
if (operation & NSDragOperationDelete) {
dragEndScreenLocation_ = aPoint;
[delegate_ didDragBookmarkToTrash:self];
}
}
// mouseEntered: and mouseExited: are called from our
// BookmarkButtonCell. We redirect this information to our delegate.
// The controller can then perform menu-like actions (e.g. "hover over
// to open menu").
- (void)mouseEntered:(NSEvent*)event {
[delegate_ mouseEnteredButton:self event:event];
}
// See comments above mouseEntered:.
- (void)mouseExited:(NSEvent*)event {
[delegate_ mouseExitedButton:self event:event];
}
@end
@implementation BookmarkButton(Private)
- (NSImage*)dragImage {
NSRect bounds = [self bounds];
// Grab the image from the screen and put it in an |NSImage|. We can't use
// this directly since we need to clip it and set its opacity. This won't work
// if the source view is clipped. Fortunately, we don't display clipped
// bookmark buttons.
[self lockFocus];
scoped_nsobject<NSBitmapImageRep>
bitmap([[NSBitmapImageRep alloc] initWithFocusedViewRect:bounds]);
[self unlockFocus];
scoped_nsobject<NSImage> image([[NSImage alloc] initWithSize:[bitmap size]]);
[image addRepresentation:bitmap];
// Make an autoreleased |NSImage|, which will be returned, and draw into it.
// By default, the |NSImage| will be completely transparent.
NSImage* dragImage =
[[[NSImage alloc] initWithSize:[bitmap size]] autorelease];
[dragImage lockFocus];
// Draw the image with the appropriate opacity, clipping it tightly.
GradientButtonCell* cell = static_cast<GradientButtonCell*>([self cell]);
DCHECK([cell isKindOfClass:[GradientButtonCell class]]);
[[cell clipPathForFrame:bounds inView:self] setClip];
[image drawAtPoint:NSMakePoint(0, 0)
fromRect:NSMakeRect(0, 0, NSWidth(bounds), NSHeight(bounds))
operation:NSCompositeSourceOver
fraction:kDragImageOpacity];
[dragImage unlockFocus];
return dragImage;
}
@end // @implementation BookmarkButton(Private)
|