<commit_before>// Copyright (c) 2014 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#include "shell/browser/ui/tray_icon_cocoa.h"

#include <string>
#include <vector>

#include "base/message_loop/message_pump_mac.h"
#include "base/strings/sys_string_conversions.h"
#include "base/task/current_thread.h"
#include "base/task/post_task.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "shell/browser/ui/cocoa/NSString+ANSI.h"
#include "shell/browser/ui/cocoa/electron_menu_controller.h"
#include "ui/events/cocoa/cocoa_event_utils.h"
#include "ui/gfx/mac/coordinate_conversion.h"
#include "ui/native_theme/native_theme.h"

@interface StatusItemView : NSView {
  electron::TrayIconCocoa* trayIcon_;       // weak
  ElectronMenuController* menuController_;  // weak
  BOOL ignoreDoubleClickEvents_;
  base::scoped_nsobject<NSStatusItem> statusItem_;
  base::scoped_nsobject<NSTrackingArea> trackingArea_;
}

@end  // @interface StatusItemView

@implementation StatusItemView

- (void)dealloc {
  trayIcon_ = nil;
  menuController_ = nil;
  [super dealloc];
}

- (id)initWithIcon:(electron::TrayIconCocoa*)icon {
  trayIcon_ = icon;
  menuController_ = nil;
  ignoreDoubleClickEvents_ = NO;

  if ((self = [super initWithFrame:CGRectZero])) {
    [self registerForDraggedTypes:@[
      NSFilenamesPboardType,
      NSStringPboardType,
    ]];

    // Create the status item.
    NSStatusItem* item = [[NSStatusBar systemStatusBar]
        statusItemWithLength:NSVariableStatusItemLength];
    statusItem_.reset([item retain]);
    [[statusItem_ button] addSubview:self];  // inject custom view
    [self updateDimensions];
  }
  return self;
}

- (void)updateDimensions {
  [self setFrame:[statusItem_ button].frame];
}

- (void)updateTrackingAreas {
  // Use NSTrackingArea for listening to mouseEnter, mouseExit, and mouseMove
  // events.
  [self removeTrackingArea:trackingArea_];
  trackingArea_.reset([[NSTrackingArea alloc]
      initWithRect:[self bounds]
           options:NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved |
                   NSTrackingActiveAlways
             owner:self
          userInfo:nil]);
  [self addTrackingArea:trackingArea_];
}

- (void)removeItem {
  // Turn off tracking events to prevent crash.
  if (trackingArea_) {
    [self removeTrackingArea:trackingArea_];
    trackingArea_.reset();
  }
  [[NSStatusBar systemStatusBar] removeStatusItem:statusItem_];
  [self removeFromSuperview];
  statusItem_.reset();
}

- (void)setImage:(NSImage*)image {
  [[statusItem_ button] setImage:image];
  [self updateDimensions];
}

- (void)setAlternateImage:(NSImage*)image {
  [[statusItem_ button] setAlternateImage:image];
}

- (void)setIgnoreDoubleClickEvents:(BOOL)ignore {
  ignoreDoubleClickEvents_ = ignore;
}

- (BOOL)getIgnoreDoubleClickEvents {
  return ignoreDoubleClickEvents_;
}

- (void)setTitle:(NSString*)title font_type:(NSString*)font_type {
  NSMutableAttributedString* attributed_title =
      [[NSMutableAttributedString alloc] initWithString:title];

  if ([title containsANSICodes]) {
    attributed_title = [title attributedStringParsingANSICodes];
  }

  // Change font type, if specified
  CGFloat existing_size = [[[statusItem_ button] font] pointSize];
  if ([font_type isEqualToString:@"monospaced"]) {
    if (@available(macOS 10.15, *)) {
      NSDictionary* attributes = @{
        NSFontAttributeName :
            [NSFont monospacedSystemFontOfSize:existing_size
                                        weight:NSFontWeightRegular]
      };
      [attributed_title<commit_msg>fix: macOS tray icon alternate images (#33026)<commit_after>// Copyright (c) 2014 GitHub, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#include "shell/browser/ui/tray_icon_cocoa.h"

#include <string>
#include <vector>

#include "base/message_loop/message_pump_mac.h"
#include "base/strings/sys_string_conversions.h"
#include "base/task/current_thread.h"
#include "base/task/post_task.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "shell/browser/ui/cocoa/NSString+ANSI.h"
#include "shell/browser/ui/cocoa/electron_menu_controller.h"
#include "ui/events/cocoa/cocoa_event_utils.h"
#include "ui/gfx/mac/coordinate_conversion.h"
#include "ui/native_theme/native_theme.h"

@interface StatusItemView : NSView {
  electron::TrayIconCocoa* trayIcon_;       // weak
  ElectronMenuController* menuController_;  // weak
  BOOL ignoreDoubleClickEvents_;
  base::scoped_nsobject<NSStatusItem> statusItem_;
  base::scoped_nsobject<NSTrackingArea> trackingArea_;
}

@end  // @interface StatusItemView

@implementation StatusItemView

- (void)dealloc {
  trayIcon_ = nil;
  menuController_ = nil;
  [super dealloc];
}

- (id)initWithIcon:(electron::TrayIconCocoa*)icon {
  trayIcon_ = icon;
  menuController_ = nil;
  ignoreDoubleClickEvents_ = NO;

  if ((self = [super initWithFrame:CGRectZero])) {
    [self registerForDraggedTypes:@[
      NSFilenamesPboardType,
      NSStringPboardType,
    ]];

    // Create the status item.
    NSStatusItem* item = [[NSStatusBar systemStatusBar]
        statusItemWithLength:NSVariableStatusItemLength];
    statusItem_.reset([item retain]);
    [[statusItem_ button] addSubview:self];  // inject custom view
    [self updateDimensions];
  }
  return self;
}

- (void)updateDimensions {
  [self setFrame:[statusItem_ button].frame];
}

- (void)updateTrackingAreas {
  // Use NSTrackingArea for listening to mouseEnter, mouseExit, and mouseMove
  // events.
  [self removeTrackingArea:trackingArea_];
  trackingArea_.reset([[NSTrackingArea alloc]
      initWithRect:[self bounds]
           options:NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved |
                   NSTrackingActiveAlways
             owner:self
          userInfo:nil]);
  [self addTrackingArea:trackingArea_];
}

- (void)removeItem {
  // Turn off tracking events to prevent crash.
  if (trackingArea_) {
    [self removeTrackingArea:trackingArea_];
    trackingArea_.reset();
  }
  [[NSStatusBar systemStatusBar] removeStatusItem:statusItem_];
  [self removeFromSuperview];
  statusItem_.reset();
}

- (void)setImage:(NSImage*)image {
  [[statusItem_ button] setImage:image];
  [self updateDimensions];
}

- (void)setAlternateImage:(NSImage*)image {
  [[statusItem_ button] setAlternateImage:image];

  // We need to change the button type here because the default button type for
  // NSStatusItem, NSStatusBarButton, does not display alternate content when
  // clicked. NSButtonTypeMomentaryChange displays its alternate content when
  // clicked and returns to its normal content when the user releases it, which
  // is the behavior users would expect when clicking a button with an alternate
  // image set.
  [[statusItem_ button] setButtonType:NSButtonTypeMomentaryChange];
  [self updateDimensions];
}

- (void)setIgnoreDoubleClickEvents:(BOOL)ignore {
  ignoreDoubleClickEvents_ = ignore;
}

- (BOOL)getIgnoreDoubleClickEvents {
  return ignoreDoubleClickEvents_;
}

- (void)setTitle:(NSString*)title font_type:(NSString*)font_type {
  NSMutableAttributedString* attributed_title =
      [[NSMutableAttributedString alloc] initWithString:title];

  if ([title containsANSICodes]) {
    attributed_title = [title attributedStringParsingANSICodes];
  }

  // Change font type, if specified
  CGFloat existing_size = [[[statusItem_ button] font] pointSize];
  if ([font_type isEqualToString:@"monospaced"]) {
    if (@available(macOS 10.15, *)) {
      NSDictionary* attributes = @{
        NSFontAttributeName :
            [NSFont monospacedSystemFontOfSize:existing_size
                                        weight:NSFontWeightRegular]
      };
      [attributed_title
{'fraction_non_alphanumeric': 0.10762218283109648, 'fraction_numerical': 0.0026589009875917953, 'mean_word_length': 4.3516260162601625, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5432029', 'n_tokens_mistral': 2542, 'n_tokens_neox': 2425, 'n_words': 499}