using System; using System.Collections; using System.Text; using Mono.Unix; using System.Runtime.InteropServices; namespace Tomboy { public class TomboyTray : Gtk.EventBox { NoteManager manager; Gtk.Tooltips tips; Gtk.Image image; PreferencesDialog prefs_dlg; SyncDialog sync_dlg; int icon_size_last = -1; static Gdk.Pixbuf note_icon; static TomboyTray () { // Cache this since we use it a lot. note_icon = GuiUtils.GetIcon ("tomboy", 16); } public TomboyTray (NoteManager manager) : base () { this.manager = manager; this.image = new Gtk.Image (); this.CanFocus = true; this.ButtonPressEvent += ButtonPress; this.Add (image); this.ShowAll (); string tip_text = Catalog.GetString ("Tomboy Notes"); if ((bool) Preferences.Get (Preferences.ENABLE_KEYBINDINGS)) { string shortcut = GConfKeybindingToAccel.GetShortcut ( Preferences.KEYBINDING_SHOW_NOTE_MENU); if (shortcut != null) tip_text += String.Format (" ({0})", shortcut); } tips = new Gtk.Tooltips (); tips.SetTip (this, tip_text, null); tips.Enable (); tips.Sink (); SetupDragAndDrop (); InitSomeStuff (); } void ButtonPress (object sender, Gtk.ButtonPressEventArgs args) { Gtk.Widget parent = (Gtk.Widget) sender; switch (args.Event.Button) { case 1: Gtk.Menu recent_menu = MakeRecentNotesMenu (parent); GuiUtils.PopupMenu (recent_menu, args.Event); args.RetVal = true; break; case 2: // Give some visual feedback Gtk.Drag.Highlight (this); args.RetVal = PastePrimaryClipboard (); Gtk.Drag.Unhighlight (this); break; } } void PrependTimestampedText (Note note, DateTime timestamp, string text) { NoteBuffer buffer = note.Buffer; StringBuilder insert_text = new StringBuilder (); insert_text.Append ("\n"); // initial newline string date_format = Catalog.GetString ("dddd, MMMM d, h:mm tt"); insert_text.Append (timestamp.ToString (date_format)); insert_text.Append ("\n"); // begin content insert_text.Append (text); insert_text.Append ("\n"); // trailing newline buffer.Undoer.FreezeUndo (); // Insert the date and list of links... Gtk.TextIter cursor = buffer.StartIter; cursor.ForwardLines (1); // skip title buffer.Insert (ref cursor, insert_text.ToString ()); // Make the date string a small font... cursor = buffer.StartIter; cursor.ForwardLines (2); // skip title & leading newline Gtk.TextIter end = cursor; end.ForwardToLineEnd (); // end of date buffer.ApplyTag ("datetime", cursor, end); // Select the text we've inserted (avoid trailing newline)... end = cursor; end.ForwardChars (insert_text.Length - 1); buffer.MoveMark (buffer.SelectionBound, cursor); buffer.MoveMark (buffer.InsertMark, end); buffer.Undoer.ThawUndo (); } bool PastePrimaryClipboard () { Gtk.Clipboard clip = GetClipboard (Gdk.Selection.Primary); string text = clip.WaitForText (); if (text == null || text.Trim() == string.Empty) return false; Note link_note = manager.Find (Catalog.GetString ("Start Here")); if (link_note == null) return false; link_note.Window.Present (); PrependTimestampedText (link_note, DateTime.Now, text); return true; } Gtk.Menu MakeRecentNotesMenu (Gtk.Widget parent) { Gtk.Menu menu = new Gtk.Menu (); menu.AttachToWidget (parent, GuiUtils.DetachMenu); bool enable_keybindings = (bool) Preferences.Get (Preferences.ENABLE_KEYBINDINGS); Gtk.ImageMenuItem item; item = new Gtk.ImageMenuItem (Catalog.GetString ("Create _New Note")); item.Image = new Gtk.Image (Gtk.Stock.New, Gtk.IconSize.Menu); item.Activated += CreateNewNote; menu.Append (item); if (enable_keybindings) GConfKeybindingToAccel.AddAccelerator ( item, Preferences.KEYBINDING_CREATE_NEW_NOTE); // FIXME: Pull this from GConf int min_size = 5; int max_size = 18; int list_size = 0; DateTime two_days_ago = DateTime.Today.AddDays (-2); // List the i most recently changed notes, and any // currently opened notes... foreach (Note note in manager.Notes) { if (note.IsSpecial) continue; if ((note.IsOpened && note.Window.IsMapped) || note.ChangeDate > two_days_ago || list_size < min_size) { item = MakeNoteMenuItem (note); menu.Append (item); if (++list_size == max_size) break; } } Note start = manager.Find (Catalog.GetString ("Start Here")); if (start != null) { item = MakeNoteMenuItem (start); menu.Append (item); if (enable_keybindings) GConfKeybindingToAccel.AddAccelerator ( item, Preferences.KEYBINDING_OPEN_START_HERE); } menu.Append (new Gtk.SeparatorMenuItem ()); item = new Gtk.ImageMenuItem (Catalog.GetString ("_Table of Contents")); item.Image = new Gtk.Image (Gtk.Stock.SortAscending, Gtk.IconSize.Menu); item.Activated += ViewRecentChanges; menu.Append (item); if (enable_keybindings) GConfKeybindingToAccel.AddAccelerator ( item, Preferences.KEYBINDING_OPEN_RECENT_CHANGES); item = new Gtk.ImageMenuItem (Catalog.GetString ("_Search Notes...")); item.Image = new Gtk.Image (Gtk.Stock.Find, Gtk.IconSize.Menu); item.Activated += SearchNotes; menu.Append (item); //item = new Gtk.ImageMenuItem (Catalog.GetString ("S_ynchronize...")); //item.Image = new Gtk.Image (Gtk.Stock.Find, Gtk.IconSize.Menu); //item.Activated += SyncNotes; //menu.Append (item); if (enable_keybindings) GConfKeybindingToAccel.AddAccelerator ( item, Preferences.KEYBINDING_OPEN_SEARCH); menu.ShowAll (); return menu; } Gtk.ImageMenuItem MakeNoteMenuItem (Note note) { string display_name = note.Title; if (note.IsNew) display_name += Catalog.GetString (" (new)"); display_name = FormatForLabel (display_name); Gtk.ImageMenuItem item = new Gtk.ImageMenuItem (display_name); item.Image = new Gtk.Image (note_icon); item.Data ["Note"] = note; item.Activated += ShowNote; return item; } string FormatForLabel (string name) { // Replace underscores ("_") with double-underscores ("__") // so Note menuitems are not created with mnemonics. return name.Replace ("_", "__"); } void ShowNote (object sender, EventArgs args) { Note note = (Note) ((Gtk.Widget) sender).Data ["Note"]; if (note != null) note.Window.Present (); } void CreateNewNote (object sender, EventArgs args) { try { Note new_note = manager.Create (); new_note.Window.Show (); } catch (Exception e) { HIGMessageDialog dialog = new HIGMessageDialog ( null, 0, Gtk.MessageType.Error, Gtk.ButtonsType.Ok, Catalog.GetString ("Cannot create new note"), e.Message); dialog.Run (); dialog.Destroy (); } } void SearchNotes (object sender, EventArgs args) { NoteFindDialog find_dialog = NoteFindDialog.GetInstance (manager); find_dialog.Present (); } void ViewRecentChanges (object sender, EventArgs args) { Gtk.Window recent = new NoteRecentChanges (manager); recent.Show (); } // Used by TomboyApplet to modify the icon background. public Gtk.Image Image { get { return image; } } public void ShowMenu (bool select_first_item) { Gtk.Menu recent_menu = MakeRecentNotesMenu (this); if (select_first_item) recent_menu.SelectFirst (false); GuiUtils.PopupMenu (recent_menu, null); } void OnPreferencesResponse (object sender, Gtk.ResponseArgs args) { ((Gtk.Widget) sender).Destroy (); prefs_dlg = null; } public void ShowPreferences () { if (prefs_dlg == null) { prefs_dlg = new PreferencesDialog (); prefs_dlg.Response += OnPreferencesResponse; } prefs_dlg.Present (); } void OnSyncResponse (object sender, Gtk.ResponseArgs args) { ((Gtk.Widget) sender).Destroy (); sync_dlg = null; } public void ShowSync () { //Console.WriteLine("showSync"); if (sync_dlg == null) { sync_dlg = new SyncDialog (); sync_dlg.Response += OnSyncResponse; } sync_dlg.Present (); } public void ShowAbout () { string [] authors = new string [] { "Alex Graveley " }; string [] documenters = new string [] { "Alex Graveley " }; string translators = Catalog.GetString ("translator-credits"); if (translators == "translator-credits") translators = null; Gtk.AboutDialog about = new Gtk.AboutDialog (); about.Name = "Tomboy"; about.Version = Defines.VERSION; about.Logo = GuiUtils.GetIcon ("tomboy", 48); about.Copyright = Catalog.GetString ("Copyright \xa9 2004-2006 Alex Graveley"); about.Comments = Catalog.GetString ("A simple and easy to use desktop " + "note-taking application."); about.Website = "http://www.beatniksoftware.com/tomboy"; about.WebsiteLabel = Catalog.GetString("Homepage & Donations"); about.Authors = authors; about.Documenters = documenters; about.TranslatorCredits = translators; about.IconName = "tomboy"; about.Run (); about.Destroy (); } // Support dropping text/uri-lists and _NETSCAPE_URLs currently. void SetupDragAndDrop () { Gtk.TargetEntry [] targets = new Gtk.TargetEntry [] { new Gtk.TargetEntry ("text/uri-list", 0, 0), new Gtk.TargetEntry ("_NETSCAPE_URL", 0, 0) }; Gtk.Drag.DestSet (this, Gtk.DestDefaults.All, targets, Gdk.DragAction.Copy); DragDataReceived += OnDragDataReceived; } // Pop up Start Here and insert dropped links, in the form: // Wednesday, December 8, 6:45 AM // http://luna/kwiki/index.cgi?AdelaideUniThoughts // http://www.beatniksoftware.com/blog/ // And select the inserted text. // // FIXME: Make undoable, make sure our date-sizing tag never "bleeds". // void OnDragDataReceived (object sender, Gtk.DragDataReceivedArgs args) { UriList uri_list = new UriList (args.SelectionData); if (uri_list.Count == 0) return; StringBuilder insert_text = new StringBuilder (); bool more_than_one = false; foreach (Uri uri in uri_list) { if (more_than_one) insert_text.Append ("\n"); if (uri.IsFile) insert_text.Append (uri.LocalPath); else insert_text.Append (uri.ToString ()); more_than_one = true; } Note link_note = manager.Find (Catalog.GetString ("Start Here")); if (link_note != null) { link_note.Window.Present (); PrependTimestampedText (link_note, DateTime.Now, insert_text.ToString ()); } } protected override void OnSizeAllocated (Gdk.Rectangle rect) { base.OnSizeAllocated (rect); int icon_size = Math.Min (rect.Height, rect.Width); if (icon_size_last != icon_size) { icon_size_last = icon_size; image.Pixbuf = GuiUtils.GetIcon (GetIconName (), icon_size); } } void ReloadIcon () { icon_size_last = -1; QueueResize (); } // NOTHING TO SEE HERE void InitSomeStuff () { manager.NoteDeleted += OnNoteDeletedUpdateIcon; manager.NoteAdded += OnNoteAddedUpdateIcon; manager.NoteRenamed += OnNoteRenamedUpdateIcon; } void OnNoteDeletedUpdateIcon (object sender, Note changed) { if (changed.Title == "Tintin") ReloadIcon (); } void OnNoteAddedUpdateIcon (object sender, Note changed) { if (changed.Title == "Tintin") ReloadIcon (); } void OnNoteRenamedUpdateIcon (Note note, string old_title) { if (note.Title == "Tintin" || old_title == "Tintin") ReloadIcon (); } string GetIconName () { if (manager.Find ("Tintin") != null) return "tintin"; return "tomboy"; } // GO ABOUT YOUR BUSINESS } // // This is a helper to take the XKeybinding string from GConf, and // convert it to a widget accelerator label, so note menu items can // display their global X keybinding. // // FIXME: It would be totally sweet to allow setting the accelerator // visually through the menuitem, and have the new value be stored in // GConf. // public class GConfKeybindingToAccel { static Gtk.AccelGroup accel_group; static GConfKeybindingToAccel () { accel_group = new Gtk.AccelGroup (); } public static string GetShortcut (string gconf_path) { try { string binding = (string) Preferences.Get (gconf_path); if (binding == null || binding == String.Empty || binding == "disabled") return null; binding = binding.Replace ("<", ""); binding = binding.Replace (">", "-"); return binding; } catch { return null; } } [DllImport("libtomboy")] static extern bool egg_accelerator_parse_virtual (string keystring, out uint keysym, out uint virtual_mods); [DllImport("libtomboy")] static extern void egg_keymap_resolve_virtual_modifiers (IntPtr keymap, uint virtual_mods, out Gdk.ModifierType real_mods); public static bool GetAccelKeys (string gconf_path, out uint keyval, out Gdk.ModifierType mods) { keyval = 0; mods = 0; try { string binding = (string) Preferences.Get (gconf_path); if (binding == null || binding == String.Empty || binding == "disabled") return false; uint virtual_mods = 0; if (!egg_accelerator_parse_virtual (binding, out keyval, out virtual_mods)) return false; Gdk.Keymap keymap = Gdk.Keymap.Default; egg_keymap_resolve_virtual_modifiers (keymap.Handle, virtual_mods, out mods); return true; } catch { return false; } } public static void AddAccelerator (Gtk.MenuItem item, string gconf_path) { uint keyval; Gdk.ModifierType mods; if (GetAccelKeys (gconf_path, out keyval, out mods)) item.AddAccelerator ("activate", accel_group, keyval, mods, Gtk.AccelFlags.Visible); } } }