text:
#import "MaterialTextFields.h"

#import "MDCTextInputArt.h"
#import "MDCTextInputCommonFundament.h"

#import "MaterialAnimationTiming.h"
#import "MaterialMath.h"
#import "MaterialPalettes.h"
#import "MaterialTypography.h"

static NSString *const MDCTextInputFundamentCharacterRelativeSuperviewKey =
    @"MDCTextInputFundamentCharacterRelativeSuperviewKey";
static NSString *const MDCTextInputFundamentClearButtonKey = @"MDCTextInputFundamentClearButtonKey";
static NSString *const MDCTextInputFundamentClearButtonColorKey =
    @"MDCTextInputFundamentClearButtonColorKey";
static NSString *const MDCTextInputFundamentClearButtonImageKey =
    @"MDCTextInputFundamentClearButtonImageKey";
static NSString *const MDCTextInputFundamentHidesPlaceholderKey =
    @"MDCTextInputFundamentHidesPlaceholderKey";
static NSString *const MDCTextInputFundamentLeadingLabelKey =
    @"MDCTextInputFundamentLeadingLabelKey";
static NSString *const MDCTextInputFundamentMDCAdjustsFontsKey =
    @"MDCTextInputFundamentMDCAdjustsFontsKey";
static NSString *const MDCTextInputPositioningDelegateKey = @"MDCTextInputPositioningDelegateKey";
static NSString *const MDCTextInputFundamentPlaceholderLabelKey =
    @"MDCTextInputFundamentPlaceholderLabelKey";
static NSString *const MDCTextInputFundamentTextColorKey = @"MDCTextInputFundamentTextColorKey";
static NSString *const MDCTextInputFundamentTrailingLabelKey =
    @"MDCTextInputFundamentTrailingLabelKey";
static NSString *const MDCTextInputFundamentUnderlineViewKey =
    @"MDCTextInputFundamentUnderlineViewKey";

static NSString *const MDCTextInputUnderlineKVOKeyColor = @"color";
static NSString *const MDCTextInputUnderlineKVOKeyLineHeight = @"lineHeight";

static const CGFloat MDCTextInputClearButtonImageBuiltInPadding = 2.5f;
static const CGFloat MDCTextInputClearButtonImageSquareWidthHeight = 24.f;
static const CGFloat MDCTextInputHintTextOpacity = 0.54f;
static const CGFloat MDCTextInputOverlayViewToEditingRectPadding = 2.f;
const CGFloat MDCTextInputFullPadding = 16.f;
const CGFloat MDCTextInputHalfPadding = 8.f;

static inline UIColor *_Nonnull MDCTextInputCursorColor() {
  return [MDCPalette bluePalette].accent700;
}

static inline UIColor *MDCTextInputDefaultPlaceholderTextColor() {
  return [UIColor colorWithWhite:0 alpha:MDCTextInputHintTextOpacity];
}

static inline UIColor *MDCTextInputTextColor() {
  return [UIColor colorWithWhite:0 alpha:[MDCTypography body1FontOpacity]];
}

static inline UIColor *MDCTextInputUnderlineColor() {
  return [UIColor lightGrayColor];
}

@interface MDCTextInputCommonFundament () {
  BOOL _mdc_adjustsFontForContentSizeCategory;
}

@property(nonatomic, strong) UIImage *clearButtonImage;
@property(nonatomic, strong) NSLayoutConstraint *clearButtonWidth;
@property(nonatomic, assign) BOOL isRegisteredForKVO;
@property(nonatomic, strong) NSLayoutConstraint *placeholderHeight;
@property(nonatomic, strong) NSLayoutConstraint *placeholderLeading;
@property(nonatomic, strong) NSLayoutConstraint *placeholderLeadingLeftViewTrailing;
@property(nonatomic, strong) NSLayoutConstraint *placeholderTop;
@property(nonatomic, strong) NSLayoutConstraint *placeholderTrailing;
@property(nonatomic, strong) NSLayoutConstraint *placeholderTrailingRightViewLeading;
@property(nonatomic, strong) UIView *relativeSuperview;
@property(nonatomic, weak) UIView<MDCTextInput> *textInput;
@property(nonatomic, strong) NSLayoutConstraint *underlineY;

@end

@implementation MDCTextInputCommonFundament

// We never use the text property. Instead always read from the text field.

@synthesize attributedText = _do_no_use_attributedText;
@synthesize clearButton = _clearButton;
@synthesize clearButtonColor = _clearButtonColor;
@synthesize clearButtonMode = _clearButtonMode;
@synthesize hidesPlaceholderOnInput = _hidesPlaceholderOnInput;
@synthesize leadingUnderlineLabel = _leadingUnderlineLabel;
@synthesize placeholderLabel = _placeholderLabel;
@synthesize positioningDelegate = _positioningDelegate;
@synthesize textColor = _textColor;
@synthesize trailingUnderlineLabel = _trailingUnderlineLabel;
@synthesize underline = _underline;

- (instancetype)init {
  [self doesNotRecognizeSelector:_cmd];
  return nil;
}

- (nonnull instancetype)initWithTextInput:(UIView<MDCTextInput> *_Nonnull)textInput {
  self = [super init];
  if (self) {
    _textInput = textInput;

    [self commonMDCTextInputCommonFundamentInit];
    self.textInput.font = [UIFont mdc_preferredFontForMaterialTextStyle:MDCFontTextStyleBody1];

    // Initialize elements of UI
    [self setupRelativeSuperview];

    [self setupPlaceholderLabel];
    [self setupUnderlineView];
    [self setupClearButton];
    [self setupUnderlineLabels];

    [self updateColors];
    [self mdc_setAdjustsFontForContentSizeCategory:NO];

    [self subscribeForKVO];
  }
  return self;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
  self = [super init];
  if (self) {
    [self commonMDCTextInputCommonFundamentInit];

    _clearButton = [aDecoder decodeObjectForKey:MDCTextInputFundamentClearButtonKey];
    _clearButtonImage = [aDecoder decodeObjectForKey:MDCTextInputFundamentClearButtonImageKey];
    _clearButtonColor = [aDecoder decodeObjectForKey:MDCTextInputFundamentClearButtonColorKey];
    _hidesPlaceholderOnInput = [aDecoder decodeBoolForKey:MDCTextInputFundamentHidesPlaceholderKey];
    _leadingUnderlineLabel = [aDecoder decodeObjectForKey:MDCTextInputFundamentLeadingLabelKey];
    _mdc_adjustsFontForContentSizeCategory =
        [aDecoder decodeBoolForKey:MDCTextInputFundamentMDCAdjustsFontsKey];
    _placeholderLabel = [aDecoder decodeObjectForKey:MDCTextInputFundamentPlaceholderLabelKey];
    _positioningDelegate = [aDecoder decodeObjectForKey:MDCTextInputPositioningDelegateKey];
    _relativeSuperview =
        [aDecoder decodeObjectForKey:MDCTextInputFundamentCharacterRelativeSuperviewKey];
    _textColor = [aDecoder decodeObjectForKey:MDCTextInputFundamentTextColorKey];
    _trailingUnderlineLabel = [aDecoder decodeObjectForKey:MDCTextInputFundamentTrailingLabelKey];
    _underline = [aDecoder decodeObjectForKey:MDCTextInputFundamentUnderlineViewKey];

    [self subscribeForKVO];
  }
  return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder {
  [aCoder encodeObject:self.clearButton forKey:MDCTextInputFundamentClearButtonKey];
  [aCoder encodeObject:self.clearButtonColor forKey:MDCTextInputFundamentClearButtonColorKey];
  [aCoder encodeObject:self.clearButtonImage forKey:MDCTextInputFundamentClearButtonImageKey];
  [aCoder encodeBool:self.hidesPlaceholderOnInput forKey:MDCTextInputFundamentHidesPlaceholderKey];
  [aCoder encodeObject:self.leadingUnderlineLabel forKey:MDCTextInputFundamentLeadingLabelKey];
  [aCoder encodeBool:self.mdc_adjustsFontForContentSizeCategory
              forKey:MDCTextInputFundamentMDCAdjustsFontsKey];
  [aCoder encodeObject:self.placeholderLabel forKey:MDCTextInputFundamentPlaceholderLabelKey];
  [aCoder encodeObject:self.positioningDelegate forKey:MDCTextInputPositioningDelegateKey];
  [aCoder encodeObject:self.relativeSuperview
                forKey:MDCTextInputFundamentCharacterRelativeSuperviewKey];
  [aCoder encodeObject:self.textColor forKey:MDCTextInputFundamentTextColorKey];
  [aCoder encodeObject:self.trailingUnderlineLabel forKey:MDCTextInputFundamentTrailingLabelKey];
  [aCoder encodeObject:self.underline forKey:MDCTextInputFundamentUnderlineViewKey];
}

- (instancetype)copyWithZone:(NSZone *)zone {
  MDCTextInputCommonFundament *copy =
      [[MDCTextInputCommonFundament alloc] initWithTextInput:self.textInput];

  copy.clearButtonColor = self.clearButtonColor;
  copy.clearButtonImage = self.clearButtonImage;
  copy.clearButtonMode = self.clearButtonMode;
  copy.enabled = self.isEnabled;
  copy.hidesPlaceholderOnInput = self.hidesPlaceholderOnInput;
  copy.mdc_adjustsFontForContentSizeCategory = self.mdc_adjustsFontForContentSizeCategory;
  copy.positioningDelegate = self.positioningDelegate;
  if (![self.relativeSuperview isKindOfClass:[MDCTextField class]]) {
    copy.relativeSuperview = [self.relativeSuperview copy];
  }
  copy.text = [self.text copy];
  copy.textColor = self.textColor;
  copy.underline.lineHeight = self.underline.lineHeight;
  copy.underline.color = self.underline.color;

  return copy;
}

- (void)dealloc {
  [self unsubscribeFromNotifications];
  [self unsubscribeFromKVO];
}

- (void)commonMDCTextInputCommonFundamentInit {
  _cursorColor = MDCTextInputCursorColor();
  _textColor = MDCTextInputTextColor();
}

// UITextViews are subclasses of UIScrollView and that complicates autolayout. This container is
// just a box to give a 'real' value to constraints related to superview.
- (void)setupRelativeSuperview {
  if ([_textInput isKindOfClass:[UIScrollView class]]) {
    _relativeSuperview = [[UIView alloc] initWithFrame:CGRectZero];
    [_textInput addSubview:_relativeSuperview];
    [_textInput sendSubviewToBack:_relativeSuperview];
    _relativeSuperview.opaque = NO;
    _relativeSuperview.backgroundColor = [UIColor clearColor];
    [_textInput setContentHuggingPriority:UILayoutPriorityDefaultHigh
                                  forAxis:UILayoutConstraintAxisVertical];
    [_textInput setContentHuggingPriority:UILayoutPriorityDefaultHigh
                                  forAxis:UILayoutConstraintAxisHorizontal];
  } else {
    _relativeSuperview = _textInput;
  }
}

- (void)setupClearButton {
  _clearButton = [[UIButton alloc] initWithFrame:CGRectZero];
  _clearButton.translatesAutoresizingMaskIntoConstraints = NO;
  [_clearButton setContentCompressionResistancePriority:UILayoutPriorityDefaultLow - 1
                                                forAxis:UILayoutConstraintAxisHorizontal];
  [_clearButton setContentCompressionResistancePriority:UILayoutPriorityDefaultLow - 1
                                                forAxis:UILayoutConstraintAxisVertical];
  [_clearButton setContentHuggingPriority:UILayoutPriorityDefaultLow + 1
                                  forAxis:UILayoutConstraintAxisHorizontal];
  [_clearButton setContentHuggingPriority:UILayoutPriorityDefaultLow + 1
                                  forAxis:UILayoutConstraintAxisVertical];

  _clearButton.opaque = NO;

  [_textInput addSubview:_clearButton];
  [_textInput sendSubviewToBack:_clearButton];

  NSLayoutConstraint *height = [NSLayoutConstraint constraintWithItem:_clearButton
                                                            attribute:NSLayoutAttributeHeight
                                                            relatedBy:NSLayoutRelationEqual
                                                               toItem:_clearButton
                                                            attribute:NSLayoutAttributeWidth
                                                           multiplier:1
                                                             constant:0];
  self.clearButtonWidth =
      [NSLayoutConstraint constraintWithItem:_clearButton
                                   attribute:NSLayoutAttributeWidth
                                   relatedBy:NSLayoutRelationEqual
                                      toItem:nil
                                   attribute:NSLayoutAttributeNotAnAttribute
                                  multiplier:1
                                    constant:MDCTextInputClearButtonImageSquareWidthHeight];
  NSLayoutConstraint *bottom = [NSLayoutConstraint
      constraintWithItem:_clearButton
               attribute:NSLayoutAttributeBottom
               relatedBy:NSLayoutRelationEqual
                  toItem:_underline
               attribute:NSLayoutAttributeTop
              multiplier:1
                constant:-1 * MDCTextInputHalfPadding + MDCTextInputClearButtonImageBuiltInPadding];
  self.placeholderTrailing = [NSLayoutConstraint constraintWithItem:_placeholderLabel
                                                          attribute:NSLayoutAttributeTrailing
                                                          relatedBy:NSLayoutRelationLessThanOrEqual
                                                             toItem:_clearButton
                                                          attribute:NSLayoutAttributeTrailing
                                                         multiplier:1
                                                           constant:MDCTextInputHalfPadding];
  NSLayoutConstraint *trailingSuperview =
      [NSLayoutConstraint constraintWithItem:_clearButton
                                   attribute:NSLayoutAttributeTrailing
                                   relatedBy:NSLayoutRelationEqual
                                      toItem:_relativeSuperview
                                   attribute:NSLayoutAttributeTrailing
                                  multiplier:1
                                    constant:MDCTextInputClearButtonImageBuiltInPadding];

  height.priority = UILayoutPriorityDefaultLow;
  self.clearButtonWidth.priority = UILayoutPriorityDefaultLow;
  bottom.priority = UILayoutPriorityDefaultLow;
  self.placeholderTrailing.priority = UILayoutPriorityDefaultLow;
  trailingSuperview.priority = UILayoutPriorityDefaultLow;

  [NSLayoutConstraint activateConstraints:@[
    height, self.clearButtonWidth, bottom, self.placeholderLeading, trailingSuperview
  ]];

  [_clearButton addTarget:self
                   action:@selector(clearButtonDidTouch)
         forControlEvents:UIControlEventTouchUpInside];
}

- (void)setupPlaceholderLabel {
  _placeholderLabel = [[UILabel alloc] initWithFrame:CGRectZero];
  _placeholderLabel.translatesAutoresizingMaskIntoConstraints = NO;
  [_placeholderLabel setContentCompressionResistancePriority:UILayoutPriorityDefaultLow - 1
                                                     forAxis:UILayoutConstraintAxisHorizontal];
  _placeholderLabel.textAlignment = NSTextAlignmentNatural;

  _placeholderLabel.userInteractionEnabled = NO;
  _placeholderLabel.opaque = NO;

  _placeholderLabel.textColor = MDCTextInputDefaultPlaceholderTextColor();
  _placeholderLabel.font = _textInput.font;

  [_textInput addSubview:_placeholderLabel];
  [_textInput sendSubviewToBack:_placeholderLabel];

  [NSLayoutConstraint activateConstraints:[self placeholderDefaultConstaints]];

  _hidesPlaceholderOnInput = YES;
}

- (void)setupUnderlineLabels {
  _leadingUnderlineLabel = [[UILabel alloc] initWithFrame:CGRectZero];
  _trailingUnderlineLabel = [[UILabel alloc] initWithFrame:CGRectZero];

  _leadingUnderlineLabel.textColor = MDCTextInputDefaultPlaceholderTextColor();
  _leadingUnderlineLabel.font = _textInput.font;
  _leadingUnderlineLabel.textAlignment = NSTextAlignmentNatural;

  _leadingUnderlineLabel.opaque = NO;

  [_leadingUnderlineLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
  [_textInput addSubview:_leadingUnderlineLabel];

  _trailingUnderlineLabel.textColor = [UIColor grayColor];
  _trailingUnderlineLabel.font = _textInput.font;

  _trailingUnderlineLabel.opaque = NO;

  [_trailingUnderlineLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
  [_textInput addSubview:_trailingUnderlineLabel];

  NSLayoutConstraint *leadingLeading =
      [NSLayoutConstraint constraintWithItem:_leadingUnderlineLabel
                                   attribute:NSLayoutAttributeLeading
                                   relatedBy:NSLayoutRelationEqual
                                      toItem:_relativeSuperview
                                   attribute:NSLayoutAttributeLeading
                                  multiplier:1
                                    constant:0];
  leadingLeading.priority = UILayoutPriorityDefaultLow;

  NSLayoutConstraint *trailingTrailing =
      [NSLayoutConstraint constraintWithItem:_trailingUnderlineLabel
                                   attribute:NSLayoutAttributeTrailing
                                   relatedBy:NSLayoutRelationEqual
                                      toItem:_relativeSuperview
                                   attribute:NSLayoutAttributeTrailing
                                  multiplier:1
                                    constant:0];
  trailingTrailing.priority = UILayoutPriorityDefaultLow;

  NSLayoutConstraint *labelSpacing =
      [NSLayoutConstraint constraintWithItem:_leadingUnderlineLabel
                                   attribute:NSLayoutAttributeTrailing
                                   relatedBy:NSLayoutRelationEqual
                                      toItem:_trailingUnderlineLabel
                                   attribute:NSLayoutAttributeLeading
                                  multiplier:1
                                    constant:0];
  labelSpacing.priority = UILayoutPriorityDefaultLow;

  [NSLayoutConstraint activateConstraints:@[ labelSpacing, leadingLeading, trailingTrailing ]];

  NSLayoutConstraint *leadingBottom = [NSLayoutConstraint constraintWithItem:_leadingUnderlineLabel
                                                                   attribute:NSLayoutAttributeBottom
                                                                   relatedBy:NSLayoutRelationEqual
                                                                      toItem:_relativeSuperview
                                                                   attribute:NSLayoutAttributeBottom
                                                                  multiplier:1
                                                                    constant:0];
  leadingBottom.priority = UILayoutPriorityDefaultLow;

  NSLayoutConstraint *trailingBottom =
      [NSLayoutConstraint constraintWithItem:_trailingUnderlineLabel
                                   attribute:NSLayoutAttributeBottom
                                   relatedBy:NSLayoutRelationEqual
                                      toItem:_relativeSuperview
                                   attribute:NSLayoutAttributeBottom
                                  multiplier:1
                                    constant:0];
  trailingBottom.priority = UILayoutPriorityDefaultLow;

  [NSLayoutConstraint activateConstraints:@[ leadingBottom, trailingBottom ]];

  [_trailingUnderlineLabel
      setContentCompressionResistancePriority:UILayoutPriorityRequired
                                      forAxis:UILayoutConstraintAxisHorizontal];
  [_trailingUnderlineLabel setContentHuggingPriority:UILayoutPriorityRequired
                                             forAxis:UILayoutConstraintAxisHorizontal];
}

- (void)setupUnderlineView {
  _underline = [[MDCTextInputUnderlineView alloc] initWithFrame:CGRectZero];
  _underline.color = MDCTextInputUnderlineColor();
  _underline.translatesAutoresizingMaskIntoConstraints = NO;

  [self.textInput addSubview:_underline];
  [self.textInput sendSubviewToBack:_underline];

  [NSLayoutConstraint constraintWithItem:_underline
                               attribute:NSLayoutAttributeLeading
                               relatedBy:NSLayoutRelationEqual
                                  toItem:_relativeSuperview
                               attribute:NSLayoutAttributeLeading
                              multiplier:1
                                constant:0]
      .active = YES;
  [NSLayoutConstraint constraintWithItem:_underline
                               attribute:NSLayoutAttributeTrailing
                               relatedBy:NSLayoutRelationEqual
                                  toItem:_relativeSuperview
                               attribute:NSLayoutAttributeTrailing
                              multiplier:1
                                constant:0]
      .active = YES;

  CGFloat estimatedTextHeight = MDCCeil(self.font.lineHeight * 2.f) / 2.f;
  _underlineY =
      [NSLayoutConstraint constraintWithItem:_underline
                                   attribute:NSLayoutAttributeCenterY
                                   relatedBy:NSLayoutRelationEqual
                                      toItem:_relativeSuperview
                                   attribute:NSLayoutAttributeTop
                                  multiplier:1
                                    constant:[self textContainerInset].top + estimatedTextHeight +
                                             MDCTextInputHalfPadding];
  _underlineY.priority = UILayoutPriorityDefaultLow;
  _underlineY.active = YES;
}

- (void)unsubscribeFromNotifications {
  NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
  [defaultCenter removeObserver:self];
}

- (void)subscribeForKVO {
  if (!_textInput) {
    return;
  }
  [_underline addObserver:self forKeyPath:MDCTextInputUnderlineKVOKeyColor options:0 context:nil];
  [_underline addObserver:self
               forKeyPath:MDCTextInputUnderlineKVOKeyLineHeight
                  options:0
                  context:nil];
  _isRegisteredForKVO = YES;
}

- (void)unsubscribeFromKVO {
  if (!self.textInput || !self.isRegisteredForKVO) {
    return;
  }
  @try {
    [self.underline removeObserver:self forKeyPath:MDCTextInputUnderlineKVOKeyColor];
    [self.underline removeObserver:self forKeyPath:MDCTextInputUnderlineKVOKeyLineHeight];
  } @catch (NSException *exception) {
  }
  _isRegisteredForKVO = NO;
}

#pragma mark - Mirrored Layout Methods

- (void)layoutSubviewsOfInput {
  if (self.relativeSuperview != self.textInput) {
    self.relativeSuperview.frame = self.textInput.bounds;
    [self.textInput setNeedsUpdateConstraints];
  }

  if ([self.textInput isKindOfClass:[UITextView class]] &&
      !UIEdgeInsetsEqualToEdgeInsets(((UITextView *)self.textInput).textContainerInset,
                                     self.textContainerInset)) {
    ((UITextView *)self.textInput).textContainerInset = self.textContainerInset;
  }

  [self updatePlaceholderAlpha];

  if ([self needsUpdateConstraintsForPlaceholderToOverlayViewsPosition] ||
      [self needsUpdateUnderlinePosition]) {
    [self.textInput setNeedsUpdateConstraints];
  }

  [self updateColors];
  [self updateClearButton];
}

- (void)updateConstraintsOfInput {
  [self updateClearButtonConstraint];
  [self updatePlaceholderPosition];
  [self updateUnderlinePosition];
}

#pragma mark - Clear Button Implementation

- (void)updateClearButton {
  CGSize clearButtonSize = CGSizeMake(MDCTextInputClearButtonImageSquareWidthHeight,
                                      MDCTextInputClearButtonImageSquareWidthHeight);
  if (!self.clearButtonImage || !CGSizeEqualToSize(self.clearButtonImage.size, clearButtonSize)) {
    self.clearButtonImage =
        [self drawnClearButtonImage:clearButtonSize color:self.clearButtonColor];
  }

  if (![self.clearButton imageForState:UIControlStateNormal]) {
    [self.clearButton setImage:self.clearButtonImage forState:UIControlStateNormal];
    [self.clearButton setImage:self.clearButtonImage forState:UIControlStateHighlighted];
    [self.clearButton setImage:self.clearButtonImage forState:UIControlStateSelected];
  }

  CGFloat clearButtonAlpha = [self clearButtonAlpha];
  self.clearButton.alpha = clearButtonAlpha;

  if (self.clearButtonWidth.constant !=
      MDCTextInputClearButtonImageSquareWidthHeight * clearButtonAlpha) {
    [self.textInput setNeedsUpdateConstraints];
  }

  [self.clearButton.superview bringSubviewToFront:self.clearButton];
}

- (void)updateClearButtonConstraint {
  CGFloat constant = MDCTextInputClearButtonImageSquareWidthHeight * [self clearButtonAlpha];
  if (self.clearButtonWidth.constant != constant) {
    self.clearButtonWidth.constant = constant;
    [self.textInput invalidateIntrinsicContentSize];
  }
}

- (CGFloat)clearButtonAlpha {
  CGFloat clearButtonAlpha = 0;
  if (self.text.length > 0) {
    switch (self.clearButtonMode) {
      case UITextFieldViewModeAlways:
        clearButtonAlpha = 1;
        break;
      case UITextFieldViewModeWhileEditing:
        if (self.isEditing) {
          clearButtonAlpha = 1;
        }
        break;
      case UITextFieldViewModeUnlessEditing:
        if (!self.isEditing) {
          clearButtonAlpha = 1;
        }
        break;
      default:
        break;
    }
  }

  if ([self.textInput isKindOfClass:[UITextField class]] &&
      ((UITextField *)self.textInput).rightView.superview) {
    clearButtonAlpha = 0;
  }

  return clearButtonAlpha;
}

- (UIImage *)drawnClearButtonImage:(CGSize)size color:(UIColor *)color {
  NSAssert1(size.width >= 0, @"drawnClearButtonImage was passed a size with a width less than 0 %@",
            NSStringFromCGSize(size));
  NSAssert1(size.height >= 0,
            @"drawnClearButtonImage was passed a size with a height less than 0 %@",
            NSStringFromCGSize(size));

  if (CGSizeEqualToSize(size, CGSizeZero)) {
    size = CGSizeMake(MDCTextInputClearButtonImageSquareWidthHeight,
                      MDCTextInputClearButtonImageSquareWidthHeight);
  }
  CGFloat scale = [UIScreen mainScreen].scale;
  CGRect bounds = CGRectMake(0, 0, size.width * scale, size.height * scale);
  UIGraphicsBeginImageContextWithOptions(bounds.size, false, scale);
  [color setFill];
  [MDCPathForClearButtonImageFrame(bounds) fill];
  UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();

  return image;
}

- (void)clearButtonDidTouch {
  self.textInput.text = nil;
}

#pragma mark - Underline View Implementation

- (CGFloat)underlineYConstant {
  CGFloat estimatedTextHeight = MDCCeil(self.font.lineHeight * 2.f) / 2.f;

  return [self textContainerInset].top + estimatedTextHeight + MDCTextInputHalfPadding;
}

- (BOOL)needsUpdateUnderlinePosition {
  return self.underlineY.constant != [self underlineYConstant];
}

- (void)updateUnderlinePosition {
  self.underlineY.constant = [self underlineYConstant];
  [self.textInput invalidateIntrinsicContentSize];
}

#pragma mark - Properties Implementation

- (NSAttributedString *)attributedPlaceholder {
  id placeholderString = self.placeholderLabel.text;
  if ([placeholderString isKindOfClass:[NSString class]]) {
    // TODO: (larche) Return string attributes also. Tho I feel like that should come from the
    // placeholderLabel itself somehow.
    NSAttributedString *constructedString =
        [[NSAttributedString alloc] initWithString:(NSString *)placeholderString attributes:nil];
    return constructedString;
  } else if ([placeholderString isKindOfClass:[NSAttributedString class]]) {
    return (NSAttributedString *)placeholderString;
  }

  return nil;
}

- (void)setAttributedPlaceholder:(NSAttributedString *)attributedPlaceholder {
  self.placeholderLabel.text = attributedPlaceholder.string;
  // TODO: (larche) Read string attributes also. Tho I feel like that should come from the
  // placeholderLabel itself somehow.

  [self updatePlaceholderAlpha];
  [self.textInput setNeedsUpdateConstraints];
}

- (void)setClearButtonColor:(UIColor *)clearButtonColor {
  if (![_clearButtonColor isEqual:clearButtonColor]) {
    _clearButtonColor = clearButtonColor;
    self.clearButtonImage =
        [self drawnClearButtonImage:self.clearButtonImage.size color:_clearButtonColor];
  }
}

- (void)setClearButtonMode:(UITextFieldViewMode)clearButtonMode {
  _clearButtonMode = clearButtonMode;
  [self updateClearButton];
}

- (void)setEnabled:(BOOL)enabled {
  _enabled = enabled;
  self.underline.enabled = enabled;
}

- (UIFont *)font {
  return self.textInput.font;
}

- (void)setFont:(UIFont *)font {
  [self.textInput setFont:font];
}

- (void)setHidesPlaceholderOnInput:(BOOL)hidesPlaceholderOnInput {
  _hidesPlaceholderOnInput = hidesPlaceholderOnInput;
  [self updatePlaceholderAlpha];
}

- (BOOL)isEditing {
  return self.textInput.isEditing;
}

- (NSString *)placeholder {
  id placeholderString = self.placeholderLabel.text;
  if ([placeholderString isKindOfClass:[NSString class]]) {
    return (NSString *)placeholderString;
  } else if ([placeholderString isKindOfClass:[NSAttributedString class]]) {
    return [(NSAttributedString *)placeholderString string];
  }

  return nil;
}

- (void)setPlaceholder:(NSString *)placeholder {
  self.placeholderLabel.text = placeholder;
  [self updatePlaceholderAlpha];
  [self.textInput setNeedsLayout];
}

- (NSString *)text {
  return self.textInput.text;
}

- (void)setText:(NSString *)text {
  [self.textInput setText:text];
}

- (void)setTextColor:(UIColor *)textColor {
  if (!textColor) {
    textColor = MDCTextInputTextColor();
  }

  if (_textColor != textColor) {
    _textColor = textColor;
    [self updateColors];
  }
}

- (UIEdgeInsets)textContainerInset {
  UIEdgeInsets textContainerInset = UIEdgeInsetsZero;

  textContainerInset.top = MDCTextInputFullPadding;
  textContainerInset.bottom = MDCTextInputFullPadding;

  if ([self.textInput.positioningDelegate respondsToSelector:@selector(textContainerInset:)]) {
    return [self.textInput.positioningDelegate textContainerInset:textContainerInset];
  }
  return textContainerInset;
}

- (void)setTextInput:(UIView<MDCTextInput> *)textInput {
  _textInput = textInput;

  [_textInput setNeedsLayout];
}

#pragma mark - Layout

- (void)updateColors {
  self.textInput.tintColor = self.cursorColor;
  self.textInput.textColor = self.textColor;
}

- (void)updateFontsForDynamicType {
  if (self.mdc_adjustsFontForContentSizeCategory) {
    UIFont *textFont = [UIFont mdc_preferredFontForMaterialTextStyle:MDCFontTextStyleBody1];
    self.textInput.font = textFont;
    self.leadingUnderlineLabel.font = textFont;
    self.trailingUnderlineLabel.font = textFont;
    self.placeholderLabel.font = textFont;
  }
}

- (BOOL)needsUpdateConstraintsForPlaceholderToOverlayViewsPosition {
  if (![self.textInput isKindOfClass:[MDCTextField class]]) {
    return NO;
  }

  MDCTextField *textField = (MDCTextField *)self.textInput;

  return (textField.leftView.superview && !self.placeholderLeadingLeftViewTrailing) ||
         (!textField.leftView.superview && self.placeholderLeadingLeftViewTrailing) ||
         (textField.rightView.superview && !self.placeholderTrailingRightViewLeading) ||
         (!textField.rightView.superview && self.placeholderTrailingRightViewLeading);
}

- (void)updatePlaceholderToOverlayViewsPosition {
  if (![self.textInput isKindOfClass:[MDCTextField class]]) {
    return;
  }

  MDCTextField *textField = (MDCTextField *)self.textInput;
  if (textField.leftView.superview && !self.placeholderLeadingLeftViewTrailing) {
    self.placeholderLeadingLeftViewTrailing =
        [NSLayoutConstraint constraintWithItem:textField.placeholderLabel
                                     attribute:NSLayoutAttributeLeading
                                     relatedBy:NSLayoutRelationEqual
                                        toItem:textField.leftView
                                     attribute:NSLayoutAttributeTrailing
                                    multiplier:1
                                      constant:MDCTextInputOverlayViewToEditingRectPadding];
    self.placeholderLeadingLeftViewTrailing.priority = UILayoutPriorityDefaultLow + 1;
    self.placeholderLeadingLeftViewTrailing.active = YES;
  } else if (!textField.leftView.superview && self.placeholderLeadingLeftViewTrailing) {
    self.placeholderLeadingLeftViewTrailing = nil;
  }

  if (textField.rightView.superview && !self.placeholderTrailingRightViewLeading) {
    self.placeholderTrailingRightViewLeading =
        [NSLayoutConstraint constraintWithItem:textField.placeholderLabel
                                     attribute:NSLayoutAttributeTrailing
                                     relatedBy:NSLayoutRelationLessThanOrEqual
                                        toItem:textField.rightView
                                     attribute:NSLayoutAttributeLeading
                                    multiplier:1
                                      constant:MDCTextInputOverlayViewToEditingRectPadding];
    self.placeholderTrailingRightViewLeading.priority = UILayoutPriorityDefaultLow + 1;
    self.placeholderTrailingRightViewLeading.active = YES;
  } else if (!textField.rightView.superview && self.placeholderTrailingRightViewLeading) {
    self.placeholderTrailingRightViewLeading = nil;
  }

  [textField invalidateIntrinsicContentSize];
}

- (void)updatePlaceholderAlpha {
  CGFloat opacity = (self.hidesPlaceholderOnInput && self.textInput.text.length > 0) ? 0 : 1;
  self.placeholderLabel.alpha = opacity;
}

- (void)updatePlaceholderPosition {
  self.placeholderTop.constant = [self textContainerInset].top;

  [self updatePlaceholderToOverlayViewsPosition];
  [self.textInput invalidateIntrinsicContentSize];
}

- (NSArray<NSLayoutConstraint *> *)placeholderDefaultConstaints {
  self.placeholderTop = [NSLayoutConstraint constraintWithItem:_placeholderLabel
                                                     attribute:NSLayoutAttributeTop
                                                     relatedBy:NSLayoutRelationEqual
                                                        toItem:_relativeSuperview
                                                     attribute:NSLayoutAttributeTop
                                                    multiplier:1
                                                      constant:[self textContainerInset].top];

  // This can be affected by .leftView and .rightView.
  // See updatePlaceholderToOverlayViewsPosition()
  self.placeholderLeading = [NSLayoutConstraint constraintWithItem:_placeholderLabel
                                                         attribute:NSLayoutAttributeLeading
                                                         relatedBy:NSLayoutRelationEqual
                                                            toItem:_relativeSuperview
                                                         attribute:NSLayoutAttributeLeading
                                                        multiplier:1
                                                          constant:[self textContainerInset].left];
  NSLayoutConstraint *placeholderTrailing =
      [NSLayoutConstraint constraintWithItem:_placeholderLabel
                                   attribute:NSLayoutAttributeTrailing
                                   relatedBy:NSLayoutRelationLessThanOrEqual
                                      toItem:_relativeSuperview
                                   attribute:NSLayoutAttributeTrailing
                                  multiplier:1
                                    constant:[self textContainerInset].right];

  [self.placeholderTop setPriority:UILayoutPriorityDefaultLow];
  [self.placeholderLeading setPriority:UILayoutPriorityDefaultLow];

  return @[ self.placeholderTop, self.placeholderLeading, placeholderTrailing ];
}

#pragma mark - Text Input Events

- (void)didBeginEditing {
  [self updateClearButton];
  [self.textInput invalidateIntrinsicContentSize];
}

- (void)didChange {
  [self updateClearButton];
  [self updatePlaceholderAlpha];
  [self updatePlaceholderPosition];
}

- (void)didEndEditing {
  [self updateClearButton];
}

- (void)didSetFont {
  UIFont *font = self.textInput.font;
  self.placeholderLabel.font = font;

  [self updatePlaceholderPosition];
}

- (void)didSetText {
  [self didChange];
  [self.textInput setNeedsLayout];
}

#pragma mark - KVO

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary<NSKeyValueChangeKey, id> *)change
                       context:(void *)context {
  // Listening to outside setting of underline properties.
  if (object != self.underline) {
    return;
  }

  if ([keyPath isEqualToString:MDCTextInputUnderlineKVOKeyColor]) {
    if (!self.underline.color) {
      self.underline.color = MDCTextInputUnderlineColor();
    }
    [self updateColors];
  } else if ([keyPath isEqualToString:MDCTextInputUnderlineKVOKeyLineHeight]) {
    [self.textInput setNeedsUpdateConstraints];
  } else {
    return;
  }
}

#pragma mark - Accessibility

- (BOOL)mdc_adjustsFontForContentSizeCategory {
  return _mdc_adjustsFontForContentSizeCategory;
}

- (void)mdc_setAdjustsFontForContentSizeCategory:(BOOL)adjusts {
  _mdc_adjustsFontForContentSizeCategory = adjusts;

  if (adjusts) {
    [self updateFontsForDynamicType];
  }

  if (_mdc_adjustsFontForContentSizeCategory) {
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(contentSizeCategoryDidChange:)
                                                 name:UIContentSizeCategoryDidChangeNotification
                                               object:nil];
  } else {
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:UIContentSizeCategoryDidChangeNotification
                                                  object:nil];
  }
}

- (void)contentSizeCategoryDidChange:(NSNotification *)notification {
  [self updateFontsForDynamicType];
}

@end


text:
package org.caleydo.view.domino.internal;

import gleem.linalg.Vec2f;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.media.opengl.GL2;
import javax.media.opengl.GL2ES1;

import org.caleydo.core.util.base.ICallback;
import org.caleydo.core.util.color.Color;
import org.caleydo.core.view.opengl.canvas.IGLMouseListener.IMouseEvent;
import org.caleydo.core.view.opengl.layout2.GLElementContainer;
import org.caleydo.core.view.opengl.layout2.GLGraphics;
import org.caleydo.core.view.opengl.layout2.IGLElementContext;
import org.caleydo.core.view.opengl.layout2.IPopupLayer;
import org.caleydo.core.view.opengl.layout2.IPopupLayer.IPopupElement;
import org.caleydo.core.view.opengl.layout2.PickableGLElement;
import org.caleydo.core.view.opengl.layout2.basic.IScrollBar;
import org.caleydo.core.view.opengl.layout2.basic.IScrollBar.IScrollBarCallback;
import org.caleydo.core.view.opengl.layout2.basic.ScrollBar;
import org.caleydo.core.view.opengl.layout2.basic.ScrollingDecorator.IHasMinSize;
import org.caleydo.core.view.opengl.layout2.geom.Rect;
import org.caleydo.core.view.opengl.layout2.layout.IGLLayout2;
import org.caleydo.core.view.opengl.layout2.layout.IGLLayoutElement;
import org.caleydo.core.view.opengl.picking.Pick;
import org.caleydo.view.domino.internal.undo.ZoomCmd;
import org.eclipse.swt.SWT;

import com.google.common.collect.Iterables;

/**
 * @author Samuel Gratzl
 *
 */
public class MiniMapCanvas extends GLElementContainer implements IGLLayout2, IScrollBarCallback {
	private static final int AUTO_SHIFT_AREA = 50;
	private static final int AUTO_SHIFT = 25;
	private static final int AUTO_SHIFT_TIMER = 150;

	private final Collection<ICallback<MiniMapCanvas>> onViewPortChange = new ArrayList<>();
	private final ScrollBarImpl vertical;
	private final ScrollBarImpl horizontal;
	private final float scrollBarWidth = 10;

	private MiniMap miniMap;

	private Vec2f shift = new Vec2f(0, 0);

	private Vec2f autoShift = new Vec2f(0, 0);
	private int autoShiftTimer = 0;

	public MiniMapCanvas() {
		setLayout(this);

		vertical = new ScrollBarImpl(new ScrollBar(false));
		vertical.scrollBar.setCallback(this);
		vertical.scrollBar.setWidth(scrollBarWidth);
		horizontal = new ScrollBarImpl(new ScrollBar(true));
		horizontal.scrollBar.setCallback(this);
		horizontal.scrollBar.setWidth(scrollBarWidth);
	}

	@Override
	protected void init(IGLElementContext context) {
		horizontal.pickingId = context.registerPickingListener(horizontal.scrollBar);
		vertical.pickingId = context.registerPickingListener(vertical.scrollBar);
		super.init(context);
	}

	@Override
	protected void takeDown() {
		context.unregisterPickingListener(horizontal.pickingId);
		context.unregisterPickingListener(vertical.pickingId);
		super.takeDown();
	}

	public void addOnViewPortChange(ICallback<MiniMapCanvas> listener) {
		onViewPortChange.add(listener);
	}

	public void removeOnViewPortChange(ICallback<MiniMapCanvas> listener) {
		onViewPortChange.remove(listener);
	}

	private void fireViewPortChange() {
		for (ICallback<MiniMapCanvas> listener : onViewPortChange)
			listener.on(this);
	}

	@Override
	public boolean doLayout(List<? extends IGLLayoutElement> children, float w, float h, IGLLayoutElement parent,
			int deltaTimeMs) {
		for (IGLLayoutElement child : children) {
			if (child.asElement() instanceof IManuallyShifted)
				child.setBounds(0, 0, w - scrollBarWidth, h - scrollBarWidth);
			else
				child.setBounds(-shift.x(), -shift.y(), w - scrollBarWidth, h - scrollBarWidth);
		}

		Rect r = getPaintingCanvas();
		final float offsetX = shift.x() - r.x() + 50;
		final float offsetY = shift.y() - r.y() + 50;
		horizontal.scrollBar.setBounds(offsetX, w - scrollBarWidth, r.width() + 100);
		vertical.scrollBar.setBounds(offsetY, h - scrollBarWidth, r.height() + 100);

		if (miniMap != null)
			miniMap.updateBounds();
		return false;
	}

	/**
	 * @return
	 */
	public Rect getClippingRect() {
		Vec2f s = getSize();
		return new Rect(shift.x(), shift.y(), s.x() - scrollBarWidth, s.y() - scrollBarWidth);
	}

	public Rect getPaintingCanvas() {
		Vec2f s = getSize();
		Rect r = new Rect(shift.x(), shift.y(), s.x() - scrollBarWidth, s.y() - scrollBarWidth);
		for (IHasMiniMap mini : Iterables.filter(this, IHasMiniMap.class)) {
			final Rect b = mini.getBoundingBox();
			if (mini instanceof IManuallyShifted && b != null) {
				b.xy(b.xy().plus(shift));
			}
			r = Rect.union(r, b);
		}
		return r;
	}

	/**
	 * @param dx
	 * @param dy
	 */
	public void shiftViewport(float dx, float dy) {
		shift.add(new Vec2f(dx, dy));
		relayout();
		for (IManuallyShifted m : Iterables.filter(this, IManuallyShifted.class))
			m.setShift(shift.x(), shift.y());
		fireViewPortChange();
	}

	/**
	 * @param rectBounds
	 */
	public void scrollInfoView(Rect bounds) {
		Rect visible = getClippingRect();
		// already visible
		if (visible.contains(bounds))
			return;
		float xShift = 0;

		// offset to the border
		final int offset = 30;

		if (bounds.x() > visible.x2())
			xShift = bounds.x2() + offset - visible.x2();
		else if (bounds.x2() < visible.x())
			xShift = bounds.x() - offset - visible.x();

		float yShift = 0;
		if (bounds.y() > visible.y2())
			yShift = bounds.y2() + offset - visible.y2();
		else if (bounds.y2() < visible.y())
			yShift = bounds.y() - offset - visible.y();
		shiftViewport(xShift, yShift);
	}

	@Override
	protected void renderImpl(GLGraphics g, float w, float h) {
		clipStart(g, w, h);
		super.renderImpl(g, w, h);
		clipEnd(g);
	}

	@Override
	protected void renderPickImpl(GLGraphics g, float w, float h) {
		clipStart(g, w, h);
		super.renderPickImpl(g, w, h);
		clipEnd(g);
	}

	private void clipStart(GLGraphics g, float w, float h) {
		final GL2 gl = g.gl;

		{
			g.move(0, h - scrollBarWidth);
			g.pushName(horizontal.pickingId);
			if (g.isPickingPass())
				horizontal.scrollBar.renderPick(g, w - scrollBarWidth, scrollBarWidth, this);
			else
				horizontal.scrollBar.render(g, w - scrollBarWidth, scrollBarWidth, this);
			g.popName();
			g.move(0, -h + scrollBarWidth);
		}
		{
			g.move(w - scrollBarWidth, 0);
			g.pushName(vertical.pickingId);
			if (g.isPickingPass())
				vertical.scrollBar.renderPick(g, scrollBarWidth, h - scrollBarWidth, this);
			else
				vertical.scrollBar.render(g, scrollBarWidth, h - scrollBarWidth, this);
			g.popName();
			g.move(-w + scrollBarWidth, 0);
		}

		gl.glPushAttrib(GL2.GL_ENABLE_BIT);
		{
			double[] clipPlane1 = new double[] { 1.0, 0.0, 0.0, 0 };
			double[] clipPlane3 = new double[] { -1.0, 0.0, 0.0, w - scrollBarWidth };
			gl.glClipPlane(GL2ES1.GL_CLIP_PLANE0, clipPlane1, 0);
			gl.glClipPlane(GL2ES1.GL_CLIP_PLANE1, clipPlane3, 0);
			gl.glEnable(GL2ES1.GL_CLIP_PLANE0);
			gl.glEnable(GL2ES1.GL_CLIP_PLANE1);
		}
		{
			double[] clipPlane2 = new double[] { 0.0, 1.0, 0.0, 0 };
			double[] clipPlane4 = new double[] { 0.0, -1.0, 0.0, h - scrollBarWidth };
			gl.glClipPlane(GL2ES1.GL_CLIP_PLANE2, clipPlane2, 0);
			gl.glClipPlane(GL2ES1.GL_CLIP_PLANE3, clipPlane4, 0);
			gl.glEnable(GL2ES1.GL_CLIP_PLANE2);
			gl.glEnable(GL2ES1.GL_CLIP_PLANE3);
		}
	}

	private void clipEnd(GLGraphics g) {
		g.gl.glPopAttrib();
	}

	@Override
	public float getHeight(IScrollBar scrollBar) {
		if (horizontal != null && horizontal.scrollBar == scrollBar)
			return getSize().x();
		else
			return getSize().y();
	}

	@Override
	public void onScrollBarMoved(IScrollBar scrollBar, float value) {
		Rect r = getPaintingCanvas();
		if (horizontal.scrollBar == scrollBar) {
			final float ox = value + r.x() - 50;
			shiftViewport(ox - shift.x(), 0);
		} else {
			final float oy = value + r.y() - shift.y() - 50;
			shiftViewport(0, oy);
		}
	}

	private class MiniMap extends PickableGLElement implements IHasMinSize {
		static final float MINI_MAP_FACTOR = 0.2f;
		private boolean hovered;
		/**
		 *
		 */
		public MiniMap() {
			setPicker(null);
		}

		@Override
		public Vec2f getMinSize() {
			Rect r = getPaintingCanvas();
			Vec2f s = r.size();
			s.scale(MINI_MAP_FACTOR);
			return s;
		}

		private Rect getVisibleRect() {
			Rect r = getClippingRect();
			Rect p = getPaintingCanvas();
			// System.out.println(r + " " + p);
			r.xy(r.xy().minus(p.xy()));
			r.xy(r.xy().times(MINI_MAP_FACTOR));
			r.size(r.size().times(MINI_MAP_FACTOR));
			return r;
		}

		public void updateBounds() {
			if (!isVisible())
				return;
			Vec2f r = getMinSize();
			// update my parent size
			findParent(IPopupElement.class).setContentSize(r.x(), r.y());
		}

		@Override
		protected void onMouseOver(Pick pick) {
			hovered = true;
			repaint();
		}

		@Override
		protected void onMouseOut(Pick pick) {
			hovered = false;
			repaint();
		}

		@Override
		protected void onDragDetected(Pick pick) {
			context.getSWTLayer().setCursor(SWT.CURSOR_HAND);
			if (pick.isAnyDragging())
				return;
			pick.setDoDragging(true);
		}

		@Override
		protected void onDragged(Pick pick) {
			if (!pick.isDoDragging())
				return;
			// TODO move the viewport
			Rect old = getVisibleRect();
			shiftViewport(pick.getDx() / MINI_MAP_FACTOR, pick.getDy() / MINI_MAP_FACTOR);
			float shiftX = Math.min(0, old.x() + pick.getDx());
			float shiftY = Math.min(0, old.y() + pick.getDy());
			findParent(IPopupElement.class).shift(shiftX, shiftY);
			updateBounds();
		}

		@Override
		protected void onMouseWheel(Pick pick) {
			IMouseEvent event = (IMouseEvent) pick;
			UndoStack undo = MiniMapCanvas.this.findParent(Domino.class).getUndo();
			if (event.getWheelRotation() != 0)
				undo.push(new ZoomCmd(ScaleLogic.shiftLogic(event, new Vec2f(100, 100)), toMousePos(pick
						.getPickedPoint())));
		}

		/**
		 * @param pickedPoint
		 * @return
		 */
		private Vec2f toMousePos(Vec2f pos) {
			pos = toRelative(pos);
			Rect p = getPaintingCanvas();
			pos = pos.times(1.f / MINI_MAP_FACTOR);
			pos.minus(p.xy());
			return pos;
		}

		@Override
		protected void onMouseReleased(Pick pick) {
			context.getSWTLayer().setCursor(-1);
		}

		@Override
		protected void renderImpl(GLGraphics g, float w, float h) {
			g.save();
			g.gl.glScalef(MINI_MAP_FACTOR, MINI_MAP_FACTOR, 1);
			Rect p = getPaintingCanvas();
			g.move(-p.x(), -p.y());
			g.color(1, 1, 1, 0.5f).fillRect(p);

			float sx = -shift.x();
			float sy = -shift.y();
			// g.move(sx, sy);
			for (IHasMiniMap mini : Iterables.filter(MiniMapCanvas.this, IHasMiniMap.class)) {
				if (!mini.getVisibility().doRender())
					continue;
				if (mini instanceof IManuallyShifted) {
					g.move(-sx, -sy);
					mini.renderMiniMap(g);
					g.move(sx, sy);
				} else {
					// Vec2f loc = mini.getLocation();
					// g.move(loc.x(), loc.y());
					mini.renderMiniMap(g);
					// g.move(-loc.x(), -loc.y());
				}
			}

			Rect r = getClippingRect();
			g.color(Color.BLACK);
			if (hovered)
				g.lineWidth(2);
			g.drawRect(r.x(), r.y(), r.width(), r.height());
			g.lineWidth(1);
			g.color(0, 0, 1, 0.08f);
			g.fillRect(p.x(), p.y(), p.width(), r.y() - p.y());
			g.fillRect(p.x(), r.y2(), p.width(), p.height() + p.y() - r.y2());
			g.fillRect(p.x(), r.y(), r.x() - p.x(), r.height());
			g.fillRect(r.x2(), r.y(), p.width() + p.x() - r.x2(), r.height());

			g.restore();
			super.renderImpl(g, w, h);
		}

		@Override
		protected void renderPickImpl(GLGraphics g, float w, float h) {
			if (getVisibility() == EVisibility.PICKABLE) {
				Rect p = getPaintingCanvas();
				Rect r = getClippingRect();
				r = r.times(MINI_MAP_FACTOR);
				g.fillRect(r.x() - p.x() * MINI_MAP_FACTOR + 10, r.y() - p.y() * MINI_MAP_FACTOR + 10, r.width() - 20,
						r.height() - 20);
			}
		}
		/**
		 * @return
		 */
		public boolean isVisible() {
			return context != null;
		}

	}

	public interface IHasMiniMap {
		void renderMiniMap(GLGraphics g);

		Rect getBoundingBox();

		Vec2f getLocation();

		EVisibility getVisibility();
	}

	public interface IManuallyShifted {
		void setShift(float x, float y);
	}

	/**
	 *
	 */
	public void toggleShowMiniMap() {
		final IPopupLayer popup = context.getPopupLayer();
		if (this.miniMap != null && this.miniMap.isVisible()) {
			popup.hide(miniMap);
			this.miniMap = null;
		} else {
			this.miniMap = new MiniMap();
			Vec2f size = this.miniMap.getMinSize();
			Vec2f mySize = getSize();
			Vec2f loc = getAbsoluteLocation();
			float x = loc.x() + mySize.x() - size.x() - 4 - scrollBarWidth;
			float y = loc.y() + mySize.y() - size.y() - 4 - scrollBarWidth;
			popup.show(miniMap, new Rect(x, y, size.x(), size.y()), IPopupLayer.FLAG_CLOSEABLE
					| IPopupLayer.FLAG_BORDER
					| IPopupLayer.FLAG_MOVEABLE);
		}
	}

	@Override
	public void layout(int deltaTimeMs) {
		if (autoShiftTimer >= 0) {
			autoShiftTimer -= deltaTimeMs;
			if (autoShiftTimer <= 0) {
				shiftViewport(autoShift.x(), autoShift.y());
				autoShiftTimer = AUTO_SHIFT_TIMER;
			}
		}
		super.layout(deltaTimeMs);
	}

	/**
	 * idea if the cursor stands at a corner
	 *
	 * @param mousePos
	 */
	public void autoShift(Vec2f mousePos) {
		Vec2f mouse = toRelative(mousePos);
		Vec2f size = getSize();

		if (mouse.x() < AUTO_SHIFT_AREA)
			autoShift.setX(-AUTO_SHIFT);
		else if (mouse.x() >= (size.x() - AUTO_SHIFT_AREA))
			autoShift.setX(AUTO_SHIFT);
		else
			autoShift.setX(0);
		if (mouse.y() < AUTO_SHIFT_AREA)
			autoShift.setY(-AUTO_SHIFT);
		else if (mouse.y() >= (size.y() - AUTO_SHIFT_AREA))
			autoShift.setY(AUTO_SHIFT);
		else
			autoShift.setY(0);
		if (autoShift.x() != 0 || autoShift.y() != 0) {
			autoShiftTimer = AUTO_SHIFT_TIMER;
		} else
			autoShiftTimer = -1;
	}

	public void stopAutoShift() {
		autoShiftTimer = -1;
	}

	private class ScrollBarImpl {
		final IScrollBar scrollBar;
		int pickingId;

		public ScrollBarImpl(IScrollBar scrollBar) {
			this.scrollBar = scrollBar;
		}
	}
}

redpajama_set_name: RedPajamaGithub


text:
using CSharpExtensions.Collections.Generic;
using NUnit.Framework;

namespace CSharpExtensions.Tests
{
    [TestFixture]
    internal class SingleLinkedListTests
    {
        [Test]
        public void Constructor_Test()
        {
            SingleLinkedList<int> list = new SingleLinkedList<int>(new int[] { 1, 2, 3 });
            int expected = 3;
            int actual = list.Count;
            Assert.AreEqual(expected, actual);
        }

        [Test]
        public void AddAfter_Value_Test()
        {
            SingleLinkedList<int> list = new SingleLinkedList<int>(new int[] { 1, 2, 3 });
            SingleLinkedListNode<int> node = list.AddAfter(list.First, 4);
            Assert.AreEqual(4, list.Count);
            Assert.AreEqual(2, node.Next.Value);
        }

        [Test]
        public void AddAfter_Node_Test()
        {
            SingleLinkedList<int> list = new SingleLinkedList<int>(new int[] { 1, 2, 3 });
            SingleLinkedListNode<int> node = new SingleLinkedListNode<int>(4);
            list.AddAfter(list.First, node);
            Assert.AreEqual(4, list.Count);
            Assert.AreEqual(2, node.Next.Value);
        }

        [Test]
        public void AddFirst_Value_Test()
        {
            SingleLinkedList<int> list = new SingleLinkedList<int>(new int[] { 1, 2, 3 });
            SingleLinkedListNode<int> node = list.AddFirst(4);
            Assert.AreEqual(4, list.Count);
            Assert.AreEqual(node, list.First);
        }

        [Test]
        public void AddFirst_Node_Test()
        {
            SingleLinkedList<int> list = new SingleLinkedList<int>(new int[] { 1, 2, 3 });
            SingleLinkedListNode<int> node = new SingleLinkedListNode<int>(4);
            list.AddFirst(node);
            Assert.AreEqual(4, list.Count);
            Assert.AreEqual(4, list.First.Value);
        }
    }
}


text:
- Atom One Dark (`akamud.vscode-theme-onedark`)
- Material Theme (`Equinusocio.vsc-material-theme`)
- Quiet Light for VSC (better version of the default) (`onecrayon.theme-quietlight-vsc`)
- Color Highlight (`naumovs.color-highlight`)
- Docker (`PeterJausovec.vscode-docker`)
- Prettify JSON (`mohsen1.prettify-json`)
- Sass (`robinbentley.sass-indented`)
- vetur (Vue) (`octref.vetur`)
- Project Manager (`alefragnani.project-manager`)


text:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>p.runtime</groupId>
    <artifactId>PJavaRuntime</artifactId>
    <version>${revision}</version>

    <name>Java runtime for the P programming language</name>
    <url>https://github.com/p-org/P</url>

    <licenses>
      <license>
        <name>MIT License</name>
        <url>https://spdx.org/licenses/MIT.html</url>
      </license>
    </licenses>

    <dependencies>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.8.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.17.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.17.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-layout-template-json</artifactId>
            <version>2.17.2</version>
        </dependency>
        <dependency>
            <groupId>org.jacoco</groupId>
            <artifactId>jacoco-maven-plugin</artifactId>
            <version>0.8.8</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.jacoco</groupId>
                <artifactId>jacoco-maven-plugin</artifactId>
                <version>0.8.8</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>prepare-agent</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>report</id>
                        <phase>test</phase>
                        <goals>
                            <goal>report</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <log4j2.configurationFile>${project.basedir}/src/main/resources/log4j2.xml</log4j2.configurationFile>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <revision>1.0-SNAPSHOT</revision>
    </properties>

</project>


text:
(function() {var implementors = {};
implementors["cargonauts"] = ["impl<'output, Target> SerializeTuple for TupleSerializer<'output, Target> where
    Target: 'output + Target, ",];

            if (window.register_implementors) {
                window.register_implementors(implementors);
            } else {
                window.pending_implementors = implementors;
            }

})()


text:
<?
/**
 *  Router.php
 *
 *  This class manages all the routing process from the request to the controller's
 *  method call.
 */
class Router
{
    private $cfg;
    private $load;

    function __construct($loader, $config) {
        $this->load = $loader;
        $this->cfg = $config;
    }

    /**
     * This method loads the requested controller, makes the method call and passes
     * the params array to it. It takes a request as a parameter in the form of
     * an associative array as follows:
     * $req = [
     *      "controller" => "ControllerName",
     *      "method" => "MethodName",
     *      "params" => ["Param1", "Param2", "Param3"]
     * ]
     *
     * If no controller is specified in the request, the default controller will
     * be loaded. Similarly, if no specific method is requested then the Index
     * method will be called.
     */
    public function handle($req) {
        $controller = $this->load->controller($req['controller']) ?: $this->load->controller($this->cfg->get("APP", "DEFAULT_CONTROLLER"));
        $method = method_exists($controller, $req['method']) ? $req['method'] : "index";

        call_user_func_array(array($controller, $method), $req['params']);
    }
}
?>


text:
<bill session="115" type="h" number="3777" updated="2017-09-29T07:13:11Z">
  <state datetime="2017-09-14">REFERRED</state>
  <status>
    <introduced datetime="2017-09-14"/>
  </status>
  <introduced datetime="2017-09-14"/>
  <titles>
    <title type="short" as="introduced">Juab County Conveyance Act of 2017</title>
    <title type="short" as="introduced">Juab County Conveyance Act of 2017</title>
    <title type="official" as="introduced">To direct the Secretary of Agriculture to convey certain National Forest System land containing the Nephi Work Center in Juab County, Utah, to Juab County.</title>
    <title type="display">Juab County Conveyance Act of 2017</title>
  </titles>
  <sponsor bioguide_id="L000584"/>
  <cosponsors/>
  <actions>
    <action datetime="2017-09-14">
      <text>Introduced in House</text>
    </action>
    <action datetime="2017-09-14" state="REFERRED">
      <text>Referred to the House Committee on Natural Resources.</text>
    </action>
    <action datetime="2017-09-25">
      <text>Referred to the Subcommittee on Federal Lands.</text>
    </action>
  </actions>
  <committees>
    <committee subcommittee="" code="HSII" name="House Natural Resources" activity="Referral"/>
    <committee subcommittee="Federal Lands" code="HSII10" name="House Natural Resources" activity="Referral"/>
  </committees>
  <relatedbills/>
  <subjects>
    <term name="Public lands and natural resources"/>
  </subjects>
  <amendments/>
  <committee-reports/>
</bill>


text:
/*
---

name: Locale.da-DK.Date

description: Date messages for Danish.

license: MIT-style license

authors:
  - Martin Overgaard
  - Henrik Hansen

requires:
  - /Locale

provides: [Locale.da-DK.Date]

...
*/

Locale.define('da-DK', 'Date', {

	months: ['Januar', 'Februar', 'Marts', 'April', 'Maj', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'December'],
	months_abbr: ['jan.', 'feb.', 'mar.', 'apr.', 'maj.', 'jun.', 'jul.', 'aug.', 'sep.', 'okt.', 'nov.', 'dec.'],
	days: ['Søndag', 'Mandag', 'Tirsdag', 'Onsdag', 'Torsdag', 'Fredag', 'Lørdag'],
	days_abbr: ['søn', 'man', 'tir', 'ons', 'tor', 'fre', 'lør'],

	// Culture's date order: DD-MM-YYYY
	dateOrder: ['date', 'month', 'year'],
	shortDate: '%d-%m-%Y',
	shortTime: '%H:%M',
	AM: 'AM',
	PM: 'PM',

	// Date.Extras
	ordinal: '.',

	lessThanMinuteAgo: 'mindre end et minut siden',
	minuteAgo: 'omkring et minut siden',
	minutesAgo: '{delta} minutter siden',
	hourAgo: 'omkring en time siden',
	hoursAgo: 'omkring {delta} timer siden',
	dayAgo: '1 dag siden',
	daysAgo: '{delta} dage siden',
	weekAgo: '1 uge siden',
	weeksAgo: '{delta} uger siden',
	monthAgo: '1 måned siden',
	monthsAgo: '{delta} måneder siden',
	yearAgo: '1 år siden',
	yearsAgo: '{delta} år siden',

	lessThanMinuteUntil: 'mindre end et minut fra nu',
	minuteUntil: 'omkring et minut fra nu',
	minutesUntil: '{delta} minutter fra nu',
	hourUntil: 'omkring en time fra nu',
	hoursUntil: 'omkring {delta} timer fra nu',
	dayUntil: '1 dag fra nu',
	daysUntil: '{delta} dage fra nu',
	weekUntil: '1 uge fra nu',
	weeksUntil: '{delta} uger fra nu',
	monthUntil: '1 måned fra nu',
	monthsUntil: '{delta} måneder fra nu',
	yearUntil: '1 år fra nu',
	yearsUntil: '{delta} år fra nu'

});


text:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <ImageView
            android:id="@+id/iv_icon"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:src="@drawable/btn_assistivetouch" />

        <TextView
            android:id="@+id/summary"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="summary"
            android:textColor="#ffffffff"
            android:textSize="11.0dip" />
    </LinearLayout>

</RelativeLayout>


text:
<?php

namespace App\Pages;

use Zippy\Binding\PropertyBinding;
use Zippy\Html\Label;
use Zippy\Html\Panel;
use Zippy\Html\Link\ClickLink;
use App\Helper;
use App\Application as App;
use App\System;
use App\Entity\User;

class Base extends \Zippy\Html\WebPage
{

    public function __construct($params = null) {
        global $_config;

        \Zippy\Html\WebPage::__construct();

        $user = System::getUser();
        if ($user->user_id == 0) {
            App::Redirect("\\App\\Pages\\Userlogin");
            return;
        }

        $this->add(new ClickLink('logout', $this, 'LogoutClick'));
        $this->add(new Label('username', $user->username));

        $this->add(new ClickLink("pageinfo"));

        $options = System::getOptions('common');

        $this->_tvars["docmenu"]  = Helper::generateMenu(1);
        $this->_tvars["repmenu"]  = Helper::generateMenu(2);
        $this->_tvars["regmenu"]  = Helper::generateMenu(3);
        $this->_tvars["refmenu"]  = Helper::generateMenu(4);

        $this->_tvars["islogined"] = $user->user_id > 0;
        $this->_tvars["isadmin"] = $user->userlogin == 'admin';
        $pi = $this->getPageInfo();

        if (strlen($pi) == 0) {
            $this->pageinfo->setVisible(false);
        }

        $this->_tvars["defoptions"] = ($options['defstore'] > 0  )  == false;

        $this->_tvars["smart"] = Helper::generateSmartMenu();
        $this->_tvars["picontent"] = $pi;

        if (Helper::usends()) {
            $this->_tvars["usends"] = true;
            $this->_tvars["_nds"] = Helper::nds();
            $this->_tvars["nds_"] = Helper::nds(true);
        } else {
            $this->_tvars["usends"] = false;
            $this->_tvars["_nds"] = 0;
            $this->_tvars["nds_"] = 0;
        }
    }

    public function LogoutClick($sender) {
        setcookie("remember", '', 0);
        System::setUser(new \App\Entity\User());
        $_SESSION['user_id'] = 0;
        $_SESSION['userlogin'] = 'Гость';

        //$page = $this->getOwnerPage();
        //  $page = get_class($page)  ;
        App::Redirect("\\App\\Pages\\UserLogin");
        ;
        ;
        //    App::$app->getresponse()->toBack();
    }

    public function getPageInfo() {
        $class = explode("\\", get_class($this));
        $classname = $class[count($class) - 1];
        return \App\Helper::getMetaNotes($classname);
    }

    //вывод ошибки,  используется   в дочерних страницах
    public function setError($msg) {
        System::setErrorMsg($msg);
    }

    public function setSuccess($msg) {
        System::setSuccesMsg($msg);
    }

    public function setWarn($msg) {
        System::setWarnMsg($msg);
    }

    public function setInfo($msg) {
        System::setInfoMsg($msg);
    }

    final protected function isError() {
        return strlen(System::getErrorMsg()) > 0;
    }

    public function beforeRender() {
        $user = System::getUser();
        $this->_tvars['notcnt'] = \App\Entity\Notify::isNotify($user->user_id);

    }

    protected function afterRender() {
        if (strlen(System::getErrorMsg()) > 0)
            App::$app->getResponse()->addJavaScript("toastr.error('" . System::getErrorMsg() . "')        ", true);
        if (strlen(System::getWarnMsg()) > 0)
            App::$app->getResponse()->addJavaScript("toastr.warning('" . System::getWarnMsg() . "')        ", true);
        if (strlen(System::getSuccesMsg()) > 0)
            App::$app->getResponse()->addJavaScript("toastr.success('" . System::getSuccesMsg() . "')        ", true);
        if (strlen(System::getInfoMsg()) > 0)
            App::$app->getResponse()->addJavaScript("toastr.info('" . System::getInfoMsg() . "')        ", true);

        $this->setError('');
        $this->setSuccess('');

        $this->setInfo('');
        $this->setWarn('');
    }

    //Перезагрузить страницу  с  клиента
    //например для  сброса  адресной строки  после  команды удаления
    protected final function resetURL() {
        \App\Application::$app->setReloadPage();
    }

}


text:
package com.scienceminer.nerd.kb.model;

import java.util.concurrent.*;

import com.scienceminer.nerd.kb.model.hadoop.DbPage;
import com.scienceminer.nerd.kb.db.*;

/**
 * Represents redirects in Wikipedia.
 *
 *  -> to be replaced by com.scienceminer.nerd.kb.Variant
 */
public class Redirect extends Page {

	public Redirect(KBLowerEnvironment env, int id) {
		super(env, id);
	}

	protected Redirect(KBLowerEnvironment env, int id, DbPage pd) {
		super(env, id, pd);
	}

	public Article getTarget() {
		int currId = id;
		ConcurrentMap redirectsFollowed = new ConcurrentHashMap();
		while (redirectsFollowed.get(currId) == null) {
			redirectsFollowed.put(currId, currId);
			Integer targetId = env.getDbRedirectTargetBySource().retrieve(currId);
			if (targetId == null)
				return null;

			Page target = Page.createPage(env, targetId);
			if (!target.exists())
				return null;

			if (target.getType() == PageType.redirect)
				currId = targetId;
			else if (target.getType() == PageType.article)
				return (Article)target;
		}

		return null;
	}

}


text:
program derived_types

    implicit none
    type book
        character(len=40) :: title
        character(len=40) :: author
        integer           :: year
        integer           :: ID
        character(len=40) :: publisher
    end type book

    type(book) :: book1
    type(book) :: book2

    book1%title     = "World"
    book1%author    = "Me"
    book1%year      = 1985
    book1%ID        = 12
    book1%publisher = "Venezia"

    book2%title     = "Friuli"
    book2%author    = "MT"
    book2%year      = 1946
    book2%ID        = 22
    book2%publisher = "Savorgnano"

    print *, book1%title
    print *, book1%author
    print *, book1%year
    print *, book1%ID
    print *, book1%publisher

    print *, book2%title
    print *, book2%author
    print *, book2%year
    print *, book2%ID
    print *, book2%publisher

end program derived_types