From d43cc4e7b5e95d4f4a8ed4d8fff99b6f846fe074 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Fri, 30 Dec 2005 00:34:21 +0000 Subject: many changes, read the diffs git-svn-id: svn://localhost/trunk/ardour2@214 d708f5d6-7413-0410-9779-e7cbd77b26cf --- gtk2_ardour/SConscript | 2 +- gtk2_ardour/actions.cc | 45 +++--- gtk2_ardour/actions.h | 2 +- gtk2_ardour/ardour_ui.cc | 4 - gtk2_ardour/ardour_ui2.cc | 14 +- gtk2_ardour/ardour_ui_dialogs.cc | 83 +++++++---- gtk2_ardour/crossfade_view.cc | 4 +- gtk2_ardour/editor.cc | 258 ++++++++++++++------------------- gtk2_ardour/editor.h | 42 +++--- gtk2_ardour/editor_canvas.cc | 59 ++++++++ gtk2_ardour/editor_canvas_events.cc | 6 +- gtk2_ardour/editor_edit_groups.cc | 18 +-- gtk2_ardour/editor_imageframe.cc | 4 +- gtk2_ardour/editor_keyboard.cc | 9 +- gtk2_ardour/editor_region_list.cc | 22 ++- gtk2_ardour/editor_route_list.cc | 22 +-- gtk2_ardour/location_ui.cc | 15 +- gtk2_ardour/location_ui.h | 10 +- libs/gtkmm2ext/dndtreeview.cc | 51 ++++--- libs/gtkmm2ext/gtk_ui.cc | 4 + libs/gtkmm2ext/gtkmm2ext/dndtreeview.h | 1 + 21 files changed, 375 insertions(+), 300 deletions(-) diff --git a/gtk2_ardour/SConscript b/gtk2_ardour/SConscript index abffaf623a..7dae69592a 100644 --- a/gtk2_ardour/SConscript +++ b/gtk2_ardour/SConscript @@ -207,7 +207,7 @@ executable = 'ardour.bin' ardour = gtkardour.Program(target = executable, source = gtkardour_files + extra_sources) mtest = gtkardour.Program(target = 'mtest', source = mtest_files) -#itest = gtkardour.Program(target = 'itest', source = itest_files) +itest = gtkardour.Program(target = 'itest', source = itest_files) Default(ardour) diff --git a/gtk2_ardour/actions.cc b/gtk2_ardour/actions.cc index 01dbde829a..5e33dc8aeb 100644 --- a/gtk2_ardour/actions.cc +++ b/gtk2_ardour/actions.cc @@ -19,6 +19,7 @@ */ #include +#include #include #include @@ -32,6 +33,7 @@ #include #include "actions.h" +#include "i18n.h" using namespace std; using namespace Gtk; @@ -199,35 +201,25 @@ ActionManager::get_widget (const char * name) } RefPtr -ActionManager::get_action (const char * _name) +ActionManager::get_action (const char* group_name, const char* action_name) { /* the C++ API for functions used here appears to be broken in gtkmm2.6, so we fall back to the C level. */ - ustring name(_name); GList* list = gtk_ui_manager_get_action_groups (ui_manager->gobj()); GList* node; RefPtr act; - if (name.substr (0,9) != "") { - error << "badly specified action name: " << name << endmsg; - return act; - } - - ustring::size_type last_slash = name.find_last_of ('/'); - ustring group_name = name.substr (10, last_slash - 10); - ustring action_name = name.substr (last_slash+1); - for (node = list; node; node = g_list_next (node)) { GtkActionGroup* _ag = (GtkActionGroup*) node->data; - if (group_name == gtk_action_group_get_name (_ag)) { - + if (strcmp (group_name, gtk_action_group_get_name (_ag)) == 0) { + GtkAction* _act; - if ((_act = gtk_action_group_get_action (_ag, action_name.c_str())) != 0) { + if ((_act = gtk_action_group_get_action (_ag, action_name)) != 0) { act = Glib::wrap (_act, true); break; } @@ -246,14 +238,33 @@ ActionManager::set_sensitive (vector >& actions, bool state) } void -ActionManager::uncheck_toggleaction (const char * actionname) +ActionManager::uncheck_toggleaction (const char * name) { - RefPtr act = get_action (actionname); + char *last_slash = strrchr (name, '/'); + + if (last_slash == 0) { + fatal << string_compose (_("programmer error: %1 %2"), X_("illegal toggle action name"), name) << endmsg; + /*NOTREACHED*/ + return; + } + + /* 10 = strlen ("/") */ + size_t len = last_slash - (name + 10); + + char* group_name = new char[len+1]; + memcpy (group_name, name + 10, len); + group_name[len] = '\0'; + + char* action_name = last_slash + 1; + + RefPtr act = get_action (group_name, action_name); if (act) { RefPtr tact = RefPtr::cast_dynamic(act); tact->set_active (false); } else { - error << "Invalid action name: " << actionname << endmsg; + error << "Unknown action name: " << name << endmsg; } + + delete [] group_name; } diff --git a/gtk2_ardour/actions.h b/gtk2_ardour/actions.h index a95bee4463..fb60b34371 100644 --- a/gtk2_ardour/actions.h +++ b/gtk2_ardour/actions.h @@ -37,7 +37,7 @@ class ActionManager static Glib::RefPtr ui_manager; static Gtk::Widget* get_widget (const char * name); - static Glib::RefPtr get_action (const char * name); + static Glib::RefPtr get_action (const char* group, const char* name); static void add_action_group (Glib::RefPtr); diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc index fca8dea231..d806313ef3 100644 --- a/gtk2_ardour/ardour_ui.cc +++ b/gtk2_ardour/ardour_ui.cc @@ -831,10 +831,6 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], string rcfile) theArdourUI = this; } - // allow run-time rebinding of accels - - Settings::get_default()->property_gtk_can_change_accels() = true; - ActionManager::init (); m_new_session_dialog = 0; diff --git a/gtk2_ardour/ardour_ui2.cc b/gtk2_ardour/ardour_ui2.cc index 291a0df0b6..7fcbdc6a85 100644 --- a/gtk2_ardour/ardour_ui2.cc +++ b/gtk2_ardour/ardour_ui2.cc @@ -279,19 +279,19 @@ ARDOUR_UI::setup_transport () RefPtr act; - act = ActionManager::get_action (X_("/Transport/Stop")); + act = ActionManager::get_action (X_("Transport"), X_("Stop")); act->connect_proxy (stop_button); - act = ActionManager::get_action (X_("/Transport/Roll")); + act = ActionManager::get_action (X_("Transport"), X_("Roll")); act->connect_proxy (roll_button); - act = ActionManager::get_action (X_("/Transport/Record")); + act = ActionManager::get_action (X_("Transport"), X_("Record")); act->connect_proxy (rec_button); - act = ActionManager::get_action (X_("/Transport/GotoStart")); + act = ActionManager::get_action (X_("Transport"), X_("GotoStart")); act->connect_proxy (goto_start_button); - act = ActionManager::get_action (X_("/Transport/GotoEnd")); + act = ActionManager::get_action (X_("Transport"), X_("GotoEnd")); act->connect_proxy (goto_end_button); - act = ActionManager::get_action (X_("/Transport/Loop")); + act = ActionManager::get_action (X_("Transport"), X_("Loop")); act->connect_proxy (auto_loop_button); - act = ActionManager::get_action (X_("/Transport/PlaySelection")); + act = ActionManager::get_action (X_("Transport"), X_("PlaySelection")); act->connect_proxy (play_selection_button); ARDOUR_UI::instance()->tooltips().set_tip (roll_button, _("Play from playhead")); diff --git a/gtk2_ardour/ardour_ui_dialogs.cc b/gtk2_ardour/ardour_ui_dialogs.cc index 1a0ba6618f..fe34ac523e 100644 --- a/gtk2_ardour/ardour_ui_dialogs.cc +++ b/gtk2_ardour/ardour_ui_dialogs.cc @@ -180,14 +180,16 @@ ARDOUR_UI::unload_session () int ARDOUR_UI::create_connection_editor () { +#if 0 if (connection_editor == 0) { -// connection_editor = new ConnectionEditor (); -// connection_editor->signal_unmap().connect (sigc::bind (ptr_fun(&ActionManager::uncheck_toggleaction), X_("/Common/ToggleConnections"))); + connection_editor = new ConnectionEditor (); + connection_editor->signal_unmap().connect (sigc::bind (ptr_fun(&ActionManager::uncheck_toggleaction), X_("/Common/ToggleConnections"))); } if (session) { -// connection_editor->set_session (session); + connection_editor->set_session (session); } +#endif return 0; } @@ -199,13 +201,17 @@ ARDOUR_UI::toggle_connection_editor () return; } - //GTK2FIX #if 0 - - if (connection_editor_check->get_active()){ - connection_editor->present(); - } else { - connection_editor->hide_all(); + RefPtr act = ActionManager::get_action (X_("Common"), X_("ToggleConnections")); + if (act) { + RefPtr tact = RefPtr::cast_dynamic(act); + + if (tact->get_active()) { + connection_editor->show_all (); + connection_editor->present (); + } else { + connection_editor->hide (); + } } #endif } @@ -213,11 +219,16 @@ ARDOUR_UI::toggle_connection_editor () void ARDOUR_UI::toggle_big_clock_window () { - if (big_clock_window->is_visible()) { - big_clock_window->hide (); - } else { - big_clock_window->show_all (); - big_clock_window->present (); + RefPtr act = ActionManager::get_action (X_("Common"), X_("ToggleBigClock")); + if (act) { + RefPtr tact = RefPtr::cast_dynamic(act); + + if (tact->get_active()) { + big_clock_window->show_all (); + big_clock_window->present (); + } else { + big_clock_window->hide (); + } } } @@ -230,10 +241,16 @@ ARDOUR_UI::toggle_options_window () option_editor->set_session (session); } - if (option_editor->is_visible()) { - option_editor->hide (); - } else { - option_editor->present (); + RefPtr act = ActionManager::get_action (X_("Common"), X_("ToggleOptionsEditor")); + if (act) { + RefPtr tact = RefPtr::cast_dynamic(act); + + if (tact->get_active()) { + option_editor->show_all (); + option_editor->present (); + } else { + option_editor->hide (); + } } } @@ -264,10 +281,16 @@ ARDOUR_UI::toggle_location_window () return; } - if (location_ui->is_visible()) { - location_ui->hide(); - } else { - location_ui->present(); + RefPtr act = ActionManager::get_action (X_("Common"), X_("ToggleLocations")); + if (act) { + RefPtr tact = RefPtr::cast_dynamic(act); + + if (tact->get_active()) { + location_ui->show_all (); + location_ui->present (); + } else { + location_ui->hide (); + } } } @@ -289,10 +312,16 @@ ARDOUR_UI::toggle_route_params_window () return; } - if (route_params->is_visible ()) { - route_params->hide (); - } else { - route_params->present (); + RefPtr act = ActionManager::get_action (X_("Common"), X_("ToggleInspector")); + if (act) { + RefPtr tact = RefPtr::cast_dynamic(act); + + if (tact->get_active()) { + route_params->show_all (); + route_params->present (); + } else { + route_params->hide (); + } } } @@ -314,7 +343,7 @@ ARDOUR_UI::toggle_sound_file_browser () return; } - RefPtr act = ActionManager::ui_manager->get_action (X_("/Common/ToggleSoundFileBrowser")); + RefPtr act = ActionManager::get_action (X_("Common"), X_("ToggleSoundFileBrowser")); if (act) { RefPtr tact = RefPtr::cast_dynamic(act); diff --git a/gtk2_ardour/crossfade_view.cc b/gtk2_ardour/crossfade_view.cc index 46d61c5e78..aef54f2c5d 100644 --- a/gtk2_ardour/crossfade_view.cc +++ b/gtk2_ardour/crossfade_view.cc @@ -31,6 +31,7 @@ #include "public_editor.h" #include "regionview.h" #include "utils.h" +#include "canvas_impl.h" using namespace sigc; using namespace ARDOUR; @@ -78,8 +79,7 @@ CrossfadeView::CrossfadeView (ArdourCanvas::Group *parent, vestigial_frame->hide(); show_vestigial = false; - // GTK2FIX - // group->signal_event.connect (bind (mem_fun (tv.editor, &PublicEditor::canvas_crossfade_view_event), group, this)); + group->signal_event().connect (bind (mem_fun (tv.editor, &PublicEditor::canvas_crossfade_view_event), group, this)); crossfade_changed (Change (~0)); diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index 35038740cc..5d5f3e44c0 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -523,27 +523,27 @@ Editor::Editor (AudioEngine& eng) bottom_hbox.set_spacing (3); route_display_model = ListStore::create(route_display_columns); - route_list.set_model (route_display_model); - route_list.append_column (_("Tracks"), route_display_columns.text); - route_list.set_name ("TrackListDisplay"); - route_list.get_selection()->set_mode (Gtk::SELECTION_MULTIPLE); - route_list.set_reorderable (true); + route_list_display.set_model (route_display_model); + route_list_display.append_column (_("Tracks"), route_display_columns.text); + route_list_display.set_name ("TrackListDisplay"); + route_list_display.get_selection()->set_mode (Gtk::SELECTION_MULTIPLE); + route_list_display.set_reorderable (true); - route_list.set_size_request (75,-1); - route_list.set_headers_visible (true); - route_list.set_headers_clickable (true); + route_list_display.set_size_request (75,-1); + route_list_display.set_headers_visible (true); + route_list_display.set_headers_clickable (true); // GTK2FIX - // route_list.signal_rows_reordered().connect (mem_fun (*this, &Editor::queue_route_list_reordered)); + // route_list_display.signal_rows_reordered().connect (mem_fun (*this, &Editor::queue_route_list_reordered)); // GTK2FIX // route_display_model->set_sort_func (0, mem_fun (*this, &Editor::route_list_compare_func)); - route_list_scroller.add (route_list); + route_list_scroller.add (route_list_display); route_list_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC); - route_list.get_selection()->signal_changed().connect (mem_fun (*this, &Editor::route_display_selection_changed)); - route_list.signal_columns_changed().connect (mem_fun(*this, &Editor::route_list_column_click)); + route_list_display.get_selection()->signal_changed().connect (mem_fun (*this, &Editor::route_display_selection_changed)); + route_list_display.signal_columns_changed().connect (mem_fun(*this, &Editor::route_list_column_click)); edit_group_list_button_label.set_text (_("Edit Groups")); edit_group_list_button_label.set_name ("EditGroupTitleButton"); @@ -551,69 +551,70 @@ Editor::Editor (AudioEngine& eng) edit_group_list_button.set_name ("EditGroupTitleButton"); group_model = ListStore::create(group_columns); - edit_group_list.set_model (group_model); - edit_group_list.append_column (_("active"), group_columns.is_active); - edit_group_list.append_column (_("groupname"), group_columns.text); - edit_group_list.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0)); - edit_group_list.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(1)); + edit_group_display.set_model (group_model); + edit_group_display.append_column (_("active"), group_columns.is_active); + edit_group_display.append_column (_("groupname"), group_columns.text); + edit_group_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0)); + edit_group_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(1)); /* use checkbox for the active column */ - CellRendererToggle *active_cell = dynamic_cast(edit_group_list.get_column_cell_renderer (0)); + CellRendererToggle *active_cell = dynamic_cast(edit_group_display.get_column_cell_renderer (0)); active_cell->property_activatable() = true; active_cell->property_radio() = false; - edit_group_list.set_name ("MixerGroupList"); - //edit_group_list.set_shadow_type (Gtk::SHADOW_IN); + edit_group_display.set_name ("MixerGroupList"); + //edit_group_display.set_shadow_type (Gtk::SHADOW_IN); - edit_group_list.columns_autosize (); - edit_group_list.get_selection()->set_mode (Gtk::SELECTION_MULTIPLE); - edit_group_list.set_reorderable (false); + edit_group_display.columns_autosize (); + edit_group_display.get_selection()->set_mode (Gtk::SELECTION_MULTIPLE); + edit_group_display.set_reorderable (false); - edit_group_list.set_size_request (75, -1); - edit_group_list.set_headers_visible (true); + edit_group_display.set_size_request (75, -1); + edit_group_display.set_headers_visible (true); - edit_group_list_scroller.add (edit_group_list); + edit_group_list_scroller.add (edit_group_display); edit_group_list_scroller.set_policy (Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); edit_group_list_button.signal_clicked().connect (mem_fun(*this, &Editor::edit_group_list_button_clicked)); - edit_group_list.signal_button_press_event().connect (mem_fun(*this, &Editor::edit_group_list_button_press_event)); - edit_group_list.get_selection()->signal_changed().connect (mem_fun(*this, &Editor::edit_group_selection_changed)); + edit_group_display.signal_button_press_event().connect (mem_fun(*this, &Editor::edit_group_list_button_press_event)); + edit_group_display.get_selection()->signal_changed().connect (mem_fun(*this, &Editor::edit_group_selection_changed)); TreeModel::Row row = *(group_model->append()); row[group_columns.is_active] = false; row[group_columns.text] = (_("-all-")); row[group_columns.routegroup] = 0; - edit_group_list.get_selection()->select (row); + edit_group_display.get_selection()->select (row); edit_group_vbox.pack_start (edit_group_list_button, false, false); edit_group_vbox.pack_start (edit_group_list_scroller, true, true); + region_list_display.set_size_request (100, -1); + region_list_display.set_name ("RegionListDisplay"); + region_list_model = TreeStore::create (region_list_columns); - region_list_sort_model = TreeModelSort::create (region_list_model); region_list_model->set_sort_func (0, mem_fun (*this, &Editor::region_list_sorter)); - region_list_display.set_model (region_list_sort_model); + region_list_display.set_model (region_list_model); region_list_display.append_column (_("Regions"), region_list_columns.name); region_list_display.set_reorderable (true); - region_list_display.set_size_request (100, -1); - region_list_display.set_data ("editor", this); - region_list_display.set_flags (Gtk::CAN_FOCUS); - region_list_display.set_name ("RegionListDisplay"); - - region_list_scroller.add (region_list_display); - region_list_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC); + region_list_display.get_selection()->set_mode (SELECTION_SINGLE); + region_list_display.add_object_drag (region_list_columns.region.index(), "regions"); + /* setup DnD handling */ + list region_list_target_table; region_list_target_table.push_back (TargetEntry ("STRING")); region_list_target_table.push_back (TargetEntry ("text/plain")); region_list_target_table.push_back (TargetEntry ("text/uri-list")); region_list_target_table.push_back (TargetEntry ("application/x-rootwin-drop")); + + region_list_display.add_drop_targets (region_list_target_table); + region_list_display.signal_drag_data_received().connect (mem_fun(*this, &Editor::region_list_display_drag_data_received)); - // GTK2FIX - // region_list_display.drag_dest_set (region_list_target_table, DEST_DEFAULT_ALL, GdkDragAction (Gdk::ACTION_COPY|Gdk::ACTION_MOVE)); - // region_list_display.signal_drag_data_received().connect (mem_fun(*this, &Editor::region_list_display_drag_data_received)); + region_list_scroller.add (region_list_display); + region_list_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC); region_list_display.signal_key_press_event().connect (mem_fun(*this, &Editor::region_list_display_key_press)); region_list_display.signal_key_release_event().connect (mem_fun(*this, &Editor::region_list_display_key_release)); @@ -666,9 +667,7 @@ Editor::Editor (AudioEngine& eng) global_hpacker.pack_start (global_vpacker, true, true); set_name ("EditorWindow"); - cerr << "Adding accel group " << ActionManager::ui_manager->get_accel_group()->gobj() << endl; add_accel_group (ActionManager::ui_manager->get_accel_group()); - cerr << "... done\n"; vpacker.pack_end (global_hpacker, true, true); @@ -1235,17 +1234,17 @@ Editor::connect_to_session (Session *t) redisplay_named_selections (); // GTK2FIX - // route_list.set_model (Glib::RefPtr(0)); + // route_list_display.set_model (Glib::RefPtr(0)); route_display_model->clear (); session->foreach_route (this, &Editor::handle_new_route); - // route_list.select_all (); + // route_list_display.select_all (); // GTK2FIX - //route_list.sort (); + //route_list_display.sort (); route_list_reordered (); - // route_list.set_model (route_display_model); + // route_list_display.set_model (route_display_model); for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { (static_cast(*i))->set_samples_per_unit (frames_per_unit); @@ -1279,7 +1278,7 @@ Editor::connect_to_session (Session *t) TreeModel::Children rows = route_display_model->children(); TreeModel::Children::iterator i; - //route_list.freeze (); + //route_list_display.freeze (); for (i = rows.begin(); i != rows.end(); ++i) { TimeAxisView *tv = (*i)[route_display_columns.tv]; @@ -1287,13 +1286,13 @@ Editor::connect_to_session (Session *t) if ((atv = dynamic_cast(tv)) != 0) { if (atv->route().master()) { - route_list.get_selection()->unselect (i); + route_list_display.get_selection()->unselect (i); //(*i)->unselect (); } } } - //route_list.thaw (); + //route_list_display.thaw (); } } @@ -2762,130 +2761,85 @@ Editor::stop_canvas_autoscroll () int Editor::convert_drop_to_paths (vector& paths, - GdkDragContext *context, + const RefPtr& context, gint x, gint y, - GtkSelectionData *data, + const SelectionData& data, guint info, guint time) { - string spath; - char *path; - int state; - gchar *tname = gdk_atom_name (data->type); - - if (session == 0 || strcmp (tname, "text/plain") != 0) { + if (session == 0) { return -1; } - /* Parse the "uri-list" format that Nautilus provides, - where each pathname is delimited by \r\n - */ + vector uris = data.get_uris(); - path = (char *) data->data; - state = 0; + if (uris.empty()) { + + /* This is seriously fucked up. Nautilus doesn't say that its URI lists + are actually URI lists. So do it by hand. + */ - for (int n = 0; n < data->length; ++n) { + if (data.get_target() != "text/plain") { + return -1; + } + + /* Parse the "uri-list" format that Nautilus provides, + where each pathname is delimited by \r\n + */ + + cerr << "by hand parsing of URI list\n"; + + const char* p = data.get_text().c_str(); + const char* q; - switch (state) { - case 0: - if (path[n] == '\r') { - state = 1; - } else { - spath += path[n]; - } - break; - case 1: - if (path[n] == '\n') { - paths.push_back (spath); - spath = ""; - state = 0; - } else { - warning << _("incorrectly formatted URI list, ignored") - << endmsg; - return -1; + while (p) + { + if (*p != '#') + { + while (g_ascii_isspace (*p)) + p++; + + q = p; + while (*q && (*q != '\n') && (*q != '\r')) + q++; + + if (q > p) + { + q--; + while (q > p && g_ascii_isspace (*q)) + q--; + + if (q > p) + { + uris.push_back (ustring (p, q - p + 1)); + } + } } - break; + p = strchr (p, '\n'); + if (p) + p++; } - } - - /* nautilus and presumably some other file managers prefix even text/plain with file:// */ - - for (vector::iterator p = paths.begin(); p != paths.end(); ++p) { - - // cerr << "dropped text was " << *p << endl; - url_decode (*p); + cerr << "end result = " << uris.size() << endl; - // cerr << "decoded was " << *p << endl; - - if ((*p).substr (0,7) == "file://") { - (*p) = (*p).substr (7); + if (uris.empty()) { + return -1; } } - return 0; -} - -void -Editor::track_canvas_drag_data_received (GdkDragContext *context, - gint x, - gint y, - GtkSelectionData *data, - guint info, - guint time) -{ - TimeAxisView* tvp; - AudioTimeAxisView* tv; - double cy; - vector paths; - string spath; - GdkEvent ev; - jack_nframes_t frame; - - if (convert_drop_to_paths (paths, context, x, y, data, info, time)) { - goto out; - } - - /* D-n-D coordinates are window-relative, so convert to "world" coordinates - */ - - double wx; - double wy; - - track_canvas.c2w( x, y, wx, wy); - - ev.type = GDK_BUTTON_RELEASE; - ev.button.x = wx; - ev.button.y = wy; - - frame = event_frame (&ev, 0, &cy); - - snap_to (frame); - - if ((tvp = trackview_by_y_position (cy)) == 0) { - - /* drop onto canvas background: create a new track */ - - insert_paths_as_new_tracks (paths, false); - - - } else if ((tv = dynamic_cast(tvp)) != 0) { - - /* check that its an audio track, not a bus */ - - if (tv->get_diskstream()) { - - for (vector::iterator p = paths.begin(); p != paths.end(); ++p) { - insert_sndfile_into (*p, true, tv, frame); - } + for (vector::iterator i = uris.begin(); i != uris.end(); ++i) { + cerr << "looking at " << (*i) << endl; + if ((*i).substr (0,7) == "file://") { + string p = *i; + url_decode (p); + cerr << "adding " << p << endl; + paths.push_back (p.substr (7)); } - } - out: - gtk_drag_finish (context, TRUE, FALSE, time); + return 0; } void diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index 6b1342e2be..25ff94c561 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -40,6 +40,7 @@ #include #include +#include #include #include @@ -688,7 +689,7 @@ class Editor : public PublicEditor }; RegionListDisplayModelColumns region_list_columns; - Gtk::TreeView region_list_display; + Gtkmm2ext::DnDTreeView region_list_display; Glib::RefPtr region_list_model; Glib::RefPtr region_list_sort_model; Glib::RefPtr toggle_full_region_list_action; @@ -738,8 +739,8 @@ class Editor : public PublicEditor NamedSelectionDisplayModelColumns named_selection_columns; Glib::RefPtr named_selection_model; - Gtk::TreeView named_selection_display; - Gtk::ScrolledWindow named_selection_scroller; + Gtkmm2ext::DnDTreeView named_selection_display; + Gtk::ScrolledWindow named_selection_scroller; void name_selection(); void named_selection_name_chosen (); @@ -1435,9 +1436,9 @@ class Editor : public PublicEditor Glib::RefPtr route_display_selection; gint route_list_compare_func (Gtk::TreeModel::iterator, Gtk::TreeModel::iterator); - Gtk::TreeView route_list; //GTK2FIX rename to route_display - Gtk::ScrolledWindow route_list_scroller; - Gtk::Menu *route_list_menu; + Gtkmm2ext::DnDTreeView route_list_display; + Gtk::ScrolledWindow route_list_scroller; + Gtk::Menu* route_list_menu; void route_list_column_click (); void build_route_list_menu (); @@ -1465,12 +1466,12 @@ class Editor : public PublicEditor Glib::RefPtr group_model; Glib::RefPtr group_selection; - Gtk::Button edit_group_list_button; - Gtk::Label edit_group_list_button_label; - Gtk::TreeView edit_group_list; - Gtk::ScrolledWindow edit_group_list_scroller; - Gtk::Menu *edit_group_list_menu; - Gtk::VBox edit_group_vbox; + Gtk::Button edit_group_list_button; + Gtk::Label edit_group_list_button_label; + Gtkmm2ext::DnDTreeView edit_group_display; + Gtk::ScrolledWindow edit_group_list_scroller; + Gtk::Menu* edit_group_list_menu; + Gtk::VBox edit_group_vbox; void edit_group_list_column_click (gint); void build_edit_group_list_menu (); @@ -1524,27 +1525,28 @@ class Editor : public PublicEditor /* Drag-n-Drop */ int convert_drop_to_paths (std::vector& paths, - GdkDragContext *context, + const Glib::RefPtr& context, gint x, gint y, - GtkSelectionData *data, + const Gtk::SelectionData& data, guint info, guint time); - void track_canvas_drag_data_received (GdkDragContext *context, + void track_canvas_drag_data_received (const Glib::RefPtr& context, gint x, gint y, - GtkSelectionData *data, + const Gtk::SelectionData& data, guint info, guint time); - - void region_list_display_drag_data_received (GdkDragContext *context, + + void region_list_display_drag_data_received (const Glib::RefPtr& context, gint x, gint y, - GtkSelectionData *data, + const Gtk::SelectionData& data, guint info, guint time); - + + /* audio export */ ExportDialog *export_dialog; diff --git a/gtk2_ardour/editor_canvas.cc b/gtk2_ardour/editor_canvas.cc index 933d33eeb5..029ddb4408 100644 --- a/gtk2_ardour/editor_canvas.cc +++ b/gtk2_ardour/editor_canvas.cc @@ -36,6 +36,7 @@ #include "rgb_macros.h" #include "utils.h" #include "time_axis_view.h" +#include "audio_time_axis.h" #include "i18n.h" @@ -399,3 +400,61 @@ Editor::time_canvas_map_handler (GdkEventAny* ev) return false; } +void +Editor::track_canvas_drag_data_received (const RefPtr& context, + int x, int y, + const SelectionData& data, + guint info, guint time) +{ + TimeAxisView* tvp; + AudioTimeAxisView* tv; + double cy; + vector paths; + string spath; + GdkEvent ev; + jack_nframes_t frame; + + if (convert_drop_to_paths (paths, context, x, y, data, info, time)) { + goto out; + } + + /* D-n-D coordinates are window-relative, so convert to "world" coordinates + */ + + double wx; + double wy; + + track_canvas.c2w( x, y, wx, wy); + + ev.type = GDK_BUTTON_RELEASE; + ev.button.x = wx; + ev.button.y = wy; + + frame = event_frame (&ev, 0, &cy); + + snap_to (frame); + + if ((tvp = trackview_by_y_position (cy)) == 0) { + + /* drop onto canvas background: create a new track */ + + insert_paths_as_new_tracks (paths, false); + + + } else if ((tv = dynamic_cast(tvp)) != 0) { + + /* check that its an audio track, not a bus */ + + if (tv->get_diskstream()) { + + for (vector::iterator p = paths.begin(); p != paths.end(); ++p) { + insert_sndfile_into (*p, true, tv, frame); + } + } + + } + + out: + context->drag_finish (true, false, time); +} + diff --git a/gtk2_ardour/editor_canvas_events.cc b/gtk2_ardour/editor_canvas_events.cc index f09c4d028f..da38a79d11 100644 --- a/gtk2_ardour/editor_canvas_events.cc +++ b/gtk2_ardour/editor_canvas_events.cc @@ -147,7 +147,7 @@ bool Editor::canvas_region_view_event (GdkEvent *event, ArdourCanvas::Item* item, AudioRegionView *rv) { gint ret = FALSE; - + switch (event->type) { case GDK_BUTTON_PRESS: case GDK_2BUTTON_PRESS: @@ -444,7 +444,7 @@ Editor::canvas_crossfade_view_event (GdkEvent* event, ArdourCanvas::Item* item, if ((atv = dynamic_cast(&tv)) != 0) { if (atv->is_audio_track()) { - + AudioPlaylist* pl = atv->get_diskstream()->playlist(); Playlist::RegionList* rl = pl->regions_at (event_frame (event)); @@ -459,7 +459,7 @@ Editor::canvas_crossfade_view_event (GdkEvent* event, ArdourCanvas::Item* item, delete rl; return canvas_region_view_event (event, arv->get_canvas_group(), arv); - } + } } } diff --git a/gtk2_ardour/editor_edit_groups.cc b/gtk2_ardour/editor_edit_groups.cc index faaafc8f2c..b833c8ac5e 100644 --- a/gtk2_ardour/editor_edit_groups.cc +++ b/gtk2_ardour/editor_edit_groups.cc @@ -82,7 +82,7 @@ Editor::select_all_edit_groups () Gtk::TreeModel::Children children = group_model->children(); for(Gtk::TreeModel::Children::iterator iter = children.begin(); iter != children.end(); ++iter) { - edit_group_list.get_selection()->select (iter); + edit_group_display.get_selection()->select (iter); } } @@ -126,7 +126,7 @@ Editor::edit_group_list_button_press_event (GdkEventButton* ev) int cellx; int celly; - if (!edit_group_list.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) { + if (!edit_group_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) { return false; } @@ -135,10 +135,10 @@ Editor::edit_group_list_button_press_event (GdkEventButton* ev) case 1: if (Keyboard::is_edit_event (ev)) { - // RouteGroup* group = (RouteGroup *) edit_group_list.row(row).get_data (); + // RouteGroup* group = (RouteGroup *) edit_group_display.row(row).get_data (); // edit_route_group (group); - return stop_signal (edit_group_list, "button_press_event"); + return stop_signal (edit_group_display, "button_press_event"); } else { /* allow regular select to occur */ @@ -157,7 +157,7 @@ Editor::edit_group_list_button_press_event (GdkEventButton* ev) break; } - return stop_signal (edit_group_list, "button_press_event"); + return stop_signal (edit_group_display, "button_press_event"); } void @@ -165,7 +165,7 @@ Editor::edit_group_selection_changed () { TreeModel::iterator i; TreeModel::Children rows = group_model->children(); - Glib::RefPtr selection = edit_group_list.get_selection(); + Glib::RefPtr selection = edit_group_display.get_selection(); for (i = rows.begin(); i != rows.end(); ++i) { RouteGroup* group; @@ -212,12 +212,12 @@ Editor::group_flags_changed (void* src, RouteGroup* group) // select row } - CList_Helpers::RowIterator ri = edit_group_list.rows().find_data (group); + CList_Helpers::RowIterator ri = edit_group_display.rows().find_data (group); if (group->is_active()) { - edit_group_list.cell (ri->get_row_num(),0).set_pixmap (check_pixmap, check_mask); + edit_group_display.cell (ri->get_row_num(),0).set_pixmap (check_pixmap, check_mask); } else { - edit_group_list.cell (ri->get_row_num(),0).set_pixmap (empty_pixmap, empty_mask); + edit_group_display.cell (ri->get_row_num(),0).set_pixmap (empty_pixmap, empty_mask); } */ } diff --git a/gtk2_ardour/editor_imageframe.cc b/gtk2_ardour/editor_imageframe.cc index 798a5c2950..a8dce7d64b 100644 --- a/gtk2_ardour/editor_imageframe.cc +++ b/gtk2_ardour/editor_imageframe.cc @@ -1090,7 +1090,7 @@ Editor::handle_new_imageframe_time_axis_view(const string & track_name, void* sr row[route_display_columns.text] = iftav->name(); row[route_display_columns.tv] = iftav; - route_list.get_selection()->select (row); + route_list_display.get_selection()->select (row); iftav->GoingAway.connect(bind(mem_fun(*this, &Editor::remove_route), (TimeAxisView*)iftav)) ; iftav->gui_changed.connect(mem_fun(*this, &Editor::handle_gui_changes)) ; @@ -1107,7 +1107,7 @@ Editor::handle_new_imageframe_marker_time_axis_view(const string & track_name, T row[route_display_columns.text] = mta->name(); row[route_display_columns.tv] = mta; - route_list.get_selection()->select (row); + route_list_display.get_selection()->select (row); mta->GoingAway.connect(bind(mem_fun(*this, &Editor::remove_route), (TimeAxisView*)mta)) ; } diff --git a/gtk2_ardour/editor_keyboard.cc b/gtk2_ardour/editor_keyboard.cc index 72404a88a8..abc9466af5 100644 --- a/gtk2_ardour/editor_keyboard.cc +++ b/gtk2_ardour/editor_keyboard.cc @@ -28,7 +28,7 @@ void Editor::kbd_driver (sigc::slot theslot, bool use_track_canvas, bool use_time_canvas, bool can_select) { gint x, y; - double dx, dy; + double worldx, worldy; GdkEvent ev; Gdk::ModifierType mask; Glib::RefPtr evw = track_canvas.get_window()->get_pointer (x, y, mask); @@ -46,10 +46,11 @@ Editor::kbd_driver (sigc::slot theslot, bool use_track_canvas, b selection->set (entered_regionview); } - track_canvas.c2w(x, y, dx, dy); + track_canvas.window_to_world (x, y, worldx, worldy); + ev.type = GDK_BUTTON_PRESS; - ev.button.x = dx; - ev.button.y = dy; + ev.button.x = worldx; + ev.button.y = worldy; ev.button.state = 0; /* XXX correct? */ theslot (&ev); diff --git a/gtk2_ardour/editor_region_list.cc b/gtk2_ardour/editor_region_list.cc index de0abee8bf..f17645b6f2 100644 --- a/gtk2_ardour/editor_region_list.cc +++ b/gtk2_ardour/editor_region_list.cc @@ -41,6 +41,7 @@ using namespace sigc; using namespace ARDOUR; using namespace Gtk; +using namespace Glib; using namespace Editing; #define wave_cursor_width 43 @@ -345,7 +346,7 @@ Editor::redisplay_regions () add_audio_region_to_region_display (*r); } - region_list_display.set_model (region_list_sort_model); + region_list_display.set_model (region_list_model); } } @@ -362,7 +363,7 @@ Editor::build_region_list_menu () /* now grab specific menu items that we need */ - toggle_full_region_list_action = ActionManager::get_action ("/RegionList/rlShowAll"); + toggle_full_region_list_action = ActionManager::get_action (X_("RegionList"), X_("rlShowAll")); } void @@ -646,7 +647,7 @@ Editor::reset_region_list_sort_type (RegionListSortType type) break; } - region_list_sort_model->set_sort_func (0, mem_fun (*this, &Editor::region_list_sorter)); + // region_list_sort_model->set_sort_func (0, mem_fun (*this, &Editor::region_list_sorter)); } } @@ -656,7 +657,7 @@ Editor::reset_region_list_sort_direction (bool up) // GTK2FIX //region_list_display.set_sort_type (up ? GTK_SORT_ASCENDING : GTK_SORT_DESCENDING); /* reset to force resort */ - region_list_sort_model->set_sort_func (0, mem_fun (*this, &Editor::region_list_sorter)); + // region_list_sort_model->set_sort_func (0, mem_fun (*this, &Editor::region_list_sorter)); } void @@ -710,18 +711,15 @@ Editor::remove_region_from_region_list () } void -Editor::region_list_display_drag_data_received (GdkDragContext *context, - gint x, - gint y, - GtkSelectionData *data, - guint info, - guint time) +Editor::region_list_display_drag_data_received (const RefPtr& context, + int x, int y, + const SelectionData& data, + guint info, guint time) { vector paths; if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) { do_embed_sndfiles (paths, false); + context->drag_finish (true, false, time); } - - gtk_drag_finish (context, TRUE, FALSE, time); } diff --git a/gtk2_ardour/editor_route_list.cc b/gtk2_ardour/editor_route_list.cc index 9dc765ca49..a3c295d47e 100644 --- a/gtk2_ardour/editor_route_list.cc +++ b/gtk2_ardour/editor_route_list.cc @@ -64,7 +64,7 @@ Editor::handle_new_route (Route& route) ignore_route_list_reorder = true; if (tv->marked_for_display()) { - route_list.get_selection()->select (row); + route_list_display.get_selection()->select (row); } if ((atv = dynamic_cast (tv)) != 0) { @@ -151,7 +151,7 @@ Editor::route_display_selection_changed () TimeAxisView *tv; TreeModel::Children rows = route_display_model->children(); TreeModel::Children::iterator i; - Glib::RefPtr selection = route_list.get_selection(); + Glib::RefPtr selection = route_list_display.get_selection(); for (i = rows.begin(); i != rows.end(); ++i) { tv = (*i)[route_display_columns.tv]; @@ -177,7 +177,7 @@ Editor::unselect_strip_in_display (TimeAxisView& tv) { TreeModel::Children rows = route_display_model->children(); TreeModel::Children::iterator i; - Glib::RefPtr selection = route_list.get_selection(); + Glib::RefPtr selection = route_list_display.get_selection(); for (i = rows.begin(); i != rows.end(); ++i) { if ((*i)[route_display_columns.tv] == &tv) { @@ -191,7 +191,7 @@ Editor::select_strip_in_display (TimeAxisView* tv) { TreeModel::Children rows = route_display_model->children(); TreeModel::Children::iterator i; - Glib::RefPtr selection = route_list.get_selection(); + Glib::RefPtr selection = route_list_display.get_selection(); for (i = rows.begin(); i != rows.end(); ++i) { if ((*i)[route_display_columns.tv] == tv) { @@ -272,14 +272,14 @@ Editor::hide_all_tracks (bool with_select) TimeAxisView *tv = row[route_display_columns.tv]; if (with_select) { - route_list.get_selection()->unselect (i); + route_list_display.get_selection()->unselect (i); } else { tv->set_marked_for_display (false); tv->hide(); } } - //route_list.thaw (); + //route_list_display.thaw (); reset_scrolling_region (); } @@ -328,7 +328,7 @@ Editor::select_all_routes () TreeModel::Children::iterator i; for (i = rows.begin(); i != rows.end(); ++i) { - route_list.get_selection()->select (i); + route_list_display.get_selection()->select (i); } } @@ -345,7 +345,7 @@ Editor::select_all_audiotracks () if ((atv = dynamic_cast(tv)) != 0) { if (atv->is_audio_track()) { - route_list.get_selection()->select (i); + route_list_display.get_selection()->select (i); } } @@ -366,7 +366,7 @@ Editor::unselect_all_audiotracks () if ((atv = dynamic_cast(tv)) != 0) { if (atv->is_audio_track()) { - route_list.get_selection()->unselect (i); + route_list_display.get_selection()->unselect (i); } } @@ -387,7 +387,7 @@ Editor::select_all_audiobus () if ((atv = dynamic_cast(tv)) != 0) { if (!atv->is_audio_track()) { - route_list.get_selection()->select (i); + route_list_display.get_selection()->select (i); } } @@ -408,7 +408,7 @@ Editor::unselect_all_audiobus () if ((atv = dynamic_cast(tv)) != 0) { if (!atv->is_audio_track()) { - route_list.get_selection()->unselect (i); + route_list_display.get_selection()->unselect (i); } } diff --git a/gtk2_ardour/location_ui.cc b/gtk2_ardour/location_ui.cc index 564cc4dca2..c62c7a2e90 100644 --- a/gtk2_ardour/location_ui.cc +++ b/gtk2_ardour/location_ui.cc @@ -575,15 +575,12 @@ LocationUI::LocationUI () set_wmclass(_("ardour_locations"), "Ardour"); set_name ("LocationWindow"); - signal_delete_event().connect (bind (ptr_fun (just_hide_it), static_cast(this))); - - add (location_hpacker); + get_vbox()->pack_start (location_hpacker); location_vpacker.set_border_width (10); location_vpacker.set_spacing (5); - location_vpacker.pack_start (loop_edit_row, false, false); location_vpacker.pack_start (punch_edit_row, false, false); @@ -857,7 +854,13 @@ LocationUI::session_gone() punch_edit_row.set_session (0); punch_edit_row.set_location (0); - - ArdourDialog::session_gone(); + + ArdourDialog::session_gone (); } +bool +LocationUI::on_delete_event (GdkEventAny* ev) +{ + hide (); + return true; +} diff --git a/gtk2_ardour/location_ui.h b/gtk2_ardour/location_ui.h index 4c6b2da8e2..580d47ff5d 100644 --- a/gtk2_ardour/location_ui.h +++ b/gtk2_ardour/location_ui.h @@ -148,13 +148,10 @@ class LocationUI : public ArdourDialog void set_session (ARDOUR::Session *); private: - - - ARDOUR::LocationStack *locations; - + ARDOUR::LocationStack* locations; + void session_gone(); - Gtk::VBox location_vpacker; Gtk::HBox location_hpacker; @@ -197,6 +194,9 @@ class LocationUI : public ArdourDialog void location_removed (ARDOUR::Location *); void location_added (ARDOUR::Location *); void map_locations (ARDOUR::Locations::LocationList&); + + protected: + bool on_delete_event (GdkEventAny*); }; #endif // __ardour_location_ui_h__ diff --git a/libs/gtkmm2ext/dndtreeview.cc b/libs/gtkmm2ext/dndtreeview.cc index f230fc42b2..dee55acae9 100644 --- a/libs/gtkmm2ext/dndtreeview.cc +++ b/libs/gtkmm2ext/dndtreeview.cc @@ -1,4 +1,5 @@ #include +#include #include @@ -13,13 +14,24 @@ DnDTreeView::DnDTreeView () : TreeView () { draggable.push_back (TargetEntry ("GTK_TREE_MODEL_ROW", TARGET_SAME_WIDGET)); - + data_column = -1; + enable_model_drag_source (draggable); enable_model_drag_dest (draggable); - - suggested_action = Gdk::DragAction (0); + + suggested_action = Gdk::DragAction (0); } +void +DnDTreeView::add_drop_targets (list& targets) +{ + for (list::iterator i = targets.begin(); i != targets.end(); ++i) { + draggable.push_back (*i); + } + enable_model_drag_source (draggable); + enable_model_drag_dest (draggable); +} + void DnDTreeView::add_object_drag (int column, string type_name) { @@ -57,11 +69,13 @@ DnDTreeView::serialize_pointers (RefPtr model, TreeSelection::ListHan void DnDTreeView::on_drag_data_get(const RefPtr& context, SelectionData& selection_data, guint info, guint time) { + cerr << "DnDTreeview::drag_data_get, target = " << selection_data.get_target() << endl; + if (selection_data.get_target() == "GTK_TREE_MODEL_ROW") { TreeView::on_drag_data_get (context, selection_data, info, time); - } else { + } else if (data_column >= 0) { Gtk::TreeSelection::ListHandle_Path selection = get_selection()->get_selected_rows (); SerializedObjectPointers* sr = serialize_pointers (get_model(), &selection, selection_data.get_target()); @@ -73,20 +87,23 @@ DnDTreeView::on_drag_data_get(const RefPtr& context, SelectionData& void DnDTreeView::on_drag_data_received(const RefPtr& context, int x, int y, const SelectionData& selection_data, guint info, guint time) { - if (suggested_action) { - /* this is a drag motion callback. just update the status to - say that we are still dragging, and that's it. - */ - suggested_action = Gdk::DragAction (0); - TreeView::on_drag_data_received (context, x, y, selection_data, info, time); - return; - } - + cerr << "DnDTreeview::drag_data_received @ " << x << ',' << y << " target = " << selection_data.get_target() << endl; + + if (suggested_action) { + /* this is a drag motion callback. just update the status to + say that we are still dragging, and that's it. + */ + suggested_action = Gdk::DragAction (0); + TreeView::on_drag_data_received (context, x, y, selection_data, info, time); + return; + } + if (selection_data.get_target() == "GTK_TREE_MODEL_ROW") { TreeView::on_drag_data_received (context, x, y, selection_data, info, time); - } else { + } else if (data_column >= 0) { + /* object D-n-D */ const SerializedObjectPointers* sr = reinterpret_cast(selection_data.get_data()); @@ -94,9 +111,9 @@ DnDTreeView::on_drag_data_received(const RefPtr& context, int x, in if (sr) { signal_object_drop (sr->type, sr->cnt, const_cast(sr->ptr)); } - - context->drag_finish (true, false, time); - + + } else { + /* some kind of target type added by the app, which will be handled by a signal handler */ } } diff --git a/libs/gtkmm2ext/gtk_ui.cc b/libs/gtkmm2ext/gtk_ui.cc index 722b6a15ef..292753604f 100644 --- a/libs/gtkmm2ext/gtk_ui.cc +++ b/libs/gtkmm2ext/gtk_ui.cc @@ -53,6 +53,10 @@ UI::UI (string name, int *argc, char ***argv, string rcfile) theMain = new Main (argc, argv); tips = new Tooltips; + // allow run-time rebinding of accels + + Settings::get_default()->property_gtk_can_change_accels() = true; + if (pthread_key_create (&thread_request_buffer_key, 0)) { cerr << _("cannot create thread request buffer key") << endl; throw failed_constructor(); diff --git a/libs/gtkmm2ext/gtkmm2ext/dndtreeview.h b/libs/gtkmm2ext/gtkmm2ext/dndtreeview.h index 1c91396987..bc7a2df10f 100644 --- a/libs/gtkmm2ext/gtkmm2ext/dndtreeview.h +++ b/libs/gtkmm2ext/gtkmm2ext/dndtreeview.h @@ -17,6 +17,7 @@ class DnDTreeView : public Gtk::TreeView DnDTreeView (); ~DnDTreeView() {} + void add_drop_targets (std::list&); void add_object_drag (int column, std::string type_name); sigc::signal signal_object_drop; -- cgit v1.2.3