diff --git a/BlackEnvelope.png b/BlackEnvelope.png
deleted file mode 100644
index 2ec6bb9..0000000
Binary files a/BlackEnvelope.png and /dev/null differ
diff --git a/BlackEnvelope@2x.png b/BlackEnvelope@2x.png
deleted file mode 100644
index f52df61..0000000
Binary files a/BlackEnvelope@2x.png and /dev/null differ
diff --git a/BlackEnvelope@2x.pxm b/BlackEnvelope@2x.pxm
deleted file mode 100644
index ed7cc11..0000000
Binary files a/BlackEnvelope@2x.pxm and /dev/null differ
diff --git a/BlueEnvelope.png b/BlueEnvelope.png
deleted file mode 100644
index e033c89..0000000
Binary files a/BlueEnvelope.png and /dev/null differ
diff --git a/BlueEnvelope@2x.png b/BlueEnvelope@2x.png
deleted file mode 100644
index e033c89..0000000
Binary files a/BlueEnvelope@2x.png and /dev/null differ
diff --git a/Design/Orangered.sketch b/Design/Orangered.sketch
new file mode 100644
index 0000000..3cd14df
Binary files /dev/null and b/Design/Orangered.sketch differ
diff --git a/Orangered.svg b/Design/Orangered.svg
similarity index 100%
rename from Orangered.svg
rename to Design/Orangered.svg
diff --git a/English.lproj/InfoPlist.strings b/English.lproj/InfoPlist.strings
deleted file mode 100644
index 477b28f..0000000
--- a/English.lproj/InfoPlist.strings
+++ /dev/null
@@ -1,2 +0,0 @@
-/* Localized versions of Info.plist keys */
-
diff --git a/English.lproj/MainMenu.xib b/English.lproj/MainMenu.xib
deleted file mode 100644
index a09cc8e..0000000
--- a/English.lproj/MainMenu.xib
+++ /dev/null
@@ -1,410 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- NSAllRomanInputSourcesLocaleIdentifier
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/GreyEnvelope.png b/GreyEnvelope.png
deleted file mode 100644
index 300bd15..0000000
Binary files a/GreyEnvelope.png and /dev/null differ
diff --git a/GreyEnvelope@2x.png b/GreyEnvelope@2x.png
deleted file mode 100644
index 300bd15..0000000
Binary files a/GreyEnvelope@2x.png and /dev/null differ
diff --git a/HighlightEnvelope.png b/HighlightEnvelope.png
deleted file mode 100644
index e0d5092..0000000
Binary files a/HighlightEnvelope.png and /dev/null differ
diff --git a/HighlightEnvelope@2x.png b/HighlightEnvelope@2x.png
deleted file mode 100644
index e0d5092..0000000
Binary files a/HighlightEnvelope@2x.png and /dev/null differ
diff --git a/NSDataGzipCategory.h b/NSDataGzipCategory.h
deleted file mode 100644
index cf6fc07..0000000
--- a/NSDataGzipCategory.h
+++ /dev/null
@@ -1,17 +0,0 @@
-//
-// NSDataGzipCategory.h
-// Orangered
-//
-// Created by Alan Westbrook on 6/20/10.
-// Copyright 2010 Voidref Software. All rights reserved.
-//
-
-#import
-
-@interface NSData (NSDataGzipCategory)
-
-// gzip utility
-- (NSData *)gzipInflate;
-- (NSData *)gzipDeflate;
-
-@end
diff --git a/NSDataGzipCategory.m b/NSDataGzipCategory.m
deleted file mode 100644
index 75bacd5..0000000
--- a/NSDataGzipCategory.m
+++ /dev/null
@@ -1,100 +0,0 @@
-//
-// NSDataGzipCategory.m
-// Orangered
-//
-// Created by Alan Westbrook on 6/20/10.
-// Copyright 2010 Voidref Software. All rights reserved.
-//
-
-#import "NSDataGzipCategory.h"
-
-
-#import
-
-@implementation NSData (NSDataGzipCategory)
-
-- (NSData *)gzipInflate
-{
- if ([self length] == 0) return self;
-
- unsigned long full_length = [self length];
- unsigned long half_length = [self length] / 2;
-
- NSMutableData *decompressed = [NSMutableData dataWithLength: full_length + half_length];
- BOOL done = NO;
- int status;
-
- z_stream strm;
- strm.next_in = (Bytef *)[self bytes];
- strm.avail_in = (uInt)[self length];
- strm.total_out = 0;
- strm.zalloc = Z_NULL;
- strm.zfree = Z_NULL;
-
- if (inflateInit2(&strm, (15+32)) != Z_OK) return nil;
- while (!done)
- {
- // Make sure we have enough room and reset the lengths.
- if (strm.total_out >= [decompressed length])
- [decompressed increaseLengthBy: half_length];
- strm.next_out = [decompressed mutableBytes] + strm.total_out;
- strm.avail_out = (uInt)([decompressed length] - strm.total_out);
-
- // Inflate another chunk.
- status = inflate (&strm, Z_SYNC_FLUSH);
- if (status == Z_STREAM_END) done = YES;
- else if (status != Z_OK) break;
- }
- if (inflateEnd (&strm) != Z_OK) return nil;
-
- // Set real length.
- if (done)
- {
- [decompressed setLength: strm.total_out];
- return [NSData dataWithData: decompressed];
- }
- else return nil;
-}
-
-- (NSData *)gzipDeflate
-{
- if ([self length] == 0) return self;
-
- z_stream strm;
-
- strm.zalloc = Z_NULL;
- strm.zfree = Z_NULL;
- strm.opaque = Z_NULL;
- strm.total_out = 0;
- strm.next_in=(Bytef *)[self bytes];
- strm.avail_in = (uint)[self length];
-
- // Compresssion Levels:
- // Z_NO_COMPRESSION
- // Z_BEST_SPEED
- // Z_BEST_COMPRESSION
- // Z_DEFAULT_COMPRESSION
-
- if (deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (15+16), 8, Z_DEFAULT_STRATEGY) != Z_OK) return nil;
-
- NSMutableData *compressed = [NSMutableData dataWithLength:16384]; // 16K chunks for expansion
-
- do {
-
- if (strm.total_out >= [compressed length])
- [compressed increaseLengthBy: 16384];
-
- strm.next_out = [compressed mutableBytes] + strm.total_out;
- strm.avail_out = (uint)([compressed length] - strm.total_out);
-
- deflate(&strm, Z_FINISH);
-
- } while (strm.avail_out == 0);
-
- deflateEnd(&strm);
-
- [compressed setLength: strm.total_out];
- return [NSData dataWithData:compressed];
-}
-
-@end
diff --git a/Orangered-Swift/AppDelegate.swift b/Orangered-Swift/AppDelegate.swift
new file mode 100644
index 0000000..7bb73bc
--- /dev/null
+++ b/Orangered-Swift/AppDelegate.swift
@@ -0,0 +1,19 @@
+//
+// AppDelegate.swift
+// Orangered-Swift
+//
+// Created by Alan Westbrook on 5/29/16.
+// Copyright © 2016 Rockwood Software. All rights reserved.
+//
+
+import Cocoa
+
+class AppDelegate: NSObject, NSApplicationDelegate {
+
+ var controller:StatusItemController!
+
+ func applicationDidFinishLaunching(_ aNotification: Notification) {
+ controller = StatusItemController()
+ }
+}
+
diff --git a/Orangered/Images.xcassets/AppIcon.appiconset/Contents.json b/Orangered-Swift/Assets.xcassets/AppIcon.appiconset/Contents.json
similarity index 79%
rename from Orangered/Images.xcassets/AppIcon.appiconset/Contents.json
rename to Orangered-Swift/Assets.xcassets/AppIcon.appiconset/Contents.json
index 73237f4..72f58e5 100644
--- a/Orangered/Images.xcassets/AppIcon.appiconset/Contents.json
+++ b/Orangered-Swift/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -1,16 +1,16 @@
{
"images" : [
- {
- "idiom" : "mac",
- "scale" : "2x",
- "size" : "16x16"
- },
{
"size" : "16x16",
"idiom" : "mac",
"filename" : "Orangered16x16.png",
"scale" : "1x"
},
+ {
+ "idiom" : "mac",
+ "size" : "16x16",
+ "scale" : "2x"
+ },
{
"size" : "32x32",
"idiom" : "mac",
@@ -19,8 +19,8 @@
},
{
"idiom" : "mac",
- "scale" : "2x",
- "size" : "32x32"
+ "size" : "32x32",
+ "scale" : "2x"
},
{
"size" : "128x128",
@@ -30,8 +30,8 @@
},
{
"idiom" : "mac",
- "scale" : "2x",
- "size" : "128x128"
+ "size" : "128x128",
+ "scale" : "2x"
},
{
"size" : "256x256",
@@ -41,8 +41,8 @@
},
{
"idiom" : "mac",
- "scale" : "2x",
- "size" : "256x256"
+ "size" : "256x256",
+ "scale" : "2x"
},
{
"size" : "512x512",
@@ -52,8 +52,8 @@
},
{
"idiom" : "mac",
- "scale" : "2x",
- "size" : "512x512"
+ "size" : "512x512",
+ "scale" : "2x"
}
],
"info" : {
diff --git a/Orangered/Images.xcassets/AppIcon.appiconset/Orangered128x128.png b/Orangered-Swift/Assets.xcassets/AppIcon.appiconset/Orangered128x128.png
similarity index 100%
rename from Orangered/Images.xcassets/AppIcon.appiconset/Orangered128x128.png
rename to Orangered-Swift/Assets.xcassets/AppIcon.appiconset/Orangered128x128.png
diff --git a/Orangered/Images.xcassets/AppIcon.appiconset/Orangered16x16.png b/Orangered-Swift/Assets.xcassets/AppIcon.appiconset/Orangered16x16.png
similarity index 100%
rename from Orangered/Images.xcassets/AppIcon.appiconset/Orangered16x16.png
rename to Orangered-Swift/Assets.xcassets/AppIcon.appiconset/Orangered16x16.png
diff --git a/Orangered/Images.xcassets/AppIcon.appiconset/Orangered256x256.png b/Orangered-Swift/Assets.xcassets/AppIcon.appiconset/Orangered256x256.png
similarity index 100%
rename from Orangered/Images.xcassets/AppIcon.appiconset/Orangered256x256.png
rename to Orangered-Swift/Assets.xcassets/AppIcon.appiconset/Orangered256x256.png
diff --git a/Orangered/Images.xcassets/AppIcon.appiconset/Orangered32x32.png b/Orangered-Swift/Assets.xcassets/AppIcon.appiconset/Orangered32x32.png
similarity index 100%
rename from Orangered/Images.xcassets/AppIcon.appiconset/Orangered32x32.png
rename to Orangered-Swift/Assets.xcassets/AppIcon.appiconset/Orangered32x32.png
diff --git a/Orangered/Images.xcassets/AppIcon.appiconset/Orangered512x512.png b/Orangered-Swift/Assets.xcassets/AppIcon.appiconset/Orangered512x512.png
similarity index 100%
rename from Orangered/Images.xcassets/AppIcon.appiconset/Orangered512x512.png
rename to Orangered-Swift/Assets.xcassets/AppIcon.appiconset/Orangered512x512.png
diff --git a/Orangered-Swift/Assets.xcassets/Contents.json b/Orangered-Swift/Assets.xcassets/Contents.json
new file mode 100644
index 0000000..da4a164
--- /dev/null
+++ b/Orangered-Swift/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Orangered-Swift/Assets.xcassets/active.imageset/Contents.json b/Orangered-Swift/Assets.xcassets/active.imageset/Contents.json
new file mode 100644
index 0000000..48f61c9
--- /dev/null
+++ b/Orangered-Swift/Assets.xcassets/active.imageset/Contents.json
@@ -0,0 +1,22 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "active.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "active@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Orangered-Swift/Assets.xcassets/active.imageset/active.png b/Orangered-Swift/Assets.xcassets/active.imageset/active.png
new file mode 100644
index 0000000..0f4ab03
Binary files /dev/null and b/Orangered-Swift/Assets.xcassets/active.imageset/active.png differ
diff --git a/Orangered-Swift/Assets.xcassets/active.imageset/active@2x.png b/Orangered-Swift/Assets.xcassets/active.imageset/active@2x.png
new file mode 100644
index 0000000..ee47d15
Binary files /dev/null and b/Orangered-Swift/Assets.xcassets/active.imageset/active@2x.png differ
diff --git a/Orangered-Swift/Assets.xcassets/alt-active.imageset/Contents.json b/Orangered-Swift/Assets.xcassets/alt-active.imageset/Contents.json
new file mode 100644
index 0000000..2131f4e
--- /dev/null
+++ b/Orangered-Swift/Assets.xcassets/alt-active.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "alt-active@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Orangered-Swift/Assets.xcassets/alt-active.imageset/alt-active@2x.png b/Orangered-Swift/Assets.xcassets/alt-active.imageset/alt-active@2x.png
new file mode 100644
index 0000000..bc6e09f
Binary files /dev/null and b/Orangered-Swift/Assets.xcassets/alt-active.imageset/alt-active@2x.png differ
diff --git a/Orangered-Swift/Assets.xcassets/alt-logged-in-dark.imageset/Contents.json b/Orangered-Swift/Assets.xcassets/alt-logged-in-dark.imageset/Contents.json
new file mode 100644
index 0000000..5777c70
--- /dev/null
+++ b/Orangered-Swift/Assets.xcassets/alt-logged-in-dark.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "alt-logged-in-dark.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Orangered-Swift/Assets.xcassets/alt-logged-in-dark.imageset/alt-logged-in-dark.png b/Orangered-Swift/Assets.xcassets/alt-logged-in-dark.imageset/alt-logged-in-dark.png
new file mode 100644
index 0000000..6125ec8
Binary files /dev/null and b/Orangered-Swift/Assets.xcassets/alt-logged-in-dark.imageset/alt-logged-in-dark.png differ
diff --git a/Orangered-Swift/Assets.xcassets/alt-logged-in.imageset/Contents.json b/Orangered-Swift/Assets.xcassets/alt-logged-in.imageset/Contents.json
new file mode 100644
index 0000000..968efc3
--- /dev/null
+++ b/Orangered-Swift/Assets.xcassets/alt-logged-in.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "alt-logged-in.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Orangered-Swift/Assets.xcassets/alt-logged-in.imageset/alt-logged-in.png b/Orangered-Swift/Assets.xcassets/alt-logged-in.imageset/alt-logged-in.png
new file mode 100644
index 0000000..3db094a
Binary files /dev/null and b/Orangered-Swift/Assets.xcassets/alt-logged-in.imageset/alt-logged-in.png differ
diff --git a/Orangered-Swift/Assets.xcassets/alt-message-dark.imageset/Contents.json b/Orangered-Swift/Assets.xcassets/alt-message-dark.imageset/Contents.json
new file mode 100644
index 0000000..ba59f41
--- /dev/null
+++ b/Orangered-Swift/Assets.xcassets/alt-message-dark.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "alt-message-dark.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Orangered-Swift/Assets.xcassets/alt-message-dark.imageset/alt-message-dark.png b/Orangered-Swift/Assets.xcassets/alt-message-dark.imageset/alt-message-dark.png
new file mode 100644
index 0000000..a4023cc
Binary files /dev/null and b/Orangered-Swift/Assets.xcassets/alt-message-dark.imageset/alt-message-dark.png differ
diff --git a/Orangered-Swift/Assets.xcassets/alt-message.imageset/Contents.json b/Orangered-Swift/Assets.xcassets/alt-message.imageset/Contents.json
new file mode 100644
index 0000000..6f95a79
--- /dev/null
+++ b/Orangered-Swift/Assets.xcassets/alt-message.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "alt-message.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Orangered-Swift/Assets.xcassets/alt-message.imageset/alt-message.png b/Orangered-Swift/Assets.xcassets/alt-message.imageset/alt-message.png
new file mode 100644
index 0000000..a4023cc
Binary files /dev/null and b/Orangered-Swift/Assets.xcassets/alt-message.imageset/alt-message.png differ
diff --git a/Orangered-Swift/Assets.xcassets/alt-mod-dark.imageset/Contents.json b/Orangered-Swift/Assets.xcassets/alt-mod-dark.imageset/Contents.json
new file mode 100644
index 0000000..81589f8
--- /dev/null
+++ b/Orangered-Swift/Assets.xcassets/alt-mod-dark.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "alt-mod-dark.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Orangered-Swift/Assets.xcassets/alt-mod-dark.imageset/alt-mod-dark.png b/Orangered-Swift/Assets.xcassets/alt-mod-dark.imageset/alt-mod-dark.png
new file mode 100644
index 0000000..870811e
Binary files /dev/null and b/Orangered-Swift/Assets.xcassets/alt-mod-dark.imageset/alt-mod-dark.png differ
diff --git a/Orangered-Swift/Assets.xcassets/alt-mod.imageset/Contents.json b/Orangered-Swift/Assets.xcassets/alt-mod.imageset/Contents.json
new file mode 100644
index 0000000..1202756
--- /dev/null
+++ b/Orangered-Swift/Assets.xcassets/alt-mod.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "alt-mod.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Orangered-Swift/Assets.xcassets/alt-mod.imageset/alt-mod.png b/Orangered-Swift/Assets.xcassets/alt-mod.imageset/alt-mod.png
new file mode 100644
index 0000000..870811e
Binary files /dev/null and b/Orangered-Swift/Assets.xcassets/alt-mod.imageset/alt-mod.png differ
diff --git a/Orangered-Swift/Assets.xcassets/alt-not-connected-dark.imageset/Contents.json b/Orangered-Swift/Assets.xcassets/alt-not-connected-dark.imageset/Contents.json
new file mode 100644
index 0000000..d25e112
--- /dev/null
+++ b/Orangered-Swift/Assets.xcassets/alt-not-connected-dark.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "alt-not-connected-dark.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Orangered-Swift/Assets.xcassets/alt-not-connected-dark.imageset/alt-not-connected-dark.png b/Orangered-Swift/Assets.xcassets/alt-not-connected-dark.imageset/alt-not-connected-dark.png
new file mode 100644
index 0000000..bd9e66a
Binary files /dev/null and b/Orangered-Swift/Assets.xcassets/alt-not-connected-dark.imageset/alt-not-connected-dark.png differ
diff --git a/Orangered-Swift/Assets.xcassets/alt-not-connected.imageset/Contents.json b/Orangered-Swift/Assets.xcassets/alt-not-connected.imageset/Contents.json
new file mode 100644
index 0000000..9289489
--- /dev/null
+++ b/Orangered-Swift/Assets.xcassets/alt-not-connected.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "alt-not-connected.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Orangered-Swift/Assets.xcassets/alt-not-connected.imageset/alt-not-connected.png b/Orangered-Swift/Assets.xcassets/alt-not-connected.imageset/alt-not-connected.png
new file mode 100644
index 0000000..6b2c137
Binary files /dev/null and b/Orangered-Swift/Assets.xcassets/alt-not-connected.imageset/alt-not-connected.png differ
diff --git a/Orangered-Swift/Assets.xcassets/logged-in-dark.imageset/Contents.json b/Orangered-Swift/Assets.xcassets/logged-in-dark.imageset/Contents.json
new file mode 100644
index 0000000..52a2fe3
--- /dev/null
+++ b/Orangered-Swift/Assets.xcassets/logged-in-dark.imageset/Contents.json
@@ -0,0 +1,22 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "logged-in-dark.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "logged-in-dark@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Orangered-Swift/Assets.xcassets/logged-in-dark.imageset/logged-in-dark.png b/Orangered-Swift/Assets.xcassets/logged-in-dark.imageset/logged-in-dark.png
new file mode 100644
index 0000000..0f4ab03
Binary files /dev/null and b/Orangered-Swift/Assets.xcassets/logged-in-dark.imageset/logged-in-dark.png differ
diff --git a/Orangered-Swift/Assets.xcassets/logged-in-dark.imageset/logged-in-dark@2x.png b/Orangered-Swift/Assets.xcassets/logged-in-dark.imageset/logged-in-dark@2x.png
new file mode 100644
index 0000000..ee47d15
Binary files /dev/null and b/Orangered-Swift/Assets.xcassets/logged-in-dark.imageset/logged-in-dark@2x.png differ
diff --git a/Orangered-Swift/Assets.xcassets/logged-in.imageset/Contents.json b/Orangered-Swift/Assets.xcassets/logged-in.imageset/Contents.json
new file mode 100644
index 0000000..b0b1152
--- /dev/null
+++ b/Orangered-Swift/Assets.xcassets/logged-in.imageset/Contents.json
@@ -0,0 +1,22 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "logged-in.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "logged-in@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Orangered-Swift/Assets.xcassets/logged-in.imageset/logged-in.png b/Orangered-Swift/Assets.xcassets/logged-in.imageset/logged-in.png
new file mode 100644
index 0000000..938565b
Binary files /dev/null and b/Orangered-Swift/Assets.xcassets/logged-in.imageset/logged-in.png differ
diff --git a/Orangered-Swift/Assets.xcassets/logged-in.imageset/logged-in@2x.png b/Orangered-Swift/Assets.xcassets/logged-in.imageset/logged-in@2x.png
new file mode 100644
index 0000000..3fcf856
Binary files /dev/null and b/Orangered-Swift/Assets.xcassets/logged-in.imageset/logged-in@2x.png differ
diff --git a/Orangered-Swift/Assets.xcassets/message-dark.imageset/Contents.json b/Orangered-Swift/Assets.xcassets/message-dark.imageset/Contents.json
new file mode 100644
index 0000000..b10cda0
--- /dev/null
+++ b/Orangered-Swift/Assets.xcassets/message-dark.imageset/Contents.json
@@ -0,0 +1,22 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "message-dark.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "message-dark@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Orangered-Swift/Assets.xcassets/message-dark.imageset/message-dark.png b/Orangered-Swift/Assets.xcassets/message-dark.imageset/message-dark.png
new file mode 100644
index 0000000..8e9767e
Binary files /dev/null and b/Orangered-Swift/Assets.xcassets/message-dark.imageset/message-dark.png differ
diff --git a/Orangered-Swift/Assets.xcassets/message-dark.imageset/message-dark@2x.png b/Orangered-Swift/Assets.xcassets/message-dark.imageset/message-dark@2x.png
new file mode 100644
index 0000000..15a63d2
Binary files /dev/null and b/Orangered-Swift/Assets.xcassets/message-dark.imageset/message-dark@2x.png differ
diff --git a/Orangered-Swift/Assets.xcassets/message.imageset/Contents.json b/Orangered-Swift/Assets.xcassets/message.imageset/Contents.json
new file mode 100644
index 0000000..84da9a5
--- /dev/null
+++ b/Orangered-Swift/Assets.xcassets/message.imageset/Contents.json
@@ -0,0 +1,22 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "message.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "message@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Orangered-Swift/Assets.xcassets/message.imageset/message.png b/Orangered-Swift/Assets.xcassets/message.imageset/message.png
new file mode 100644
index 0000000..4a8a6d3
Binary files /dev/null and b/Orangered-Swift/Assets.xcassets/message.imageset/message.png differ
diff --git a/Orangered-Swift/Assets.xcassets/message.imageset/message@2x.png b/Orangered-Swift/Assets.xcassets/message.imageset/message@2x.png
new file mode 100644
index 0000000..58da040
Binary files /dev/null and b/Orangered-Swift/Assets.xcassets/message.imageset/message@2x.png differ
diff --git a/Orangered-Swift/Assets.xcassets/mod-dark.imageset/Contents.json b/Orangered-Swift/Assets.xcassets/mod-dark.imageset/Contents.json
new file mode 100644
index 0000000..5a784f2
--- /dev/null
+++ b/Orangered-Swift/Assets.xcassets/mod-dark.imageset/Contents.json
@@ -0,0 +1,22 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "mod-dark.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "mod-dark@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Orangered-Swift/Assets.xcassets/mod-dark.imageset/mod-dark.png b/Orangered-Swift/Assets.xcassets/mod-dark.imageset/mod-dark.png
new file mode 100644
index 0000000..29842dd
Binary files /dev/null and b/Orangered-Swift/Assets.xcassets/mod-dark.imageset/mod-dark.png differ
diff --git a/Orangered-Swift/Assets.xcassets/mod-dark.imageset/mod-dark@2x.png b/Orangered-Swift/Assets.xcassets/mod-dark.imageset/mod-dark@2x.png
new file mode 100644
index 0000000..658f80b
Binary files /dev/null and b/Orangered-Swift/Assets.xcassets/mod-dark.imageset/mod-dark@2x.png differ
diff --git a/Orangered-Swift/Assets.xcassets/mod.imageset/Contents.json b/Orangered-Swift/Assets.xcassets/mod.imageset/Contents.json
new file mode 100644
index 0000000..54d920f
--- /dev/null
+++ b/Orangered-Swift/Assets.xcassets/mod.imageset/Contents.json
@@ -0,0 +1,22 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "mod.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "mod@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Orangered-Swift/Assets.xcassets/mod.imageset/mod.png b/Orangered-Swift/Assets.xcassets/mod.imageset/mod.png
new file mode 100644
index 0000000..62b556c
Binary files /dev/null and b/Orangered-Swift/Assets.xcassets/mod.imageset/mod.png differ
diff --git a/Orangered-Swift/Assets.xcassets/mod.imageset/mod@2x.png b/Orangered-Swift/Assets.xcassets/mod.imageset/mod@2x.png
new file mode 100644
index 0000000..0314a4b
Binary files /dev/null and b/Orangered-Swift/Assets.xcassets/mod.imageset/mod@2x.png differ
diff --git a/Orangered-Swift/Assets.xcassets/not-connected-dark.imageset/Contents.json b/Orangered-Swift/Assets.xcassets/not-connected-dark.imageset/Contents.json
new file mode 100644
index 0000000..860ca5d
--- /dev/null
+++ b/Orangered-Swift/Assets.xcassets/not-connected-dark.imageset/Contents.json
@@ -0,0 +1,22 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "not-connected-dark.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "not-connected-dark@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Orangered-Swift/Assets.xcassets/not-connected-dark.imageset/not-connected-dark.png b/Orangered-Swift/Assets.xcassets/not-connected-dark.imageset/not-connected-dark.png
new file mode 100644
index 0000000..05db0af
Binary files /dev/null and b/Orangered-Swift/Assets.xcassets/not-connected-dark.imageset/not-connected-dark.png differ
diff --git a/Orangered-Swift/Assets.xcassets/not-connected-dark.imageset/not-connected-dark@2x.png b/Orangered-Swift/Assets.xcassets/not-connected-dark.imageset/not-connected-dark@2x.png
new file mode 100644
index 0000000..9e43a8e
Binary files /dev/null and b/Orangered-Swift/Assets.xcassets/not-connected-dark.imageset/not-connected-dark@2x.png differ
diff --git a/Orangered-Swift/Assets.xcassets/not-connected.imageset/Contents.json b/Orangered-Swift/Assets.xcassets/not-connected.imageset/Contents.json
new file mode 100644
index 0000000..ff94f03
--- /dev/null
+++ b/Orangered-Swift/Assets.xcassets/not-connected.imageset/Contents.json
@@ -0,0 +1,22 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "not-connected.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "not-connected@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Orangered-Swift/Assets.xcassets/not-connected.imageset/not-connected.png b/Orangered-Swift/Assets.xcassets/not-connected.imageset/not-connected.png
new file mode 100644
index 0000000..61b8f62
Binary files /dev/null and b/Orangered-Swift/Assets.xcassets/not-connected.imageset/not-connected.png differ
diff --git a/Orangered-Swift/Assets.xcassets/not-connected.imageset/not-connected@2x.png b/Orangered-Swift/Assets.xcassets/not-connected.imageset/not-connected@2x.png
new file mode 100644
index 0000000..8093b66
Binary files /dev/null and b/Orangered-Swift/Assets.xcassets/not-connected.imageset/not-connected@2x.png differ
diff --git a/Orangered-Info.plist b/Orangered-Swift/Info.plist
similarity index 64%
rename from Orangered-Info.plist
rename to Orangered-Swift/Info.plist
index 6aaf4d1..8f9794c 100644
--- a/Orangered-Info.plist
+++ b/Orangered-Swift/Info.plist
@@ -3,33 +3,33 @@
CFBundleDevelopmentRegion
- English
+ en
CFBundleExecutable
- ${EXECUTABLE_NAME}
+ $(EXECUTABLE_NAME)
CFBundleGetInfoString
- 1.0.4, http://www.voidref.com/orangered/Orangered!.html
+ 2.0, http://www.voidref.com/orangered/Orangered!.html
CFBundleIdentifier
- com.voidref.${PRODUCT_NAME:rfc1034identifier}
+ $(PRODUCT_BUNDLE_IDENTIFIER)
CFBundleInfoDictionaryVersion
6.0
CFBundleName
- ${PRODUCT_NAME}
+ $(PRODUCT_NAME)
CFBundlePackageType
APPL
CFBundleShortVersionString
- 1.0.5
+ 2.0
CFBundleSignature
- vOfR
+ ????
CFBundleVersion
- 105
+ 5
LSApplicationCategoryType
public.app-category.utilities
LSMinimumSystemVersion
- ${MACOSX_DEPLOYMENT_TARGET}
+ $(MACOSX_DEPLOYMENT_TARGET)
LSUIElement
- NSMainNibFile
- MainMenu
+ NSHumanReadableCopyright
+ Copyright © 2016 Rockwood Software. All rights reserved.
NSPrincipalClass
NSApplication
diff --git a/Orangered-Swift/LoginViewController.swift b/Orangered-Swift/LoginViewController.swift
new file mode 100644
index 0000000..1d670c9
--- /dev/null
+++ b/Orangered-Swift/LoginViewController.swift
@@ -0,0 +1,134 @@
+//
+// LoginViewController.swift
+// Orangered
+//
+// Created by Alan Westbrook on 6/13/16.
+// Copyright © 2016 Rockwood Software. All rights reserved.
+//
+
+import Cocoa
+
+let kHelpURL = URL(string: "https://www.github.com/voidref/orangered")
+
+typealias LoginAction = (name:String, password:String) -> Void
+
+class LoginViewController: NSViewController {
+
+ private let nameLabel = NSTextField()
+ private let passwordLabel = NSTextField()
+ private let nameField = NSTextField()
+ private let passwordField = NSSecureTextField()
+ private let loginButton = NSButton()
+ private let helpButton = NSButton()
+ private let loginAction:LoginAction
+
+ init(loginAction action: LoginAction) {
+ loginAction = action
+
+ // Effit, I want to override init (unfailable override), but I am required to call a failable initializer?
+ super.init(nibName: nil, bundle: nil)!
+
+ title = NSLocalizedString("Orangered! Login", comment: "The login window title")
+ }
+
+ required init?(coder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ setup()
+ }
+
+ override func loadView() {
+ // Don't call into super, as we don't want it to try to load from a nib
+ view = NSVisualEffectView()
+ view.translatesAutoresizingMaskIntoConstraints = false
+ }
+
+ private func setup() {
+ view.translatesAutoresizingMaskIntoConstraints = false
+ for subview in [nameLabel, nameField, passwordLabel, passwordField, loginButton] { add(subview) }
+
+ func setup(label:NSTextField, text:String) {
+ label.stringValue = text
+ label.isEditable = false
+ label.backgroundColor = #colorLiteral(red: 0.6470588235, green: 0.631372549, blue: 0.7725490196, alpha: 0)
+ label.drawsBackground = false
+ label.sizeToFit()
+ label.isBezeled = false
+ }
+
+ setup(label: nameLabel, text: NSLocalizedString("User Name:", comment: "reddit user name field label"))
+ setup(label: passwordLabel, text: NSLocalizedString("Password:", comment: "password field label"))
+
+ let pref = UserDefaults.standard
+ nameField.stringValue = pref.username ?? ""
+ passwordField.stringValue = pref.password ?? ""
+
+ loginButton.title = NSLocalizedString("Login", comment: "login button title on the login window")
+ loginButton.bezelStyle = .rounded
+ loginButton.keyEquivalent = "\r"
+ loginButton.target = self
+ loginButton.action = #selector(loginClicked)
+
+ let space:CGFloat = 16
+ let fieldWidth:CGFloat = 160
+
+ let fieldGuide = NSLayoutGuide()
+ view.addLayoutGuide(fieldGuide)
+
+ helpButton.bezelStyle = .helpButton
+ helpButton.title = ""
+ helpButton.target = self
+ helpButton.action = #selector(helpClicked)
+ add(helpButton)
+
+ NSLayoutConstraint.activate([
+ fieldGuide.topAnchor.constraint(equalTo: nameLabel.topAnchor),
+ fieldGuide.leadingAnchor.constraint(equalTo: nameLabel.leadingAnchor),
+ fieldGuide.trailingAnchor.constraint(equalTo: nameField.trailingAnchor),
+ fieldGuide.centerXAnchor.constraint(equalTo: view.centerXAnchor),
+ fieldGuide.bottomAnchor.constraint(equalTo: passwordLabel.bottomAnchor),
+
+ nameLabel.trailingAnchor.constraint(equalTo: nameField.leadingAnchor, constant: -space / 2),
+ nameField.firstBaselineAnchor.constraint(equalTo: nameLabel.firstBaselineAnchor),
+ nameField.widthAnchor.constraint(equalToConstant: fieldWidth),
+
+ passwordLabel.topAnchor.constraint(equalTo: nameLabel.bottomAnchor, constant: space / 2),
+ passwordLabel.trailingAnchor.constraint(equalTo: nameLabel.trailingAnchor),
+ passwordField.firstBaselineAnchor.constraint(equalTo: passwordLabel.firstBaselineAnchor),
+ passwordField.leadingAnchor.constraint(equalTo: nameField.leadingAnchor),
+ passwordField.trailingAnchor.constraint(equalTo: nameField.trailingAnchor),
+
+ loginButton.topAnchor.constraint(equalTo: fieldGuide.bottomAnchor, constant: space),
+ loginButton.trailingAnchor.constraint(equalTo: fieldGuide.trailingAnchor),
+
+ helpButton.leadingAnchor.constraint(equalTo: fieldGuide.leadingAnchor),
+ helpButton.centerYAnchor.constraint(equalTo: loginButton.centerYAnchor),
+
+ view.topAnchor.constraint(equalTo: nameLabel.topAnchor, constant: -space),
+ view.bottomAnchor.constraint(equalTo: loginButton.bottomAnchor, constant: space),
+ view.widthAnchor.constraint(equalTo: fieldGuide.widthAnchor, constant: space * 2)
+ ])
+ }
+
+ private func add(_ sub:NSView) {
+ sub.translatesAutoresizingMaskIntoConstraints = false
+ view.addSubview(sub)
+ }
+
+ @objc private func loginClicked() {
+ loginAction(name: nameField.stringValue, password: passwordField.stringValue)
+ }
+
+ @objc private func helpClicked() {
+ if let urlActual = kHelpURL {
+ NSWorkspace.shared().open(urlActual)
+ }
+ else {
+ print("Umm, fix your url?")
+ }
+ }
+}
+
diff --git a/Orangered-Swift/Menu.swift b/Orangered-Swift/Menu.swift
new file mode 100644
index 0000000..6d1061b
--- /dev/null
+++ b/Orangered-Swift/Menu.swift
@@ -0,0 +1,28 @@
+//
+// Menu.swift
+// Orangered
+//
+// Created by Alan Westbrook on 6/13/16.
+// Copyright © 2016 Rockwood Software. All rights reserved.
+//
+
+import Foundation
+import Cocoa
+
+
+class Menu: NSMenu {
+
+ init() {
+ super.init(title: "Orangered!")
+
+ setup()
+ }
+
+ required init(coder aDecoder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+
+ private func setup() {
+ autoenablesItems = false
+ }
+}
diff --git a/Orangered-Swift/PrefViewController.swift b/Orangered-Swift/PrefViewController.swift
new file mode 100644
index 0000000..b3628f1
--- /dev/null
+++ b/Orangered-Swift/PrefViewController.swift
@@ -0,0 +1,63 @@
+//
+// PrefViewController.swift
+// Orangered
+//
+// Created by Alan Westbrook on 6/17/16.
+// Copyright © 2016 Rockwood Software. All rights reserved.
+//
+
+import Cocoa
+import ServiceManagement
+
+class PrefViewController: NSViewController {
+
+ let startAtLogin = NSButton(cbWithTitle: "Start at Login", target: nil, action: #selector(salClicked))
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ setup()
+ }
+
+ override func loadView() {
+ // Don't call into super, as we don't want it to try to load from a nib
+ view = NSView()
+ view.translatesAutoresizingMaskIntoConstraints = false
+
+ }
+
+ private func setup() {
+ startAtLogin.target = self
+ startAtLogin.translatesAutoresizingMaskIntoConstraints = false
+
+ title = "Orangered! Preferences"
+
+ view.addSubview(startAtLogin)
+
+ NSLayoutConstraint.activate([
+ startAtLogin.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 10),
+ startAtLogin.topAnchor.constraint(equalTo: view.topAnchor, constant: 10),
+ view.bottomAnchor.constraint(equalTo: startAtLogin.bottomAnchor, constant: 10),
+ view.widthAnchor.constraint(equalTo: startAtLogin.widthAnchor, constant: 20)
+ ])
+ }
+
+ @objc private func salClicked() {
+ print(SMLoginItemSetEnabled("com.rockwood.Orangered", true))
+ }
+}
+
+extension NSButton {
+ convenience init(cbWithTitle title: String, target: NSObject?, action: Selector) {
+ if #available(OSX 10.12, *) {
+ self.init(checkboxWithTitle: title, target: target, action: action)
+ } else {
+ self.init()
+ setButtonType(.switch)
+ self.title = title
+ self.target = target
+ self.action = action
+ }
+
+ }
+}
diff --git a/Orangered-Swift/StatusItemController.swift b/Orangered-Swift/StatusItemController.swift
new file mode 100644
index 0000000..ffe7a62
--- /dev/null
+++ b/Orangered-Swift/StatusItemController.swift
@@ -0,0 +1,408 @@
+//
+// StatusItemController.swift
+// Orangered
+//
+// Created by Alan Westbrook on 6/13/16.
+// Copyright © 2016 Rockwood Software. All rights reserved.
+//
+
+import Foundation
+import Cocoa
+
+private let kUpdateURL = URL(string: "http://voidref.com/orangered/version")
+private let kRedditCookieURL = URL(string: "https://reddit.com")
+private let kLoginMenuTitle = NSLocalizedString("Login…", comment: "Menu item title for bringing up the login window")
+private let kLogoutMenuTitle = NSLocalizedString("Log Out", comment: "Menu item title for logging out")
+private let kAttemptingLoginTitle = NSLocalizedString("Attempting Login…", comment: "Title of the login menu item while it's attemping to log in")
+private let kOpenMailboxRecheckDelay = 5.0
+
+class StatusItemController: NSObject, NSUserNotificationCenterDelegate {
+
+ enum State {
+ case loggedout
+ case invalidcredentials
+ case disconnected
+ case mailfree
+ case orangered
+ case modmail
+ case update
+
+ private static let urlMap = [
+ loggedout: nil,
+ invalidcredentials: nil,
+ disconnected: nil,
+ mailfree: URL(string: "https://www.reddit.com/message/inbox/"),
+ orangered: URL(string: "https://www.reddit.com/message/unread/"),
+ modmail: URL(string: "https://www.reddit.com/message/moderator/"),
+ update: nil
+ ]
+
+ func image(forAppearance appearanceName: String, useAlt:Bool = false) -> NSImage {
+ let imageMap = [
+ State.loggedout: "not-connected",
+ State.invalidcredentials: "not-connected",
+ State.disconnected: "not-connected",
+ State.mailfree: "logged-in",
+ State.orangered: "message",
+ State.modmail: "mod",
+ State.update: "BlueEnvelope" // TODO: Sort this out
+ ]
+
+ guard let basename = imageMap[self] else {
+ fatalError("you really messed up this time, missing case: imageMap for \(self)")
+ }
+
+ var name = basename
+
+ if useAlt {
+ name = "alt-\(basename)"
+ }
+
+ if appearanceName == NSAppearanceNameVibrantDark {
+ name = "\(name)-dark"
+ }
+
+
+ guard let image = NSImage(named: name) else {
+ fatalError("fix yo assets, missing image: \(name)")
+ }
+
+ return image
+ }
+
+ func mailboxUrl() -> URL? {
+ return State.urlMap[self]!
+ }
+ }
+
+ private var state = State.disconnected {
+ willSet {
+ if newValue != state {
+ // In order to avoid having to set flags for handling values set that were already set, we check `willSet`. This, however, necessitates we reschedule handling until the value is actually set as there doesn't seem to be a way to let it set and then call a method synchronously
+ DispatchQueue.main.async(execute: {
+ self.handleStateChanged()
+ })
+ }
+ }
+ }
+
+ private let statusItem = NSStatusBar.system().statusItem(withLength: NSSquareStatusItemLength)
+
+ private var statusPoller:Timer?
+ private let prefs = UserDefaults.standard
+ private var statusConnection:URLSession?
+ private let session = URLSession.shared
+ private var loginWindowController:NSWindowController?
+ private var prefWindowController:NSWindowController?
+ private var mailboxItem:NSMenuItem?
+ private var loginItem:NSMenuItem?
+ private var mailCount = 0
+
+ override init() {
+ super.init()
+
+ prefs.useAltImages = false
+ setup()
+ if prefs.loggedIn {
+ login()
+ }
+ }
+
+ private func setup() {
+ NSUserNotificationCenter.default.delegate = self
+ setupMenu()
+ }
+
+ private func setupMenu() {
+ let menu = Menu()
+
+ let mailbox = NSMenuItem(title: NSLocalizedString("Mailbox…", comment:"Menu item for opening the reddit mailbox"),
+ action: #selector(handleMailboxItemSelected), keyEquivalent: "")
+ mailbox.isEnabled = false
+ menu.addItem(mailbox)
+
+ mailboxItem = mailbox
+
+ menu.addItem(NSMenuItem.separator())
+ let login = NSMenuItem(title: kLoginMenuTitle,
+ action: #selector(handleLoginItemSelected), keyEquivalent: "")
+ menu.addItem(login)
+ loginItem = login
+
+#if PrefsDone
+ let prefsItem = NSMenuItem(title: NSLocalizedString("Preferences…", comment:"Menu item title for opening the preferences window"),
+ action: #selector(handlePrefItemSelected), keyEquivalent: "")
+ menu.addItem(prefsItem)
+#endif
+ let quitItem = NSMenuItem(title: NSLocalizedString("Quit", comment:"Quit menu item title"),
+ action: #selector(quit), keyEquivalent: "")
+ menu.addItem(quitItem)
+
+ menu.items.forEach { (item) in
+ item.target = self
+ }
+
+ statusItem.menu = menu
+
+ var altImageName = "active"
+ if prefs.useAltImages {
+ altImageName = "alt-\(altImageName)"
+ }
+
+ statusItem.alternateImage = NSImage(named: altImageName)
+ updateIcon()
+ }
+
+ private func login() {
+ guard let url = URL(string: "https://ssl.reddit.com/api/login") else {
+ print("Error bad url, wat?")
+ return
+ }
+
+ guard let uname = prefs.username, let password = prefs.password else {
+ showLoginWindow()
+ return
+ }
+
+ var request = URLRequest(url: url)
+ request.httpMethod = "POST"
+ request.httpBody = "user=\(uname)&passwd=\(password)".data(using: String.Encoding.utf8)
+
+ loginItem?.title = kAttemptingLoginTitle
+
+ let task = session.dataTask(with: request) { (data, response, error) in
+ self.handleLogin(response: response as? HTTPURLResponse, data:data, error: error)
+ }
+
+ task.resume()
+ }
+
+ private func handleLogin(response:HTTPURLResponse?, data:Data?, error:NSError?) {
+ if let dataActual = data, let
+ dataString = String(data:dataActual, encoding:String.Encoding.utf8) {
+ if dataString.contains("wrong password") {
+ // TODO: wrong password error
+ state = .invalidcredentials
+ let alert = NSAlert()
+ alert.messageText = NSLocalizedString("Username and password do not match any recognized by Reddit", comment: "username/password mismatch error")
+ alert.addButton(withTitle: NSLocalizedString("Lemme fix that...", comment:"Wrong password dialog acknowledgement button"))
+ alert.runModal()
+
+ // There seems to be a problem with showing another window while this one has just been dismissed, rescheduling on the main thread solves this.
+ DispatchQueue.main.async {
+ self.showLoginWindow()
+ }
+
+ return
+ }
+ }
+
+ guard let headers = response?.allHeaderFields as? [String:String] else {
+ print("wrong headers ... or so: \(response?.allHeaderFields)")
+ return
+ }
+
+ guard let url = response?.url else {
+ print("missing url from response: \(response)")
+ return
+ }
+
+ let cookies = HTTPCookie.cookies(withResponseHeaderFields: headers, for: url)
+
+ if cookies.count < 1 {
+ print("Login error: \(response)")
+ state = .disconnected
+ }
+ else {
+ HTTPCookieStorage.shared.setCookies(cookies, for: kRedditCookieURL, mainDocumentURL: nil)
+
+ prefs.loggedIn = true
+ state = .mailfree
+ setupStatusPoller()
+ }
+ }
+
+ private func setupStatusPoller() {
+ statusPoller?.invalidate()
+ let interval:TimeInterval = 60
+ statusPoller = Timer(timeInterval: interval, target: self, selector: #selector(checkReddit), userInfo: nil, repeats: true)
+ RunLoop.main.add(statusPoller!, forMode: RunLoopMode.defaultRunLoopMode)
+ statusPoller?.fire()
+ }
+
+ private func showLoginWindow() {
+ let login = LoginViewController { [weak self] (name, password) in
+ self?.loginWindowController?.close()
+ self?.prefs.username = name
+ self?.prefs.password = password
+ self?.login()
+ }
+
+ let window = NSPanel(contentViewController: login)
+ window.appearance = NSAppearance(named: NSAppearanceNameVibrantLight)
+ loginWindowController = NSWindowController(window: window)
+
+ NSApp.activateIgnoringOtherApps(true)
+ loginWindowController?.showWindow(self)
+ }
+
+ private func showPrefWindow() {
+ let pref = NSWindowController(window: NSPanel(contentViewController: PrefViewController()))
+
+ prefWindowController = pref
+ NSApp.activateIgnoringOtherApps(true)
+ pref.showWindow(self)
+ }
+
+ private func interpretResponse(json: AnyObject) {
+ // Crude, but remarkably immune to data restructuring as long as the key value pairs don't change.
+
+ guard let jsonActual = json["data"] as? [String:AnyObject] else {
+ print("response json unexpected format: \(json)")
+ return
+ }
+
+ if let newMailCount = jsonActual["inbox_count"] as? Int {
+ if newMailCount != mailCount {
+ mailCount = newMailCount
+
+ if mailCount > 0 {
+ notifyMail()
+ }
+ }
+ }
+
+ if let modMailState = jsonActual["has_mod_mail"] as? Bool, modMailState == true {
+ state = .modmail
+ return
+ }
+
+ if let mailState = jsonActual["has_mail"] as? Bool {
+ if mailState {
+ state = .orangered
+ }
+ else {
+ state = .mailfree
+ }
+ }
+ else {
+ // probably login error
+ state = .disconnected
+ }
+ }
+
+ private func handleStateChanged() {
+ updateIcon()
+ mailboxItem?.isEnabled = true
+ loginItem?.title = prefs.loggedIn ? kLogoutMenuTitle : kLoginMenuTitle
+
+ switch state {
+ case .orangered, .modmail:
+ notifyMail()
+
+ case .disconnected:
+ DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 10, execute: {
+ self.login()
+ })
+ fallthrough
+ case .loggedout, .invalidcredentials:
+ mailboxItem?.isEnabled = false
+
+
+ case .mailfree, .update:
+ break
+ }
+ }
+
+ private func updateIcon() {
+ statusItem.image = state.image(forAppearance: statusItem.button!.effectiveAppearance.name, useAlt: prefs.useAltImages)
+ }
+
+ private func notifyMail() {
+ let note = NSUserNotification()
+ note.title = "Orangered!"
+ note.informativeText = NSLocalizedString("You have a new message on reddit!", comment: "new message notification text")
+ note.actionButtonTitle = NSLocalizedString("Read", comment: "notification call to action button")
+
+ if mailCount > 1 {
+ note.informativeText = String
+ .localizedStringWithFormat(NSLocalizedString("You have %@ unread messages on reddit",
+ comment: "plural message notification text"),
+ mailCount)
+ }
+
+ NSUserNotificationCenter.default.deliver(note)
+ }
+
+ private func openMailbox() {
+ if let url = state.mailboxUrl() {
+ NSWorkspace.shared().open(url)
+ }
+
+ DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + kOpenMailboxRecheckDelay) {
+ self.checkReddit()
+ }
+
+ NSUserNotificationCenter.default.removeAllDeliveredNotifications()
+ }
+
+ private func logout() {
+ prefs.loggedIn = false
+ statusPoller?.invalidate()
+ let storage = HTTPCookieStorage.shared
+ storage.cookies(for: kRedditCookieURL!)?.forEach { storage.deleteCookie($0) }
+ state = .loggedout
+ }
+
+ @objc private func checkReddit() {
+ guard let uname = prefs.username,
+ let url = URL(string: "http://www.reddit.com/user/\(uname)/about.json") else {
+ print("User name empty")
+ return
+ }
+
+ let task = session.dataTask(with: url) { (data, response, error) in
+ if let dataActual = data {
+ do {
+ try self.interpretResponse(json: JSONSerialization.jsonObject(with: dataActual, options: .allowFragments))
+ } catch let error {
+ print("Error reading response json: \(error)")
+ }
+ }
+ else {
+ print("Failure: \(response)")
+ }
+ }
+
+ task.resume()
+ }
+
+ @objc private func quit() {
+ NSApplication.shared().stop(nil)
+ }
+
+ @objc func handleLoginItemSelected() {
+ if prefs.loggedIn {
+ logout()
+ }
+ else {
+ showLoginWindow()
+ }
+ }
+
+ @objc func handlePrefItemSelected() {
+ showPrefWindow()
+ }
+
+ @objc func handleMailboxItemSelected() {
+ openMailbox()
+ }
+
+
+ // MARK: User Notification Center
+
+ @objc func userNotificationCenter(_ center: NSUserNotificationCenter, didActivate notification: NSUserNotification) {
+ openMailbox()
+ }
+}
+
diff --git a/Orangered-Swift/UserDefaults+Orangered.swift b/Orangered-Swift/UserDefaults+Orangered.swift
new file mode 100644
index 0000000..89410d0
--- /dev/null
+++ b/Orangered-Swift/UserDefaults+Orangered.swift
@@ -0,0 +1,131 @@
+//
+// UserDefaults+Orangered.swift
+// Orangered
+//
+// Created by Alan Westbrook on 6/13/16.
+// Copyright © 2016 Rockwood Software. All rights reserved.
+//
+
+import Foundation
+import Cocoa
+
+private let kUserNameKey = "username"
+private let kLoggedInKey = "logged in"
+private let kUseAltImagesKey = "use alt images"
+private let kServiceName = "Orangered!"
+
+extension UserDefaults {
+ var username:String? {
+ get {
+ return string(forKey: kUserNameKey)
+ }
+ set {
+ set(newValue, forKey: kUserNameKey)
+ }
+ }
+
+ var password:String? {
+ get {
+ let pass = getPassword()
+ UserDefaults.keychainItem = nil
+ return pass
+ }
+ set {
+ setPassword(newValue!)
+ }
+ }
+
+ var loggedIn:Bool {
+ get {
+ return bool(forKey: kLoggedInKey)
+ }
+ set {
+ set(newValue, forKey: kLoggedInKey)
+ }
+ }
+
+ var useAltImages:Bool {
+ get {
+ return bool(forKey: kUseAltImagesKey)
+ }
+ set {
+ set(newValue, forKey: kUseAltImagesKey)
+ }
+ }
+
+ // C APIs are *the worst*
+ static private var keychainItem:SecKeychainItem? = nil
+
+ private func getPassword() -> String? {
+
+ guard let uname = username else {
+ print("no user name set")
+ return nil
+ }
+
+ var passwordLength:UInt32 = 0
+ var passwordData:UnsafeMutablePointer? = nil
+
+ let err = SecKeychainFindGenericPassword(nil,
+ UInt32(kServiceName.characters.count),
+ kServiceName,
+ UInt32(uname.characters.count),
+ uname,
+ &passwordLength,
+ &passwordData,
+ &UserDefaults.keychainItem)
+ if let pass = passwordData, err == errSecSuccess {
+ let password = String(bytesNoCopy: pass, length: Int(passwordLength), encoding: String.Encoding.utf8, freeWhenDone: true)
+ return password
+ }
+ else {
+ print("Error grabbing password: \(err)")
+ }
+
+ return nil
+ }
+
+ private func setPassword(_ pass:String) {
+ guard let uname = username else {
+ print("No username")
+ return
+ }
+
+ if let _ = getPassword() {
+ // have to update instead of setting
+ updatePassword(pass)
+ return
+ }
+
+ let result = SecKeychainAddGenericPassword(nil,
+ UInt32(kServiceName.characters.count),
+ kServiceName,
+ UInt32(uname.characters.count),
+ uname,
+ UInt32(pass.characters.count),
+ pass,
+ nil)
+
+ if result != errSecSuccess {
+ print("error setting key: \(result)")
+ }
+ }
+
+ private func updatePassword(_ password:String) {
+ guard let itemActual = UserDefaults.keychainItem else {
+ print("Must grab a password to init the item before updatint it, bleah")
+ return
+ }
+
+ let result = SecKeychainItemModifyAttributesAndData(itemActual,
+ nil,
+ UInt32(password.characters.count),
+ password)
+
+ if result != errSecSuccess {
+ print("error updating the keychain: \(result)")
+ }
+
+ UserDefaults.keychainItem = nil
+ }
+}
diff --git a/Orangered-Swift/main.swift b/Orangered-Swift/main.swift
new file mode 100644
index 0000000..efd90c6
--- /dev/null
+++ b/Orangered-Swift/main.swift
@@ -0,0 +1,17 @@
+//
+// main.swift
+// Orangered
+//
+// Created by Alan Westbrook on 5/29/16.
+// Copyright © 2016 Rockwood Software. All rights reserved.
+//
+
+import AppKit
+
+autoreleasepool { () -> () in
+ let app = NSApplication.shared()
+ let delegate = AppDelegate()
+ app.delegate = delegate
+ app.run()
+}
+
diff --git a/Orangered.icns b/Orangered.icns
deleted file mode 100755
index 5123812..0000000
Binary files a/Orangered.icns and /dev/null differ
diff --git a/Orangered.png b/Orangered.png
deleted file mode 100644
index 2221d0a..0000000
Binary files a/Orangered.png and /dev/null differ
diff --git a/Orangered.xcodeproj/project.pbxproj b/Orangered.xcodeproj/project.pbxproj
index 0f69922..ef3d5ad 100644
--- a/Orangered.xcodeproj/project.pbxproj
+++ b/Orangered.xcodeproj/project.pbxproj
@@ -7,91 +7,51 @@
objects = {
/* Begin PBXBuildFile section */
- 1DDD58160DA1D0A300B32029 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1DDD58140DA1D0A300B32029 /* MainMenu.xib */; };
- 256AC3DA0F4B6AC300CF3369 /* OrangeredAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 256AC3D90F4B6AC300CF3369 /* OrangeredAppDelegate.m */; };
- 2D6EB99111CC0B1D008888B7 /* Orangered.icns in Resources */ = {isa = PBXBuildFile; fileRef = 2D6EB99011CC0B1D008888B7 /* Orangered.icns */; };
- 2D7D323811CCBF9200D9A067 /* BlueEnvelope.png in Resources */ = {isa = PBXBuildFile; fileRef = 2D7D323711CCBF9200D9A067 /* BlueEnvelope.png */; };
- 2D7D32B611CD47DA00D9A067 /* todo.txt in Resources */ = {isa = PBXBuildFile; fileRef = 2D7D32B511CD47DA00D9A067 /* todo.txt */; };
- 2D7D369511D1FC9C00D9A067 /* HighlightEnvelope.png in Resources */ = {isa = PBXBuildFile; fileRef = 2D7D369411D1FC9C00D9A067 /* HighlightEnvelope.png */; };
- 2D7D36C311D6D5D100D9A067 /* prefs.m in Sources */ = {isa = PBXBuildFile; fileRef = 2D7D36C211D6D5D100D9A067 /* prefs.m */; };
- 2D7D38A511D8750100D9A067 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D7D38A411D8750100D9A067 /* Security.framework */; };
- 2D7D38B711D8789200D9A067 /* modmail.png in Resources */ = {isa = PBXBuildFile; fileRef = 2D7D38B611D8789200D9A067 /* modmail.png */; };
- 2DC5530B11C9EF4E00D9B5C1 /* GreyEnvelope.png in Resources */ = {isa = PBXBuildFile; fileRef = 2DC5530A11C9EF4E00D9B5C1 /* GreyEnvelope.png */; };
- 2DC5532D11C9F52000D9B5C1 /* BlackEnvelope.png in Resources */ = {isa = PBXBuildFile; fileRef = 2DC5532C11C9F52000D9B5C1 /* BlackEnvelope.png */; };
- 2DC5533D11C9F78C00D9B5C1 /* OrangeredEnvelope.png in Resources */ = {isa = PBXBuildFile; fileRef = 2DC5533C11C9F78C00D9B5C1 /* OrangeredEnvelope.png */; };
- 2DC5542711CA1D2700D9B5C1 /* o-mail.png in Resources */ = {isa = PBXBuildFile; fileRef = 2DC5542611CA1D2700D9B5C1 /* o-mail.png */; };
- 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; };
- 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; };
- 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
- E776C14C187901190044BDE2 /* BlackEnvelope@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = E776C14B187901190044BDE2 /* BlackEnvelope@2x.png */; };
- E776C14E187903A40044BDE2 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E776C14D187903A40044BDE2 /* Images.xcassets */; };
- E776C1511879046B0044BDE2 /* GreyEnvelope@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = E776C14F1879046B0044BDE2 /* GreyEnvelope@2x.png */; };
- E776C1521879046B0044BDE2 /* HighlightEnvelope@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = E776C1501879046B0044BDE2 /* HighlightEnvelope@2x.png */; };
- E776C1561879049E0044BDE2 /* OrangeredEnvelope@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = E776C1551879049E0044BDE2 /* OrangeredEnvelope@2x.png */; };
- E776C158187905690044BDE2 /* BlueEnvelope@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = E776C157187905690044BDE2 /* BlueEnvelope@2x.png */; };
+ E72964D31D14BF2B0088A337 /* PrefViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E72964D21D14BF2B0088A337 /* PrefViewController.swift */; };
+ E757731C1D0F9E5500BA4B85 /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E757731B1D0F9E5500BA4B85 /* LoginViewController.swift */; };
+ E7591DBB1CFB60450074F56B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7591DBA1CFB60450074F56B /* AppDelegate.swift */; };
+ E7591DBD1CFB60450074F56B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E7591DBC1CFB60450074F56B /* Assets.xcassets */; };
+ E7591DC61CFC017A0074F56B /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7591DC51CFC017A0074F56B /* main.swift */; };
+ E7E2FFAA1D0F7D4800C8234C /* StatusItemController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7E2FFA91D0F7D4800C8234C /* StatusItemController.swift */; };
+ E7E2FFAC1D0F7DFF00C8234C /* UserDefaults+Orangered.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7E2FFAB1D0F7DFF00C8234C /* UserDefaults+Orangered.swift */; };
+ E7E2FFAE1D0F7E8200C8234C /* Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7E2FFAD1D0F7E8200C8234C /* Menu.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
- 089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; };
1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; };
- 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; };
- 1DDD58150DA1D0A300B32029 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/MainMenu.xib; sourceTree = ""; };
- 256AC3D80F4B6AC300CF3369 /* OrangeredAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OrangeredAppDelegate.h; sourceTree = ""; };
- 256AC3D90F4B6AC300CF3369 /* OrangeredAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OrangeredAppDelegate.m; sourceTree = ""; };
- 256AC3F00F4B6AF500CF3369 /* Orangered_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Orangered_Prefix.pch; sourceTree = ""; };
- 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; };
29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; };
- 2D6EB99011CC0B1D008888B7 /* Orangered.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = Orangered.icns; sourceTree = ""; };
- 2D7D323711CCBF9200D9A067 /* BlueEnvelope.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = BlueEnvelope.png; sourceTree = ""; };
- 2D7D32B511CD47DA00D9A067 /* todo.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = todo.txt; sourceTree = ""; };
- 2D7D369411D1FC9C00D9A067 /* HighlightEnvelope.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = HighlightEnvelope.png; sourceTree = ""; };
- 2D7D36C111D6D5D100D9A067 /* prefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = prefs.h; sourceTree = ""; };
- 2D7D36C211D6D5D100D9A067 /* prefs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = prefs.m; sourceTree = ""; };
2D7D38A411D8750100D9A067 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
- 2D7D38B611D8789200D9A067 /* modmail.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = modmail.png; sourceTree = ""; };
- 2DC5530A11C9EF4E00D9B5C1 /* GreyEnvelope.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = GreyEnvelope.png; sourceTree = ""; };
- 2DC5532C11C9F52000D9B5C1 /* BlackEnvelope.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = BlackEnvelope.png; sourceTree = ""; };
- 2DC5533C11C9F78C00D9B5C1 /* OrangeredEnvelope.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = OrangeredEnvelope.png; sourceTree = ""; };
- 2DC5542611CA1D2700D9B5C1 /* o-mail.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "o-mail.png"; sourceTree = ""; };
- 8D1107310486CEB800E47090 /* Orangered-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Orangered-Info.plist"; sourceTree = ""; };
- 8D1107320486CEB800E47090 /* Orangered.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Orangered.app; sourceTree = BUILT_PRODUCTS_DIR; };
- E776C14B187901190044BDE2 /* BlackEnvelope@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "BlackEnvelope@2x.png"; sourceTree = ""; };
- E776C14D187903A40044BDE2 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Orangered/Images.xcassets; sourceTree = ""; };
- E776C14F1879046B0044BDE2 /* GreyEnvelope@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "GreyEnvelope@2x.png"; sourceTree = ""; };
- E776C1501879046B0044BDE2 /* HighlightEnvelope@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "HighlightEnvelope@2x.png"; sourceTree = ""; };
- E776C1551879049E0044BDE2 /* OrangeredEnvelope@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "OrangeredEnvelope@2x.png"; sourceTree = ""; };
- E776C157187905690044BDE2 /* BlueEnvelope@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "BlueEnvelope@2x.png"; sourceTree = ""; };
+ E72964D21D14BF2B0088A337 /* PrefViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrefViewController.swift; sourceTree = ""; };
+ E757731B1D0F9E5500BA4B85 /* LoginViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginViewController.swift; sourceTree = ""; };
+ E7591DB81CFB60450074F56B /* Orangered.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Orangered.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ E7591DBA1CFB60450074F56B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ E7591DBC1CFB60450074F56B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = "Orangered-Swift/Assets.xcassets"; sourceTree = ""; };
+ E7591DC11CFB60450074F56B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = "Orangered-Swift/Info.plist"; sourceTree = ""; };
+ E7591DC51CFC017A0074F56B /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; };
+ E7E2FFA91D0F7D4800C8234C /* StatusItemController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusItemController.swift; sourceTree = ""; };
+ E7E2FFAB1D0F7DFF00C8234C /* UserDefaults+Orangered.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UserDefaults+Orangered.swift"; sourceTree = ""; };
+ E7E2FFAD1D0F7E8200C8234C /* Menu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Menu.swift; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
- 8D11072E0486CEB800E47090 /* Frameworks */ = {
+ E7591DB51CFB60450074F56B /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */,
- 2D7D38A511D8750100D9A067 /* Security.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
- 080E96DDFE201D6D7F000001 /* Classes */ = {
- isa = PBXGroup;
- children = (
- 256AC3D80F4B6AC300CF3369 /* OrangeredAppDelegate.h */,
- 256AC3D90F4B6AC300CF3369 /* OrangeredAppDelegate.m */,
- 2D7D36C111D6D5D100D9A067 /* prefs.h */,
- 2D7D36C211D6D5D100D9A067 /* prefs.m */,
- );
- name = Classes;
- sourceTree = "";
- };
1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = {
isa = PBXGroup;
children = (
+ 29B97325FDCFA39411CA2CEA /* Foundation.framework */,
1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */,
+ 29B97324FDCFA39411CA2CEA /* AppKit.framework */,
+ 2D7D38A411D8750100D9A067 /* Security.framework */,
);
name = "Linked Frameworks";
sourceTree = "";
@@ -99,10 +59,6 @@
1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = {
isa = PBXGroup;
children = (
- 2D7D38A411D8750100D9A067 /* Security.framework */,
- 29B97324FDCFA39411CA2CEA /* AppKit.framework */,
- 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */,
- 29B97325FDCFA39411CA2CEA /* Foundation.framework */,
);
name = "Other Frameworks";
sourceTree = "";
@@ -110,7 +66,7 @@
19C28FACFE9D520D11CA2CBB /* Products */ = {
isa = PBXGroup;
children = (
- 8D1107320486CEB800E47090 /* Orangered.app */,
+ E7591DB81CFB60450074F56B /* Orangered.app */,
);
name = Products;
sourceTree = "";
@@ -118,45 +74,19 @@
29B97314FDCFA39411CA2CEA /* Orangered */ = {
isa = PBXGroup;
children = (
- 080E96DDFE201D6D7F000001 /* Classes */,
- 29B97315FDCFA39411CA2CEA /* Other Sources */,
+ E7591DB91CFB60450074F56B /* Source */,
29B97317FDCFA39411CA2CEA /* Resources */,
29B97323FDCFA39411CA2CEA /* Frameworks */,
19C28FACFE9D520D11CA2CBB /* Products */,
- 2D7D32B511CD47DA00D9A067 /* todo.txt */,
);
name = Orangered;
sourceTree = "";
};
- 29B97315FDCFA39411CA2CEA /* Other Sources */ = {
- isa = PBXGroup;
- children = (
- 256AC3F00F4B6AF500CF3369 /* Orangered_Prefix.pch */,
- 29B97316FDCFA39411CA2CEA /* main.m */,
- );
- name = "Other Sources";
- sourceTree = "";
- };
29B97317FDCFA39411CA2CEA /* Resources */ = {
isa = PBXGroup;
children = (
- E776C157187905690044BDE2 /* BlueEnvelope@2x.png */,
- 2DC5532C11C9F52000D9B5C1 /* BlackEnvelope.png */,
- E776C14B187901190044BDE2 /* BlackEnvelope@2x.png */,
- 2D7D323711CCBF9200D9A067 /* BlueEnvelope.png */,
- 2DC5530A11C9EF4E00D9B5C1 /* GreyEnvelope.png */,
- E776C14F1879046B0044BDE2 /* GreyEnvelope@2x.png */,
- 2D7D369411D1FC9C00D9A067 /* HighlightEnvelope.png */,
- E776C1501879046B0044BDE2 /* HighlightEnvelope@2x.png */,
- E776C14D187903A40044BDE2 /* Images.xcassets */,
- 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */,
- 1DDD58140DA1D0A300B32029 /* MainMenu.xib */,
- 2D7D38B611D8789200D9A067 /* modmail.png */,
- 2DC5542611CA1D2700D9B5C1 /* o-mail.png */,
- 8D1107310486CEB800E47090 /* Orangered-Info.plist */,
- 2D6EB99011CC0B1D008888B7 /* Orangered.icns */,
- 2DC5533C11C9F78C00D9B5C1 /* OrangeredEnvelope.png */,
- E776C1551879049E0044BDE2 /* OrangeredEnvelope@2x.png */,
+ E7591DBC1CFB60450074F56B /* Assets.xcassets */,
+ E7591DC11CFB60450074F56B /* Info.plist */,
);
name = Resources;
sourceTree = "";
@@ -170,25 +100,39 @@
name = Frameworks;
sourceTree = "";
};
+ E7591DB91CFB60450074F56B /* Source */ = {
+ isa = PBXGroup;
+ children = (
+ E7591DBA1CFB60450074F56B /* AppDelegate.swift */,
+ E757731B1D0F9E5500BA4B85 /* LoginViewController.swift */,
+ E7591DC51CFC017A0074F56B /* main.swift */,
+ E7E2FFAD1D0F7E8200C8234C /* Menu.swift */,
+ E7E2FFA91D0F7D4800C8234C /* StatusItemController.swift */,
+ E7E2FFAB1D0F7DFF00C8234C /* UserDefaults+Orangered.swift */,
+ E72964D21D14BF2B0088A337 /* PrefViewController.swift */,
+ );
+ name = Source;
+ path = "Orangered-Swift";
+ sourceTree = "";
+ };
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
- 8D1107260486CEB800E47090 /* Orangered */ = {
+ E7591DB71CFB60450074F56B /* Orangered */ = {
isa = PBXNativeTarget;
- buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "Orangered" */;
+ buildConfigurationList = E7591DC41CFB60450074F56B /* Build configuration list for PBXNativeTarget "Orangered" */;
buildPhases = (
- 8D1107290486CEB800E47090 /* Resources */,
- 8D11072C0486CEB800E47090 /* Sources */,
- 8D11072E0486CEB800E47090 /* Frameworks */,
+ E7591DB41CFB60450074F56B /* Sources */,
+ E7591DB51CFB60450074F56B /* Frameworks */,
+ E7591DB61CFB60450074F56B /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = Orangered;
- productInstallPath = "$(HOME)/Applications";
- productName = Orangered;
- productReference = 8D1107320486CEB800E47090 /* Orangered.app */;
+ productName = "Orangered-Swift";
+ productReference = E7591DB81CFB60450074F56B /* Orangered.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
@@ -197,11 +141,14 @@
29B97313FDCFA39411CA2CEA /* Project object */ = {
isa = PBXProject;
attributes = {
- LastUpgradeCheck = 0640;
- ORGANIZATIONNAME = "Voidref Software";
+ LastSwiftUpdateCheck = 0730;
+ LastUpgradeCheck = 0800;
+ ORGANIZATIONNAME = "Rockwood Software";
TargetAttributes = {
- 8D1107260486CEB800E47090 = {
+ E7591DB71CFB60450074F56B = {
+ CreatedOnToolsVersion = 7.3.1;
DevelopmentTeam = SR7K2S8GE4;
+ LastSwiftMigration = 0800;
};
};
};
@@ -211,271 +158,153 @@
hasScannedForEncodings = 1;
knownRegions = (
en,
+ Base,
);
mainGroup = 29B97314FDCFA39411CA2CEA /* Orangered */;
projectDirPath = "";
projectRoot = "";
targets = (
- 8D1107260486CEB800E47090 /* Orangered */,
+ E7591DB71CFB60450074F56B /* Orangered */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
- 8D1107290486CEB800E47090 /* Resources */ = {
+ E7591DB61CFB60450074F56B /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- E776C158187905690044BDE2 /* BlueEnvelope@2x.png in Resources */,
- 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */,
- E776C1521879046B0044BDE2 /* HighlightEnvelope@2x.png in Resources */,
- 1DDD58160DA1D0A300B32029 /* MainMenu.xib in Resources */,
- 2DC5530B11C9EF4E00D9B5C1 /* GreyEnvelope.png in Resources */,
- 2DC5532D11C9F52000D9B5C1 /* BlackEnvelope.png in Resources */,
- 2DC5533D11C9F78C00D9B5C1 /* OrangeredEnvelope.png in Resources */,
- E776C1561879049E0044BDE2 /* OrangeredEnvelope@2x.png in Resources */,
- E776C1511879046B0044BDE2 /* GreyEnvelope@2x.png in Resources */,
- E776C14E187903A40044BDE2 /* Images.xcassets in Resources */,
- 2DC5542711CA1D2700D9B5C1 /* o-mail.png in Resources */,
- 2D6EB99111CC0B1D008888B7 /* Orangered.icns in Resources */,
- 2D7D323811CCBF9200D9A067 /* BlueEnvelope.png in Resources */,
- 2D7D32B611CD47DA00D9A067 /* todo.txt in Resources */,
- 2D7D369511D1FC9C00D9A067 /* HighlightEnvelope.png in Resources */,
- 2D7D38B711D8789200D9A067 /* modmail.png in Resources */,
- E776C14C187901190044BDE2 /* BlackEnvelope@2x.png in Resources */,
+ E7591DBD1CFB60450074F56B /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
- 8D11072C0486CEB800E47090 /* Sources */ = {
+ E7591DB41CFB60450074F56B /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- 8D11072D0486CEB800E47090 /* main.m in Sources */,
- 256AC3DA0F4B6AC300CF3369 /* OrangeredAppDelegate.m in Sources */,
- 2D7D36C311D6D5D100D9A067 /* prefs.m in Sources */,
+ E7E2FFAC1D0F7DFF00C8234C /* UserDefaults+Orangered.swift in Sources */,
+ E7591DC61CFC017A0074F56B /* main.swift in Sources */,
+ E7591DBB1CFB60450074F56B /* AppDelegate.swift in Sources */,
+ E72964D31D14BF2B0088A337 /* PrefViewController.swift in Sources */,
+ E757731C1D0F9E5500BA4B85 /* LoginViewController.swift in Sources */,
+ E7E2FFAA1D0F7D4800C8234C /* StatusItemController.swift in Sources */,
+ E7E2FFAE1D0F7E8200C8234C /* Menu.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
-/* Begin PBXVariantGroup section */
- 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = {
- isa = PBXVariantGroup;
- children = (
- 089C165DFE840E0CC02AAC07 /* English */,
- );
- name = InfoPlist.strings;
- sourceTree = "";
- };
- 1DDD58140DA1D0A300B32029 /* MainMenu.xib */ = {
- isa = PBXVariantGroup;
- children = (
- 1DDD58150DA1D0A300B32029 /* English */,
- );
- name = MainMenu.xib;
- sourceTree = "";
- };
-/* End PBXVariantGroup section */
-
/* Begin XCBuildConfiguration section */
- C01FCF4B08A954540054247B /* Debug */ = {
+ C01FCF4F08A954540054247B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
- ALWAYS_SEARCH_USER_PATHS = NO;
- ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
- CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
- CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
- CLANG_WARN_OBJCPP_ARC_ABI = YES;
- CLANG_WARN_OBJC_EXPLICIT_OWNERSHIP_TYPE = YES;
- CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = NO;
- CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
- CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS = NO;
- CLANG_WARN_OBJC_RECEIVER_WEAK = YES;
- CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
- CODE_SIGN_IDENTITY = "Developer ID Application";
- COMBINE_HIDPI_IMAGES = YES;
- COPY_PHASE_STRIP = NO;
- FRAMEWORK_SEARCH_PATHS = (
- "$(inherited)",
- "\"$(SRCROOT)\"",
- );
- GCC_DYNAMIC_NO_PIC = NO;
- GCC_ENABLE_OBJC_GC = unsupported;
- GCC_INCREASE_PRECOMPILED_HEADER_SHARING = YES;
- GCC_OPTIMIZATION_LEVEL = 0;
- GCC_PRECOMPILE_PREFIX_HEADER = YES;
- GCC_PREFIX_HEADER = Orangered_Prefix.pch;
- GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
- GCC_WARN_MULTIPLE_DEFINITION_TYPES_FOR_SELECTOR = YES;
- INFOPLIST_FILE = "Orangered-Info.plist";
- INSTALL_PATH = "$(HOME)/Applications";
- MACOSX_DEPLOYMENT_TARGET = 10.8;
- PRODUCT_NAME = Orangered;
+ CODE_SIGN_IDENTITY = "Mac Developer";
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ LLVM_LTO = NO;
+ RUN_CLANG_STATIC_ANALYZER = YES;
SDKROOT = macosx;
- WARNING_CFLAGS = (
- "-Weverything",
- "-Wno-direct-ivar-access",
- "-Wno-objc-missing-property-synthesis",
- );
+ VALID_ARCHS = x86_64;
+ WARNING_CFLAGS = "-Weverything";
};
- name = Debug;
+ name = Release;
};
- C01FCF4C08A954540054247B /* Release */ = {
+ E7591DC21CFB60450074F56B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
- CLANG_ENABLE_OBJC_ARC = YES;
- CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
- CLANG_WARN_OBJCPP_ARC_ABI = YES;
- CLANG_WARN_OBJC_EXPLICIT_OWNERSHIP_TYPE = YES;
- CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = NO;
- CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
- CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS = NO;
- CLANG_WARN_OBJC_RECEIVER_WEAK = YES;
- CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
- CODE_SIGN_IDENTITY = "Developer ID Application";
+ CODE_SIGN_IDENTITY = "Mac Developer";
COMBINE_HIDPI_IMAGES = YES;
- DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
- FRAMEWORK_SEARCH_PATHS = (
- "$(inherited)",
- "\"$(SRCROOT)\"",
- );
- GCC_ENABLE_OBJC_GC = unsupported;
- GCC_INCREASE_PRECOMPILED_HEADER_SHARING = YES;
- GCC_PRECOMPILE_PREFIX_HEADER = YES;
- GCC_PREFIX_HEADER = Orangered_Prefix.pch;
- GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
- GCC_WARN_MULTIPLE_DEFINITION_TYPES_FOR_SELECTOR = YES;
- INFOPLIST_FILE = "Orangered-Info.plist";
- INSTALL_PATH = "$(HOME)/Applications";
- MACOSX_DEPLOYMENT_TARGET = 10.8;
- PRODUCT_NAME = Orangered;
- SDKROOT = macosx;
- WARNING_CFLAGS = (
- "-Weverything",
- "-Wno-direct-ivar-access",
- "-Wno-objc-missing-property-synthesis",
- );
- };
- name = Release;
- };
- C01FCF4F08A954540054247B /* Debug */ = {
- isa = XCBuildConfiguration;
- buildSettings = {
- CLANG_ENABLE_OBJC_ARC = YES;
- GCC_C_LANGUAGE_STANDARD = "compiler-default";
- GCC_DEBUGGING_SYMBOLS = full;
- GCC_OPTIMIZATION_LEVEL = 0;
- GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
- GCC_TREAT_WARNINGS_AS_ERRORS = YES;
- GCC_VERSION = "";
- GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
- GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
- GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
- GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
- GCC_WARN_ABOUT_RETURN_TYPE = YES;
- GCC_WARN_CHECK_SWITCH_STATEMENTS = YES;
- GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
- GCC_WARN_MISSING_PARENTHESES = YES;
- GCC_WARN_MULTIPLE_DEFINITION_TYPES_FOR_SELECTOR = NO;
- GCC_WARN_PEDANTIC = YES;
- GCC_WARN_PROTOTYPE_CONVERSION = YES;
- GCC_WARN_SHADOW = YES;
- GCC_WARN_SIGN_COMPARE = YES;
- GCC_WARN_STRICT_SELECTOR_MATCH = YES;
- GCC_WARN_UNDECLARED_SELECTOR = YES;
- GCC_WARN_UNINITIALIZED_AUTOS = YES;
- GCC_WARN_UNKNOWN_PRAGMAS = YES;
- GCC_WARN_UNUSED_FUNCTION = YES;
- GCC_WARN_UNUSED_LABEL = YES;
- GCC_WARN_UNUSED_PARAMETER = YES;
- GCC_WARN_UNUSED_VALUE = YES;
- GCC_WARN_UNUSED_VARIABLE = YES;
- ONLY_ACTIVE_ARCH = YES;
- RUN_CLANG_STATIC_ANALYZER = YES;
- SDKROOT = macosx;
- VALID_ARCHS = x86_64;
- WARNING_CFLAGS = "-Weverything";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ INFOPLIST_FILE = "Orangered-Swift/Info.plist";
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ PRODUCT_BUNDLE_IDENTIFIER = com.rockwood.Orangered;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 3.0;
};
name = Debug;
};
- C01FCF5008A954540054247B /* Release */ = {
+ E7591DC31CFB60450074F56B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
- CLANG_ENABLE_OBJC_ARC = YES;
- CLANG_X86_VECTOR_INSTRUCTIONS = avx;
- CODE_SIGN_IDENTITY = "iPhone Developer: Alan Westbrook (X969Q5275N)";
- GCC_C_LANGUAGE_STANDARD = "compiler-default";
- GCC_ENABLE_OBJC_GC = supported;
- GCC_ENABLE_SSE3_EXTENSIONS = YES;
- GCC_ENABLE_SSE41_EXTENSIONS = YES;
- GCC_ENABLE_SSE42_EXTENSIONS = YES;
- GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
- GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
- GCC_TREAT_NONCONFORMANT_CODE_ERRORS_AS_WARNINGS = YES;
- GCC_TREAT_WARNINGS_AS_ERRORS = YES;
- GCC_VERSION = "";
- GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
- GCC_WARN_ABOUT_GLOBAL_CONSTRUCTORS = YES;
- GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
- GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
- GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
- GCC_WARN_ABOUT_RETURN_TYPE = YES;
- GCC_WARN_CHECK_SWITCH_STATEMENTS = YES;
- GCC_WARN_EFFECTIVE_CPLUSPLUS_VIOLATIONS = YES;
- GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES;
- GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES;
- GCC_WARN_INHIBIT_ALL_WARNINGS = NO;
- GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
- GCC_WARN_MISSING_PARENTHESES = YES;
- GCC_WARN_MULTIPLE_DEFINITION_TYPES_FOR_SELECTOR = NO;
- GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES;
- GCC_WARN_PEDANTIC = YES;
- GCC_WARN_PROTOTYPE_CONVERSION = NO;
- GCC_WARN_SHADOW = YES;
- GCC_WARN_SIGN_COMPARE = YES;
- GCC_WARN_STRICT_SELECTOR_MATCH = YES;
- GCC_WARN_UNDECLARED_SELECTOR = YES;
- GCC_WARN_UNINITIALIZED_AUTOS = YES;
- GCC_WARN_UNKNOWN_PRAGMAS = YES;
- GCC_WARN_UNUSED_FUNCTION = YES;
- GCC_WARN_UNUSED_LABEL = YES;
- GCC_WARN_UNUSED_PARAMETER = YES;
- GCC_WARN_UNUSED_VALUE = YES;
- GCC_WARN_UNUSED_VARIABLE = YES;
- LLVM_LTO = NO;
- RUN_CLANG_STATIC_ANALYZER = YES;
- SDKROOT = macosx;
- VALID_ARCHS = x86_64;
- WARNING_CFLAGS = "-Weverything";
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ CODE_SIGN_IDENTITY = "Mac Developer";
+ COMBINE_HIDPI_IMAGES = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ INFOPLIST_FILE = "Orangered-Swift/Info.plist";
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ PRODUCT_BUNDLE_IDENTIFIER = com.rockwood.Orangered;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
+ SWIFT_VERSION = 3.0;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
- C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "Orangered" */ = {
+ C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Orangered" */ = {
isa = XCConfigurationList;
buildConfigurations = (
- C01FCF4B08A954540054247B /* Debug */,
- C01FCF4C08A954540054247B /* Release */,
+ C01FCF4F08A954540054247B /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
- C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Orangered" */ = {
+ E7591DC41CFB60450074F56B /* Build configuration list for PBXNativeTarget "Orangered" */ = {
isa = XCConfigurationList;
buildConfigurations = (
- C01FCF4F08A954540054247B /* Debug */,
- C01FCF5008A954540054247B /* Release */,
+ E7591DC21CFB60450074F56B /* Debug */,
+ E7591DC31CFB60450074F56B /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
diff --git a/Orangered.xcodeproj/xcshareddata/xcschemes/Orangered.xcscheme b/Orangered.xcodeproj/xcshareddata/xcschemes/Orangered.xcscheme
new file mode 100644
index 0000000..7cdbab9
--- /dev/null
+++ b/Orangered.xcodeproj/xcshareddata/xcschemes/Orangered.xcscheme
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/OrangeredAppDelegate.h b/OrangeredAppDelegate.h
deleted file mode 100644
index d160d9f..0000000
--- a/OrangeredAppDelegate.h
+++ /dev/null
@@ -1,87 +0,0 @@
-//
-// OrangeredAppDelegate.h
-// Orangered
-//
-// Created by Alan Westbrook on 6/16/10.
-// Copyright 2010 __MyCompanyName__. All rights reserved.
-//
-
-#import "prefs.h"
-
-@interface OrangeredAppDelegate : NSObject
-
-@property (strong) Prefs* prefs;
-
-@property (strong) NSStatusItem* status;
-@property (strong) IBOutlet NSMenu* menu;
-@property (strong) IBOutlet NSMenuItem* update;
-@property (strong) IBOutlet NSMenuItem* about;
-
-@property (strong) IBOutlet NSWindow* aboutWindow;
-@property (strong) IBOutlet NSTextField* versionTF;
-@property (strong) IBOutlet NSButton* aboutEnvelope;
-@property (strong) IBOutlet NSTextField* creditsTF;
-@property (strong) IBOutlet NSTextField* sloganTF;
-@property (strong) IBOutlet NSTextField* logoTF;
-
-
-@property (strong) IBOutlet NSWindow* loginWindow;
-@property (strong) IBOutlet NSTextField* userentry;
-@property (strong) IBOutlet NSTextField* passwordentry;
-@property (strong) IBOutlet NSTextField* loginerror;
-@property (strong) IBOutlet NSButton* savepassword;
-@property (strong) IBOutlet NSProgressIndicator* loginProgress;
-@property (strong) IBOutlet NSProgressIndicator* appUpdateCheckProgress;
-
-@property (strong) IBOutlet NSWindow* prefWindow;
-@property (strong) IBOutlet NSButton* openAtLoginCB;
-@property (strong) IBOutlet NSButton* logDiagnosticsCB;
-@property (strong) IBOutlet NSButton* autoUpdateCheckCB;
-@property (strong) IBOutlet NSTextField* redditCheckIntervalTF;
-@property (strong) IBOutlet NSTextField* appUpdateResultTF;
-
-@property (strong) NSString* currentIcon;
-@property (strong) NSString* noMailIcon;
-
-- (IBAction) loginChanged: (id)sender;
-- (IBAction) showLoginWindow: (id)sender;
-- (IBAction) showPrefsWindow: (id)sender;
-- (IBAction) donePrefsWindow: (id)sender;
-- (IBAction) openMailbox: (id)sender;
-- (IBAction) updateMenuItemClicked: (id)sender;
-- (IBAction) checkForAppUpdate: (id)sender;
-- (IBAction) loadAtStartupClicked: (id)sender;
-- (IBAction) showAboutWindow: (id)sender;
-- (IBAction) showAboutButtonClicked: (id)sender;
-- (IBAction) aboutEnvelopeClicked: (id)sender;
-
-- (void) setupPollers;
-- (void) login;
-- (void) updateStatus: (NSTimer*)theTimer;
-- (NSString*) userDataUrl;
-- (void) parseStatus;
-- (void) parseLogin: (NSHTTPURLResponse*) response;
-- (void) setLoadAtStartup;
-- (void) setMessageStatus: (NSString*) imageName;
-
-// NSURLConnection delegate methods:
-- (void) connection: (NSURLConnection *)connection
- didFailWithError: (NSError *)error;
-
-- (void) connection: (NSURLConnection *)connection
- didReceiveData: (NSData *)data;
-
-- (void) connection: (NSURLConnection *)connection
- didReceiveResponse: (NSURLResponse *)response;
-
-- (void) connection: (NSURLConnection *)connection
- didSendBodyData: (NSInteger)bytesWritten
- totalBytesWritten: (NSInteger)totalBytesWritten
- totalBytesExpectedToWrite: (NSInteger)totalBytesExpectedToWrite;
-
-- (void) connectionDidFinishLoading: (NSURLConnection *)connection;
-
-- (void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification;
-
-
-@end
diff --git a/OrangeredAppDelegate.m b/OrangeredAppDelegate.m
deleted file mode 100644
index bb4b438..0000000
--- a/OrangeredAppDelegate.m
+++ /dev/null
@@ -1,796 +0,0 @@
-//
-// OrangeredAppDelegate.m
-// Orangered
-//
-// Created by voidref on 6/16/10.
-// Copyright 2010 Voidref Software. All rights reserved.
-//
-
-#import "OrangeredAppDelegate.h"
-
-// I can't believe this is working.
-@interface NSMenuItem (hiddenpropcat)
-@property BOOL hidden;
-@end
-
-@interface OrangeredAppDelegate()
-{
- NSMenuItem* preference;
-
- BOOL hasModMail;
-
- NSTimer* statusPoller;
- NSTimer* updatePoller;
-
- NSMutableData* statusData;
- NSMutableData* loginData;
- NSMutableData* appUpdateData;
-
- NSURLConnection* statusConnection;
- NSURLConnection* loginConnection;
- NSURLConnection* appUpdateConnection;
-}
-@end
-
-@implementation OrangeredAppDelegate
-
-
-static NSString* GreyEnvelope = @"GreyEnvelope";
-static NSString* BlackEnvelope = @"BlackEnvelope";
-static NSString* BlueEnvelope = @"BlueEnvelope";
-static NSString* OrangeredEnvelope = @"OrangeredEnvelope";
-static NSString* HighlightEnvelope = @"HighlightEnvelope";
-static NSString* ModMailIcon = @"modmail";
-
-static const int AppUpdatePollInterval = (60 * 60 * 24); // 1 day
-
-// Sadly a macro seems the easiest way to do this right now...
-#define OrangeLog1(x) if (true == self.prefs.logDiagnostics) { NSLog(x); }
-#define OrangeLog(x, y) if (true == self.prefs.logDiagnostics) { NSLog(x, y); }
-
-// --------------------------------------------------------------------------------------------------------------------
-- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
-{
- // We don't use this. Must appease the warning gods.
-#pragma unused(aNotification)
-
- self.prefs = [[Prefs alloc] init];
- [self setLoadAtStartup];
-
- hasModMail = NO;
- statusData = nil;
- loginData = nil;
- appUpdateData = nil;
- updatePoller = nil;
- self.versionTF.stringValue = [NSString stringWithFormat:@"Version %@", [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]];
- self.creditsTF.stringValue = @"written by Alan Westbrook (voidref)\n\nSpecial Thanks to the following redditors:\n"
- "ashleyw\n"
- "Condawg\n"
- "dawnerd\n"
- "despideme\n"
- "derekaw\n"
- "EthicalReasoning\n"
- "giftedmunchkin\n"
- "kevinhoagland\n"
- "loggedout\n"
- "polyGone\n"
- "RamenStein\n"
- "shinratdr\n"
- "sporadicmonster\n";
-
- [self.creditsTF setHidden:YES];
-
- self.loginWindow.collectionBehavior = NSWindowCollectionBehaviorCanJoinAllSpaces;
- self.aboutWindow.collectionBehavior = NSWindowCollectionBehaviorCanJoinAllSpaces;
- self.prefWindow.collectionBehavior = NSWindowCollectionBehaviorCanJoinAllSpaces;
-
- self.status = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
- self.status.menu = self.menu;
- self.status.highlightMode = YES;
- self.status.alternateImage = [NSImage imageNamed:HighlightEnvelope];
- [self setMessageStatus: GreyEnvelope];
-
- self.menu.delegate = self;
- self.menu.autoenablesItems = NO;
-
- self.currentIcon = GreyEnvelope;
- self.noMailIcon = BlackEnvelope;
-
- // detect first run / empty username
- // We have to have an account name in order to check status!
- if (nil == _prefs.name)
- {
- [self showLoginWindow:nil];
- }
- else
- {
- [self updateStatus:nil];
- }
-
- [self setupPollers];
- [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self];
-
-}
-
-// -------------------------------------------------------------------------------------------------------------------
-- (void) setupPollers
-{
- NSInteger interval = self.prefs.redditCheckInterval * 60;
-
- if (60 > interval)
- {
- interval = 60;
- }
-
- [statusPoller invalidate];
- statusPoller = [NSTimer scheduledTimerWithTimeInterval:interval
- target:self
- selector:@selector(updateStatus:)
- userInfo:nil
- repeats:YES];
- // App update poller.
- if (YES == self.prefs.autoUpdateCheck)
- {
- if (nil == updatePoller)
- {
- updatePoller = [NSTimer scheduledTimerWithTimeInterval:AppUpdatePollInterval
- target:self
- selector:@selector(checkForAppUpdate:)
- userInfo:nil
- repeats:YES];
- }
- }
- else if (nil != updatePoller)
- {
- [updatePoller invalidate];
- updatePoller = nil;
- }
-
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-
-// --------------------------------------------------------------------------------------------------------------------
-- (IBAction) loginChanged:(id)sender
-{
-#pragma unused(sender)
-
- NSString* uname = [_userentry stringValue];
- NSString* pword = [_passwordentry stringValue];
-
- if ((uname.length < 1) || (pword.length < 1))
- {
- self.loginerror.stringValue = @"Username and passwrord are required";
- return;
- }
- else
- {
- self.loginerror.stringValue = @"";
- }
-
- self.prefs.name = uname;
- self.prefs.savePassword = ([_savepassword state] == NSOnState);
-
- self.prefs.password = pword;
-
- [self login];
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (void) login
-{
- if ((self.prefs.name.length < 1) || (self.prefs.password.length < 1))
- {
- // show window
- [self showLoginWindow:nil];
- return;
- }
-
- [self.loginProgress startAnimation:nil];
- [self.loginProgress setHidden:NO];
-
- OrangeLog(@"Logging in user: %@", self.prefs.name);
-
- NSURL* url = [NSURL URLWithString:@"https://ssl.reddit.com/api/login"];
-
- NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url
- cachePolicy:NSURLRequestUseProtocolCachePolicy
- timeoutInterval:self.prefs.timeout];
- [request setHTTPMethod: @"POST"];
- [request setHTTPBody: [[NSString stringWithFormat:@"user=%@&passwd=%@", self.prefs.name, self.prefs.password] dataUsingEncoding:NSUTF8StringEncoding]];
-
- loginConnection = [[NSURLConnection alloc] initWithRequest:request
- delegate:self];
- if (nil != loginConnection)
- {
- loginData = [NSMutableData data];
- }
- else
- {
- // Is there a way to find the exact error?
- self.loginerror.stringValue = @"Could not estabilsh connection to reddit.";
- OrangeLog(@"login error: %@", self.loginerror.stringValue);
- }
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (void) parseLogin:(NSHTTPURLResponse*) response
-{
- [self.loginProgress stopAnimation:nil];
- [self.loginProgress setHidden:YES];
-
- OrangeLog(@"Response Headers: %@", [response allHeaderFields]);
-
- NSArray* cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:[response allHeaderFields]
- forURL:[response URL]];
-
- if (cookies.count == 0)
- {
- // set a flag for error check?
- }
- else
- {
- OrangeLog(@"Setting Cookie Array: %@", cookies);
- NSHTTPCookieStorage* cstorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
- [cstorage setCookies:cookies
- forURL:[NSURL URLWithString:@"http://www.reddit.com/"]
- mainDocumentURL:nil];
-
- self.loginerror.stringValue = @"";
- [self.loginWindow close];
- [self updateStatus:nil];
- }
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (void) updateStatus: (NSTimer*)theTimer
-{
-#pragma unused(theTimer)
-
- // We only show one status at a time anyway, but we do need to continue polling if we have no connection.
- if ((self.currentIcon == BlackEnvelope) || (self.currentIcon == GreyEnvelope))
- {}
- else return;
-
- OrangeLog1(@"Updating status");
- NSURL* url = [NSURL URLWithString:[self userDataUrl]];
-
- NSURLRequest* request = [NSURLRequest requestWithURL:url
- cachePolicy:NSURLRequestUseProtocolCachePolicy
- timeoutInterval:self.prefs.timeout];
-
- statusConnection = [[NSURLConnection alloc] initWithRequest:request
- delegate:self];
- if (nil != statusConnection)
- {
- if (nil == statusData)
- {
- statusData = [NSMutableData data];
- }
- }
- else
- {
- // Is there a way to find the exact error?
- self.loginerror.stringValue = @"Could not estabilsh connection to reddit";
- }
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (void) parseStatus
-{
- NSString* statusResult = [[NSString alloc] initWithData:statusData
- encoding:NSUTF8StringEncoding];
-
- if ([statusResult rangeOfString:@"\"has_mail\": true"].location != NSNotFound )
- {
- self.loginerror.stringValue = @"";
- self.currentIcon = OrangeredEnvelope;
-
- NSUserNotification* note = [NSUserNotification new];
- note.title = @"Orangered!";
- note.informativeText = @"You have a new message on reddit!";
- note.actionButtonTitle = @"Read";
- note.otherButtonTitle = @"";
-
- [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:note];
-
- }
- else if ([statusResult rangeOfString:@"\"has_mail\": null"].location != NSNotFound )
- {
- // We are no longer logged in for some reason.
- static int failcounter = 0;
- ++failcounter;
-
- if ( failcounter > 1 )
- {
- OrangeLog1(@"failed to log in twice in one update.");
- failcounter = 0;
- return;
- }
-
- OrangeLog1(@"Status Update failed due to not being logged in, attempting re-login");
-
- // Not sure this will actually do anything as we login sync and block the main thread here.
- self.currentIcon = GreyEnvelope;
- [self setMessageStatus: self.currentIcon];
-
- // Try to log in.
- [self login];
-
- // Reset counter after trying a second log in.
- --failcounter;
-
- // login as already called us again on success
- // Ok, this logic is convoluted, need to fix that.
- return;
- }
- else if ([statusResult rangeOfString:@"\"has_mail\": false"].location != NSNotFound )
- {
- self.currentIcon = self.noMailIcon;
- }
-
- // Mod mail overrides all
- if ([statusResult rangeOfString:@"\"has_mod_mail\": true"].location != NSNotFound)
- {
- self.currentIcon = ModMailIcon;
- hasModMail = YES;
- }
- else
- {
- hasModMail = NO;
- }
-
-
- OrangeLog(@"CheckResult: %@", statusResult);
- [self setMessageStatus: self.currentIcon];
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (IBAction) showLoginWindow:(id)sender
-{
-#pragma unused(sender)
-
- [_savepassword setState:self.prefs.savePassword];
-
- if (nil != self.prefs.name) [_userentry setStringValue:self.prefs.name];
-
- if (nil != self.prefs.password) [_passwordentry setStringValue:self.prefs.password];
-
- self.appUpdateResultTF.stringValue = @"";
-
- // open window and force to the front
- [NSApp activateIgnoringOtherApps:YES];
- [self.loginWindow makeKeyAndOrderFront:nil];
- [self.loginWindow orderFrontRegardless];
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (IBAction) showPrefsWindow: (id)sender
-{
-#pragma unused(sender)
- [self.autoUpdateCheckCB setState: self.prefs.autoUpdateCheck];
- [self.logDiagnosticsCB setState:self.prefs.logDiagnostics];
- [self.openAtLoginCB setState: self.prefs.openAtLogin];
- [self.redditCheckIntervalTF setStringValue: [NSString stringWithFormat:@"%ld", self.prefs.redditCheckInterval]];
- self.appUpdateResultTF.stringValue = @"";
-
- // open window and force to the front
- [NSApp activateIgnoringOtherApps:YES];
- [_prefWindow makeKeyAndOrderFront:nil];
- [_prefWindow orderFrontRegardless];
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (IBAction) donePrefsWindow: (id)sender
-{
-#pragma unused(sender)
- NSInteger minutes = [[self.redditCheckIntervalTF stringValue] integerValue];
-
- // It seems I can't get the validator/formatter to work right, blea.
- if (1 > minutes)
- {
- minutes = 1;
- }
-
- bool adjustPollers = false;
- if (minutes != self.prefs.redditCheckInterval)
- {
- self.prefs.redditCheckInterval = minutes;
- adjustPollers = true;
- }
-
- if (self.prefs.autoUpdateCheck != (BOOL)[self.autoUpdateCheckCB state])
- {
- self.prefs.autoUpdateCheck = (BOOL)[self.autoUpdateCheckCB state];
- adjustPollers = true;
- }
-
- if (true == adjustPollers)
- {
- [self setupPollers];
- }
-
- self.prefs.logDiagnostics = (BOOL)[self.logDiagnosticsCB state];
-
- [_prefWindow close];
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (NSString*) userDataUrl
-{
- return [NSString stringWithFormat:@"http://www.reddit.com/user/%@/about.json", self.prefs.name];
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (IBAction) openMailbox:(id)sender
-{
-#pragma unused(sender)
-
- // Why didn't I do this with enums?
- if (NO == hasModMail)
- {
- if (self.currentIcon == self.noMailIcon)
- {
- system("open http://www.reddit.com/message/inbox/ &");
- }
- else
- {
- system("open http://www.reddit.com/message/unread/ &");
- }
- }
- else
- {
- system("open http://www.reddit.com/message/moderator/ &");
- hasModMail = NO;
- }
-
- // Lets assume they don't want to see the modified envelope after they do this or wait for the next check.
- self.currentIcon = self.noMailIcon;
- [self setMessageStatus: self.currentIcon];
-
- [[NSUserNotificationCenter defaultUserNotificationCenter] removeAllDeliveredNotifications];
-
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (IBAction) updateMenuItemClicked: (id)sender
-{
- (void)sender;
-
- self.appUpdateResultTF.stringValue = @"";
- self.update.hidden = YES;
- self.about.hidden = NO;
- system("open http://www.voidref.com/Site/Orangered.dmg &");
- self.noMailIcon = BlackEnvelope;
-
- // We can do this because we probably will not be checking again.
- [self updateStatus:nil];
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (IBAction) checkForAppUpdate: (id)sender
-{
-#pragma unused(sender)
-
- if (NO == self.prefs.autoUpdateCheck) return;
-
- [self.appUpdateCheckProgress startAnimation:nil];
- [self.appUpdateCheckProgress setHidden:NO];
- self.appUpdateResultTF.stringValue = @"Checking for update...";
-
- NSURL* url = [NSURL URLWithString:@"http://www.voidref.com/orangered/orangered_version.txt"];
-
- NSURLRequest* request = [NSURLRequest requestWithURL:url
- cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
- timeoutInterval:self.prefs.timeout];
-
- appUpdateConnection = [[NSURLConnection alloc] initWithRequest:request
- delegate:self];
- if (nil != appUpdateConnection)
- {
- if (nil == appUpdateData)
- {
- appUpdateData = [NSMutableData data];
- }
- }
- else
- {
- // Is there a way to find the exact error?
- self.appUpdateResultTF.stringValue = @"Could not estabilsh connection to Orangered! update server.";
- }
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (void) parseAppUpdateResult
-{
- NSString* checkResult = [[NSString alloc] initWithData:appUpdateData
- encoding:NSUTF8StringEncoding];
-
- checkResult = [checkResult stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
- NSString* currentVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
- if (([checkResult compare:currentVersion] != NSOrderedSame) &&
- (NO == [checkResult hasPrefix:@"<"]) // This happens on website error
- )
- {
- self.appUpdateResultTF.stringValue = [NSString stringWithFormat:@"New version available: %@", checkResult];
- self.update.hidden = NO;
- self.update.title = [NSString stringWithFormat:@"Get Update (%@)", checkResult];
- self.noMailIcon = BlueEnvelope;
- [self setMessageStatus: self.noMailIcon];
- self.about.hidden = YES;
- }
- else
- {
- self.appUpdateResultTF.stringValue = [NSString stringWithFormat:@"Orangered! is up to date, Version: %@", currentVersion];
- }
-
- [self.appUpdateCheckProgress stopAnimation:nil];
- [self.appUpdateCheckProgress setHidden:YES];
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (IBAction) automaticCheckForUpdateClicked: (id)sender
-{
- NSButton *btn = (NSButton*)sender;
- self.prefs.autoUpdateCheck = (btn.state == NSOnState);
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (IBAction) loadAtStartupClicked: (id)sender
-{
- NSButton *btn = (NSButton*)sender;
- self.prefs.openAtLogin = (btn.state == NSOnState);
- [self setLoadAtStartup];
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (IBAction) showAboutWindow: (id)sender
-{
-#pragma unused(sender)
- [self.logoTF setHidden:NO];
- [self.sloganTF setHidden:NO];
- [self.versionTF setHidden:NO];
- [self.aboutEnvelope setHidden:NO];
- [self.creditsTF setHidden:YES];
-
- // open window and force to the front
- [NSApp activateIgnoringOtherApps:YES];
- [_aboutWindow makeKeyAndOrderFront:nil];
- [_aboutWindow orderFrontRegardless];
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (IBAction) showAboutButtonClicked: (id)sender
-{
- OrangeLog(@"Button: %@", [sender title]);
-
- NSString* url = nil;
- switch ([sender tag])
- {
- case 0:
- url = @"http://www.voidref.com/orangered/Orangered!.html";
- break;
-
- case 1:
- url = @"http://www.reddit.com/r/Orangered_app/";
- break;
-
- case 2:
- url = @"http://www.github.com/voidref/orangered/";
- break;
- }
-
- if (nil != url)
- {
- NSString* command = [NSString stringWithFormat:@"open %@ &", url];
- system([command UTF8String]);
- }
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (IBAction) aboutEnvelopeClicked: (id)sender
-{
-#pragma unused(sender)
- [self.logoTF setHidden:YES];
- [self.sloganTF setHidden:YES];
- [self.versionTF setHidden:YES];
- [self.aboutEnvelope setHidden:YES];
- [self.creditsTF setHidden:NO];
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (void) setLoadAtStartup
-{
- BOOL exists = NO;
- NSURL* thePath = [[NSBundle mainBundle] bundleURL];
-
- LSSharedFileListRef loginItems = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
-
- if (loginItems)
- {
- UInt32 seedValue = 0;
- CFArrayRef loginItemsArrayRef = LSSharedFileListCopySnapshot(loginItems, &seedValue);
- NSArray* loginItemsArray = (__bridge NSArray *)loginItemsArrayRef;
-
- LSSharedFileListItemRef removeItem = NULL;
-
- for (id item in loginItemsArray)
- {
- LSSharedFileListItemRef itemRef = (__bridge LSSharedFileListItemRef)item;
- CFURLRef URL = NULL;
-
- if (LSSharedFileListItemResolve(itemRef, 0, &URL, NULL) == noErr)
- {
- if ([[[(__bridge NSURL *)URL path] lastPathComponent] isEqualToString: [[thePath path] lastPathComponent]])
- {
- exists = YES;
- CFRelease(URL);
- removeItem = (__bridge LSSharedFileListItemRef)item;
- break;
- }
- }
- }
-
- CFRelease(loginItemsArrayRef);
-
-
- BOOL add = self.prefs.openAtLogin;
- if (add && !exists)
- {
- OrangeLog1(@"Adding to startup items.");
- LSSharedFileListItemRef item = LSSharedFileListInsertItemURL(loginItems,
- kLSSharedFileListItemBeforeFirst,
- NULL,
- NULL,
- (__bridge CFURLRef)thePath,
- NULL,
- NULL);
-
- if (item) CFRelease(item);
- }
- else if (!add && exists)
- {
- OrangeLog1(@"Removing from startup items.");
- LSSharedFileListItemRemove(loginItems, removeItem);
- }
-
- CFRelease(loginItems);
- }
-}
-
-
-// --------------------------------------------------------------------------------------------------------------------
-- (void) setMessageStatus: (NSString*) imageName
-{
- OrangeLog(@"Setting Status image to: %@", imageName);
- self.status.image = [NSImage imageNamed:imageName];
-}
-
-
-#pragma mark NSURLConnection delegate interface
-// --------------------------------------------------------------------------------------------------------------------
-- (void) connection: (NSURLConnection *)connection
- didFailWithError: (NSError *)error
-{
- OrangeLog(@"connection Error: %@", error);
-
- [self.loginProgress stopAnimation:nil];
- [self.loginProgress setHidden:YES];
-
- if ( connection == statusConnection)
- {
- self.loginerror.stringValue = [NSString stringWithFormat:@"Unable to retrieve status: %@", [error localizedDescription]];
- [self setMessageStatus: GreyEnvelope];
- }
- else if (connection == loginConnection)
- {
- self.loginerror.stringValue = [NSString stringWithFormat:@"Unable to login: %@", [error localizedDescription]];
- [self setMessageStatus: GreyEnvelope];
- }
- else if (connection == appUpdateConnection)
- {
- // I bet nobody cares
- }
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (void) connection: (NSURLConnection *)connection
- didReceiveData: (NSData *)data
-{
- if (connection == statusConnection)
- {
- [statusData appendData:data];
- }
- else if (connection == loginConnection)
- {
- [loginData appendData:data];
- }
- else if (connection == appUpdateConnection)
- {
- [appUpdateData appendData:data];
- }
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (void) connection: (NSURLConnection *)connection
- didReceiveResponse: (NSURLResponse *)response
-{
- OrangeLog(@"Got response for %@", [[response URL] path]);
-
- // Here we zero out the data to prepare it to accept new data
- if (connection == statusConnection)
- {
- statusData.length = 0;
- }
- else if (connection == loginConnection)
- {
- // Is casting this way the right thing to do?
- [self parseLogin:(NSHTTPURLResponse*)response];
- loginData.length = 0;
- }
- else if (connection == appUpdateConnection)
- {
- appUpdateData.length = 0;
- }
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (void) connection: (NSURLConnection *)connection
- didSendBodyData: (NSInteger)bytesWritten
- totalBytesWritten: (NSInteger)totalBytesWritten
- totalBytesExpectedToWrite: (NSInteger)totalBytesExpectedToWrite
-{
-#pragma unused(connection, bytesWritten)
-
- if (totalBytesWritten != totalBytesExpectedToWrite)
- {
- [self.loginProgress stopAnimation:nil];
- [self.loginProgress setHidden:YES];
-
- self.loginerror.stringValue = @"Could not complete login request, connection severed (I think).";
- }
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (void) connectionDidFinishLoading: (NSURLConnection *)connection
-{
- if ( connection == statusConnection)
- {
- [self parseStatus];
- }
- else if (connection == loginConnection)
- {
- NSString* output = [[NSString alloc] initWithData:loginData
- encoding:NSASCIIStringEncoding];
- OrangeLog(@"Data result: %@", output);
-
- if ([output rangeOfString:@"WRONG_PASSWORD"].location != NSNotFound)
- {
- self.loginerror.stringValue = @"Could not log in: Wrong password.";
- }
- else
- {
- // hmmm.
- }
- }
- else if (connection == appUpdateConnection)
- {
- [self parseAppUpdateResult];
- }
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (void)userNotificationCenter:(NSUserNotificationCenter *)center_
- didActivateNotification:(NSUserNotification *)notification_
-{
-#pragma unused(center_)
-#pragma unused(notification_)
-
- [self openMailbox:self];
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-// --------------------------------------------------------------------------------------------------------------------
-
-
-@end
diff --git a/OrangeredEnvelope.png b/OrangeredEnvelope.png
deleted file mode 100644
index 213ec32..0000000
Binary files a/OrangeredEnvelope.png and /dev/null differ
diff --git a/OrangeredEnvelope@2x.png b/OrangeredEnvelope@2x.png
deleted file mode 100644
index 213ec32..0000000
Binary files a/OrangeredEnvelope@2x.png and /dev/null differ
diff --git a/Orangered_Prefix.pch b/Orangered_Prefix.pch
deleted file mode 100644
index b66f239..0000000
--- a/Orangered_Prefix.pch
+++ /dev/null
@@ -1,7 +0,0 @@
-//
-// Prefix header for all source files of the 'Orangered' target in the 'Orangered' project
-//
-
-#ifdef __OBJC__
- @import Cocoa;
-#endif
diff --git a/README b/README
deleted file mode 100644
index 39c591f..0000000
--- a/README
+++ /dev/null
@@ -1,5 +0,0 @@
-This small application is designed to sit up in your Mac OS X menu bar and poll every 60 seconds to see if you have any reddit.com messages awaiting your feverent attention. For the reddit obsessed.
-
-Please see todo.txt to see what's going on.
-
-
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d2e3b53
--- /dev/null
+++ b/README.md
@@ -0,0 +1,38 @@
+
+# Orangered!
+
+This small application is designed to sit up in your Mac OS X menu bar and poll every 60 seconds to see if you have any reddit.com messages awaiting your feverent attention.
+
+For the reddit obsessed.
+
+# Ufo Colors
+
+ * Outline : Not logged in
+ * Filled : Logged in, no messages
+ * Orange Canopy: You have a message! Make with the clicking!
+ * Magenta Canopy: Mod mail, look at you, Mr. Importante!
+
+
+# Special Thanks to the following redditors:
+ * Traviscat
+ * ashleyw
+ * Condawg
+ * dawnerd
+ * despideme
+ * derekaw
+ * EthicalReasoning
+ * giftedmunchkin
+ * kevinhoagland
+ * loggedout
+ * polyGone
+ * RamenStein
+ * shinratdr
+ * sporadicmonster
+ * pkamb
+
+# Features
+
+This application is deliverately as featureless as possible, if you are looking for a commercially supported expierence, you may look at Peter Kamb's excellent Orangered notifier for Reddit: https://itunes.apple.com/us/app/orangered-notifier-for-reddit/id468366517?mt=12
+
+
+
diff --git a/bg.png b/bg.png
deleted file mode 100644
index e47a9ba..0000000
Binary files a/bg.png and /dev/null differ
diff --git a/main.m b/main.m
deleted file mode 100644
index 5a8be7e..0000000
--- a/main.m
+++ /dev/null
@@ -1,12 +0,0 @@
-//
-// main.m
-// Orangered
-//
-// Created by Alan Westbrook on 6/16/10.
-// Copyright 2010 __MyCompanyName__. All rights reserved.
-//
-
-int main(int argc, char *argv[])
-{
- return NSApplicationMain(argc, (const char **) argv);
-}
diff --git a/modmail.png b/modmail.png
deleted file mode 100644
index e9d28f5..0000000
Binary files a/modmail.png and /dev/null differ
diff --git a/modmailgrey.png b/modmailgrey.png
deleted file mode 100644
index 5129cae..0000000
Binary files a/modmailgrey.png and /dev/null differ
diff --git a/o-mail.png b/o-mail.png
deleted file mode 100644
index 32f9e84..0000000
Binary files a/o-mail.png and /dev/null differ
diff --git a/prefs.h b/prefs.h
deleted file mode 100644
index 09ae18f..0000000
--- a/prefs.h
+++ /dev/null
@@ -1,23 +0,0 @@
-//
-// prefs.h
-// Orangered
-//
-// Created by Alan Westbrook on 6/26/10.
-// Copyright 2010 Voidref Software. All rights reserved.
-//
-
-
-@interface Prefs : NSObject
-
-@property (strong,nonatomic) NSString* password;
-@property (strong,nonatomic) NSString* name;
-@property (nonatomic) BOOL savePassword;
-@property (nonatomic) BOOL openAtLogin;
-@property (nonatomic) BOOL autoUpdateCheck;
-@property (nonatomic) BOOL logDiagnostics;
-@property (nonatomic) NSInteger timeout;
-@property (nonatomic) NSInteger redditCheckInterval;
-
-- (id) init;
-
-@end
diff --git a/prefs.m b/prefs.m
deleted file mode 100644
index e29a550..0000000
--- a/prefs.m
+++ /dev/null
@@ -1,206 +0,0 @@
-//
-// prefs.m
-// Orangered
-//
-// Created by Alan Westbrook on 6/26/10.
-// Copyright 2010 Voidref Software. All rights reserved.
-//
-
-#import "prefs.h"
-
-@interface Prefs()
-{
- NSString *_password;
-}
-
-@property (strong) NSUserDefaults* settings;
-
-@end
-
-@implementation Prefs
-
-static NSString* PasswordKey = @"password";
-static NSString* UserNameKey = @"username";
-static NSString* SavePassKey = @"save password";
-static NSString* OpenAtLoginKey = @"open at login";
-static NSString* AutoUpdateKey = @"auto update check";
-static NSString* CheckFreqKey = @"reddit check frequency";
-static NSString* TimeoutKey = @"network timeout";
-static NSString* LogDiagnosticsKey = @"Log Diagnostics";
-static const char* ServiceName = "Orangered!";
-
-
-// --------------------------------------------------------------------------------------------------------------------
-- (id) init
-{
- self = [super init];
- if (nil != self)
- {
- self.settings = [NSUserDefaults standardUserDefaults];
- self.name = [self.settings stringForKey:UserNameKey];
- self.savePassword = [self.settings boolForKey:SavePassKey];
-
- // Since we are async, we can let it try for a long time.
- self.timeout = 120;
-
- self.openAtLogin = [self.settings boolForKey:OpenAtLoginKey];
-
- self.redditCheckInterval = [self.settings integerForKey:CheckFreqKey];
- if (self.redditCheckInterval == 0) self.redditCheckInterval = 1;
-
- self.autoUpdateCheck = YES;
- if (nil != [self.settings objectForKey:AutoUpdateKey]) self.autoUpdateCheck = [self.settings boolForKey:AutoUpdateKey];
-
- self.openAtLogin = [self.settings boolForKey:OpenAtLoginKey];
- self.logDiagnostics = [self.settings boolForKey:LogDiagnosticsKey];
- }
-
- return self;
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-
-
-// --------------------------------------------------------------------------------------------------------------------
-- (OSStatus) storePasswordInKeychain
-{
- OSStatus status =
- SecKeychainAddGenericPassword (
- NULL, // default keychain
- 10, // length of service name
- ServiceName, // service name
- (UInt32)self.name.length, // length of account name
- [self.name UTF8String], // account name
- (UInt32)self.password.length, // length of password
- [self.password UTF8String], // pointer to password data
- NULL // the item reference
- );
- return status;
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (NSString *) getPasswordFromKeychain
-{
- SecKeychainItemRef ref = nil;
- UInt32 len = 0;
- void* data = NULL;
- OSStatus status =
- SecKeychainFindGenericPassword (
- NULL, // default keychain
- 10, // length of service name
- ServiceName, // service name
- (UInt32)self.name.length, // length of account name
- [self.name UTF8String], // account name
- &len, // length of password
- &data, // pointer to password data
- &ref // the item reference
- );
-
- NSString *result = nil;
- if (len > 0 && status == errSecSuccess )
- {
- result = [[NSString alloc] initWithBytes:data
- length:len
- encoding:NSUTF8StringEncoding];
-
- }
- else
- {
- result = self->_password;
- }
-
- if (NULL != data) SecKeychainItemFreeContent(NULL, data);
-
- return result;
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (NSString*) password
-{
- return [self getPasswordFromKeychain];
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (void) setPassword:(NSString*)value
-{
- _password = value;
-
- if (NSOnState == self.savePassword)
- {
- [self storePasswordInKeychain];
- }
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (NSString*) name
-{
- return [self.settings stringForKey:UserNameKey];
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (void) setName:(NSString*)value
-{
- [self.settings setObject:value
- forKey:UserNameKey];
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (void) setSavePassword:(BOOL)value
-{
- _savePassword = value;
-
- [self.settings setBool:value
- forKey:SavePassKey];
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (void) setOpenAtLogin:(BOOL)value
-{
- _openAtLogin = value;
-
- [self.settings setBool:value
- forKey:OpenAtLoginKey];
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (void) setAutoUpdateCheck:(BOOL)value
-{
- _autoUpdateCheck = value;
-
- [self.settings setBool:value
- forKey:AutoUpdateKey];
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (void) setLogDiagnostics:(BOOL)value
-{
- _logDiagnostics = value;
-
- [self.settings setBool:value
- forKey:LogDiagnosticsKey];
-}
-
-
-// --------------------------------------------------------------------------------------------------------------------
-- (void) setTimeout:(NSInteger)value
-{
- _timeout = value;
-
- [self.settings setInteger:value
- forKey:TimeoutKey];
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (NSInteger) redditCheckInterval
-{
- return [self.settings integerForKey:CheckFreqKey];
-}
-
-// --------------------------------------------------------------------------------------------------------------------
-- (void) setRedditCheckInterval:(NSInteger)value
-{
- [self.settings setInteger:value
- forKey:CheckFreqKey];
-}
-
-@end
diff --git a/todo.txt b/todo.txt
deleted file mode 100644
index 046276f..0000000
--- a/todo.txt
+++ /dev/null
@@ -1,95 +0,0 @@
-Todo for 1.0.1
-==============
-
- Todo for post 1.0.1
-=====================
- * Write a plugin api so people someone can write growl integration if they want. (I'll leave the GROWL ifdefs in the source until this happens)
- * Option to unobscure password
- * Options for sounds
- * Count of new messages
- * Display for karmas
- * http://sparkle.andymatuschak.org/ integration
- * put app up on Bodega
- * Multiple account checking
- * Use system password input instead of my custom one. (Research this, is there such a thing?)
-
-=== Changes 1.2 -> 1.3
- * No longer shells out to curl for reddit related networking
- * System wide proxy is respected.
- * Unorangereds icon when Open Mailbox is invoked.
- * Attempt to re-login on every update attempt.
-
-=== Changes a3 -> a4
- * reset error message in login window when appropriate
- * Retain blue envelope for no-new-mail until application is updated.
- * Inform user if the wrong password has been entered.
- * Inform user that they must enter a user namd AND password in order to log in.
- * Now works for 32 and 64 bit mode on Intel.
- * Obscure password input field
-
-=== Changes a4 - a5
- * Added Highlight image so Status Item enwhitens when selected.
- * Login window now shows up in all spaces.
- * Centralize all option values to a single collection class
- * Async networking.
- * Login spinner.
- * Source is now on github (http://github.com/voidref/orangered)
-
-=== a5 - b1
- * How about we actually save the prefs out when they change, like name, etc...
- * Use version string from info.plist
- * Store password in keychain istead of plist
- * Change the mail icon to the 'has mod mail' icon (alienhead) when you have mod mail and make the inbox take you to the mod mail inbox
- * Remove dropshadow from Login Window as it was causing the high perf video card to activate (core animation) and the 'g' descender was getting cut off anyway and looking lame.
- * Convert app update check to async.
- * Add Orangered! to start a login automatically.
- * Option to open at OS Login (Login Items).
- * Option for check frequency.
- * Option for manual app update check vs automatic
- * Add manual check for update
-
-=== b1 - b2
- * Increased timeout for network connections.
- * Fix logic error on first run, now opens Login window if there is no user name.
- * Fix default value of check interval from 1 hour to 1 minute
- * Fix auto update check option not functioning
- * Add feedback in the prefs window when manually checking for new version.
- * Fix about window not being topmost when invoked / New About Window
- * Option for logging diagnostics
-
-=== b2 - rc1
- * Fix update check result when there is no update
- * Force all windows to be focused when invoking
- * Add Credits 'easter egg' (click on envelope in about window)
- * Create a DMG based distribution
- * Change download to get dmg instead of zip
-
-=== rc1 - 1
- * Changed version number
-
-=== 1.0 - 1.0.1
- * Fix app update check bug.
- * Keyboard accelearators for window closing (Command + W)
- * Updated version info display to show "Version X" instead of just "X"
- * Change app update checker to 1 time per day instead of being based on how often your status is checked.
- * Updated app icon, lets hope it's ok with reddit, it may be infringing...
-
-=== 1.0.1 - X
- * Upddate login and about look. (Helevetica Neue / watermark graphics)
-
-=== 1.0.2
- * Fix moderator message bug (http://www.reddit.com/r/Orangered_app/comments/ds7ru/orangeredbug_after_receiving_a_moderator_message/)
- * Change no new message mailbox opening to all messages instead of new messages
- - Thanks to Wizpig64 for both the aforementioned changes
- * Fix bug where clicking the console log option would also toggle the start at login option.
-
-=== 1.0.3
- * Update version info get place to be voidref.com/orangered/orangered_version.txt
- * Fix update get when there's no site to get the version from.
- * Fix the goto site button to go to voidref.com/orangered/Orangered!.html
- * Updated the todo.txt (famously misnamed at this point) with this section
-
-=== 1.0.4
- * Apparently I am an idiot, and missed the previous 1.0.3 release somehow.
- * Fixed check against version and removed whitespace that suddenly started showing up
-