Add copyright headers to all .cs files
[pdfmod:pdfmod.git] / src / PdfMod / Gui / Actions.cs
1 // Copyright (C) 2009 Novell, Inc.
2 // Copyright (C) 2009 Julien Rebetez
3 // Copyright (C) 2009 Łukasz Jernaś
4 // Copyright (C) 2009 Andreu Correa Casablanca
5 //
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19
20 using System;
21 using System.IO;
22 using System.Collections.Generic;
23 using System.Runtime.InteropServices;
24 using System.Linq;
25
26 using Mono.Unix;
27 using Gtk;
28
29 using Hyena;
30 using Hyena.Gui;
31
32 using PdfSharp.Pdf;
33
34 using PdfMod.Pdf;
35 using PdfMod.Pdf.Actions;
36
37 namespace PdfMod.Gui
38 {
39     public class Actions : HyenaActionGroup
40     {
41         private Client app;
42         private UndoManager undo_manager;
43         private const string WIKI_URL = "http://live.gnome.org/PdfMod";
44
45         private static string [] require_doc_actions = new string[] {
46             "Save", "SaveAs", "Properties", "Undo", "Redo", "ZoomFit", "OpenInViewer",
47             "SelectAll", "SelectEvens", "SelectOdds", "SelectMatching", "InsertFrom", "ExportImages"
48         };
49
50         private static string [] require_page_actions = new string[] {
51             "Remove", "Extract", "RotateRight", "RotateLeft"
52         };
53
54         public UndoManager UndoManager {
55             get { return undo_manager; }
56         }
57
58         public Actions (Client app, ActionManager action_manager) : base (action_manager, "Global")
59         {
60             this.app = app;
61             undo_manager = new UndoManager ();
62
63             AddImportant (
64                 new ActionEntry ("Open",   Gtk.Stock.Open,   null, "<control>O", Catalog.GetString ("Open a document"), OnOpen),
65                 new ActionEntry ("InsertFrom", Gtk.Stock.Add, Catalog.GetString("_Insert From..."), null, Catalog.GetString("Insert pages from another document"), OnInsertFrom),
66                 new ActionEntry ("Save",   Gtk.Stock.Save,   null, "<control>S", Catalog.GetString ("Save changes to this document, overwriting the existing file"), OnSave),
67                 new ActionEntry ("SaveAs", Gtk.Stock.SaveAs, null, "<control><shift>S", Catalog.GetString ("Save this document to a new file"), OnSaveAs),
68
69                 new ActionEntry ("FileMenu", null, Catalog.GetString ("_File"), null, null, null),
70                 new ActionEntry ("RecentMenu", null, Catalog.GetString ("Recent _Files"), null, null, null),
71                 new ActionEntry ("Close", Gtk.Stock.Close, null, "<control>W", null, OnClose),
72                 new ActionEntry ("Remove", Gtk.Stock.Remove, null, "Delete", null, OnRemove),
73                 new ActionEntry ("Extract", Gtk.Stock.New, null, null, null, OnExtractPages),
74                 new ActionEntry ("RotateRight", null, Catalog.GetString ("Rotate Right"), "bracketright", Catalog.GetString ("Rotate right"), OnRotateRight),
75                 new ActionEntry ("RotateLeft", null, Catalog.GetString ("Rotate Left"), "bracketleft", Catalog.GetString ("Rotate left"), OnRotateLeft),
76                 new ActionEntry ("ExportImages", null, Catalog.GetString ("Export Images"), null, Catalog.GetString ("Save all images in this document to a new folder"), OnExportImages),
77
78                 new ActionEntry ("EditMenu", null, Catalog.GetString ("_Edit"), null, null, null),
79                 new ActionEntry ("SelectAll", Stock.SelectAll, null, "<control>A", null, OnSelectAll),
80                 new ActionEntry ("SelectEvens", null, Catalog.GetString ("Select Even Pages"), null, null, OnSelectEvens),
81                 new ActionEntry ("SelectOdds", null, Catalog.GetString ("Select Odd Pages"), null, null, OnSelectOdds),
82                 new ActionEntry ("SelectMatching", null, Catalog.GetString ("Select Matching..."), "<control>F", null, OnSelectMatching),
83                 new ActionEntry ("Undo", Stock.Undo, null, "<control>z", null, OnUndo),
84                 new ActionEntry ("Redo", Stock.Redo, null, "<control>y", null, OnRedo),
85
86                 new ActionEntry ("ViewMenu", null, Catalog.GetString ("_View"), null, null, null),
87                 new ActionEntry ("ZoomIn", Stock.ZoomIn, null, "<control>plus", null, OnZoomIn),
88                 new ActionEntry ("ZoomOut", Stock.ZoomOut, null, "<control>minus", null, OnZoomOut),
89
90                 new ActionEntry ("HelpMenu", null, Catalog.GetString ("_Help"), null, null, null),
91                 new ActionEntry ("Help", Stock.Help, Catalog.GetString ("_Contents"), "F1", null, OnHelp),
92                 new ActionEntry ("About", Stock.About, null, null, null, OnAbout),
93
94                 new ActionEntry ("PageContextMenu", null, "", null, null, OnPageContextMenu),
95
96                 new ActionEntry ("OpenInViewer", null, Catalog.GetString ("Open in Viewer"), "F5", Catalog.GetString ("Open in viewer"), OnOpenInViewer)
97             );
98
99             AddImportant (
100                 new ToggleActionEntry ("Properties", Stock.Properties, null, "<alt>Return", Catalog.GetString ("View and edit the title, keywords, and more for this document"), OnProperties, false),
101                 new ToggleActionEntry ("ZoomFit", Stock.ZoomFit, null, "<control>0", null, OnZoomFit, true),
102                 new ToggleActionEntry ("ViewToolbar", null, Catalog.GetString ("Toolbar"), null, null, OnViewToolbar, Client.Configuration.ShowToolbar),
103                 new ToggleActionEntry ("FullScreenView", null, Catalog.GetString ("Fullscreen"), "F11", null, OnFullScreenView, false)
104             );
105
106             this["RotateRight"].IconName = "object-rotate-right";
107             this["RotateLeft"].IconName = "object-rotate-left";
108             this["ExportImages"].IconName = "image-x-generic";
109
110             // Don't show Help unless we can at least access some of the GNOME api
111             UpdateAction ("Help", false);
112             try {
113                 Gnome.Program.Get ();
114                 UpdateAction ("Help", true);
115             } catch {}
116
117             Update ();
118             app.IconView.SelectionChanged += OnChanged;
119             app.IconView.ZoomChanged += delegate { Update (); };
120             app.DocumentLoaded += (o, a) => {
121                 app.Document.Changed += () => Update ();
122                 Update ();
123             };
124             undo_manager.UndoChanged += OnChanged;
125
126             AddUiFromFile ("UIManager.xml");
127             Register ();
128
129             // Add additional menu item keybindings
130             var item = ActionManager.UIManager.GetWidget ("/MainMenu/ViewMenu/ZoomIn");
131             item.AddAccelerator ("activate", ActionManager.UIManager.AccelGroup, (uint) Gdk.Key.KP_Add, Gdk.ModifierType.ControlMask, Gtk.AccelFlags.Visible);
132             item.AddAccelerator ("activate", ActionManager.UIManager.AccelGroup, (uint) Gdk.Key.equal, Gdk.ModifierType.ControlMask, Gtk.AccelFlags.Visible);
133
134             item = ActionManager.UIManager.GetWidget ("/MainMenu/ViewMenu/ZoomOut");
135             item.AddAccelerator ("activate", ActionManager.UIManager.AccelGroup, (uint) Gdk.Key.KP_Subtract, Gdk.ModifierType.ControlMask, Gtk.AccelFlags.Visible);
136             item.AddAccelerator ("activate", ActionManager.UIManager.AccelGroup, (uint) Gdk.Key.underscore, Gdk.ModifierType.ControlMask, Gtk.AccelFlags.Visible);
137
138             item = ActionManager.UIManager.GetWidget ("/MainMenu/FileMenu/Close");
139             item.AddAccelerator ("activate", ActionManager.UIManager.AccelGroup, (uint) Gdk.Key.q, Gdk.ModifierType.ControlMask, Gtk.AccelFlags.Visible);
140
141             // Set up recent documents menu
142             MenuItem recent_item = ActionManager.UIManager.GetWidget ("/MainMenu/FileMenu/RecentMenu") as MenuItem;
143             var recent_chooser_item = new RecentChooserMenu (RecentManager.Default) {
144                 Filter = new RecentFilter (),
145                 SortType = RecentSortType.Mru
146             };
147             recent_chooser_item.Filter.AddPattern ("*.pdf");
148             recent_chooser_item.ItemActivated += delegate {
149                 Client.RunIdle (delegate { app.LoadPath (recent_chooser_item.CurrentUri); });
150             };
151             recent_item.Submenu = recent_chooser_item;
152         }
153
154         private void OnChanged (object o, EventArgs args)
155         {
156             Update ();
157         }
158
159         private void Update ()
160         {
161             bool have_doc = app.Document != null;
162             foreach (string action in require_doc_actions)
163                 UpdateAction (action, true, have_doc);
164
165             bool have_page = have_doc && app.IconView.SelectedItems.Length > 0;
166             foreach (string action in require_page_actions)
167                 UpdateAction (action, true, have_page);
168
169             UpdateAction ("Undo", true, have_doc && undo_manager.CanUndo);
170             UpdateAction ("Redo", true, have_doc && undo_manager.CanRedo);
171
172             var undo = undo_manager.UndoAction as IDescribedUndoAction;
173             this["Undo"].Label = undo == null
174                 ? Catalog.GetString ("_Undo")
175                 : String.Format (Catalog.GetString ("Undo {0}"), undo.Description);
176
177             var redo = undo_manager.RedoAction as IDescribedUndoAction;
178             this["Redo"].Label = redo == null
179                 ? Catalog.GetString ("_Redo")
180                 : String.Format (Catalog.GetString ("Redo {0}"), redo.Description);
181
182             UpdateActions (true, have_doc && app.Document.HasUnsavedChanged, "Save", "SaveAs");
183             UpdateAction ("ZoomIn", true, have_doc && app.IconView.CanZoomIn);
184             UpdateAction ("ZoomOut", true, have_doc && app.IconView.CanZoomOut);
185
186             int selection_count = app.IconView.SelectedItems.Length;
187             this["Remove"].Label = String.Format (Catalog.GetPluralString (
188                 "Remove Page", "Remove {0} Pages", selection_count),
189                 selection_count);
190             this["Remove"].Tooltip = String.Format (Catalog.GetPluralString (
191                 "Remove the selected page", "Remove the {0} selected pages", selection_count),
192                 selection_count);
193             this["Extract"].Label = String.Format (Catalog.GetPluralString (
194                 "Extract Page", "Extract {0} Pages", selection_count),
195                 selection_count);
196             this["Extract"].Tooltip = String.Format (Catalog.GetPluralString (
197                 "Extract the selected page", "Extract the {0} selected pages", selection_count),
198                 selection_count);
199         }
200
201 #region Action Handlers
202
203         private void OnOpen (object o, EventArgs args)
204         {
205             var chooser = new Gtk.FileChooserDialog (Catalog.GetString ("Select PDF"), app.Window, FileChooserAction.Open);
206             chooser.AddFilter (GtkUtilities.GetFileFilter (Catalog.GetString ("PDF Documents"), new string [] {"pdf"}));
207             chooser.AddFilter (GtkUtilities.GetFileFilter (Catalog.GetString ("All Files"), new string [] {"*"}));
208             chooser.SelectMultiple = false;
209             chooser.AddButton (Stock.Cancel, ResponseType.Cancel);
210             chooser.AddButton (Stock.Open, ResponseType.Ok);
211             chooser.DefaultResponse = ResponseType.Ok;
212             if (app.Document != null) {
213                 chooser.SetCurrentFolder (System.IO.Path.GetDirectoryName (app.Document.SuggestedSavePath));
214             } else {
215                 chooser.SetCurrentFolder (Client.Configuration.LastOpenFolder);
216             }
217
218             var response = chooser.Run ();
219             string filename = chooser.Filename;
220             chooser.Destroy ();
221
222             if (response == (int)ResponseType.Ok) {
223                 Client.RunIdle (delegate { app.LoadPath (filename); });
224             }
225         }
226
227         private void OnOpenInViewer (object o, EventArgs args)
228         {
229             System.Diagnostics.Process.Start (app.Document.CurrentStateUri);
230         }
231
232         private void OnFullScreenView (object o, EventArgs args)
233         {
234             bool fullscreen = (this["FullScreenView"] as ToggleAction).Active;
235
236             if (fullscreen) {
237                 app.Window.Fullscreen ();
238             } else {
239                 app.Window.Unfullscreen ();
240             }
241         }
242
243         private void OnInsertFrom (object o, EventArgs args)
244         {
245             var chooser = new Gtk.FileChooserDialog (Catalog.GetString ("Select PDF"), app.Window, FileChooserAction.Open);
246             chooser.AddFilter (GtkUtilities.GetFileFilter ("PDF Documents", new string [] {"pdf"}));
247             chooser.AddFilter (GtkUtilities.GetFileFilter (Catalog.GetString ("All Files"), new string [] {"*"}));
248             chooser.SelectMultiple = false;
249             chooser.SetCurrentFolder (System.IO.Path.GetDirectoryName (app.Document.SuggestedSavePath));
250             chooser.AddButton (Stock.Cancel, ResponseType.Cancel);
251             chooser.AddButton (Stock.Open, ResponseType.Ok);
252             chooser.DefaultResponse = ResponseType.Ok;
253
254             var response = chooser.Run ();
255             string filename = chooser.Filename;
256             chooser.Destroy();
257
258             if (response == (int)ResponseType.Ok) {
259                 app.Document.AddFromUri (new Uri (filename));
260             }
261         }
262
263         private void OnSave (object o, EventArgs args)
264         {
265             app.Document.Save (app.Document.SuggestedSavePath);
266             undo_manager.Clear ();
267         }
268
269         private void OnSaveAs (object o, EventArgs args)
270         {
271             var chooser = new Gtk.FileChooserDialog (Catalog.GetString ("Save as..."), app.Window, FileChooserAction.Save);
272             chooser.SelectMultiple = false;
273             chooser.DoOverwriteConfirmation = true;
274             chooser.AddButton (Stock.Cancel, ResponseType.Cancel);
275             chooser.AddButton (Stock.SaveAs, ResponseType.Ok);
276             chooser.AddFilter (GtkUtilities.GetFileFilter ("PDF Documents", new string [] {"pdf"}));
277             chooser.AddFilter (GtkUtilities.GetFileFilter (Catalog.GetString ("All Files"), new string [] {"*"}));
278             chooser.SetCurrentFolder (System.IO.Path.GetDirectoryName (app.Document.SuggestedSavePath));
279             chooser.CurrentName = System.IO.Path.GetFileName (app.Document.SuggestedSavePath);
280             chooser.DefaultResponse = ResponseType.Ok;
281
282             var response = chooser.Run ();
283             string filename = chooser.Filename;
284             chooser.Destroy ();
285
286             if (response == (int)ResponseType.Ok) {
287                 Log.DebugFormat ("Saving {0} to {1}", app.Document.Uri, filename);
288                 app.Document.Save (filename);
289                 undo_manager.Clear ();
290             }
291         }
292
293         private void OnProperties (object o, EventArgs args)
294         {
295             app.EditorBox.Visible = (this["Properties"] as ToggleAction).Active;
296             if (app.EditorBox.Visible) {
297                 app.EditorBox.GrabFocus ();
298             }
299         }
300
301         private void OnRemove (object o, EventArgs args)
302         {
303             var action = new RemoveAction (app.Document, app.IconView.SelectedPages);
304             action.Do ();
305             // Undo isn't working yet
306             //undo_manager.AddUndoAction (action);
307         }
308
309         private void OnExtractPages (object o, EventArgs args)
310         {
311             var doc = new PdfDocument ();
312             var pages = new List<Page> (app.IconView.SelectedPages);
313             foreach (var page in pages) {
314                 doc.AddPage (page.Pdf);
315             }
316
317             var path = Client.GetTmpFilename ();
318             doc.Save (path);
319             doc.Dispose ();
320
321             app.LoadPath (path, Path.Combine (
322                 Path.GetDirectoryName (app.Document.SuggestedSavePath),
323                 String.Format ("[{0}] {1}",
324                     GLib.Markup.EscapeText (Document.GetPageSummary (pages, 10)),
325                     Path.GetFileName (app.Document.SuggestedSavePath))
326             ));
327         }
328
329         private void OnExportImages (object o, EventArgs args)
330         {
331             var action = new ExportImagesAction (app.Document, app.Document.Pages);
332             if (action.ExportableImageCount == 0) {
333                 Log.Information ("Found zero exportable images in the selected pages");
334                 return;
335             }
336
337             var export_path_base = Path.Combine (
338                 Path.GetDirectoryName (app.Document.SuggestedSavePath),
339                 // Translators: This is used for creating a folder name, be careful!
340                 //String.Format (Catalog.GetString ("{0} - Images for {1}"), app.Document.Title ?? app.Document.Filename, GetPageSummary (pages, 10))
341                 app.Document.Title ?? System.IO.Path.GetFileNameWithoutExtension (app.Document.Filename)
342             );
343
344             var export_path = export_path_base;
345             int i = 1;
346             while (Directory.Exists (export_path) && i < 100) {
347                 export_path = String.Format ("{0} ({1})", export_path_base, i++);
348             }
349
350             try {
351                 Directory.CreateDirectory (export_path);
352             } catch (Exception e) {
353                 Hyena.Log.Exception (e);
354             }
355
356             action.Do (export_path);
357             System.Diagnostics.Process.Start (export_path);
358         }
359
360         private void OnUndo (object o, EventArgs args)
361         {
362             undo_manager.Undo ();
363         }
364
365         private void OnRedo (object o, EventArgs args)
366         {
367             undo_manager.Redo ();
368         }
369
370         [DllImport ("glib-2.0.dll")]
371         private static extern IntPtr g_get_language_names ();
372
373         private string CombinePaths (params string [] parts)
374         {
375             string path = parts[0];
376             for (int i = 1; i < parts.Length; i++) {
377                 path = System.IO.Path.Combine (path, parts[i]);
378             }
379             return path;
380         }
381
382         private void OnHelp (object o, EventArgs args)
383         {
384             bool shown = false;
385             try {
386                 IntPtr lang_ptr = g_get_language_names ();
387                 var langs = GLib.Marshaller.NullTermPtrToStringArray (lang_ptr, false);
388
389                 string help_dir = null;
390                 foreach (var dir in new string [] { Core.Defines.PREFIX + "/share/gnome/help/", "/usr/local/share/gnome/help/", "docs/" }) {
391                     help_dir = dir;
392                     if (System.IO.Directory.Exists (dir + "pdfmod/")) {
393                         break;
394                     }
395                 }
396
397                 foreach (var lang in langs) {
398                     var help_path = CombinePaths (help_dir, "pdfmod", lang, "pdfmod.xml");
399                     if (System.IO.File.Exists (help_path)) {
400                         System.Diagnostics.Process.Start (String.Format ("ghelp://{0}", help_path));
401                         shown = true;
402                         break;
403                     }
404                 }
405             } catch (Exception e) {
406                 Hyena.Log.Exception ("Error opening help", e);
407             }
408
409             if (!shown) {
410                 var message_dialog = new Hyena.Widgets.HigMessageDialog (
411                     app.Window, DialogFlags.Modal, MessageType.Warning, ButtonsType.None,
412                     Catalog.GetString ("Error opening help"),
413                     Catalog.GetString ("Would you like to open PDF Mod's online documentation?")
414                 );
415                 message_dialog.AddButton (Stock.No, ResponseType.No, false);
416                 message_dialog.AddButton (Stock.Yes, ResponseType.Yes, true);
417
418                 var response = (ResponseType) message_dialog.Run ();
419                 message_dialog.Destroy ();
420                 if (response == ResponseType.Yes) {
421                     System.Diagnostics.Process.Start (WIKI_URL);
422                 }
423             }
424         }
425
426         private void OnAbout (object o, EventArgs args)
427         {
428             Gtk.AboutDialog.SetUrlHook ((dlg, link) => { System.Diagnostics.Process.Start (link); });
429
430             var dialog = new Gtk.AboutDialog () {
431                 ProgramName = "PDF Mod",
432                 Version = Core.Defines.VERSION,
433                 Website = WIKI_URL,
434                 WebsiteLabel = Catalog.GetString ("Visit Website"),
435                 Authors = new string [] {
436                     Catalog.GetString ("Primary Development:"),
437                     "\tGabriel Burt",
438                     "",
439                     Catalog.GetString ("Contributors:"),
440                     "\tSandy Armstrong",
441                     "\tAaron Bockover",
442                     "\tOlivier Le Thanh Duong",
443                     "\tJulien Rebetez",
444                     "\tIgor Vatavuk",
445                     "\tBertrand Lorentz",
446                     "\tMichael McKinley",
447                     "\tŁukasz Jernaś",
448                     "\tRomain Tartière",
449                     "\tRobert Dyer",
450                     "\tAndreu Correa Casablanca"
451                 },
452                 Documenters = new string [] { "Gabriel Burt" },
453                 Artists = new string [] { "Kalle Persson" },
454                 Copyright = "Copyright 2009 Novell Inc.",
455                 TranslatorCredits = Catalog.GetString ("translator-credits")
456             };
457
458             try {
459                 dialog.Logo = Gtk.IconTheme.Default.LoadIcon ("pdfmod", 256, 0);
460             } catch {}
461
462             string [] license_paths = new string [] {
463                 Core.Defines.PREFIX + "/share/doc/packages/pdfmod/COPYING",
464                 "/usr/local/share/doc/packages/pdfmod/COPYING",
465                 "COPYING"
466             };
467
468             foreach (var path in license_paths) {
469                 try {
470                     dialog.License = System.IO.File.ReadAllText (path);
471                     break;
472                 } catch {}
473             }
474
475             dialog.Run ();
476             dialog.Destroy ();
477         }
478
479         private void OnPageContextMenu (object o, EventArgs args)
480         {
481             ShowContextMenu ("/PageContextMenu");
482         }
483
484         private void OnSelectAll (object o, EventArgs args)
485         {
486             app.IconView.SetPageSelectionMode (PageSelectionMode.All);
487         }
488
489         private void OnSelectEvens (object o, EventArgs args)
490         {
491             app.IconView.SetPageSelectionMode (PageSelectionMode.Evens);
492         }
493
494         private void OnSelectOdds (object o, EventArgs args)
495         {
496             app.IconView.SetPageSelectionMode (PageSelectionMode.Odds);
497         }
498
499         private void OnSelectMatching (object o, EventArgs args)
500         {
501             app.ToggleMatchQuery ();
502         }
503
504         private void OnZoomIn (object o, EventArgs args)
505         {
506             app.IconView.Zoom (10);
507         }
508
509         private void OnZoomOut (object o, EventArgs args)
510         {
511             app.IconView.Zoom (-10);
512         }
513
514         private void OnZoomFit (object o, EventArgs args)
515         {
516             app.IconView.ZoomFit ();
517         }
518
519         private void OnViewToolbar (object o, EventArgs args)
520         {
521             bool show = (this["ViewToolbar"] as ToggleAction).Active;
522             Client.Configuration.ShowToolbar = app.HeaderToolbar.Visible = show;
523         }
524
525         private void OnRotateRight (object o, EventArgs args)
526         {
527             Rotate (90);
528         }
529
530         private void OnRotateLeft (object o, EventArgs args)
531         {
532             Rotate (-90);
533         }
534
535         private void Rotate (int degrees)
536         {
537             if (!(app.Window.Focus is Gtk.Entry)) {
538                 var action = new RotateAction (app.Document, app.IconView.SelectedPages, degrees);
539                 action.Do ();
540                 undo_manager.AddUndoAction (action);
541             }
542         }
543
544         private void OnClose (object o, EventArgs args)
545         {
546             app.Quit ();
547         }
548
549 #endregion
550
551     }
552 }