1// Copyright (c) 2011 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 <Cocoa/Cocoa.h>
6
7#import "chrome/browser/ui/cocoa/window_size_autosaver.h"
8
9#include "base/prefs/pref_service.h"
10#include "base/prefs/scoped_user_pref_update.h"
11
12// If the window width stored in the prefs is smaller than this, the size is
13// not restored but instead cleared from the profile -- to protect users from
14// accidentally making their windows very small and then not finding them again.
15const int kMinWindowWidth = 101;
16
17// Minimum restored window height, see |kMinWindowWidth|.
18const int kMinWindowHeight = 17;
19
20@interface WindowSizeAutosaver (Private)
21- (void)save:(NSNotification*)notification;
22- (void)restore;
23@end
24
25@implementation WindowSizeAutosaver
26
27- (id)initWithWindow:(NSWindow*)window
28         prefService:(PrefService*)prefs
29                path:(const char*)path {
30  if ((self = [super init])) {
31    window_ = window;
32    prefService_ = prefs;
33    path_ = path;
34
35    [self restore];
36    [[NSNotificationCenter defaultCenter]
37      addObserver:self
38         selector:@selector(save:)
39             name:NSWindowDidMoveNotification
40           object:window_];
41    [[NSNotificationCenter defaultCenter]
42      addObserver:self
43         selector:@selector(save:)
44             name:NSWindowDidResizeNotification
45           object:window_];
46  }
47  return self;
48}
49
50- (void)dealloc {
51  [[NSNotificationCenter defaultCenter] removeObserver:self];
52  [super dealloc];
53}
54
55- (void)save:(NSNotification*)notification {
56  DictionaryPrefUpdate update(prefService_, path_);
57  base::DictionaryValue* windowPrefs = update.Get();
58  NSRect frame = [window_ frame];
59  if ([window_ styleMask] & NSResizableWindowMask) {
60    // Save the origin of the window.
61    windowPrefs->SetInteger("left", NSMinX(frame));
62    windowPrefs->SetInteger("right", NSMaxX(frame));
63    // windows's and linux's profiles have top < bottom due to having their
64    // screen origin in the upper left, while cocoa's is in the lower left. To
65    // keep the top < bottom invariant, store top in bottom and vice versa.
66    windowPrefs->SetInteger("top", NSMinY(frame));
67    windowPrefs->SetInteger("bottom", NSMaxY(frame));
68  } else {
69    // Save the origin of the window.
70    windowPrefs->SetInteger("x", frame.origin.x);
71    windowPrefs->SetInteger("y", frame.origin.y);
72  }
73}
74
75- (void)restore {
76  // Get the positioning information.
77  const base::DictionaryValue* windowPrefs = prefService_->GetDictionary(path_);
78  if ([window_ styleMask] & NSResizableWindowMask) {
79    int x1, x2, y1, y2;
80    if (!windowPrefs->GetInteger("left", &x1) ||
81        !windowPrefs->GetInteger("right", &x2) ||
82        !windowPrefs->GetInteger("top", &y1) ||
83        !windowPrefs->GetInteger("bottom", &y2)) {
84      return;
85    }
86    if (x2 - x1 < kMinWindowWidth || y2 - y1 < kMinWindowHeight) {
87      // Windows should never be very small.
88      DictionaryPrefUpdate update(prefService_, path_);
89      base::DictionaryValue* mutableWindowPrefs = update.Get();
90      mutableWindowPrefs->Remove("left", NULL);
91      mutableWindowPrefs->Remove("right", NULL);
92      mutableWindowPrefs->Remove("top", NULL);
93      mutableWindowPrefs->Remove("bottom", NULL);
94    } else {
95      [window_ setFrame:NSMakeRect(x1, y1, x2 - x1, y2 - y1) display:YES];
96
97      // Make sure the window is on-screen.
98      [window_ cascadeTopLeftFromPoint:NSZeroPoint];
99    }
100  } else {
101    int x, y;
102    if (!windowPrefs->GetInteger("x", &x) ||
103        !windowPrefs->GetInteger("y", &y))
104       return;  // Nothing stored.
105    // Turn the origin (lower-left) into an upper-left window point.
106    NSPoint upperLeft = NSMakePoint(x, y + NSHeight([window_ frame]));
107    [window_ cascadeTopLeftFromPoint:upperLeft];
108  }
109}
110
111@end
112
113