/* Copyright (c) 2007 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Ipc;
using Microsoft.Win32;
using Google.GData.Documents;
using Google.GData.Client;
namespace DocListUploader
{
///
/// The main form of the uploader, controls most of the service interaction.
/// It is not actually displayed to the user.
///
public partial class HiddenForm : Form
{
//A child form for the user to interact with.
private OptionsForm optionsForm;
//Keeps track of our logged in state.
public bool loggedIn = false;
//A connection with the DocList API.
private DocumentsService service;
//The name of the shell context menu option.
private const string KEY_NAME = "Send to Google Docs";
//Keeps track of if we've minimized the OptionsForm before.
private bool firstMinimize = true;
//The timer in milliseconds to display balloon tips.
private const int BALLOON_TIMER = 10000;
//The most recently uploaded document.
private DocumentEntry lastUploadEntry = null;
//Keeps track of if the last balloon tip was an upload complete message.
private bool lastToolTipWasUpload = false;
///
/// Constructor for the form.
///
public HiddenForm()
{
InitializeComponent();
optionsForm = new OptionsForm(this);
ListenForSuccessor();
}
///
/// The main entry point for the application.
///
[STAThread]
static void Main(string[] args)
{
bool firstInstance;
Mutex mutex = new Mutex(true, "Local\\DocListUploader", out firstInstance);
if (!firstInstance)
{
//If we have an argument, we were most likely called by
//the shell context menu
if (args.Length == 1)
{
NotifyPredecessor(args[0]);
}
return;
}
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new HiddenForm());
}
///
/// This is run by the first instance of the program in order
/// to accept information from later instances using the
/// .NET Framework's IPC mechanism.
///
public void ListenForSuccessor()
{
IpcServerChannel serverChannel = new IpcServerChannel("DocListUploader");
ChannelServices.RegisterChannel(serverChannel, false);
RemoteMessage remoteMessage = new RemoteMessage(this);
RemotingServices.Marshal(remoteMessage,"FirstInstance");
}
///
/// This is used by later instances of the program when
/// executed from the shell's context menu to pass along
/// the path of a file that will be uploaded. This message
/// is passed using the .NET Framework's IPC mechanism.
///
/// The file to be uploaded
public static void NotifyPredecessor(string file)
{
IpcClientChannel clientChannel = new IpcClientChannel();
ChannelServices.RegisterChannel(clientChannel, false);
RemoteMessage message = (RemoteMessage) Activator.GetObject(typeof(RemoteMessage), "ipc://DocListUploader/FirstInstance");
if (!message.Equals(null))
{
message.SendMessage(file);
}
}
///
/// Authenticates to Google servers
///
/// The user's username (e-mail)
/// The user's password
/// Thrown on invalid credentials.
public void Login(string username, string password)
{
if(loggedIn) {
throw new ApplicationException("Already logged in.");
}
try
{
service = new DocumentsService("DocListUploader");
((GDataRequestFactory) service.RequestFactory).KeepAlive = false;
service.setUserCredentials(username, password);
//force the service to authenticate
DocumentsListQuery query = new DocumentsListQuery();
query.NumberToRetrieve = 1;
service.Query(query);
loggedIn = true;
}
catch(AuthenticationException e)
{
loggedIn = false;
service = null;
throw e;
}
}
///
/// Logs the user out of Google Docs.
///
public void Logout()
{
loggedIn = false;
service = null;
}
///
/// Retrieves a list of documents from the server.
///
/// The list of documents as a DocumentsFeed.
public DocumentsFeed GetDocs()
{
DocumentsListQuery query = new DocumentsListQuery();
DocumentsFeed feed = service.Query(query);
return feed;
}
///
/// Some error handling around the file upload method. Used for handling files from the context menu.
///
/// The file to upload.
public void HandleUpload(string file)
{
if (!loggedIn)
{
MessageBox.Show("Please log in before uploading documents", "Upload Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
try
{
DocListNotifyIcon.ShowBalloonTip(BALLOON_TIMER, "Uploading", "Uploading " + file, ToolTipIcon.Info);
lastToolTipWasUpload = false;
UploadFile(file);
DocListNotifyIcon.ShowBalloonTip(BALLOON_TIMER, "Upload Success", "Successfully uploaded " + file, ToolTipIcon.Info);
lastToolTipWasUpload = true;
optionsForm.UpdateDocList();
}
catch (ArgumentException)
{
DocListNotifyIcon.ShowBalloonTip(BALLOON_TIMER, "Upload Error", "Error: unable to upload the file: '" + file + "'. It is not one of the valid types.", ToolTipIcon.Error);
lastToolTipWasUpload = false;
}
catch (Exception ex)
{
DocListNotifyIcon.ShowBalloonTip(BALLOON_TIMER, "Upload Error", "Error: unable to upload the file: '" + file + "'. " + ex.Message, ToolTipIcon.Error);
lastToolTipWasUpload = false;
}
}
///
/// Uploads the file to Google Docs
///
/// The file with path to upload
/// Thrown when user isn't logged in.
public void UploadFile(string fileName)
{
if (!loggedIn)
{
throw new ApplicationException("Need to be logged in to upload documents.");
}
else
{
lastUploadEntry = service.UploadDocument(fileName, null);
}
}
///
/// Called when the OptionsForm is minimized/hidden.
///
public void HandleMinimize()
{
if (firstMinimize)
{
DocListNotifyIcon.ShowBalloonTip(BALLOON_TIMER, "DocList Uploader", "Uploader now running in the system tray.", ToolTipIcon.Info);
lastToolTipWasUpload = false;
firstMinimize = false;
}
}
///
/// Adds the application to the shell's context menu by
/// creating a registry key.
///
public void Register()
{
RegistryKey key = Registry.CurrentUser.OpenSubKey("Software\\Classes\\*\\shell\\" + KEY_NAME + "\\command");
if (key == null)
{
key = Registry.CurrentUser.CreateSubKey("Software\\Classes\\*\\shell\\" + KEY_NAME + "\\command");
}
key.SetValue("", Application.ExecutablePath + " \"%1\"");
}
///
/// Removes the application from the shell's context menu
/// by deleting a registry key.
///
public void UnRegister()
{
RegistryKey key = Registry.CurrentUser.OpenSubKey("Software\\Classes\\*\\shell\\" + KEY_NAME);
if (key != null)
{
Registry.CurrentUser.DeleteSubKeyTree("Software\\Classes\\*\\shell\\" + KEY_NAME);
}
}
///
/// Displays the OptionsForm.
///
private void ShowOptionsWindow()
{
optionsForm.Show();
if (optionsForm.WindowState == FormWindowState.Minimized)
{
optionsForm.WindowState = FormWindowState.Normal;
}
}
private void DocListNotifyIcon_DoubleClick(object sender, EventArgs e)
{
optionsForm.Show();
}
private void CloseMenuItem_Click(object sender, EventArgs e)
{
Close();
}
private void HiddenForm_Load(object sender, EventArgs e)
{
Hide();
ShowOptionsWindow();
}
private void optionsToolStripMenuItem_Click(object sender, EventArgs e)
{
ShowOptionsWindow();
}
private void HiddenForm_FormClosing(object sender, FormClosingEventArgs e)
{
UnRegister();
}
private void DocListNotifyIcon_MouseClick(object sender, MouseEventArgs e)
{
//A single left click should be the same as a double-click.
if (e.Button == MouseButtons.Left)
{
ShowOptionsWindow();
}
}
private void DocListNotifyIcon_BalloonTipClicked(object sender, EventArgs e)
{
if (lastUploadEntry != null && lastToolTipWasUpload)
{
//display the last uploaded document in the browser.
try
{
Process.Start(lastUploadEntry.AlternateUri.ToString());
}
catch (Win32Exception)
{
//nothing is registered to handle URLs, so let's use IE!
Process.Start("IExplore.exe", lastUploadEntry.AlternateUri.ToString());
}
}
}
}
}