more work on livetrax layout

This commit is contained in:
Paul Davis 2024-03-20 22:20:05 -06:00
parent 279fb74492
commit 8418672558
16 changed files with 201 additions and 65 deletions

View File

@ -102,6 +102,7 @@
#include "ardour/session_directory.h"
#include "ardour/session_route.h"
#include "ardour/source_factory.h"
#include "ardour/system_exec.h"
#include "ardour/transport_master.h"
#include "ardour/transport_master_manager.h"
#include "ardour/triggerbox.h"
@ -3197,3 +3198,26 @@ ARDOUR_UI::stop_cues (int col, bool immediately)
_basic_ui->trigger_stop_col (col, immediately);
}
void
ARDOUR_UI::open_media_folder ()
{
if (!_session) {
return;
}
ARDOUR::SystemExec* cmd;
#if defined (PLATFORM_WINDOWS)
cmd = new ARDOUR::SystemExec ("open", _session->session_directory().sound_path());
#elif defined (__APPLE__)
cmd = new ARDOUR::SystemExec ("open", _session->session_directory().sound_path());
#else
cmd = new ARDOUR::SystemExec ("xdg-open", _session->session_directory().sound_path());
#endif
if (cmd->start ()) {
std::cerr << "Could not start file browser on " << _session->session_directory().sound_path() << std::endl;
error << "Could not start file browser on " << _session->session_directory().sound_path() << endmsg;
}
}

View File

@ -176,6 +176,7 @@ class NSM_Client;
class LevelMeterHBox;
class GUIObjectState;
class BasicUI;
class MeterbridgeWidget;
namespace ARDOUR {
class ControlProtocolInfo;
@ -220,6 +221,7 @@ public:
void launch_howto_report ();
void show_about ();
void hide_about ();
void open_media_folder ();
void load_from_application_api (const std::string& path);
void finish();
@ -460,6 +462,7 @@ private:
int setup_windows ();
void apply_window_settings (bool);
void connect_transport_elements ();
void setup_transport ();
void setup_clock ();
@ -630,6 +633,15 @@ private:
TimeInfoBox* livetrax_time_info_box;
ArdourWidgets::ArdourButton* livetrax_multi_out_button;
ArdourWidgets::ArdourButton* livetrax_stereo_out_button;
ArdourWidgets::ArdourButton* livetrax_meter_view_button;
ArdourWidgets::ArdourButton* livetrax_editor_view_button;
ArdourWidgets::ArdourButton* livetrax_mixer_view_button;
ArdourWidgets::ArdourButton* livetrax_lock_button;
ArdourWidgets::ArdourButton* livetrax_view_in_folder_button;
Gtk::HScrollbar* livetrax_edit_hscrollbar;
Gtk::VScrollbar* livetrax_edit_vscrollbar;
Gtk::HScrollbar* livetrax_mix_hscrollbar;
Gtk::HBox livetrax_meter_box;
int livetrax_setup_windows ();
/* menu bar and associated stuff */

View File

@ -317,7 +317,7 @@ ARDOUR_UI::update_clock_visibility ()
}
void
ARDOUR_UI::setup_transport ()
ARDOUR_UI::connect_transport_elements ()
{
RefPtr<Action> act;
/* setup actions */
@ -360,6 +360,12 @@ ARDOUR_UI::setup_transport ()
act = ActionManager::get_action (X_("Monitor Section"), X_("monitor-cut-all"));
monitor_mute_button.set_related_action (act);
if (Profile->get_livetrax()) {
act = ActionManager::get_action (X_("Common"), X_("open-media-folder"));
std::cerr << "Connecting open media with " << act.get() << std::endl;
livetrax_view_in_folder_button->set_related_action (act);
}
act = ActionManager::get_action ("Main", "ToggleLatencyCompensation");
latency_disable_button.set_related_action (act);
@ -375,6 +381,12 @@ ARDOUR_UI::setup_transport ()
secondary_clock->ValueChanged.connect (sigc::mem_fun(*this, &ARDOUR_UI::secondary_clock_value_changed));
secondary_clock->change_display_delta_mode_signal.connect (sigc::mem_fun(UIConfiguration::instance(), &UIConfiguration::set_secondary_clock_delta_mode));
big_clock->ValueChanged.connect (sigc::mem_fun(*this, &ARDOUR_UI::big_clock_value_changed));
}
void
ARDOUR_UI::setup_transport ()
{
connect_transport_elements ();
editor_visibility_button.signal_drag_failed().connect (sigc::bind (sigc::ptr_fun (drag_failed), editor));
mixer_visibility_button.signal_drag_failed().connect (sigc::bind (sigc::ptr_fun (drag_failed), mixer));
@ -1060,4 +1072,3 @@ ARDOUR_UI::update_title ()
}
snapshot_name_label.set_markup (snap_label.str());
}

View File

@ -51,6 +51,7 @@
#include "luainstance.h"
#include "luawindow.h"
#include "main_clock.h"
#include "meterbridge.h"
#include "mixer_ui.h"
#include "recorder_ui.h"
#include "trigger_page.h"
@ -395,6 +396,7 @@ ARDOUR_UI::livetrax_setup_windows ()
Gtk::Label* l;
Gtk::VBox* vb;
Gtk::HBox* hb;
livetrax_top_bar.set_spacing (12);
livetrax_top_bar.set_border_width (12);
@ -416,8 +418,8 @@ ARDOUR_UI::livetrax_setup_windows ()
livetrax_top_bar.pack_end (*vb, false, false);
livetrax_multi_out_button = manage (new ArdourWidgets::ArdourButton (_("Multi Out")));
livetrax_stereo_out_button = manage (new ArdourWidgets::ArdourButton (_("Stereo Out")));
livetrax_multi_out_button = manage (new ArdourButton (_("Multi Out")));
livetrax_stereo_out_button = manage (new ArdourButton (_("Stereo Out")));
vb = manage (new Gtk::VBox);
vb->pack_start (*livetrax_stereo_out_button, true, true);
@ -428,15 +430,49 @@ ARDOUR_UI::livetrax_setup_windows ()
/* transport bar */
l = new Gtk::Label ("this is the transport bar with other controls too");
livetrax_transport_bar.pack_start (*l, true, true);
ArdourButton::Element elements (ArdourButton::Element (ArdourButton::Text|ArdourButton::VectorIcon));
livetrax_meter_view_button = manage (new ArdourButton (_("Meter Logo"), elements));
livetrax_mixer_view_button = manage (new ArdourButton (_("Meter Logo"), elements));
livetrax_editor_view_button = manage (new ArdourButton (_("Meter Logo"), elements));
livetrax_transport_bar.pack_start (*livetrax_editor_view_button, false, false);
livetrax_transport_bar.pack_start (*livetrax_mixer_view_button, false, false);
livetrax_transport_bar.pack_start (*livetrax_meter_view_button, false, false);
hb = manage (new Gtk::HBox);
hb->pack_start (transport_ctrl, false, false);
livetrax_lock_button = manage (new ArdourButton (_("Lock Icon"), elements));
editor->mouse_mode_hbox->pack_start (*livetrax_lock_button, false, false, 12);
livetrax_transport_bar.pack_start (*hb, true, false);
livetrax_transport_bar.pack_start (*editor->mouse_mode_hbox, true, false);
livetrax_view_in_folder_button = manage (new ArdourButton (_("Folder Icon"), elements));
editor->_zoom_box.pack_start (*livetrax_view_in_folder_button, false, false, 12);
livetrax_transport_bar.pack_start (editor->_zoom_box, true, false);
livetrax_transport_bar.show_all ();
/* meter display */
l = new Gtk::Label ("this is the meter display");
l = manage (new Gtk::Label ("meter area"));
livetrax_meter_bar.pack_start (*l, true, true);
livetrax_editor_bar.pack_start (editor->contents(), true, true);
hb = manage (new Gtk::HBox);
livetrax_edit_vscrollbar = manage (new Gtk::VScrollbar (editor->vertical_adjustment));
livetrax_edit_vscrollbar->show ();
hb->pack_start (editor->contents(), true, true);
hb->pack_start (*livetrax_edit_vscrollbar, false, false);
vb = manage (new Gtk::VBox);
livetrax_edit_hscrollbar = manage (new Gtk::HScrollbar (editor->horizontal_adjustment));
livetrax_edit_hscrollbar->show ();
vb->pack_start (*hb, true, true);
vb->pack_start (*livetrax_edit_hscrollbar, false, false);
livetrax_editor_bar.pack_start (*vb, true, true);
livetrax_mixer_bar.pack_start (mixer->contents(), true, true);
we_have_dependents ();
@ -450,6 +486,9 @@ ARDOUR_UI::livetrax_setup_windows ()
main_vpacker.pack_start (livetrax_editor_bar, true, true);
main_vpacker.pack_start (livetrax_mixer_bar, true, true);
connect_transport_elements ();
setup_tooltips ();
// setup_tooltips ();
_main_window.signal_delete_event().connect (sigc::mem_fun (*this, &ARDOUR_UI::main_window_delete_event));

View File

@ -299,6 +299,7 @@ ARDOUR_UI::install_actions ()
ActionManager::register_action (common_actions, X_("website-dev"), _("Development"), mem_fun(*this, &ARDOUR_UI::launch_website_dev));
ActionManager::register_action (common_actions, X_("forums"), _("User Forums"), mem_fun(*this, &ARDOUR_UI::launch_forums));
ActionManager::register_action (common_actions, X_("howto-report"), _("How to Report a Bug"), mem_fun(*this, &ARDOUR_UI::launch_howto_report));
ActionManager::register_action (common_actions, X_("open-media-folder"), _("Open Media Folder"), mem_fun(*this, &ARDOUR_UI::open_media_folder));
act = ActionManager::register_action (common_actions, X_("Save"), _("Save"), sigc::hide_return (sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::save_state), string(""), false)));
ActionManager::session_sensitive_actions.push_back (act);

View File

@ -117,12 +117,6 @@ ARDOUR_UI::setup_profile ()
if (gdk_screen_width() < 1200 || getenv ("ARDOUR_NARROW_SCREEN")) {
Profile->set_small_screen ();
}
if (g_getenv ("MIXBUS")) {
Profile->set_mixbus ();
}
Profile->set_livetrax ();
}
int

View File

@ -347,8 +347,6 @@ Editor::Editor ()
, videotl_group (0)
, _region_boundary_cache_dirty (true)
, edit_packer (4, 4, true)
, vertical_adjustment (0.0, 0.0, 10.0, 400.0)
, horizontal_adjustment (0.0, 0.0, 1e16)
, unused_adjustment (0.0, 0.0, 10.0, 400.0)
, controls_layout (unused_adjustment, vertical_adjustment)
, _scroll_callbacks (0)
@ -3308,7 +3306,7 @@ Editor::setup_toolbar ()
mode_box->set_spacing(2);
HBox* mouse_mode_box = manage (new HBox);
HBox* mouse_mode_hbox = manage (new HBox);
mouse_mode_hbox = manage (new HBox);
VBox* mouse_mode_vbox = manage (new VBox);
Alignment* mouse_mode_align = manage (new Alignment);
@ -3351,7 +3349,9 @@ Editor::setup_toolbar ()
mouse_mode_size_group->add_widget (nudge_backward_button);
mouse_mode_hbox->set_spacing (2);
mouse_mode_hbox->pack_start (smart_mode_button, false, false);
if (!Profile->get_livetrax()) {
mouse_mode_hbox->pack_start (smart_mode_button, false, false);
}
mouse_mode_hbox->pack_start (mouse_move_button, false, false);
mouse_mode_hbox->pack_start (mouse_select_button, false, false);
@ -3360,10 +3360,15 @@ Editor::setup_toolbar ()
mouse_mode_hbox->pack_start (mouse_timefx_button, false, false);
mouse_mode_hbox->pack_start (mouse_grid_button, false, false);
mouse_mode_hbox->pack_start (mouse_draw_button, false, false);
mouse_mode_hbox->pack_start (mouse_content_button, false, false);
mouse_mode_vbox->pack_start (*mouse_mode_hbox);
if (!Profile->get_livetrax()) {
mouse_mode_hbox->pack_start (mouse_draw_button, false, false);
mouse_mode_hbox->pack_start (mouse_content_button, false, false);
}
if (!Profile->get_livetrax()) {
mouse_mode_vbox->pack_start (*mouse_mode_hbox);
}
mouse_mode_align->add (*mouse_mode_vbox);
mouse_mode_align->set (0.5, 1.0, 0.0, 0.0);
@ -3379,7 +3384,9 @@ Editor::setup_toolbar ()
mode_box->pack_start (edit_point_selector, false, false);
mode_box->pack_start (*(manage (new ArdourVSpacer ())), false, false, 3);
mode_box->pack_start (*mouse_mode_box, false, false);
if (!Profile->get_livetrax()) {
mode_box->pack_start (*mouse_mode_box, false, false);
}
/* Zoom */
@ -3532,7 +3539,9 @@ Editor::setup_toolbar ()
toolbar_hbox.pack_start (grid_box, false, false);
toolbar_hbox.pack_start (_draw_box_spacer, false, false, 3);
toolbar_hbox.pack_start (draw_box, false, false);
toolbar_hbox.pack_end (_zoom_box, false, false, 2);
if (!Profile->get_livetrax()) {
toolbar_hbox.pack_end (_zoom_box, false, false, 2);
}
toolbar_hbox.pack_end (*(manage (new ArdourVSpacer ())), false, false, 3);
toolbar_hbox.pack_end (_track_box, false, false);

View File

@ -1169,10 +1169,6 @@ private:
Gtk::Table edit_packer;
/** the adjustment that controls the overall editor vertical scroll position */
Gtk::Adjustment vertical_adjustment;
Gtk::Adjustment horizontal_adjustment;
Gtk::Adjustment unused_adjustment; // yes, really; Gtk::Layout constructor requires refs
Gtk::Layout controls_layout;
bool control_layout_scroll (GdkEventScroll* ev);
@ -1998,6 +1994,7 @@ private:
void build_edit_mode_menu ();
Gtk::VBox edit_mode_box;
void set_ripple_mode (ARDOUR::RippleMode);
void set_edit_mode (ARDOUR::EditMode);
@ -2073,7 +2070,6 @@ private:
Gtk::HBox _track_box;
Gtk::HBox _zoom_box;
void zoom_adjustment_changed();
void setup_toolbar ();

View File

@ -1049,7 +1049,9 @@ Editor::tie_vertical_scrolling ()
{
if (pending_visual_change.idle_handler_id < 0) {
_region_peak_cursor->hide ();
_summary->set_overlays_dirty ();
if (_summary) {
_summary->set_overlays_dirty ();
}
}
}

View File

@ -51,6 +51,7 @@
#include "ardour/revision.h"
#include "ardour/ardour.h"
#include "ardour/audioengine.h"
#include "ardour/profile.h"
#include "ardour/session_utils.h"
#include "ardour/filesystem_paths.h"
@ -457,6 +458,15 @@ int main (int argc, char *argv[])
exit (EXIT_FAILURE);
}
if (g_getenv ("MIXBUS")) {
ARDOUR::Profile->set_mixbus ();
}
if (g_getenv ("LIVETRAX")) {
ARDOUR::Profile->set_livetrax ();
}
try {
ui = new ARDOUR_UI (&argc, &argv, localedir.c_str());
} catch (failed_constructor& err) {

View File

@ -150,6 +150,8 @@ MeterStrip::MeterStrip (Session* sess, std::shared_ptr<ARDOUR::Route> rt)
meter_width = 12;
}
meter_width = 30;
// level meter + ticks
level_meter = new LevelMeterHBox(sess);
level_meter->set_meter (_route->shared_peak_meter().get());
@ -457,6 +459,7 @@ MeterStrip::on_theme_changed()
if (_route->shared_peak_meter()->input_streams().n_total() == 1) {
meter_width = 12;
}
meter_width = 30;
level_meter->setup_meters (220, meter_width, 6);
}
}

View File

@ -154,7 +154,7 @@ MixerStrip::MixerStrip (Mixer_UI& mx, Session* sess, std::shared_ptr<Route> rt,
, gpm (sess, 250)
, panners (sess)
, trigger_display (-1., 8*16.)
, button_size_group (Gtk::SizeGroup::create (Gtk::SIZE_GROUP_HORIZONTAL))
, button_size_group (Gtk::SizeGroup::create (ARDOUR::Profile->get_livetrax() ? Gtk::SIZE_GROUP_BOTH : Gtk::SIZE_GROUP_HORIZONTAL))
, rec_mon_table (2, 2)
, solo_iso_table (1, 2)
, mute_solo_table (1, 2)
@ -237,10 +237,16 @@ MixerStrip::init ()
rec_mon_table.set_homogeneous (true);
rec_mon_table.set_row_spacings (2);
rec_mon_table.set_col_spacings (2);
if (ARDOUR::Profile->get_mixbus()) {
rec_mon_table.resize (1, 3);
if (ARDOUR::Profile->get_livetrax()) {
rec_mon_table.resize (1, 2);
rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
} else {
if (ARDOUR::Profile->get_mixbus()) {
rec_mon_table.resize (1, 3);
rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
}
}
rec_mon_table.show ();
@ -324,23 +330,31 @@ MixerStrip::init ()
set_tooltip (&number_label, _("Double-click to edit the route color.\nRight-click to show the route operations context menu."));
global_vpacker.set_spacing (2);
global_vpacker.pack_start (width_hide_box, Gtk::PACK_SHRINK);
global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
global_vpacker.pack_start (input_button_box, Gtk::PACK_SHRINK);
global_vpacker.pack_start (invert_button_box, Gtk::PACK_SHRINK);
global_vpacker.pack_start (trigger_display, Gtk::PACK_SHRINK);
global_vpacker.pack_start (_tmaster_widget, Gtk::PACK_SHRINK);
global_vpacker.pack_start (processor_box, true, true);
if (!Profile->get_livetrax()) {
global_vpacker.pack_start (width_hide_box, Gtk::PACK_SHRINK);
global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
global_vpacker.pack_start (input_button_box, Gtk::PACK_SHRINK);
global_vpacker.pack_start (invert_button_box, Gtk::PACK_SHRINK);
global_vpacker.pack_start (trigger_display, Gtk::PACK_SHRINK);
global_vpacker.pack_start (_tmaster_widget, Gtk::PACK_SHRINK);
global_vpacker.pack_start (processor_box, true, true);
}
global_vpacker.pack_start (panners, Gtk::PACK_SHRINK);
global_vpacker.pack_start (rec_mon_table, Gtk::PACK_SHRINK);
global_vpacker.pack_start (master_volume_table, Gtk::PACK_SHRINK);
global_vpacker.pack_start (solo_iso_table, Gtk::PACK_SHRINK);
if (!Profile->get_livetrax()) {
global_vpacker.pack_start (master_volume_table, Gtk::PACK_SHRINK);
global_vpacker.pack_start (solo_iso_table, Gtk::PACK_SHRINK);
}
global_vpacker.pack_start (mute_solo_table, Gtk::PACK_SHRINK);
global_vpacker.pack_start (gpm, Gtk::PACK_SHRINK);
global_vpacker.pack_start (control_slave_ui, Gtk::PACK_SHRINK);
global_vpacker.pack_start (bottom_button_table, Gtk::PACK_SHRINK);
global_vpacker.pack_start (output_button, Gtk::PACK_SHRINK);
global_vpacker.pack_start (_comment_button, Gtk::PACK_SHRINK);
if (!Profile->get_livetrax()) {
global_vpacker.pack_start (control_slave_ui, Gtk::PACK_SHRINK);
global_vpacker.pack_start (bottom_button_table, Gtk::PACK_SHRINK);
global_vpacker.pack_start (output_button, Gtk::PACK_SHRINK);
global_vpacker.pack_start (_comment_button, Gtk::PACK_SHRINK);
} else {
global_vpacker.pack_start (name_button, Gtk::PACK_SHRINK);
}
#ifndef MIXBUS
//add a spacer underneath the master bus;
@ -702,15 +716,19 @@ MixerStrip::set_route (std::shared_ptr<Route> rt)
if (is_track ()) {
rec_mon_table.attach (*rec_enable_button, 0, 1, 0, ARDOUR::Profile->get_mixbus() ? 1 : 2);
rec_mon_table.attach (*rec_enable_button, 0, 1, 0, (ARDOUR::Profile->get_mixbus()|ARDOUR::Profile->get_livetrax()) ? 1 : 2);
rec_enable_button->show();
if (ARDOUR::Profile->get_mixbus()) {
if (ARDOUR::Profile->get_livetrax()) {
rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
} else {
rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
if (ARDOUR::Profile->get_mixbus()) {
rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
rec_mon_table.attach (*monitor_disk_button, 2, 3, 0, 1);
} else {
rec_mon_table.attach (*monitor_input_button, 1, 2, 0, 1);
rec_mon_table.attach (*monitor_disk_button, 1, 2, 1, 2);
}
}
} else {

View File

@ -169,6 +169,8 @@ public:
void toggle_monitor_action (ARDOUR::MonitorChoice monitor_choice, bool group_override = false, bool all = false);
Gtk::ScrolledWindow scroller;
protected:
void set_axis_targets_for_operation ();
PBD::ControllableSet selected_gaincontrols ();
@ -179,7 +181,6 @@ private:
Gtk::VBox _content;
Gtk::HBox global_hpacker;
Gtk::VBox global_vpacker;
Gtk::ScrolledWindow scroller;
Gtk::EventBox scroller_base;
Gtk::HBox scroller_hpacker;
Gtk::VBox mixer_scroller_vpacker;

View File

@ -35,6 +35,8 @@ ARDOUR::DataType PublicEditor::pbdid_dragged_dt = ARDOUR::DataType::NIL;
PublicEditor::PublicEditor (Gtk::Widget& content)
: Tabbable (content, _("Editor"), X_("editor"))
, vertical_adjustment (0.0, 0.0, 10.0, 400.0)
, horizontal_adjustment (0.0, 0.0, 1e16)
{
_suspend_route_redisplay_counter.store (0);
}

View File

@ -602,6 +602,11 @@ public:
Gtkmm2ext::Bindings* bindings;
virtual SelectionPropertiesBox& properties_box() const = 0;
Gtk::HBox* mouse_mode_hbox;
Gtk::HBox _zoom_box;
/** the adjustment that controls the overall editor vertical scroll position */
Gtk::Adjustment vertical_adjustment;
Gtk::Adjustment horizontal_adjustment;
protected:
friend class DisplaySuspender;

View File

@ -125,21 +125,30 @@ TransportControlUI::setup (TransportControlProvider* ui)
#undef PX_SCALE
if (!ARDOUR::Profile->get_mixbus()) {
pack_start (midi_panic_button, true, true, 0);
if (!ARDOUR::Profile->get_livetrax()) {
if (!ARDOUR::Profile->get_mixbus()) {
pack_start (midi_panic_button, true, true, 0);
} else {
pack_start (midi_panic_button, true, true, 3);
}
pack_start (click_button, true, true, 0);
pack_start (goto_start_button, true, true);
pack_start (goto_end_button, true, true);
pack_start (auto_loop_button, true, true);
if (!ARDOUR::Profile->get_mixbus()) {
pack_start (play_selection_button, true, true);
}
pack_start (roll_button, true, true);
pack_start (stop_button, true, true);
pack_start (rec_button, true, true, 3);
} else {
pack_start (midi_panic_button, true, true, 3);
pack_start (goto_start_button, true, true);
pack_start (goto_end_button, true, true);
pack_start (roll_button, true, true);
pack_start (stop_button, true, true);
pack_start (rec_button, true, true, 3);
pack_start (auto_loop_button, true, true);
}
pack_start (click_button, true, true, 0);
pack_start (goto_start_button, true, true);
pack_start (goto_end_button, true, true);
pack_start (auto_loop_button, true, true);
if (!ARDOUR::Profile->get_mixbus()) {
pack_start (play_selection_button, true, true);
}
pack_start (roll_button, true, true);
pack_start (stop_button, true, true);
pack_start (rec_button, true, true, 3);
roll_button.set_name ("transport button");
stop_button.set_name ("transport button");