main.vala 9.9 KB
Newer Older
1
/* Copyright 2009-2010 Yorba Foundation
2 3 4 5
 *
 * This software is licensed under the GNU LGPL (version 2.1 or later).
 * See the COPYING file in this distribution. 
 */
Jim Nelson's avatar
Jim Nelson committed
6

7
#if !NO_LIBUNIQUE
8

9 10 11 12 13
enum ShotwellCommand {
    // user-defined commands must be positive ints
    MOUNTED_CAMERA = 1
}

14 15
Unique.Response on_shotwell_message(Unique.App shotwell, int command, Unique.MessageData data, 
    uint timestamp) {
16 17 18
    Unique.Response response = Unique.Response.OK;
    
    switch (command) {
19
        case ShotwellCommand.MOUNTED_CAMERA:
20
            LibraryWindow.get_app().mounted_camera_shell_notification(data.get_text(), false);
21
        break;
22
        
23
        case Unique.Command.ACTIVATE:
24
            LibraryWindow.get_app().present_with_time(timestamp);
25
        break;
26
        
27
        default:
28 29
            // should be Unique.Response.PASSTHROUGH, but value isn't bound in vapi
            response = (Unique.Response) 4;
30
        break;
31 32 33 34
    }
    
    return response;
}
35
#endif
36

37 38
private Timer startup_timer = null;

39
void library_exec(string[] mounts) {
40 41 42 43
#if NO_LIBUNIQUE
    if (already_running())
        return;
#else
44
    // the library is single-instance; editing windows are one-process-per
45 46
    Unique.App shotwell = new Unique.App("org.yorba.shotwell", null);
    shotwell.add_command("MOUNTED_CAMERA", (int) ShotwellCommand.MOUNTED_CAMERA);
47
    shotwell.message_received.connect(on_shotwell_message);
48 49 50 51 52 53

    if (shotwell.is_running) {
        // send attached cameras & activate the window
        foreach (string mount in mounts) {
            Unique.MessageData data = new Unique.MessageData();
            data.set_text(mount, -1);
54
            
55 56 57 58 59
            shotwell.send_message((int) ShotwellCommand.MOUNTED_CAMERA, data);
        }
        
        shotwell.send_message((int) Unique.Command.ACTIVATE, null);
        
60
        // notified running app; this one exits
61 62
        return;
    }
63
#endif
64

65
    // initialize DatabaseTable before verification
66
    DatabaseTable.init(AppDirs.get_data_subdir("data").get_child("photo.db"));
67 68

    // validate the databases prior to using them
69
    message("Verifying database ...");
70
    string errormsg = null;
71
    string app_version;
72 73 74 75 76 77 78 79 80 81 82 83 84
    DatabaseVerifyResult result = verify_database(out app_version);
    switch (result) {
        case DatabaseVerifyResult.OK:
            // do nothing; no problems
        break;
        
        case DatabaseVerifyResult.FUTURE_VERSION:
            errormsg = _("Your photo library is not compatible with this version of Shotwell.  It appears it was created by Shotwell %s.  This version is %s.  Please use the latest version of Shotwell.").printf(
                app_version, Resources.APP_VERSION);
        break;
        
        case DatabaseVerifyResult.UPGRADE_ERROR:
            errormsg = _("Shotwell was unable to upgrade your photo library from version %s to %s.  For more information please check the Shotwell Wiki at %s").printf(
85
                app_version, Resources.APP_VERSION, Resources.get_help_url());
86 87 88 89 90 91 92 93 94 95 96 97 98 99
        break;
        
        case DatabaseVerifyResult.NO_UPGRADE_AVAILABLE:
            errormsg = _("Your photo library is not compatible with this version of Shotwell.  It appears it was created by Shotwell %s.  This version is %s.  Please clear your library by deleting %s and re-import your photos.").printf(
                app_version, Resources.APP_VERSION, AppDirs.get_data_dir().get_path());
        break;
        
        default:
            errormsg = _("Unknown error attempting to verify Shotwell's database: %d").printf(
                (int) result);
        break;
    }
    
    if (errormsg != null) {
100
        Gtk.MessageDialog dialog = new Gtk.MessageDialog(null, Gtk.DialogFlags.MODAL, 
101
            Gtk.MessageType.ERROR, Gtk.ButtonsType.OK, "%s", errormsg);
Jim Nelson's avatar
Jim Nelson committed
102
        dialog.title = Resources.APP_TITLE;
103 104
        dialog.run();
        dialog.destroy();
105 106 107 108
        
        DatabaseTable.terminate();
        
        return;
109
    }
110
    
111 112 113 114
    ProgressDialog progress_dialog = null;
    AggregateProgressMonitor aggregate_monitor = null;
    ProgressMonitor monitor = null;
    
115
    if (!no_startup_progress) {
116 117 118 119
        // only throw up a startup progress dialog if over a reasonable amount of objects ... multiplying
        // photos by two because there's two heavy-duty operations on them: creating the LibraryPhoto
        // objects and then populating the initial page with them.
        uint64 grand_total = (PhotoTable.get_instance().get_count() * 2) + EventTable.get_instance().get_count();
Jim Nelson's avatar
Jim Nelson committed
120
        if (grand_total > 20000) {
121
            progress_dialog = new ProgressDialog(null, _("Loading Shotwell"));
122 123 124 125 126 127
            progress_dialog.update_display_every(300);
            spin_event_loop();
            
            aggregate_monitor = new AggregateProgressMonitor(grand_total, progress_dialog.monitor);
            monitor = aggregate_monitor.monitor;
        }
128 129
    }
    
130
    ThumbnailCache.init();
131 132 133 134 135 136
    if (aggregate_monitor != null)
        aggregate_monitor.next_step("LibraryPhoto.init");
    LibraryPhoto.init(monitor);
    if (aggregate_monitor != null)
        aggregate_monitor.next_step("Event.init");
    Event.init(monitor);
137
    Tag.init();
138
    
139
    // create main library application window
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
    if (aggregate_monitor != null)
        aggregate_monitor.next_step("LibraryWindow");
    LibraryWindow library_window = new LibraryWindow(monitor);
    
    if (aggregate_monitor != null)
        aggregate_monitor.next_step("done");
    
    // destroy and tear down everything ... no need for them to stick around the lifetime of the
    // application
    
    monitor = null;
    aggregate_monitor = null;
    if (progress_dialog != null)
        progress_dialog.destroy();
    progress_dialog = null;
155 156

#if !NO_CAMERA    
157 158
    // report mount points
    foreach (string mount in mounts)
159
        library_window.mounted_camera_shell_notification(mount, true);
160
#endif
161 162 163

    library_window.show_all();
    
164 165 166 167 168 169 170 171
    if (Config.get_instance().get_show_welcome_dialog() &&
        LibraryPhoto.global.get_count() == 0) {
        WelcomeDialog welcome = new WelcomeDialog(library_window);
        Config.get_instance().set_show_welcome_dialog(welcome.execute());
    } else {
        Config.get_instance().set_show_welcome_dialog(false);
    }

172 173 174 175
    debug("%lf seconds to Gtk.main()", startup_timer.elapsed());
    
    Gtk.main();
    
176
    Tag.terminate();
177
    Event.terminate();
178
    LibraryPhoto.terminate();
179
    ThumbnailCache.terminate();
180

181
    DatabaseTable.terminate();
182 183
}

184
void editing_exec(string filename) {
185
    // init modules direct-editing relies on
186 187 188
    DatabaseTable.init(null);
    DirectPhoto.init();
    
189
    DirectWindow direct_window = new DirectWindow(File.new_for_commandline_arg(filename));
190 191
    direct_window.show_all();
    
192 193
    debug("%lf seconds to Gtk.main()", startup_timer.elapsed());
    
194
    Gtk.main();
195 196 197 198 199
    
    DirectPhoto.terminate();
    DatabaseTable.terminate();
}

200
bool no_startup_progress = false;
201
bool no_mimicked_images = false;
202
string data_dir = null;
203 204

const OptionEntry[] options = {
205 206 207 208
    { "no-startup-progress", 0, 0, OptionArg.NONE, &no_startup_progress,
        N_("Don't display startup progress meter"), null },
    { "no-mimicked-images", 0, 0, OptionArg.NONE, &no_mimicked_images,
        N_("Don't used JPEGs to display RAW images"), null },
209 210
    { "datadir", 'd', 0, OptionArg.FILENAME, &data_dir,
        N_("Path to Shotwell's private data"), N_("DIRECTORY") },
211
    { null }
212 213
};

214
void main(string[] args) {
215 216
    // Call AppDirs init *before* calling Gtk.init_with_args, as it will strip the
    // exec file from the array
217 218 219 220 221
    AppDirs.init(args[0]);
#if WINDOWS
    win_init(AppDirs.get_exec_dir());
#endif

222
    // init GTK (valac has already called g_threads_init())
223 224
    try {
        Gtk.init_with_args(ref args, _("[FILE]"), (OptionEntry []) options, Resources.APP_GETTEXT_PACKAGE);
225 226 227 228 229 230
    } catch (Error e) {
        print(e.message + "\n");
        print(_("Run '%s --help' to see a full list of available command line options.\n"), args[0]);
        AppDirs.terminate();
        return;
    }
231
    
232 233 234
    // init debug prior to anything else (except Gtk, which it relies on, and AppDirs, which needs
    // to be set ASAP) ... since we need to know what mode we're in, examine the command-line
    // first
235
    
236 237 238
    // walk command-line arguments for camera mounts or filename for direct editing ... only one
    // filename supported for now, so take the first one and drop the rest ... note that URIs for
    // filenames are currently not permitted, to differentiate between mount points
239
    string[] mounts = new string[0];
240
    string filename = null;
241

242
    for (int ctr = 1; ctr < args.length; ctr++) {
243 244
        string arg = args[ctr];
        
245
        if (LibraryWindow.is_mount_uri_supported(arg)) {
246
            mounts += arg;
247
        } else if (is_string_empty(filename) && !arg.contains("://")) {
248 249
            filename = arg;
        }
250 251
    }
    
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
    Debug.init(is_string_empty(filename) ? Debug.LIBRARY_PREFIX : Debug.VIEWER_PREFIX);
    
    // set custom data directory if it's been supplied
    if (data_dir != null)
        AppDirs.set_data_dir(File.parse_name(data_dir));
    
    // Verify the private data directory before continuing
    AppDirs.verify_data_dir();
    
    // init internationalization with the default system locale
    InternationalSupport.init(Resources.APP_GETTEXT_PACKAGE, args);
    
    startup_timer = new Timer();
    startup_timer.start();
    
    // set up GLib environment
    GLib.Environment.set_application_name(Resources.APP_TITLE);
    
270
    // in both the case of running as the library or an editor, Resources is always
271 272 273
    // initialized
    Resources.init();
    
274 275 276
    // since it's possible for a mount name to be passed that's not supported (and hence an empty
    // mount list), or for nothing to be on the command-line at all, only go to direct editing if a
    // filename is spec'd
277
    if (is_string_empty(filename))
278
        library_exec(mounts);
279 280
    else
        editing_exec(filename);
281 282
    
    // terminate mode-inspecific modules
283
    Resources.terminate();
284
    Debug.terminate();
285
    AppDirs.terminate();
Jim Nelson's avatar
Jim Nelson committed
286 287
}