summaryrefslogtreecommitdiffstats
path: root/chrome/browser/cocoa/reload_button.mm
blob: 404b0358fe21e1c6fc3e7ab731e4936a235be9e8 (plain)
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
// 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/reload_button.h"

#include "base/nsimage_cache_mac.h"
#include "chrome/app/chrome_dll_resource.h"
#import "chrome/browser/cocoa/view_id_util.h"

namespace {

NSString* const kReloadImageName = @"reload_Template.pdf";
NSString* const kStopImageName = @"stop_Template.pdf";

}  // namespace

@implementation ReloadButton

- (void)dealloc {
  if (trackingArea_) {
    [self removeTrackingArea:trackingArea_];
    trackingArea_.reset();
  }
  [super dealloc];
}

- (void)updateTrackingAreas {
  // If the mouse is hovering when the tracking area is updated, the
  // control could end up locked into inappropriate behavior for
  // awhile, so unwind state.
  if (isMouseInside_)
    [self mouseExited:nil];

  if (trackingArea_) {
    [self removeTrackingArea:trackingArea_];
    trackingArea_.reset();
  }
  trackingArea_.reset([[NSTrackingArea alloc]
                        initWithRect:[self bounds]
                             options:(NSTrackingMouseEnteredAndExited |
                                      NSTrackingActiveInActiveApp)
                               owner:self
                            userInfo:nil]);
  [self addTrackingArea:trackingArea_];
}

- (void)awakeFromNib {
  [self updateTrackingAreas];

  // Don't allow multi-clicks, because the user probably wouldn't ever
  // want to stop+reload or reload+stop.
  [self setIgnoresMultiClick:YES];
}

- (void)setIsLoading:(BOOL)isLoading force:(BOOL)force {
  pendingReloadMode_ = NO;

  // Can always transition to stop mode.  Only transition to reload
  // mode if forced or if the mouse isn't hovering.  Otherwise, note
  // that reload mode is desired and disable the button.
  if (isLoading) {
    [self setImage:nsimage_cache::ImageNamed(kStopImageName)];
    [self setTag:IDC_STOP];
    [self setEnabled:YES];
  } else if (force || ![self isMouseInside]) {
    [self setImage:nsimage_cache::ImageNamed(kReloadImageName)];
    [self setTag:IDC_RELOAD];
    [self setEnabled:YES];
  } else if ([self tag] == IDC_STOP) {
    pendingReloadMode_ = YES;
    [self setEnabled:NO];
  }
}

- (BOOL)sendAction:(SEL)theAction to:(id)theTarget {
  if ([self tag] == IDC_STOP) {
    // The stop command won't be valid after the attempt to change
    // back to reload.  But it "worked", so short-circuit it.
    const BOOL ret =
        pendingReloadMode_ ? YES : [super sendAction:theAction to:theTarget];

    // When the stop is processed, immediately change to reload mode,
    // even though the IPC still has to bounce off the renderer and
    // back before the regular |-setIsLoaded:force:| will be called.
    // [This is how views and gtk do it.]
    if (ret)
      [self setIsLoading:NO force:YES];

    return ret;
  }

  return [super sendAction:theAction to:theTarget];
}

- (void)mouseEntered:(NSEvent*)theEvent {
  isMouseInside_ = YES;
}

- (void)mouseExited:(NSEvent*)theEvent {
  isMouseInside_ = NO;

  // Reload mode was requested during the hover.
  if (pendingReloadMode_)
    [self setIsLoading:NO force:YES];
}

- (BOOL)isMouseInside {
  return trackingArea_ && isMouseInside_;
}

- (ViewID)viewID {
  return VIEW_ID_RELOAD_BUTTON;
}

@end  // ReloadButton

@implementation ReloadButton (Testing)

- (NSTrackingArea*)trackingArea {
  return trackingArea_;
}

@end