Upstream version 11.39.266.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / toolbar / reload_button.mm
1 // Copyright (c) 2012 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/toolbar/reload_button.h"
6
7 #include "chrome/app/chrome_command_ids.h"
8 #import "chrome/browser/ui/cocoa/view_id_util.h"
9 #include "chrome/grit/generated_resources.h"
10 #include "grit/theme_resources.h"
11 #include "ui/base/l10n/l10n_util.h"
12 #include "ui/base/l10n/l10n_util_mac.h"
13
14 namespace {
15
16 // Constant matches Windows.
17 NSTimeInterval kPendingReloadTimeout = 1.35;
18
19 }  // namespace
20
21 @interface ReloadButton ()
22 - (void)invalidatePendingReloadTimer;
23 - (void)forceReloadState:(NSTimer *)timer;
24 @end
25
26 @implementation ReloadButton
27
28 + (Class)cellClass {
29   return [ImageButtonCell class];
30 }
31
32 - (id)initWithFrame:(NSRect)frameRect {
33   if ((self = [super initWithFrame:frameRect])) {
34     // Since this is not a custom view, -awakeFromNib won't be called twice.
35     [self awakeFromNib];
36   }
37   return self;
38 }
39
40 - (void)viewWillMoveToWindow:(NSWindow *)newWindow {
41   // If this view is moved to a new window, reset its state.
42   [self setIsLoading:NO force:YES];
43   [super viewWillMoveToWindow:newWindow];
44 }
45
46 - (void)awakeFromNib {
47   // Don't allow multi-clicks, because the user probably wouldn't ever
48   // want to stop+reload or reload+stop.
49   [self setIgnoresMultiClick:YES];
50 }
51
52 - (void)invalidatePendingReloadTimer {
53   [pendingReloadTimer_ invalidate];
54   pendingReloadTimer_ = nil;
55 }
56
57 - (void)updateTag:(NSInteger)anInt {
58   if ([self tag] == anInt)
59     return;
60
61   // Forcibly remove any stale tooltip which is being displayed.
62   [self removeAllToolTips];
63   id cell = [self cell];
64   [self setTag:anInt];
65   if (anInt == IDC_RELOAD) {
66     [cell setImageID:IDR_RELOAD
67       forButtonState:image_button_cell::kDefaultState];
68     [cell setImageID:IDR_RELOAD_H
69       forButtonState:image_button_cell::kHoverState];
70     [cell setImageID:IDR_RELOAD_P
71       forButtonState:image_button_cell::kPressedState];
72     // The stop button has a disabled image but the reload button doesn't. To
73     // unset it we have to explicilty change the image ID to 0.
74     [cell setImageID:0
75       forButtonState:image_button_cell::kDisabledState];
76     [self setToolTip:l10n_util::GetNSStringWithFixup(IDS_TOOLTIP_RELOAD)];
77   } else if (anInt == IDC_STOP) {
78     [cell setImageID:IDR_STOP
79       forButtonState:image_button_cell::kDefaultState];
80     [cell setImageID:IDR_STOP_H
81       forButtonState:image_button_cell::kHoverState];
82     [cell setImageID:IDR_STOP_P
83       forButtonState:image_button_cell::kPressedState];
84     [cell setImageID:IDR_STOP_D
85       forButtonState:image_button_cell::kDisabledState];
86     [self setToolTip:l10n_util::GetNSStringWithFixup(IDS_TOOLTIP_STOP)];
87   } else {
88     NOTREACHED();
89   }
90 }
91
92 - (id)accessibilityAttributeValue:(NSString *)attribute {
93   if ([attribute isEqualToString:NSAccessibilityEnabledAttribute] &&
94       pendingReloadTimer_) {
95     return [NSNumber numberWithBool:NO];
96   } else {
97     return [super accessibilityAttributeValue:attribute];
98   }
99 }
100
101 - (void)setIsLoading:(BOOL)isLoading force:(BOOL)force {
102   // Can always transition to stop mode.  Only transition to reload
103   // mode if forced or if the mouse isn't hovering.  Otherwise, note
104   // that reload mode is desired and disable the button.
105   if (isLoading) {
106     [self invalidatePendingReloadTimer];
107     [self updateTag:IDC_STOP];
108   } else if (force) {
109     [self invalidatePendingReloadTimer];
110     [self updateTag:IDC_RELOAD];
111   } else if ([self tag] == IDC_STOP &&
112              !pendingReloadTimer_ &&
113              [[self cell] isMouseInside]) {
114     id cell = [self cell];
115     [cell setImageID:IDR_STOP_D
116       forButtonState:image_button_cell::kDefaultState];
117     [cell setImageID:IDR_STOP_D
118       forButtonState:image_button_cell::kDisabledState];
119     [cell setImageID:IDR_STOP_D
120       forButtonState:image_button_cell::kHoverState];
121     [cell setImageID:IDR_STOP_D
122       forButtonState:image_button_cell::kPressedState];
123     pendingReloadTimer_ =
124         [NSTimer timerWithTimeInterval:kPendingReloadTimeout
125                                 target:self
126                               selector:@selector(forceReloadState:)
127                               userInfo:nil
128                                repeats:NO];
129     // Must add the timer to |NSRunLoopCommonModes| because
130     // it should run in |NSEventTrackingRunLoopMode| as well as
131     // |NSDefaultRunLoopMode|.
132     [[NSRunLoop currentRunLoop] addTimer:pendingReloadTimer_
133                                  forMode:NSRunLoopCommonModes];
134   } else {
135     [self invalidatePendingReloadTimer];
136     [self updateTag:IDC_RELOAD];
137   }
138   [self setEnabled:pendingReloadTimer_ == nil];
139 }
140
141 - (void)forceReloadState:(NSTimer *)timer {
142   DCHECK_EQ(timer, pendingReloadTimer_);
143   [self setIsLoading:NO force:YES];
144   // Verify that |pendingReloadTimer_| is nil so it is not left dangling.
145   DCHECK(!pendingReloadTimer_);
146 }
147
148 - (BOOL)sendAction:(SEL)theAction to:(id)theTarget {
149   if ([self tag] == IDC_STOP) {
150     if (pendingReloadTimer_) {
151       // If |pendingReloadTimer_| then the control is currently being
152       // drawn in a disabled state, so just return. The control is NOT actually
153       // disabled, otherwise mousetracking (courtesy of the NSButtonCell)
154       // would not work.
155       return YES;
156     } else {
157       // When the stop is processed, immediately change to reload mode,
158       // even though the IPC still has to bounce off the renderer and
159       // back before the regular |-setIsLoaded:force:| will be called.
160       // [This is how views and gtk do it.]
161       BOOL ret = [super sendAction:theAction to:theTarget];
162       if (ret)
163         [self forceReloadState:pendingReloadTimer_];
164       return ret;
165     }
166   }
167
168   return [super sendAction:theAction to:theTarget];
169 }
170
171 - (ViewID)viewID {
172   return VIEW_ID_RELOAD_BUTTON;
173 }
174
175 - (void)mouseInsideStateDidChange:(BOOL)isInside {
176   [pendingReloadTimer_ fire];
177 }
178
179 @end  // ReloadButton
180
181 @implementation ReloadButton (Testing)
182
183 + (void)setPendingReloadTimeout:(NSTimeInterval)seconds {
184   kPendingReloadTimeout = seconds;
185 }
186
187 @end