Logo Search packages:      
Sourcecode: xdaliclock version File versions

AppController.m

/* xdaliclock - a melting digital clock
 * Copyright (c) 1991-2007 Jamie Zawinski <jwz@jwz.org>
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation.  No representations are made about the suitability of this
 * software for any purpose.  It is provided "as is" without express or
 * implied warranty.
 */

#import "AppController.h"
#import "DaliClockWindow.h"

char *progname = "Dali Clock";   // digital.c wants this for error messages.

@interface AppController (ForwardDeclarations)
- (void)createDaliClockWindow;
@end

@implementation AppController

+ (void)initialize;
{
  static BOOL initialized_p = NO;
  if (initialized_p)
    return;
  initialized_p = YES;

  NSUserDefaultsController *controller =
    [NSUserDefaultsController sharedUserDefaultsController];

  /* Tell the DaliClockView class to initialize its preferences.
     This can't just happen in [DaliClockView:initialize], because
     we have to ensure that DaliClockView is using the same
     NSUserDefaultsController object that we are using here.
   */
  [DaliClockView initializeDefaults:controller];


  /* Now that the DaliClockView class has initialized its preferences,
     set the defaults for those preferences handled by AppController
     rather than by DaliClockView.
   */
  NSDictionary* defaults = [NSDictionary dictionaryWithObjectsAndKeys:
    @"NO", @"windowTitlebarIsHidden",
    @"NO", @"dockIconIsHidden",
    @"2", @"windowLevel",
    nil];

  NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:100];
  [dict addEntriesFromDictionary: [controller initialValues]];
  [dict addEntriesFromDictionary:defaults];
  [controller setInitialValues:dict];
  [[controller defaults] registerDefaults:dict];
}


- (void)createDaliClockWindow;
{
  NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];

  BOOL title_p = ![prefs boolForKey:@"windowTitlebarIsHidden"];
  BOOL was_fullscreen_p = (fullscreen_window_level != 0);

  int level = [prefs integerForKey:@"windowLevel"];
  level = (level == 0 ? NSFloatingWindowLevel :
           level == 1 ? NSNormalWindowLevel-1 :
           NSNormalWindowLevel);

  if (was_fullscreen_p) {
    [NSCursor unhide];
    if (CGDisplayRelease (kCGDirectMainDisplay) != kCGErrorSuccess)
      NSLog ( @"Couldn't release the display!");
  }

  if (fullscreen_p) {
    if (CGDisplayCapture (kCGDirectMainDisplay) != kCGErrorSuccess) {
        NSLog(@"Couldn't capture the main display!");
      fullscreen_p = NO;
    }
  }

  if (fullscreen_p) {
    level = CGShieldingWindowLevel();
    fullscreen_window_level = level;
    title_p = NO;
    [NSCursor hide];
  }

  NSScreen *screen = [NSScreen mainScreen];

  // Set default size/position of the window if there is no autosave.
  //
  NSRect rect;
  rect.size.width = 625;
  rect.size.height = 128;
  rect.origin.x = ([screen frame].size.width  - rect.size.width)  / 2;
  rect.origin.y = ([screen frame].size.height - rect.size.height) / 2;

  NSRect default_rect = rect;

  // Nuke the old window if we're re-creating it.
  //
  BOOL keep_old_size_p = NO;
  if (window) {
    keep_old_size_p = !was_fullscreen_p;
    rect = [window contentRectForFrameRect:[window frame]];
    [window close];
    [window setFrameAutosaveName:@""];  // two windows can't have the same one
    [window autorelease];
    window = 0;
  }

  window = [[DaliClockWindow alloc]
            initWithContentRect:rect
                      styleMask:(title_p
                                 ? (NSTitledWindowMask |
                                    NSClosableWindowMask |
                                    NSMiniaturizableWindowMask |
                                    NSResizableWindowMask)
                                 : (NSBorderlessWindowMask))
                        backing:NSBackingStoreBuffered
                          defer:YES
                         screen:screen];
  [window setTitle:@"Dali Clock"];
  [window setOpaque:NO];
  [window setHasShadow:NO];  // windows with shadows flicker
  [window setLevel:level];
  [window setMovableByWindowBackground:!fullscreen_p];
  [window setReleasedWhenClosed:NO];
  [window setDelegate:self]; // for the "cancel" method

  if (fullscreen_p) {
    // don't save window sizes for fullscreen windows.
    [window setFrameAutosaveName:@""];
  } else {
    [window setFrameAutosaveName:@"clockWindow"];
    // need "force" for size to be updated when borderless.
    if (! [window setFrameUsingName:[window frameAutosaveName] force:YES]) {
      // If we weren't able to set the frame, then that means that no size
      // was yet saved; so instead of leaving it at the "full screen" size,
      // use the default size and position instead.
      [window setFrame:[window frameRectForContentRect:default_rect]
               display:NO];
    }
  }

  if (fullscreen_p)
    [window setFrame:[screen frame] display:NO];
  else if (keep_old_size_p)
    [window setFrame:[window frameRectForContentRect:rect] display:NO];

  // if we're re-creating the window, just move the existing view
  // from the previous window.
  if (! view)
    view = [[DaliClockView alloc] initWithFrame:[window frame]];

  [window setContentView:view];
}


/* When the window closes, exit (even if prefs still open.)
 */
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)a
{
  return YES;
}


/* Connect the various window/app-related preferences to accessor methods.
   Note that DaliClockView has its own set of preferences it deals with.
 */
- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
  [self createDaliClockWindow];
  if (!view || !window) abort();

  NSUserDefaultsController *userDefaultsController =
    [NSUserDefaultsController sharedUserDefaultsController];

  [userDefaultsController addObserver:self
                           forKeyPath:@"values.windowLevel"
                              options:nil
                              context:@selector(windowLevelDidChange:)];
  [userDefaultsController addObserver:self
                           forKeyPath:@"values.windowTitlebarIsHidden"
                              options:nil
                         context:@selector(windowTitlebarIsHiddenDidChange:)];

  [window makeKeyAndOrderFront:self];


#if 0
  /* For this to work, AppInfo.plist must have LSBackgroundOnly=1.
     (LSUIElement=1 does not work).  When that is set, the app comes
     up with no icon by default, and we are able to turn it on (but
     cannot turn it off).

     But, then there's no way out, because the window will not take
     focus.  It has no menubar, and typing Command-, does not bring
     up preferences!

     If LSUIElement=1, then Command-, does bring up preferences...
     but TransformProcessType() returns error -50 ("error in user
     parameter list").

     So, as far as I can see, there's no way to make this preference
     not be completely horrible.
   */
  if (![[NSUserDefaults standardUserDefaults]
         boolForKey:@"dockIconIsHidden"]) {
    // Dock icon is hidden by default; turn it on if it should be on.
    ProcessSerialNumber psn = { 0, kCurrentProcess }; 
    OSStatus status = 
      TransformProcessType (&psn, kProcessTransformToForegroundApplication);
    if (status)
      NSLog (@"Could not turn on the dock icon: error %d", status);
  }
  [NSMenu setMenuBarVisible:YES];  // this doesn't work.
#endif
}


- (void)windowLevelDidChange:(NSDictionary *)change
{
  NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
  int level = [prefs integerForKey:@"windowLevel"];
  level = (level == 0 ? NSFloatingWindowLevel :
           level == 1 ? NSNormalWindowLevel-1 :
           NSNormalWindowLevel);
  [window setLevel:level];
}


- (void)windowTitlebarIsHiddenDidChange:(NSDictionary *)change
{
  // re-create the window when the "Title Bar" preference changes.
  [self createDaliClockWindow];
  [window makeKeyAndOrderFront:self];
}


- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context
{
  SEL dispatchSelector = (SEL)context;
  if (dispatchSelector != NULL) {
    [self performSelector:dispatchSelector withObject:change];
  } else {
    [super observeValueForKeyPath:keyPath
                         ofObject:object
                           change:change
                          context:context];
  }
}


/* Toggle, called by the "Window / Full Screen" menu item.
   This doesn't go through the preferences because it should not be saved.
 */
- (void)performFullscreen:(id)sender
{
  fullscreen_p = !fullscreen_p;
  [self createDaliClockWindow];
  [window makeKeyAndOrderFront:self];
}


/* "Window / Minimize".
   Normally this happens in NSWindow via FirstResponder, but we bind this to
   the menu directly so that it works when the window is borderless or full
   screen.  (To minimize a full-screen window, we have to take it out of
   full-screen mode first, which is why this can't be in DaliClockWindow
   like performClose is).
 */
- (void)performMiniaturize:(id)sender
{
  if (fullscreen_p)
    // turn off fullscreen mode before miniaturizing.
    // (it does not automatically turn back on when unminiaturizing.)
    [self performFullscreen:sender];

  //[window performMiniaturize:sender];  // beeps when borderless
  [window miniaturize:sender];
}


/* This is sent by ESC and Cmd-.
   Take us out of full-screen if it is active.
 */
- (void)cancel:(id)sender
{
  if (fullscreen_p) {
    [self performFullscreen:sender];
  }
}

/* The About button in the menu bar.
 */
- (IBAction)aboutClick:(id)sender
{
  [view aboutClick:sender];
}

@end

Generated by  Doxygen 1.6.0   Back to index