r827: Fix a crash when no audio output device can be opened.
[cinelerra_cv.git] / cinelerra / patchbay.C
blob05d068a5fd609a937b37dc3970c4d34611c491a8
1 #include "apatchgui.h"
2 #include "automation.h"
3 #include "floatauto.h"
4 #include "floatautos.h"
5 #include "clip.h"
6 #include "edl.h"
7 #include "edlsession.h"
8 #include "filexml.h"
9 #include "intauto.h"
10 #include "intautos.h"
11 #include "language.h"
12 #include "localsession.h"
13 #include "mainundo.h"
14 #include "mwindow.h"
15 #include "mwindowgui.h"
16 #include "patchbay.h"
17 #include "patchgui.h"
18 #include "mainsession.h"
19 #include "theme.h"
20 #include "track.h"
21 #include "trackcanvas.h"
22 #include "tracks.h"
23 #include "vpatchgui.h"
30 NudgePopup::NudgePopup(MWindow *mwindow, PatchBay *patchbay)
31  : BC_PopupMenu(0, 
32                 0, 
33                 0, 
34                 "", 
35                 0)
37         this->mwindow = mwindow;
38         this->patchbay = patchbay;
41 NudgePopup::~NudgePopup()
46 void NudgePopup::create_objects()
48         add_item(seconds_item = new NudgePopupSeconds(this));
49         add_item(native_item = new NudgePopupNative(this));
52 void NudgePopup::activate_menu(PatchGUI *gui)
54 // Set checked status
55         seconds_item->set_checked(mwindow->edl->session->nudge_seconds ? 1 : 0);
56         native_item->set_checked(mwindow->edl->session->nudge_seconds ? 0 : 1);
58 // Set native units to track format
59         native_item->set_text(gui->track->data_type == TRACK_AUDIO ? 
60                 (char*)"Samples" : 
61                 (char*)"Frames");
63 // Show it
64         BC_PopupMenu::activate_menu();
69 NudgePopupSeconds::NudgePopupSeconds(NudgePopup *popup)
70  : BC_MenuItem("Seconds")
72         this->popup = popup;
75 int NudgePopupSeconds::handle_event()
77         popup->mwindow->edl->session->nudge_seconds = 1;
78         popup->patchbay->update();
79         return 1;
86 NudgePopupNative::NudgePopupNative(NudgePopup *popup)
87  : BC_MenuItem("")
89         this->popup = popup;
92 int NudgePopupNative::handle_event()
94         popup->mwindow->edl->session->nudge_seconds = 0;
95         popup->patchbay->update();
96         return 1;
107 PatchBay::PatchBay(MWindow *mwindow, MWindowGUI *gui)
108  : BC_SubWindow(mwindow->theme->patchbay_x,
109         mwindow->theme->patchbay_y,
110         mwindow->theme->patchbay_w,
111         mwindow->theme->patchbay_h)
113         this->mwindow = mwindow;
114         this->gui = gui;
115         button_down = 0;
116         reconfigure_trigger = 0;
117         drag_operation = Tracks::NONE;
120 PatchBay::~PatchBay() 
125 int PatchBay::delete_all_patches()
127     patches.remove_all_objects();
128     return 0;
131 int PatchBay::create_objects()
133         draw_top_background(get_parent(), 0, 0, get_w(), get_h());
134         flash();
136 // Create icons for mode types
137         mode_icons[TRANSFER_NORMAL] = new BC_Pixmap(this, 
138                 mwindow->theme->get_image("mode_normal"),
139                 PIXMAP_ALPHA);
140         mode_icons[TRANSFER_ADDITION] = new BC_Pixmap(this, 
141                 mwindow->theme->get_image("mode_add"),
142                 PIXMAP_ALPHA);
143         mode_icons[TRANSFER_SUBTRACT] = new BC_Pixmap(this, 
144                 mwindow->theme->get_image("mode_subtract"),
145                 PIXMAP_ALPHA);
146         mode_icons[TRANSFER_MULTIPLY] = new BC_Pixmap(this, 
147                 mwindow->theme->get_image("mode_multiply"),
148                 PIXMAP_ALPHA);
149         mode_icons[TRANSFER_DIVIDE] = new BC_Pixmap(this, 
150                 mwindow->theme->get_image("mode_divide"),
151                 PIXMAP_ALPHA);
152         mode_icons[TRANSFER_REPLACE] = new BC_Pixmap(this, 
153                 mwindow->theme->get_image("mode_replace"),
154                 PIXMAP_ALPHA);
155         
156         add_subwindow(nudge_popup = new NudgePopup(mwindow, this));
157         nudge_popup->create_objects();
159         return 0;
162 BC_Pixmap* PatchBay::mode_to_icon(int mode)
164         return mode_icons[mode];
167 int PatchBay::icon_to_mode(BC_Pixmap *icon)
169         for(int i = 0; i < TRANSFER_TYPES; i++)
170                 if(icon == mode_icons[i]) return i;
171         return TRANSFER_NORMAL;
174 void PatchBay::resize_event()
176         reposition_window(mwindow->theme->patchbay_x,
177                 mwindow->theme->patchbay_y,
178                 mwindow->theme->patchbay_w,
179                 mwindow->theme->patchbay_h);
180         draw_top_background(get_parent(), 0, 0, get_w(), get_h());
181         update();
182         flash();
185 int PatchBay::button_press_event()
187         int result = 0;
188 // Too much junk to support the wheel
189         return result;
193 Track *PatchBay::is_over_track()     // called from mwindow
195         int cursor_x = get_relative_cursor_x();
196         int cursor_y = get_relative_cursor_y();
197         Track *over_track = 0;
199         if(get_cursor_over_window() &&
200                 cursor_x >= 0 && 
201                 cursor_y >= 0 && 
202                 cursor_x < get_w() && 
203                 cursor_y < get_h())
204         {
205 // Get track we're inside of
206                 for(Track *track = mwindow->edl->tracks->first;
207                         track;
208                         track = track->next)
209                 {
210                         int y = track->y_pixel;
211                         int h = track->vertical_span(mwindow->theme);
212                         if(cursor_y >= y && cursor_y < y + h)
213                         {       
214                                 over_track = track;
215                         }
216                 }
217         }                               
218         return (over_track);
222 int PatchBay::cursor_motion_event()
224         int cursor_x = get_relative_cursor_x();
225         int cursor_y = get_relative_cursor_y();
226         int update_gui = 0;
228         if(drag_operation != Tracks::NONE)
229         {
230                 if(cursor_y >= 0 &&
231                         cursor_y < get_h())
232                 {
233 // Get track we're inside of
234                         for(Track *track = mwindow->edl->tracks->first;
235                                 track;
236                                 track = track->next)
237                         {
238                                 int y = track->y_pixel;
239                                 int h = track->vertical_span(mwindow->theme);
240                                 if(cursor_y >= y && cursor_y < y + h)
241                                 {
242                                         switch(drag_operation)
243                                         {
244                                                 case Tracks::PLAY:
245                                                         if(track->play != new_status)
246                                                         {
247                                                                 track->play = new_status;
248                                                                 mwindow->gui->unlock_window();
249                                                                 mwindow->restart_brender();
250                                                                 mwindow->sync_parameters(CHANGE_EDL);
251                                                                 mwindow->gui->lock_window();
252                                                                 update_gui = 1;
253                                                         }
254                                                         break;
255                                                 case Tracks::RECORD:
256                                                         if(track->record != new_status)
257                                                         {
258                                                                 track->record = new_status;
259                                                                 update_gui = 1;
260                                                         }
261                                                         break;
262                                                 case Tracks::GANG:
263                                                         if(track->gang != new_status)
264                                                         {
265                                                                 track->gang = new_status;
266                                                                 update_gui = 1;
267                                                         }
268                                                         break;
269                                                 case Tracks::DRAW:
270                                                         if(track->draw != new_status)
271                                                         {
272                                                                 track->draw = new_status;
273                                                                 update_gui = 1;
274                                                         }
275                                                         break;
276                                                 case Tracks::EXPAND:
277                                                         if(track->expand_view != new_status)
278                                                         {
279                                                                 track->expand_view = new_status;
280                                                                 mwindow->trackmovement(mwindow->edl->local_session->track_start);
281                                                                 update_gui = 0;
282                                                         }
283                                                         break;
284                                                 case Tracks::MUTE:
285                                                 {
286                                                         IntAuto *current = 0;
287                                                         Auto *keyframe = 0;
288                                                         double position = mwindow->edl->local_session->get_selectionstart(1);
289                                                         Autos *mute_autos = track->automation->autos[AUTOMATION_MUTE];
291                                                         current = (IntAuto*)mute_autos->get_prev_auto(PLAY_FORWARD, 
292                                                                 keyframe);
294                                                         if(current->value != new_status)
295                                                         {
297                                                                 current = (IntAuto*)mute_autos->get_auto_for_editing(position);
299                                                                 current->value = new_status;
301                                                                 mwindow->undo->update_undo(_("keyframe"), LOAD_AUTOMATION);
303                                                                 mwindow->gui->unlock_window();
304                                                                 mwindow->restart_brender();
305                                                                 mwindow->sync_parameters(CHANGE_PARAMS);
306                                                                 mwindow->gui->lock_window();
308                                                                 if(mwindow->edl->session->auto_conf->autos[AUTOMATION_MUTE])
309                                                                 {
310                                                                         mwindow->gui->canvas->draw_overlays();
311                                                                         mwindow->gui->canvas->flash();
312                                                                 }
313                                                                 update_gui = 1;
314                                                         }
315                                                         break;
316                                                 }
317                                         }
318                                 }
319                         }
320                 }
321         }
323         if(update_gui)
324         {
325                 update();
326         }
327         return 0;
330 void PatchBay::change_meter_format(int mode, int min, int max)
332         for(int i = 0; i < patches.total; i++)
333         {
334                 PatchGUI *patchgui = patches.values[i];
335                 if(patchgui->data_type == TRACK_AUDIO)
336                 {
337                         APatchGUI *apatchgui = (APatchGUI*)patchgui;
338                         if(apatchgui->meter)
339                         {
340                                 apatchgui->meter->change_format(mode, min, max);
341                         }
342                 }
343         }
346 void PatchBay::update_meters(ArrayList<double> *module_levels)
348         for(int level_number = 0, patch_number = 0;
349                 patch_number < patches.total && level_number < module_levels->total;
350                 patch_number++)
351         {
352                 APatchGUI *patchgui = (APatchGUI*)patches.values[patch_number];
354                 if(patchgui->data_type == TRACK_AUDIO)
355                 {
356                         if(patchgui->meter)
357                         {
358                                 double level = module_levels->values[level_number];
359                                 patchgui->meter->update(level, level > 1);
360                         }
362                         level_number++;
363                 }
364         }
367 void PatchBay::reset_meters()
369         for(int patch_number = 0;
370                 patch_number < patches.total;
371                 patch_number++)
372         {
373                 APatchGUI *patchgui = (APatchGUI*)patches.values[patch_number];
374                 if(patchgui->data_type == TRACK_AUDIO && patchgui->meter)
375                 {
376                         patchgui->meter->reset_over();
377                 }
378         }
381 void PatchBay::stop_meters()
383         for(int patch_number = 0;
384                 patch_number < patches.total;
385                 patch_number++)
386         {
387                 APatchGUI *patchgui = (APatchGUI*)patches.values[patch_number];
388                 if(patchgui->data_type == TRACK_AUDIO && patchgui->meter)
389                 {
390                         patchgui->meter->reset();
391                 }
392         }
396 #define PATCH_X 3
398 int PatchBay::update()
400         int patch_count = 0;
402 // Every patch has a GUI regardless of whether or not it is visible.
403 // Make sure GUI's are allocated for every patch and deleted for non-existant
404 // patches.
405         for(Track *current = mwindow->edl->tracks->first;
406                 current;
407                 current = NEXT, patch_count++)
408         {
409                 PatchGUI *patchgui;
410                 int y = current->y_pixel;
412                 if(patches.total > patch_count)
413                 {
414                         if(patches.values[patch_count]->track_id != current->get_id())
415                         {
416                                 delete patches.values[patch_count];
418                                 switch(current->data_type)
419                                 {
420                                         case TRACK_AUDIO:
421                                                 patchgui = patches.values[patch_count] = new APatchGUI(mwindow, this, (ATrack*)current, PATCH_X, y);
422                                                 break;
423                                         case TRACK_VIDEO:
424                                                 patchgui = patches.values[patch_count] = new VPatchGUI(mwindow, this, (VTrack*)current, PATCH_X, y);
425                                                 break;
426                                 }
427                                 patchgui->create_objects();
428                         }
429                         else
430                         {
431                                 patches.values[patch_count]->update(PATCH_X, y);
432                         }
433                 }
434                 else
435                 {
436                         switch(current->data_type)
437                         {
438                                 case TRACK_AUDIO:
439                                         patchgui = new APatchGUI(mwindow, this, (ATrack*)current, PATCH_X, y);
440                                         break;
441                                 case TRACK_VIDEO:
442                                         patchgui = new VPatchGUI(mwindow, this, (VTrack*)current, PATCH_X, y);
443                                         break;
444                         }
445                         patches.append(patchgui);
446                         patchgui->create_objects();
447                 }
448         }
450         while(patches.total > patch_count)
451         {
452                 delete patches.values[patches.total - 1];
453                 patches.remove_number(patches.total - 1);
454         }
456         return 0;
459 void PatchBay::synchronize_faders(float change, int data_type, Track *skip)
461         for(Track *current = mwindow->edl->tracks->first;
462                 current;
463                 current = NEXT)
464         {
465                 if(current->data_type == data_type &&
466                         current->gang && 
467                         current->record && 
468                         current != skip)
469                 {
470                         FloatAutos *fade_autos = (FloatAutos*)current->automation->autos[AUTOMATION_FADE];
471                         double position = mwindow->edl->local_session->get_selectionstart(1);
474                         FloatAuto *keyframe = (FloatAuto*)fade_autos->get_auto_for_editing(position);
476                         keyframe->value += change;
477                         if(data_type == TRACK_AUDIO)
478                                 CLAMP(keyframe->value, INFINITYGAIN, MAX_AUDIO_FADE);
479                         else
480                                 CLAMP(keyframe->value, 0, MAX_VIDEO_FADE);
483                         PatchGUI *patch = get_patch_of(current);
484                         if(patch) patch->update(patch->x, patch->y);
485                 }
486         }
489 void PatchBay::synchronize_nudge(int64_t value, Track *skip)
491         for(Track *current = mwindow->edl->tracks->first;
492                 current;
493                 current = NEXT)
494         {
495                 if(current->data_type == skip->data_type &&
496                         current->gang &&
497                         current->record &&
498                         current != skip)
499                 {
500                         current->nudge = value;
501                         PatchGUI *patch = get_patch_of(current);
502                         if(patch) patch->update(patch->x, patch->y);
503                 }
504         }
507 PatchGUI* PatchBay::get_patch_of(Track *track)
509         for(int i = 0; i < patches.total; i++)
510         {
511                 if(patches.values[i]->track == track)
512                         return patches.values[i];
513         }
514         return 0;
517 int PatchBay::resize_event(int top, int bottom)
519         reposition_window(mwindow->theme->patchbay_x,
520                 mwindow->theme->patchbay_y,
521                 mwindow->theme->patchbay_w,
522                 mwindow->theme->patchbay_h);
523         return 0;