Clean up Spectrograph test file.
[dictix.git] / src / dix-main.vala
blob79fc1e4e34438b5e701a229634c969cf4dcf44f3
1 /**
2 * Dictix / DixMain - dix-main.vala
4 * Copyright (C) Martin Blanchard 2011 <tinram@gmx.fr>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
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.
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 Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 using GLib;
23 using Gtk;
24 using Dix;
26 public enum State {
27 UNKNOWN,
28 WORKING,
29 READY
32 namespace Global {
33 /* App state */
34 public State state;
35 public bool now_recording;
36 public bool now_playing;
37 public bool transport_is_paused;
38 public bool seeking;
40 /* Last just-recorded record pointer, use once to set TreeView state */
41 public TreeIter last_record;
43 /* Timer value use for notification while recording */
44 public int notification_timer;
46 /* Usefull directories and files */
47 public string data_dir;
48 public string ui_file;
49 public string menu_file;
51 /* Main objects: */
52 public Recorder recorder;
53 public Player player;
54 public Transcoder transcoder;
56 /* Record list, iter atributes are thoses:
57 - 0: uri (string),
58 - 1: timeval, (uint64);
59 - 2: icon (string),
60 - 3: title (string),
61 - 4: codec (string),
62 - 5: size (uint64),
63 - 6: duration (int64).
64 - 7: channels (int).
65 - 8: sample-rate (int).
66 - 9: bit-rate (int). */
67 public ListStore records;
69 /* Settings engine */
70 public GLib.Settings settings;
73 namespace Dictix {
74 public class Main : Gtk.Application {
75 private static Ui ui;
76 private Cancellable guardian;
78 private short destroy_count;
80 public Main (string id, ApplicationFlags flags) {
81 GLib.Object (application_id: id, flags: flags);
83 ui = null;
84 guardian = null;
86 destroy_count = -1;
89 public void on_record_started_cb () {
90 if (Global.state == State.READY) {
91 Global.now_recording = true;
95 public void on_playback_started_cb () {
96 if (Global.state == State.READY) {
97 Global.now_playing = true;
98 Global.transport_is_paused = false;
102 public void on_record_stopped_cb (string uri) {
103 if (Global.state == State.READY) {
104 Global.now_recording = false;
106 if (uri != null) {
107 Global.last_record = add_record (uri);
111 if (destroy_count > 0) {
112 destroy_count--;
113 if (destroy_count == 0) {
114 just_quit ();
119 public void on_playback_stopped_cb (bool eos) {
120 if (Global.state == State.READY) {
121 Global.now_playing = false;
123 if (eos == false) {
124 Global.transport_is_paused = true;
125 } else {
126 Global.transport_is_paused = false;
130 if (destroy_count > 0) {
131 destroy_count--;
132 if (destroy_count == 0) {
133 just_quit ();
138 public void on_destroy_cb () {
139 destroy_count = 0;
141 if (Global.now_recording == true) {
142 destroy_count++;
143 Global.recorder.stop_recording ();
145 if (Global.now_playing == true) {
146 destroy_count++;
147 Global.player.stop ();
150 if (destroy_count == 0) {
151 /* We are nither recording nor playing */
152 just_quit ();
156 private void just_quit () {
157 switch (Global.state) {
158 case State.WORKING:
159 if (guardian != null) {
160 guardian.cancel ();
162 break;
163 default:
164 break;
167 Gtk.main_quit ();
170 /* timeval = uint64.MAX is just a hack to keep new records at the beginning of the list... */
171 private TreeIter add_record (string uri, uint64 timeval = uint64.MAX) {
172 TagReader reader = null;
173 string title = null;
174 string codec = null;
175 uint64 size = 0;
176 int64 duration = 0;
177 int channels = 0;
178 int sample_rate = 0;
179 int bit_rate = 0;
180 TreeIter iter;
182 reader = new TagReader (uri);
183 Global.records.prepend (out iter);
185 if (reader != null) {
186 reader.load ();
188 title = reader.get_title ();
189 codec = reader.get_codec ();
191 size = reader.get_size ();
192 duration = reader.get_duration ();
193 channels = reader.get_channels ();
194 sample_rate = reader.get_sample_rate ();
195 bit_rate = reader.get_bit_rate ();
196 } else {
197 title = _("Unknow record");
200 Global.records.set (iter,
201 Column.URI, uri.dup (),
202 Column.TIMEVAL, timeval,
203 Column.PIXBUF, null,
204 Column.TITLE, title,
205 Column.CODEC, codec,
206 Column.SIZE, size,
207 Column.DURATION, duration,
208 Column.CHANNELS, channels,
209 Column.SAMPLE_RATE, sample_rate,
210 Column.BIT_RATE, bit_rate);
212 return iter;
215 private async void list_data_directory () {
216 FileEnumerator enumeration = null;
217 File directory = null;
218 uint64 timeval = 0;
219 TreeIter iter;
221 directory = File.new_for_path (Global.data_dir);
222 try {
223 guardian = new Cancellable ();
224 enumeration = yield directory.enumerate_children_async (FILE_ATTRIBUTE_STANDARD_NAME +
225 "," +
226 FILE_ATTRIBUTE_TIME_MODIFIED,
227 FileQueryInfoFlags.NONE,
228 Priority.DEFAULT,
229 guardian);
230 /* FIXME: Why is G_FILE_ATTRIBUTE_TIME_CREATED always == 0 ?? */
231 while (true) {
232 List<FileInfo> files = null;
234 files = yield enumeration.next_files_async (10, Priority.DEFAULT);
235 if (files != null) {
236 foreach (var info in files) {
237 if (info.get_name ().has_suffix (".flac") == true) {
238 string filename = null;
239 string uri = null;
241 filename = Path.build_filename (Global.data_dir, info.get_name ());
242 uri = Filename.to_uri (filename, null);
243 timeval = info.get_attribute_uint64 (FILE_ATTRIBUTE_TIME_MODIFIED);
245 if (uri != null) {
246 add_record (uri, timeval);
250 } else {
251 /* No more files, just give last record creation time to the Recorder for naming stuff: */
252 if (Global.records.get_iter_first (out iter) == true) {
253 Global.records.get (iter, Column.TIMEVAL, out timeval);
254 if (timeval != 0) {
255 Global.recorder.set ("last-record-timeval", timeval);
259 Global.state = State.READY;
261 break;
264 } catch (Error error) {
265 warning (error.message);
269 public void on_activate_cb () {
270 if (this.get_windows () != null) {
271 /* An other instance alredy exists, just show it */
272 ui.present ();
273 } else {
274 Builder builder = null;
275 UIManager manager = null;
277 Global.state = State.WORKING;
278 Global.now_recording = false;
279 Global.now_playing = false;
280 Global.transport_is_paused = false;
281 Global.seeking = false;
282 Global.notification_timer = NotificationDelay.INVALID;
284 Environment.set_variable ("PULSE_PROP_media.role", "production", true);
286 Global.records = new ListStore (Column.NB,
287 typeof (string), /* uri */
288 typeof (uint64), /* timeval */
289 typeof (string), /* icon */
290 typeof (string), /* title */
291 typeof (string), /* codec */
292 typeof (uint64), /* size */
293 typeof (int64), /* duration */
294 typeof (int), /* channels */
295 typeof (int), /* sample-rate */
296 typeof (int)); /* bit-rate */
297 Global.records.set_sort_column_id (Column.TIMEVAL, SortType.DESCENDING);
299 Environment.set_application_name (Config.PACKAGE_NAME);
301 Global.settings = new GLib.Settings (Config.PACKAGE_SCHEMA);
303 Global.recorder = new Recorder (Global.data_dir);
304 if (Global.recorder == null) {
305 warning (_("Impossible to allocate core recorder object !"));
307 return;
309 Global.recorder.record_started.connect (on_record_started_cb);
310 Global.recorder.record_stopped.connect (on_record_stopped_cb);
312 Global.player = new Player ();
313 if (Global.player == null) {
314 warning (_("Impossible to allocate core player object !"));
316 return;
318 Global.player.playback_started.connect (on_playback_started_cb);
319 Global.player.playback_stopped.connect (on_playback_stopped_cb);
321 Global.transcoder = new Transcoder ();
322 if (Global.transcoder == null) {
323 warning (_("Impossible to allocate core transcoder object !"));
325 return;
328 builder = new Builder ();
329 try {
330 builder.add_from_file (Global.ui_file);
331 } catch (Error error) {
332 warning (error.message);
334 return;
337 manager = new UIManager ();
338 try {
339 manager.add_ui_from_file (Global.menu_file);
340 } catch (Error error) {
341 warning (error.message);
343 return;
346 ui = new Ui (builder, manager);
347 ui.destroy.connect (this.on_destroy_cb);
349 list_data_directory.begin ();
351 ui.set_application (this);
357 public int main (string[] args) {
358 Main app = null;
359 File directory = null;
361 Gtk.init (ref args);
363 Intl.bindtextdomain (Config.GETTEXT_PACKAGE, Config.PACKAGE_LOCALEDIR);
364 Intl.bind_textdomain_codeset (Config.GETTEXT_PACKAGE, "UTF-8");
365 Intl.textdomain (Config.GETTEXT_PACKAGE);
367 Global.data_dir = Path.build_filename (Environment.get_user_data_dir (), Config.PACKAGE);
368 // Global.ui_file = Path.build_filename (Config.PACKAGE_DATADIR, Config.PACKAGE, "main.ui");
369 // Global.menu_file = Path.build_filename (Config.PACKAGE_DATADIR, Config.PACKAGE, "menu.ui");
370 Global.ui_file = Path.build_filename (".", "data", "main.ui");
371 Global.menu_file = Path.build_filename (".", "data", "menu.ui");
373 directory = File.new_for_path (Global.data_dir);
374 if (directory.query_exists () == false) {
375 /* Our xdg data dir doesn't exists, let's create it */
376 try {
377 directory.make_directory ();
378 } catch (Error error) {
379 warning (error.message);
381 return 1;
385 Gst.init (ref args);
387 Notify.init (Config.PACKAGE_NAME);
389 app = new Main ("org.gnome." + Config.PACKAGE_NAME, ApplicationFlags.FLAGS_NONE);
390 app.activate.connect (app.on_activate_cb);
392 int status = app.run (args);
394 Notify.uninit ();
395 Gst.deinit ();
397 return status;