From 00c687fb9f6b68a3ba23cf030cdebbdb2383383b Mon Sep 17 00:00:00 2001 From: fgerlits Date: Fri, 12 Oct 2007 11:56:50 +0000 Subject: [PATCH] implemented multiple selection for righ-click context menus --- .../gLiveSupport/src/LiveModeWindow.cxx | 333 ++++++++++-------- .../gLiveSupport/src/LiveModeWindow.h | 61 +++- .../gLiveSupport/src/ScratchpadWindow.cxx | 120 ++++--- .../gLiveSupport/src/ScratchpadWindow.h | 43 ++- .../gLiveSupport/src/SearchWindow.cxx | 279 +++++++-------- .../products/gLiveSupport/src/SearchWindow.h | 50 ++- 6 files changed, 499 insertions(+), 387 deletions(-) diff --git a/campcaster/src/products/gLiveSupport/src/LiveModeWindow.cxx b/campcaster/src/products/gLiveSupport/src/LiveModeWindow.cxx index 4ec96861e..634ef0e36 100644 --- a/campcaster/src/products/gLiveSupport/src/LiveModeWindow.cxx +++ b/campcaster/src/products/gLiveSupport/src/LiveModeWindow.cxx @@ -87,15 +87,14 @@ LiveModeWindow :: LiveModeWindow (Gtk::ToggleButton * windowOpenerButton) isDeleting(false) { glade->get_widget_derived("treeView1", treeView); - treeModel = Gtk::ListStore::create(modelColumns); - treeView->set_model(treeModel); - treeView->connectModelSignals(treeModel); + treeView->get_selection()->set_mode(Gtk::SELECTION_MULTIPLE); treeView->appendLineNumberColumn("", 2 /* offset */, 50); treeView->appendColumn("", modelColumns.infoColumn, 200); - treeView->signal_button_press_event().connect_notify(sigc::mem_fun(*this, - &LiveModeWindow::onEntryClicked)); + treeView->signal_button_press_event().connect(sigc::mem_fun(*this, + &LiveModeWindow::onEntryClicked), + false /* call this first */); treeView->signal_row_activated().connect(sigc::mem_fun(*this, &LiveModeWindow::onDoubleClick)); treeView->signalTreeModelChanged().connect(sigc::mem_fun(*this, @@ -104,6 +103,10 @@ LiveModeWindow :: LiveModeWindow (Gtk::ToggleButton * windowOpenerButton) treeView->signal_key_press_event().connect(sigc::mem_fun(*this, &LiveModeWindow::onKeyPressed)); + treeModel = Gtk::ListStore::create(modelColumns); + treeView->set_model(treeModel); + treeView->connectModelSignals(treeModel); + glade->get_widget("cueLabel1", cueLabel); cueLabel->set_label(*getResourceUstring("cuePlayerLabel")); cuePlayer.reset(new CuePlayer(this, @@ -225,93 +228,77 @@ LiveModeWindow :: popTop(void) throw () } -/*------------------------------------------------------------------------------ - * Find the selected row. - *----------------------------------------------------------------------------*/ -Gtk::TreeModel::iterator -LiveModeWindow :: getSelected(void) throw () -{ - Glib::RefPtr selection - = treeView->get_selection(); - std::vector selectedPaths - = selection->get_selected_rows(); - - Gtk::TreeModel::iterator it; - if (selectedPaths.size() > 0) { - it = treeModel->get_iter(selectedPaths.front()); - } - return it; -} - - -/*------------------------------------------------------------------------------ - * Signal handler for the output play button clicked. - *----------------------------------------------------------------------------*/ -void -LiveModeWindow :: onOutputPlay(void) throw () -{ - Gtk::TreeModel::iterator iter = getSelected(); - - if (!iter) { - iter = treeModel->children().begin(); - } - - if (iter) { - Ptr::Ref playable = (*iter)[modelColumns.playableColumn]; - try { - gLiveSupport->playOutputAudio(playable); - gLiveSupport->setNowPlaying(playable); - treeView->removeItem(iter); - gLiveSupport->runMainLoop(); - } catch (std::runtime_error &e) { - std::cerr << "cannot play on live mode output device: " - << e.what() << std::endl; - } - } -} - - /*------------------------------------------------------------------------------ * Event handler for an entry being clicked in the list. *----------------------------------------------------------------------------*/ -void +bool LiveModeWindow :: onEntryClicked(GdkEventButton * event) throw () { if (event->type == GDK_BUTTON_PRESS && event->button == 3) { - Gtk::TreePath currentPath; - Gtk::TreeViewColumn * column; - int cell_x, - cell_y; - bool foundValidRow = treeView->get_path_at_pos( - int(event->x), int(event->y), - currentPath, column, - cell_x, cell_y); + Ptr::Ref playable = getFirstSelectedPlayable(); - if (foundValidRow) { - Gtk::TreeIter iter = treeModel->get_iter(currentPath); - if (iter) { - Ptr::Ref playable = - (*iter)[modelColumns.playableColumn]; + if (selectedPaths->size() == 1) { + if (playable->getType() == Playable::AudioClipType) { + audioClipContextMenu->popup(event->button, event->time); + return true; - if (playable) { - switch (playable->getType()) { - case Playable::AudioClipType: - audioClipContextMenu->popup(event->button, - event->time); - break; - - case Playable::PlaylistType: - playlistContextMenu->popup(event->button, - event->time); - break; - - default: - break; - } - } + } else if (playable->getType() == Playable::PlaylistType) { + playlistContextMenu->popup(event->button, event->time); + return true; } + + } else if (selectedPaths->size() > 1) { + audioClipContextMenu->popup(event->button, event->time); + return true; } } + + return false; +} + + +/*------------------------------------------------------------------------------ + * Return the first selected playable item. + *----------------------------------------------------------------------------*/ +Ptr::Ref +LiveModeWindow :: getFirstSelectedPlayable(void) throw () +{ + Ptr::Ref playable; + + Glib::RefPtr + selection = treeView->get_selection(); + selectedPaths.reset(new std::vector( + selection->get_selected_rows())); + + if (selectedPaths->size() > 0) { + selectedIter = selectedPaths->begin(); + Gtk::TreeRow row = *(treeModel->get_iter(*selectedIter)); + playable = row[modelColumns.playableColumn]; + } + + return playable; +} + + +/*------------------------------------------------------------------------------ + * Return the next selected playable item. + *----------------------------------------------------------------------------*/ +Ptr::Ref +LiveModeWindow :: getNextSelectedPlayable(void) throw () +{ + Ptr::Ref playable; + + if (selectedPaths) { + if (selectedIter != selectedPaths->end()) { + Gtk::TreeRow row = *(treeModel->get_iter(*selectedIter)); + playable = row[modelColumns.playableColumn]; + ++selectedIter; + } else { + selectedPaths.reset(); + } + } + + return playable; } @@ -334,32 +321,36 @@ bool LiveModeWindow :: onKeyPressed(GdkEventKey * event) throw () { if (event->type == GDK_KEY_PRESS) { - Gtk::TreeModel::iterator iter = getSelected(); - - if (iter) { - KeyboardShortcut::Action action = gLiveSupport->findAction( - "liveModeWindow", - Gdk::ModifierType(event->state), - event->keyval); - switch (action) { - case KeyboardShortcut::moveItemUp : + KeyboardShortcut::Action action = gLiveSupport->findAction( + "liveModeWindow", + Gdk::ModifierType(event->state), + event->keyval); + switch (action) { + case KeyboardShortcut::moveItemUp : + if (selectionIsSingle()) { treeView->onUpMenuOption(); return true; + } + break; - case KeyboardShortcut::moveItemDown : + case KeyboardShortcut::moveItemDown : + if (selectionIsSingle()) { treeView->onDownMenuOption(); return true; - - case KeyboardShortcut::removeItem : - onRemoveItemButtonClicked(); - return true; - - case KeyboardShortcut::playAudio : - onOutputPlay(); - return true; - - default : break; - } + } + break; + + case KeyboardShortcut::removeItem : + onRemoveMenuOption(); + return true; + break; + + case KeyboardShortcut::playAudio : + onOutputPlay(); + return true; + break; + + default : break; } } @@ -367,6 +358,49 @@ LiveModeWindow :: onKeyPressed(GdkEventKey * event) throw () } +/*------------------------------------------------------------------------------ + * Check whether exactly one row is selected. + *----------------------------------------------------------------------------*/ +bool +LiveModeWindow :: selectionIsSingle(void) throw () +{ + getFirstSelectedPlayable(); + + return (selectedPaths->size() == 1); +} + + +/*------------------------------------------------------------------------------ + * Signal handler for the output play button clicked. + *----------------------------------------------------------------------------*/ +void +LiveModeWindow :: onOutputPlay(void) throw () +{ + Ptr::Ref playable = getFirstSelectedPlayable(); +std::cerr << "got playable: "; +if (playable) { +std::cerr << *playable->getTitle() << std::endl; +} else { +std::cerr << "null" << std::endl; +} + if (playable) { + try { + gLiveSupport->playOutputAudio(playable); + gLiveSupport->setNowPlaying(playable); + + Gtk::TreeIter iter = treeModel->get_iter(*selectedIter); + treeView->removeItem(iter); + + gLiveSupport->runMainLoop(); + + } catch (std::runtime_error &e) { + std::cerr << "cannot play on live mode output device: " + << e.what() << std::endl; + } + } +} + + /*------------------------------------------------------------------------------ * Event handler for the Edit Playlist menu item selected from the * entry context menu. @@ -374,18 +408,15 @@ LiveModeWindow :: onKeyPressed(GdkEventKey * event) throw () void LiveModeWindow :: onEditPlaylist(void) throw () { - Gtk::TreeModel::iterator iter = getSelected(); - - if (iter) { - Ptr::Ref playable = (*iter)[modelColumns.playableColumn]; - Ptr::Ref playlist = playable->getPlaylist(); - if (playlist) { - try { - gLiveSupport->openPlaylistForEditing(playlist->getId()); - } catch (XmlRpcException &e) { - gLiveSupport->displayMessageWindow(*getResourceUstring( - "cannotEditPlaylistMsg" )); - } + Ptr::Ref playable = getNextSelectedPlayable(); + Ptr::Ref playlist = playable->getPlaylist(); + + if (playlist) { + try { + gLiveSupport->openPlaylistForEditing(playlist->getId()); + } catch (XmlRpcException &e) { + gLiveSupport->displayMessageWindow(*getResourceUstring( + "cannotEditPlaylistMsg" )); } } } @@ -398,15 +429,9 @@ LiveModeWindow :: onEditPlaylist(void) throw () void LiveModeWindow :: onSchedulePlaylist(void) throw () { - Gtk::TreeModel::iterator iter = getSelected(); - - if (!iter) { - return; - } - - Ptr::Ref playable = (*iter)[modelColumns.playableColumn]; - Ptr::Ref playlist = playable->getPlaylist(); - + Ptr::Ref playable = getNextSelectedPlayable(); + Ptr::Ref playlist = playable->getPlaylist(); + if (playlist) { schedulePlaylistWindow.reset(new SchedulePlaylistWindow(playlist)); schedulePlaylistWindow->getWindow()->set_transient_for(*mainWindow); @@ -421,15 +446,9 @@ LiveModeWindow :: onSchedulePlaylist(void) throw () void LiveModeWindow :: onExportPlaylist(void) throw () { - Gtk::TreeModel::iterator iter = getSelected(); - - if (!iter) { - return; - } - - Ptr::Ref playable = (*iter)[modelColumns.playableColumn]; - Ptr::Ref playlist = playable->getPlaylist(); - + Ptr::Ref playable = getNextSelectedPlayable(); + Ptr::Ref playlist = playable->getPlaylist(); + if (playlist) { exportPlaylistWindow.reset(new ExportPlaylistWindow(playlist)); exportPlaylistWindow->getWindow()->set_transient_for(*mainWindow); @@ -445,10 +464,9 @@ LiveModeWindow :: onExportPlaylist(void) throw () void LiveModeWindow :: onAddToPlaylist(void) throw () { - Gtk::TreeModel::iterator iter = getSelected(); + Ptr::Ref playable; - if (iter) { - Ptr::Ref playable = (*iter)[modelColumns.playableColumn]; + while ((playable = getNextSelectedPlayable())) { try { gLiveSupport->addToPlaylist(playable->getId()); } catch (XmlRpcException &e) { @@ -466,10 +484,9 @@ LiveModeWindow :: onAddToPlaylist(void) throw () void LiveModeWindow :: onUploadToHub(void) throw () { - Gtk::TreeModel::iterator iter = getSelected(); + Ptr::Ref playable; - if (iter) { - Ptr::Ref playable = (*iter)[modelColumns.playableColumn]; + while ((playable = getNextSelectedPlayable())) { gLiveSupport->uploadToHub(playable); } } @@ -514,8 +531,8 @@ LiveModeWindow :: constructAudioClipContextMenu(void) throw () &LiveModeWindow::onAddToPlaylist))); contextMenuList.push_back(Gtk::Menu_Helpers::MenuElem( *getResourceUstring("removeMenuItem"), - sigc::mem_fun(*treeView, - &ZebraTreeView::onRemoveMenuOption))); + sigc::mem_fun(*this, + &LiveModeWindow::onRemoveMenuOption))); contextMenuList.push_back(Gtk::Menu_Helpers::SeparatorElem()); contextMenuList.push_back(Gtk::Menu_Helpers::MenuElem( *getResourceUstring("uploadToHubMenuItem"), @@ -550,8 +567,8 @@ LiveModeWindow :: constructPlaylistContextMenu(void) throw () &LiveModeWindow::onAddToPlaylist))); contextMenuList.push_back(Gtk::Menu_Helpers::MenuElem( *getResourceUstring("removeMenuItem"), - sigc::mem_fun(*treeView, - &ZebraTreeView::onRemoveMenuOption))); + sigc::mem_fun(*this, + &LiveModeWindow::onRemoveMenuOption))); contextMenuList.push_back(Gtk::Menu_Helpers::SeparatorElem()); contextMenuList.push_back(Gtk::Menu_Helpers::MenuElem( *getResourceUstring("editPlaylistMenuItem"), @@ -577,13 +594,39 @@ LiveModeWindow :: constructPlaylistContextMenu(void) throw () /*------------------------------------------------------------------------------ - * Event handler for the Remove menu button getting clicked. + * Event handler for the Remove menu item selected from the context menu. *----------------------------------------------------------------------------*/ void -LiveModeWindow :: onRemoveItemButtonClicked(void) throw () +LiveModeWindow :: onRemoveMenuOption(void) throw () { isDeleting = true; - treeView->onRemoveMenuOption(); + + Glib::RefPtr selection + = treeView->get_selection(); + std::vector selectedPaths + = selection->get_selected_rows(); + + std::vector selectedIters; + for (std::vector::iterator pathIt = selectedPaths.begin(); + pathIt != selectedPaths.end(); + ++pathIt) { + selectedIters.push_back(treeModel->get_iter(*pathIt)); + } + + Gtk::TreeModel::iterator newSelection; + for (std::vector::iterator + iterIt = selectedIters.begin(); + iterIt != selectedIters.end(); + ++iterIt) { + newSelection = *iterIt; + ++newSelection; + treeModel->erase(*iterIt); + } + + if (newSelection) { + selection->select(newSelection); + } + isDeleting = false; onTreeModelChanged(); } diff --git a/campcaster/src/products/gLiveSupport/src/LiveModeWindow.h b/campcaster/src/products/gLiveSupport/src/LiveModeWindow.h index e98a991ac..8e2544544 100644 --- a/campcaster/src/products/gLiveSupport/src/LiveModeWindow.h +++ b/campcaster/src/products/gLiveSupport/src/LiveModeWindow.h @@ -119,13 +119,24 @@ class LiveModeWindow : public GuiWindow, */ Gtk::CheckButton * autoPlayNext; + /** + * The list of selected rows, as path references (row numbers). + * Reset by onEntryClicked(). + */ + Ptr >::Ref selectedPaths; + /** + * One of the selected rows, set to the first one by onEntryClicked(). + * Incremented by getNextSelectedPlayable(). + */ + std::vector::const_iterator selectedIter; + /** * Construct the right-click context menu for local audio clips. * * @return the context menu created. */ Ptr::Ref - constructAudioClipContextMenu(void) throw (); + constructAudioClipContextMenu(void) throw (); /** * Construct the right-click context menu for local playlists. @@ -133,17 +144,39 @@ class LiveModeWindow : public GuiWindow, * @return the context menu created. */ Ptr::Ref - constructPlaylistContextMenu(void) throw (); + constructPlaylistContextMenu(void) throw (); /** - * Find the selected row. - * If more than one row is selected, it returns the first one. + * Return the topmost selected row. + * Sets selectedPaths and selectedIter; does not increment it. * - * @return an iterator for the selected row; may be invalid - * if nothing is selected. + * @return the first selected playable item. */ - Gtk::TreeModel::iterator - getSelected(void) throw (); + Ptr::Ref + getFirstSelectedPlayable(void) throw (); + + /** + * Used to iterate over the selected rows. + * Can only be called after onEntryClicked() has set the selectedPaths + * and selectedIter variables. + * Returns a 0 pointer if nothing is selected or we have reached the + * end of the list of selected rows. + * Increments selectedIter after reading it. + * + * @return the next selected playable item. + */ + Ptr::Ref + getNextSelectedPlayable(void) throw (); + + /** + * Check whether exactly one row is selected. + * + * This is an auxilliary function used by onKeyPressed(). + * + * @return true if a single row is selected, false if not. + */ + bool + selectionIsSingle(void) throw (); protected: @@ -220,15 +253,18 @@ class LiveModeWindow : public GuiWindow, * This brings up the right-click context menu. * * @param event the button event recieved + * @return true if the event has been handled (a popup displayed), + * false otherwise */ - void + bool onEntryClicked(GdkEventButton * event) throw (); /** * Signal handler for the user double-clicking, or pressing Enter * on one of the entries. * - * @param event the button event recieved + * @param path the TreePath of the row clicked on (ignored). + * @param column the TreeViewColumn clicked on (ignored). */ void onDoubleClick(const Gtk::TreeModel::Path & path, @@ -285,10 +321,11 @@ class LiveModeWindow : public GuiWindow, onUploadToHub(void) throw (); /** - * Signal handler for the remove item button clicked. + * Signal handler for the "remove" menu item selected from + * the entry context menu. */ virtual void - onRemoveItemButtonClicked(void) throw (); + onRemoveMenuOption(void) throw (); /** * Signal handler for a change in the tree model. diff --git a/campcaster/src/products/gLiveSupport/src/ScratchpadWindow.cxx b/campcaster/src/products/gLiveSupport/src/ScratchpadWindow.cxx index d973c4a44..9ea50225d 100644 --- a/campcaster/src/products/gLiveSupport/src/ScratchpadWindow.cxx +++ b/campcaster/src/products/gLiveSupport/src/ScratchpadWindow.cxx @@ -114,67 +114,67 @@ ScratchpadWindow :: ScratchpadWindow ( modelColumns)); // create the right-click entry context menu for audio clips - audioClipMenu.reset(new Gtk::Menu()); - audioClipMenu->items().push_back(Gtk::Menu_Helpers::MenuElem( + audioClipContextMenu.reset(new Gtk::Menu()); + audioClipContextMenu->items().push_back(Gtk::Menu_Helpers::MenuElem( *getResourceUstring("cueMenuItem"), sigc::mem_fun(*cuePlayer, &CuePlayer::onPlayItem))); - audioClipMenu->items().push_back(Gtk::Menu_Helpers::MenuElem( + audioClipContextMenu->items().push_back(Gtk::Menu_Helpers::MenuElem( *getResourceUstring("addToLiveModeMenuItem"), sigc::mem_fun(*this, &ScratchpadWindow::onAddToLiveMode))); - audioClipMenu->items().push_back(Gtk::Menu_Helpers::MenuElem( + audioClipContextMenu->items().push_back(Gtk::Menu_Helpers::MenuElem( *getResourceUstring("addToPlaylistMenuItem"), sigc::mem_fun(*this, &ScratchpadWindow::onAddToPlaylist))); - audioClipMenu->items().push_back(Gtk::Menu_Helpers::MenuElem( + audioClipContextMenu->items().push_back(Gtk::Menu_Helpers::MenuElem( *getResourceUstring("removeMenuItem"), sigc::mem_fun(*this, &ScratchpadWindow::onRemoveMenuOption))); - audioClipMenu->items().push_back(Gtk::Menu_Helpers::SeparatorElem()); - audioClipMenu->items().push_back(Gtk::Menu_Helpers::MenuElem( + audioClipContextMenu->items().push_back(Gtk::Menu_Helpers::SeparatorElem()); + audioClipContextMenu->items().push_back(Gtk::Menu_Helpers::MenuElem( *getResourceUstring("uploadToHubMenuItem"), sigc::mem_fun(*this, &ScratchpadWindow::onUploadToHub))); - audioClipMenu->accelerate(*mainWindow); + audioClipContextMenu->accelerate(*mainWindow); // create the right-click entry context menu for playlists - playlistMenu.reset(new Gtk::Menu()); - playlistMenu->items().push_back(Gtk::Menu_Helpers::MenuElem( + playlistContextMenu.reset(new Gtk::Menu()); + playlistContextMenu->items().push_back(Gtk::Menu_Helpers::MenuElem( *getResourceUstring("cueMenuItem"), sigc::mem_fun(*cuePlayer, &CuePlayer::onPlayItem))); - playlistMenu->items().push_back(Gtk::Menu_Helpers::MenuElem( + playlistContextMenu->items().push_back(Gtk::Menu_Helpers::MenuElem( *getResourceUstring("addToLiveModeMenuItem"), sigc::mem_fun(*this, &ScratchpadWindow::onAddToLiveMode))); - playlistMenu->items().push_back(Gtk::Menu_Helpers::MenuElem( + playlistContextMenu->items().push_back(Gtk::Menu_Helpers::MenuElem( *getResourceUstring("addToPlaylistMenuItem"), sigc::mem_fun(*this, &ScratchpadWindow::onAddToPlaylist))); - playlistMenu->items().push_back(Gtk::Menu_Helpers::MenuElem( + playlistContextMenu->items().push_back(Gtk::Menu_Helpers::MenuElem( *getResourceUstring("removeMenuItem"), sigc::mem_fun(*this, &ScratchpadWindow::onRemoveMenuOption))); - playlistMenu->items().push_back(Gtk::Menu_Helpers::SeparatorElem()); - playlistMenu->items().push_back(Gtk::Menu_Helpers::MenuElem( + playlistContextMenu->items().push_back(Gtk::Menu_Helpers::SeparatorElem()); + playlistContextMenu->items().push_back(Gtk::Menu_Helpers::MenuElem( *getResourceUstring("editPlaylistMenuItem"), sigc::mem_fun(*this, &ScratchpadWindow::onEditPlaylist))); - playlistMenu->items().push_back(Gtk::Menu_Helpers::MenuElem( + playlistContextMenu->items().push_back(Gtk::Menu_Helpers::MenuElem( *getResourceUstring("schedulePlaylistMenuItem"), sigc::mem_fun(*this, &ScratchpadWindow::onSchedulePlaylist))); - playlistMenu->items().push_back(Gtk::Menu_Helpers::MenuElem( + playlistContextMenu->items().push_back(Gtk::Menu_Helpers::MenuElem( *getResourceUstring("exportPlaylistMenuItem"), sigc::mem_fun(*this, &ScratchpadWindow::onExportPlaylist))); - playlistMenu->items().push_back(Gtk::Menu_Helpers::SeparatorElem()); - playlistMenu->items().push_back(Gtk::Menu_Helpers::MenuElem( + playlistContextMenu->items().push_back(Gtk::Menu_Helpers::SeparatorElem()); + playlistContextMenu->items().push_back(Gtk::Menu_Helpers::MenuElem( *getResourceUstring("uploadToHubMenuItem"), sigc::mem_fun(*this, &ScratchpadWindow::onUploadToHub))); - playlistMenu->accelerate(*mainWindow); + playlistContextMenu->accelerate(*mainWindow); // set the user preferences key userPreferencesKey.reset(new const Glib::ustring(userPreferencesKeyName)); @@ -188,32 +188,21 @@ bool ScratchpadWindow :: onEntryClicked (GdkEventButton * event) throw () { if (event->type == GDK_BUTTON_PRESS && event->button == 3) { - Glib::RefPtr - selection = treeView->get_selection(); - selectedPaths.reset(new std::vector( - selection->get_selected_rows())); + Ptr::Ref playable = getFirstSelectedPlayable(); - if (selectedPaths->size() > 0) { - selectedIter = selectedPaths->begin(); - - if (selectedPaths->size() == 1) { - Gtk::TreeRow row = *(treeModel->get_iter(*selectedIter)); - Ptr::Ref - playable = row[modelColumns.playableColumn]; + if (selectedPaths->size() == 1) { + if (playable->getType() == Playable::AudioClipType) { + audioClipContextMenu->popup(event->button, event->time); + return true; - if (playable->getType() == Playable::AudioClipType) { - audioClipMenu->popup(event->button, event->time); - return true; - - } else if (playable->getType() == Playable::PlaylistType) { - playlistMenu->popup(event->button, event->time); - return true; - } - - } else { // selectedPaths.size() > 1 - audioClipMenu->popup(event->button, event->time); + } else if (playable->getType() == Playable::PlaylistType) { + playlistContextMenu->popup(event->button, event->time); return true; } + + } else if (selectedPaths->size() > 1) { + audioClipContextMenu->popup(event->button, event->time); + return true; } } @@ -221,6 +210,29 @@ ScratchpadWindow :: onEntryClicked (GdkEventButton * event) throw () } +/*------------------------------------------------------------------------------ + * Return the first selected playable item. + *----------------------------------------------------------------------------*/ +Ptr::Ref +ScratchpadWindow :: getFirstSelectedPlayable(void) throw () +{ + Ptr::Ref playable; + + Glib::RefPtr + selection = treeView->get_selection(); + selectedPaths.reset(new std::vector( + selection->get_selected_rows())); + + if (selectedPaths->size() > 0) { + selectedIter = selectedPaths->begin(); + Gtk::TreeRow row = *(treeModel->get_iter(*selectedIter)); + playable = row[modelColumns.playableColumn]; + } + + return playable; +} + + /*------------------------------------------------------------------------------ * Return the next selected playable item. *----------------------------------------------------------------------------*/ @@ -369,7 +381,7 @@ ScratchpadWindow :: onUploadToHub(void) throw () /*------------------------------------------------------------------------------ - * Event handler for the Remove menu item selected from the entry conext menu + * Event handler for the Remove menu item selected from the context menu. *----------------------------------------------------------------------------*/ void ScratchpadWindow :: onRemoveMenuOption(void) throw () @@ -410,13 +422,9 @@ ScratchpadWindow :: onDoubleClick(const Gtk::TreeModel::Path & path, const Gtk::TreeViewColumn * column) throw () { - Glib::RefPtr - selection = treeView->get_selection(); - selectedPaths.reset(new std::vector( - selection->get_selected_rows())); + Ptr::Ref playable = getFirstSelectedPlayable(); - if (selectedPaths->size() > 0) { - selectedIter = selectedPaths->begin(); + if (playable) { onAddToLiveMode(); } } @@ -435,14 +443,14 @@ ScratchpadWindow :: onKeyPressed(GdkEventKey * event) throw () event->keyval); switch (action) { case KeyboardShortcut::moveItemUp : - if (isSelectionSingle()) { + if (selectionIsSingle()) { treeView->onUpMenuOption(); return true; } break; case KeyboardShortcut::moveItemDown : - if (isSelectionSingle()) { + if (selectionIsSingle()) { treeView->onDownMenuOption(); return true; } @@ -451,6 +459,7 @@ ScratchpadWindow :: onKeyPressed(GdkEventKey * event) throw () case KeyboardShortcut::removeItem : onRemoveMenuOption(); return true; + break; default : break; } @@ -464,14 +473,11 @@ ScratchpadWindow :: onKeyPressed(GdkEventKey * event) throw () * Check whether exactly one row is selected. *----------------------------------------------------------------------------*/ bool -ScratchpadWindow :: isSelectionSingle(void) throw () +ScratchpadWindow :: selectionIsSingle(void) throw () { - Glib::RefPtr - selection = treeView->get_selection(); - std::vector - selectedRows = selection->get_selected_rows(); + getFirstSelectedPlayable(); - return (selectedRows.size() == 1); + return (selectedPaths->size() == 1); } diff --git a/campcaster/src/products/gLiveSupport/src/ScratchpadWindow.h b/campcaster/src/products/gLiveSupport/src/ScratchpadWindow.h index 6cbfb1ee5..bc7d67d7a 100644 --- a/campcaster/src/products/gLiveSupport/src/ScratchpadWindow.h +++ b/campcaster/src/products/gLiveSupport/src/ScratchpadWindow.h @@ -94,13 +94,33 @@ class ScratchpadWindow : public GuiWindow, */ Ptr::Ref schedulePlaylistWindow; + /** + * The list of selected rows, as path references (row numbers). + * Reset by onEntryClicked(). + */ + Ptr >::Ref selectedPaths; + /** + * One of the selected rows, set to the first one by onEntryClicked(). + * Incremented by getNextSelectedPlayable(). + */ + std::vector::const_iterator selectedIter; + + /** + * Return the topmost selected row. + * Sets selectedPaths and selectedIter; does not increment it. + * + * @return the first selected playable item. + */ + Ptr::Ref + getFirstSelectedPlayable(void) throw (); + /** * Used to iterate over the selected rows. * Reset to the first row by onEntryClicked(). * Returns a 0 pointer if nothing is selected or we have reached * the end of the list of selected rows. * - * @return the next selected item. + * @return the next selected playable item. */ Ptr::Ref getNextSelectedPlayable(void) throw (); @@ -113,7 +133,7 @@ class ScratchpadWindow : public GuiWindow, * @return true if a single row is selected, false if not. */ bool - isSelectionSingle(void) throw (); + selectionIsSingle(void) throw (); /** * Remove an item from the Scratchpad. @@ -184,18 +204,6 @@ class ScratchpadWindow : public GuiWindow, */ ZebraTreeView * treeView; - /** - * The list of selected rows, as path references (row numbers). - * Reset by onEntryClicked(); - */ - Ptr >::Ref - selectedPaths; - /** - * One of the selected rows, set to the first one by onEntryClicked(). - */ - std::vector::const_iterator - selectedIter; - /** * The cue player widget controlling the audio buttons. */ @@ -205,13 +213,13 @@ class ScratchpadWindow : public GuiWindow, * The right-click context menu for audio clips, * that comes up when right-clicking an entry in the entry list. */ - Ptr::Ref audioClipMenu; + Ptr::Ref audioClipContextMenu; /** * The right-click context menu for playlists, * that comes up when right-clicking an entry in the entry list. */ - Ptr::Ref playlistMenu; + Ptr::Ref playlistContextMenu; /** * Signal handler for the mouse clicked on one of the entries. @@ -228,7 +236,8 @@ class ScratchpadWindow : public GuiWindow, * Signal handler for the user double-clicking, or pressing Enter * on one of the entries. * - * @param event the button event recieved + * @param path the TreePath of the row clicked on (ignored). + * @param column the TreeViewColumn clicked on (ignored). */ void onDoubleClick(const Gtk::TreeModel::Path & path, diff --git a/campcaster/src/products/gLiveSupport/src/SearchWindow.cxx b/campcaster/src/products/gLiveSupport/src/SearchWindow.cxx index 2c6b517c9..be4b4dd50 100644 --- a/campcaster/src/products/gLiveSupport/src/SearchWindow.cxx +++ b/campcaster/src/products/gLiveSupport/src/SearchWindow.cxx @@ -210,6 +210,7 @@ SearchWindow :: constructSearchResultsView(void) throw () remoteSearchResults = Gtk::ListStore::create(modelColumns); glade->get_widget_derived("searchResultsTreeView1", searchResultsTreeView); + searchResultsTreeView->get_selection()->set_mode(Gtk::SELECTION_MULTIPLE); searchResultsTreeView->set_model(localSearchResults); searchResultsTreeView->connectModelSignals(localSearchResults); searchResultsTreeView->connectModelSignals(remoteSearchResults); @@ -230,9 +231,10 @@ SearchWindow :: constructSearchResultsView(void) throw () *getResourceUstring("lengthColumnLabel"), modelColumns.lengthColumn, 55); - searchResultsTreeView->signal_button_press_event().connect_notify( + searchResultsTreeView->signal_button_press_event().connect( sigc::mem_fun(*this, - &SearchWindow::onEntryClicked)); + &SearchWindow::onEntryClicked), + false /* call this first */); searchResultsTreeView->signal_row_activated().connect(sigc::mem_fun(*this, &SearchWindow::onDoubleClick)); @@ -589,49 +591,78 @@ SearchWindow :: displayRemoteSearchError(const XmlRpcException & error) /*------------------------------------------------------------------------------ * Event handler for an entry being clicked in the list *----------------------------------------------------------------------------*/ -void +bool SearchWindow :: onEntryClicked (GdkEventButton * event) throw () { if (event->type == GDK_BUTTON_PRESS && event->button == 3) { - Gtk::TreePath currentPath; - Gtk::TreeViewColumn * column; - int cell_x, - cell_y; - bool foundValidRow = searchResultsTreeView->get_path_at_pos( - int(event->x), int(event->y), - currentPath, column, - cell_x, cell_y); + Ptr::Ref playable = getFirstSelectedPlayable(); - if (foundValidRow) { - Gtk::TreeIter iter = searchResultsTreeView->get_model() - ->get_iter(currentPath); - if (iter) { - Ptr::Ref playable = - (*iter)[modelColumns.playableColumn]; + if (selectedPaths->size() == 1) { + if (playable->getType() == Playable::AudioClipType) { + audioClipContextMenu->popup(event->button, event->time); + return true; - if (playable) { - if (searchIsLocal()) { - switch (playable->getType()) { - case Playable::AudioClipType: - audioClipContextMenu->popup(event->button, - event->time); - break; - - case Playable::PlaylistType: - playlistContextMenu->popup(event->button, - event->time); - break; - - default: - break; - } - } else { - remoteContextMenu->popup(event->button, event->time); - } - } + } else if (playable->getType() == Playable::PlaylistType) { + playlistContextMenu->popup(event->button, event->time); + return true; } + + } else if (selectedPaths->size() > 1) { + audioClipContextMenu->popup(event->button, event->time); + return true; } } + + return false; +} + + +/*------------------------------------------------------------------------------ + * Return the first selected playable item. + *----------------------------------------------------------------------------*/ +Ptr::Ref +SearchWindow :: getFirstSelectedPlayable(void) throw () +{ + Ptr::Ref playable; + + Glib::RefPtr + selection = searchResultsTreeView->get_selection(); + selectedPaths.reset(new std::vector( + selection->get_selected_rows())); + + if (selectedPaths->size() > 0) { + selectedIter = selectedPaths->begin(); + Glib::RefPtr + treeModel = searchResultsTreeView->get_model(); + Gtk::TreeRow row = *(treeModel->get_iter(*selectedIter)); + playable = row[modelColumns.playableColumn]; + } + + return playable; +} + + +/*------------------------------------------------------------------------------ + * Return the next selected playable item. + *----------------------------------------------------------------------------*/ +Ptr::Ref +SearchWindow :: getNextSelectedPlayable(void) throw () +{ + Ptr::Ref playable; + + if (selectedPaths) { + if (selectedIter != selectedPaths->end()) { + Glib::RefPtr + treeModel = searchResultsTreeView->get_model(); + Gtk::TreeRow row = *(treeModel->get_iter(*selectedIter)); + playable = row[modelColumns.playableColumn]; + ++selectedIter; + } else { + selectedPaths.reset(); + } + } + + return playable; } @@ -641,22 +672,16 @@ SearchWindow :: onEntryClicked (GdkEventButton * event) throw () void SearchWindow :: onAddToScratchpad(void) throw () { - Glib::RefPtr - refSelection = searchResultsTreeView->get_selection(); - Gtk::TreeModel::iterator - iter = refSelection->get_selected(); - - if (iter) { - Ptr::Ref playable = (*iter)[modelColumns.playableColumn]; - if (playable) { - try { - gLiveSupport->addToScratchpad(playable); - } catch (XmlRpcException &e) { - Ptr::Ref errorMessage(new Glib::ustring( - "error in SearchWindow::onAddToScratchpad(): ")); - errorMessage->append(e.what()); - gLiveSupport->displayMessageWindow(*errorMessage); - } + Ptr::Ref playable; + + while ((playable = getNextSelectedPlayable())) { + try { + gLiveSupport->addToScratchpad(playable); + } catch (XmlRpcException &e) { + Ptr::Ref errorMessage(new Glib::ustring( + "error in SearchWindow::onAddToScratchpad(): ")); + errorMessage->append(e.what()); + gLiveSupport->displayMessageWindow(*errorMessage); } } } @@ -669,20 +694,16 @@ SearchWindow :: onAddToScratchpad(void) throw () void SearchWindow :: onAddToPlaylist(void) throw () { - Glib::RefPtr - refSelection = searchResultsTreeView->get_selection(); - Gtk::TreeModel::iterator - iter = refSelection->get_selected(); + Ptr::Ref playable; - if (iter) { - Ptr::Ref playable = (*iter)[modelColumns.playableColumn]; + while ((playable = getNextSelectedPlayable())) { try { gLiveSupport->addToPlaylist(playable->getId()); } catch (XmlRpcException &e) { - Ptr::Ref errorMessage(new Glib::ustring( - "error in SearchWindow::onAddToPlaylist(): ")); - errorMessage->append(e.what()); - gLiveSupport->displayMessageWindow(*errorMessage); + Ptr::Ref errorMessage(new Glib::ustring( + "error in SearchWindow::onAddToPlaylist(): ")); + errorMessage->append(e.what()); + gLiveSupport->displayMessageWindow(*errorMessage); } } } @@ -694,24 +715,18 @@ SearchWindow :: onAddToPlaylist(void) throw () void SearchWindow :: onAddToLiveMode(void) throw () { - Glib::RefPtr - refSelection = searchResultsTreeView->get_selection(); - Gtk::TreeModel::iterator - iter = refSelection->get_selected(); - - if (iter) { - Ptr::Ref playable = (*iter)[modelColumns.playableColumn]; - if (playable) { - try { - gLiveSupport->addToScratchpad(playable); - playable = gLiveSupport->getPlayable(playable->getId()); - gLiveSupport->addToLiveMode(playable); - } catch (XmlRpcException &e) { - Ptr::Ref errorMessage(new Glib::ustring( - "error in SearchWindow::onAddToLiveMode(): ")); - errorMessage->append(e.what()); - gLiveSupport->displayMessageWindow(*errorMessage); - } + Ptr::Ref playable; + + while ((playable = getNextSelectedPlayable())) { + try { + gLiveSupport->addToScratchpad(playable); + playable = gLiveSupport->getPlayable(playable->getId()); + gLiveSupport->addToLiveMode(playable); + } catch (XmlRpcException &e) { + Ptr::Ref errorMessage(new Glib::ustring( + "error in SearchWindow::onAddToLiveMode(): ")); + errorMessage->append(e.what()); + gLiveSupport->displayMessageWindow(*errorMessage); } } } @@ -724,21 +739,15 @@ SearchWindow :: onAddToLiveMode(void) throw () void SearchWindow :: onEditPlaylist(void) throw () { - Glib::RefPtr - refSelection = searchResultsTreeView->get_selection(); - Gtk::TreeModel::iterator - iter = refSelection->get_selected(); + Ptr::Ref playable = getNextSelectedPlayable(); + Ptr::Ref playlist = playable->getPlaylist(); - if (iter) { - Ptr::Ref playable = (*iter)[modelColumns.playableColumn]; - Ptr::Ref playlist = playable->getPlaylist(); - if (playlist) { - try { - gLiveSupport->openPlaylistForEditing(playlist->getId()); - } catch (XmlRpcException &e) { - gLiveSupport->displayMessageWindow(*getResourceUstring( - "cannotEditPlaylistMsg" )); - } + if (playlist) { + try { + gLiveSupport->openPlaylistForEditing(playlist->getId()); + } catch (XmlRpcException &e) { + gLiveSupport->displayMessageWindow(*getResourceUstring( + "cannotEditPlaylistMsg" )); } } } @@ -751,16 +760,7 @@ SearchWindow :: onEditPlaylist(void) throw () void SearchWindow :: onSchedulePlaylist(void) throw () { - Glib::RefPtr - refSelection = searchResultsTreeView->get_selection(); - Gtk::TreeModel::iterator - iter = refSelection->get_selected(); - - if (!iter) { - return; - } - - Ptr::Ref playable = (*iter)[modelColumns.playableColumn]; + Ptr::Ref playable = getNextSelectedPlayable(); Ptr::Ref playlist = playable->getPlaylist(); if (playlist) { @@ -777,17 +777,8 @@ SearchWindow :: onSchedulePlaylist(void) throw () void SearchWindow :: onExportPlaylist(void) throw () { - Glib::RefPtr - refSelection = searchResultsTreeView->get_selection(); - Gtk::TreeModel::iterator - iter = refSelection->get_selected(); - - if (!iter) { - return; - } - - Ptr::Ref playable = (*iter)[modelColumns.playableColumn]; - Ptr::Ref playlist = playable->getPlaylist(); + Ptr::Ref playable = getNextSelectedPlayable(); + Ptr::Ref playlist = playable->getPlaylist(); if (playlist) { exportPlaylistWindow.reset(new ExportPlaylistWindow(playlist)); @@ -803,16 +794,10 @@ SearchWindow :: onExportPlaylist(void) throw () void SearchWindow :: onUploadToHub(void) throw () { - Glib::RefPtr - refSelection = searchResultsTreeView->get_selection(); - Gtk::TreeModel::iterator - iter = refSelection->get_selected(); + Ptr::Ref playable; - if (iter) { - Ptr::Ref playable = (*iter)[modelColumns.playableColumn]; - if (playable) { - uploadToHub(playable); - } + while ((playable = getNextSelectedPlayable())) { + uploadToHub(playable); } } @@ -844,27 +829,21 @@ SearchWindow :: uploadToHub(Ptr::Ref playable) throw () void SearchWindow :: onDownloadFromHub(void) throw () { - Glib::RefPtr - refSelection = searchResultsTreeView->get_selection(); - Gtk::TreeModel::iterator - iter = refSelection->get_selected(); + Ptr::Ref playable; - if (iter) { - Ptr::Ref playable = (*iter)[modelColumns.playableColumn]; - if (playable) { - if (!gLiveSupport->existsPlayable(playable->getId())) { - try { - searchInput->set_current_page(3); - transportList->addDownload(playable); - - } catch (XmlRpcException &e) { - gLiveSupport->displayMessageWindow(*formatMessage( - "downloadFromHubErrorMsg", e.what() )); - return; - } - } else { - onAddToScratchpad(); + while ((playable = getNextSelectedPlayable())) { + if (!gLiveSupport->existsPlayable(playable->getId())) { + try { + searchInput->set_current_page(3); + transportList->addDownload(playable); + + } catch (XmlRpcException &e) { + gLiveSupport->displayMessageWindow(*formatMessage( + "downloadFromHubErrorMsg", e.what() )); + return; } + } else { + onAddToScratchpad(); } } } @@ -878,10 +857,14 @@ SearchWindow :: onDoubleClick(const Gtk::TreeModel::Path & path, const Gtk::TreeViewColumn * column) throw () { - if (searchIsLocal()) { - onAddToScratchpad(); - } else { - onDownloadFromHub(); + Ptr::Ref playable = getFirstSelectedPlayable(); + + if (playable) { + if (searchIsLocal()) { + onAddToScratchpad(); + } else { + onDownloadFromHub(); + } } } diff --git a/campcaster/src/products/gLiveSupport/src/SearchWindow.h b/campcaster/src/products/gLiveSupport/src/SearchWindow.h index 864a4abbd..7e10d1f23 100644 --- a/campcaster/src/products/gLiveSupport/src/SearchWindow.h +++ b/campcaster/src/products/gLiveSupport/src/SearchWindow.h @@ -99,6 +99,17 @@ class SearchWindow : public GuiWindow, */ int remoteSearchResultsCount; + /** + * The list of selected rows, as path references (row numbers). + * Reset by onEntryClicked(). + */ + Ptr >::Ref selectedPaths; + /** + * One of the selected rows, set to the first one by onEntryClicked(). + * Incremented by getNextSelectedPlayable(). + */ + std::vector::const_iterator selectedIter; + /** * The "search where" input field. */ @@ -339,7 +350,7 @@ class SearchWindow : public GuiWindow, displaySearchResults( Ptr::Ref searchResults, Glib::RefPtr treeModel) - throw (); + throw (); /** * Update the paging portion of the search results view. @@ -347,7 +358,7 @@ class SearchWindow : public GuiWindow, * the Backward and Forward buttons. */ void - updatePagingToolbar(void) throw (); + updatePagingToolbar(void) throw (); /** * Display a (usually error) message in the search results tree view. @@ -358,7 +369,7 @@ class SearchWindow : public GuiWindow, void displayMessage(const Glib::ustring & messageKey, Glib::RefPtr treeModel) - throw (); + throw (); /** * Display an error message which occurred during a search. @@ -369,7 +380,7 @@ class SearchWindow : public GuiWindow, void displayError(const XmlRpcException & error, Glib::RefPtr treeModel) - throw (); + throw (); /** * Display an error message which occurred during a local search. @@ -378,7 +389,7 @@ class SearchWindow : public GuiWindow, */ void displayLocalSearchError(const XmlRpcException & error) - throw (); + throw (); /** * Display an error message which occurred during a remote search. @@ -387,7 +398,27 @@ class SearchWindow : public GuiWindow, */ void displayRemoteSearchError(const XmlRpcException & error) - throw (); + throw (); + + /** + * Return the topmost selected row. + * Sets selectedPaths and selectedIter; does not increment it. + * + * @return the first selected playable item. + */ + Ptr::Ref + getFirstSelectedPlayable(void) throw (); + + /** + * Used to iterate over the selected rows. + * Reset to the first row by onEntryClicked(). + * Returns a 0 pointer if nothing is selected or we have reached + * the end of the list of selected rows. + * + * @return the next selected playable item. + */ + Ptr::Ref + getNextSelectedPlayable(void) throw (); protected: @@ -505,15 +536,18 @@ class SearchWindow : public GuiWindow, * Signal handler for the mouse clicked on one of the entries. * * @param event the button event received + * @return true if the event has been handled (a popup displayed), + * false otherwise */ - void + bool onEntryClicked(GdkEventButton * event) throw (); /** * Signal handler for the user double-clicking, or pressing Enter * on one of the entries. * - * @param event the button event recieved + * @param path the TreePath of the row clicked on (ignored). + * @param column the TreeViewColumn clicked on (ignored). */ void onDoubleClick(const Gtk::TreeModel::Path & path,