diff --git a/build/depends.py b/build/depends.py index 2a9015240f10..4c88c68b359f 100644 --- a/build/depends.py +++ b/build/depends.py @@ -826,6 +826,10 @@ def sources(self, build): "widget/wcoverartmenu.cpp", "widget/wsingletoncontainer.cpp", "widget/wmainmenubar.cpp", + "widget/wbuttonbar.cpp", + "widget/wfeatureclickbutton.cpp", + "widget/wlibrarystack.cpp", + "widget/wlibrarybreadcrumb.cpp", "musicbrainz/network.cpp", "musicbrainz/tagfetcher.cpp", @@ -839,6 +843,7 @@ def sources(self, build): "widget/wtracktableviewheader.cpp", "widget/wlibrarysidebar.cpp", "widget/wlibrary.cpp", + "widget/wbaselibrary.cpp", "widget/wlibrarytableview.cpp", "widget/wanalysislibrarytableview.cpp", "widget/wlibrarytextbrowser.cpp", @@ -866,7 +871,7 @@ def sources(self, build): "library/mixxxlibraryfeature.cpp", "library/baseplaylistfeature.cpp", "library/playlistfeature.cpp", - "library/setlogfeature.cpp", + "library/historyfeature.cpp", "library/autodj/dlgautodj.cpp", "library/dlganalysis.cpp", "library/dlgcoverartfullsize.cpp", @@ -905,6 +910,8 @@ def sources(self, build): "library/cratefeature.cpp", "library/sidebarmodel.cpp", "library/library.cpp", + "library/librarypanemanager.cpp", + "library/librarysidebarexpandedmanager.cpp", "library/scanner/libraryscanner.cpp", "library/scanner/libraryscannerdlg.cpp", diff --git a/res/skins/Deere/library.xml b/res/skins/Deere/library.xml index 0745dd779beb..e572a467fd6a 100644 --- a/res/skins/Deere/library.xml +++ b/res/skins/Deere/library.xml @@ -14,35 +14,34 @@ LibraryExpanded - vertical - - me,f - 0,0 + horizontal + + vertical + min,me + + + + min,me + LibrarySidebarButtons + + + LibrarySplitter horizontal me,me [Deere],LibrarySidebarSplitSize - 2,8 + 2,4,4 vertical - - horizontal - - - - - - PreviewDeckContainer vertical @@ -153,8 +152,9 @@ 2,8 0,0 - - + + LibrarySidebarExpanded + LibraryCoverArt @@ -170,8 +170,40 @@ - - + + + + vertical + + + 1 + + + LibraryBreadCrumb + 1 + + + 1 + + + + + + + vertical + + + 2 + + + LibraryBreadCrumb + 2 + + + 2 + + + diff --git a/res/skins/Deere/style.qss b/res/skins/Deere/style.qss index cd82be4f4009..665abc0739de 100644 --- a/res/skins/Deere/style.qss +++ b/res/skins/Deere/style.qss @@ -143,6 +143,16 @@ QTableView::indicator:unchecked { background: url(skin:/image/style_checkbox_unchecked.png); } +WBaseLibrary[showFocus="0"] { + padding: 1px 0 0 0; + border: none; +} + +WBaseLibrary[showFocus="1"] { + padding: 1px 1px 1px 1px; + border-top: 1px solid #DE6B0F; +} + /* BPM lock icon in the library "BPM" column. */ #LibraryBPMButton::indicator:checked { image: url(:/images/library/ic_library_checked.png); @@ -152,6 +162,94 @@ QTableView::indicator:unchecked { image: url(:/images/library/ic_library_unchecked.png); } +#LibrarySidebarButtons { + margin-top: 5px; +} + +#LibrarySidebarButtons, #LibrarySidebarButtons WButtonBar { + background-color: #222222; + margin-right: 13px; +} + +#LibraryBreadCrumb, +WBaseLibrary QLabel { + margin: 4px 4px; + font-size: 12px; + font-weight: bold; + color: #B3B3B3; +} + +#LibraryCoverArtSplitter QTabWidget { + border: none; +} + +#LibraryCoverArtSplitter QTabWidget::pane { + border: 1px solid #1A1A1A; +} + +#LibraryCoverArtSplitter QTabWidget QTreeView, #DlgAutoDJ { + margin: 0; + border: none; +} + +#LibraryCoverArtSplitter::handle { + image: url(skin:/image/style_handle_vertical_unchecked.svg); + background: none; +} + +#LibraryCoverArtSplitter::handle:pressed { + image: url(skin:/image/style_handle_vertical_checked.svg); + background: none; +} + +#LibraryCoverArtSplitter::handle:horizontal { + width: 6px; +} + +#LibraryCoverArtSplitter::handle:vertical { + height: 6px; +} + +#DlgAutoDJ #scrollAreaWidgetContents { + background-color: #222222; + border: none; +} + +#LibraryCoverArtSplitter QTabBar::tab { + padding: 4px; + border: none; + color: #cfb32c; + background-color: #191919; + border-top-left-radius: 2px; + border-top-right-radius: 2px; + + color: #D2D2D2; + background-color: qlineargradient(x1: 0, y1: 1, x2: 0, y2: 0, + stop: 0 #4B4B4B, + stop: 1 #4B4B4B); + border: 0px solid #4B4B4B; + outline: none; +} + +#LibraryCoverArtSplitter QTabBar::tab:hover { + border-top: 0px solid #5F5F5F; + border-right: 0px solid #5F5F5F; + border-left: 0px solid #5F5F5F; + color: #D2D2D2; + background-color: #5F5F5F; +} + +#LibraryCoverArtSplitter QTabBar::tab:!enabled { + color: #969696; + background-color: #525252; +} + +#LibraryCoverArtSplitter QTabBar::tab:selected { + color: #FDFDFD; + background-color: #006596; + border: 0px solid #006596; +} + /* button in library "Preview" column */ QPushButton#LibraryPreviewButton { width: 23px; @@ -296,25 +394,6 @@ QTreeView::branch:open:has-children:has-siblings { height: 6px; } -/* QSplitter between LibrarySidebar and CoverArt */ -#LibraryCoverArtSplitter::handle { - image: url(skin:/image/style_handle_vertical_unchecked.svg); - background: none; -} - -#LibraryCoverArtSplitter::handle:pressed { - image: url(skin:/image/style_handle_vertical_checked.svg); - background: none; -} - -#LibraryCoverArtSplitter::handle:horizontal { - width: 6px; -} - -#LibraryCoverArtSplitter::handle:vertical { - height: 6px; -} - /* library cover art widget */ #LibraryCoverArt { padding: 8px 4px 4px 4px; @@ -322,156 +401,94 @@ QTreeView::branch:open:has-children:has-siblings { border: 1px solid #1A1A1A; } /* transition time in Auto DJ tab */ -WLibrary QSpinBox { +WBaseLibrary QSpinBox { min-height: 20px; max-height: 20px; min-width: 40px; max-width: 40px; } -WLibrary QSpinBox:editable { +WBaseLibrary QSpinBox:editable { background: transparent; color:#d2d2d2; } -/* Extra declaration for QRadionButton otherwise it shows up with wrong colors in Linux with Gnome */ -WLibrary QLabel, WLibrary QRadioButton { - /* same as QTreeview */ +WBaseLibrary QLabel, WBaseLibrary QRadioButton { color: #d2d2d2; - margin: 9px 10px 6px 0px; } -WLibrary QRadioButton::indicator { +WBaseLibrary QRadioButton::indicator { margin: 0px 5px 0px 2px; width: 18px; height: 18px; } -/* Additional space for the first QRadionButton in the row */ -WLibrary QRadioButton#radioButtonRecentlyAdded { - margin: 9px 10px 6px 14px; -} - -WLibrary QRadioButton::indicator:checked { +WBaseLibrary QRadioButton::indicator:checked { background: url(skin:/icon/ic_radio_button_on_18px.svg); } -WLibrary QRadioButton::indicator:unchecked { +WBaseLibrary QRadioButton::indicator:unchecked { background: url(skin:/icon/ic_radio_button_off_18px.svg); } + +WButtonBar QAbstractButton { + border-radius: 0px; +} + /* buttons in library (in hierarchical order of appearance) Style them just as the other regular buttons */ -#DlgMissing > QPushButton, -#DlgHidden > QPushButton, -#DlgAutoDJ > QPushButton, -#DlgRecording > QPushButton, -#DlgAnalysis > QPushButton { - margin: 9px 3px 6px 3px; - padding: 4px; +WBaseLibrary QPushButton { + margin: 2px 3px 2px 3px; + padding: 2px; color: #D2D2D2; - background-color: qlineargradient(x1: 0, y1: 1, x2: 0, y2: 0, - stop: 0 #4B4B4B, - stop: 1 #4B4B4B); + + background-color: #4B4B4B; border: 1px solid #4B4B4B; border-radius: 2px; outline: none; } -#DlgMissing > QPushButton:!enabled, -#DlgHidden > QPushButton:!enabled, -#DlgAutoDJ > QPushButton:!enabled, -#DlgAnalysis > QPushButton:!enabled { +WBaseLibrary QPushButton:!enabled { /* buttons in "disabled" (not click-able) state. They are nearly invisible by default QT palette, so style accordingly */ - margin: 9px 3px 6px 3px; - padding: 4px; color: #808080; /* Default #A3A3A3 -90L HSL*/ - background-color: qlineargradient(spread:pad, - x1:0, y1:0, x2:1, y2:0, - stop:0 rgba(95, 95, 95, 127), - stop:1 rgba(95, 95, 95, 127)); - /* 50% #5F5F5F = RGBA#5F5F5F7F */ + background-color: rgba(95, 95, 95, 127); border: 0px solid #5F5F5F; - border-radius: 2px; - outline: none; } -#DlgMissing > QPushButton:hover, -#DlgHidden > QPushButton:hover, -#DlgAutoDJ > QPushButton:hover, -#DlgRecording > QPushButton:hover, -#DlgAnalysis > QPushButton:hover { - margin: 9px 3px 6px 3px; - padding: 4px; +WBaseLibrary QPushButton:hover { color: #D2D2D2; - background-color: qlineargradient(x1: 0, y1: 1, x2: 0, y2: 0, - stop: 0 #5F5F5F, - stop: 1 #5F5F5F); + background-color: #5F5F5F; border: 0px solid #5F5F5F; - border-radius: 2px; - outline: none; } -#DlgAutoDJ > QPushButton:checked, -#DlgRecording > QPushButton:checked, -#DlgAnalysis > QPushButton:checked { +WBaseLibrary QPushButton:checked { /* checkbuttons in active state */ - margin: 9px 3px 6px 3px; - padding: 4px; color: #FDFDFD; background-color: qlineargradient(x1: 0, y1: 1, x2: 0, y2: 0, stop: 0 #006596, stop: 1 #006596); border: 0px solid #006596; - border-radius: 2px; - outline: none; } -#DlgAutoDJ > QPushButton:checked:hover, -#DlgRecording > QPushButton:checked:hover, -#DlgAnalysis > QPushButton:checked:hover { +WBaseLibrary QPushButton:checked:hover { /* checkbuttons hovered over in "active" state */ - margin: 9px 3px 6px 3px; - padding: 4px; color: #FDFDFD; background-color: qlineargradient(x1: 0, y1: 1, x2: 0, y2: 0, stop: 0 #0080BE, stop: 1 #0080BE); border: 0px solid #0080BE; - border-radius: 2px; - outline: none; } -#DlgMissing > QPushButton:pressed, -#DlgHidden > QPushButton:pressed, -#DlgAutoDJ > QPushButton:pressed, -#DlgAnalysis > QPushButton:pressed { +WBaseLibrary QPushButton:pressed { /* pushbuttons in "down" state */ - margin: 9px 3px 6px 3px; - padding: 4px; color: #FDFDFD; background-color: qlineargradient(x1: 0, y1: 1, x2: 0, y2: 0, stop: 0 #006596, stop: 1 #006596); border: 0px solid #006596; - border-radius: 2px; - outline: none; -} - -/* Additional space for the first and the last QPushButton in the row */ -#DlgMissing > QPushButton#btnPurge, -#DlgHidden > QPushButton#btnUnhide, -#DlgAutoDJ > QPushButton#pushButtonAutoDJ, -#DlgRecording > QPushButton#pushButtonRecording, -#DlgAnalysis > QPushButton#pushButtonAnalyze { - margin: 9px 12px 6px 3px; } -#DlgAutoDJ > QPushButton#pushButtonShuffle { - margin: 9px 3px 6px 12px; -} - - /* Scroll bars */ QScrollBar:horizontal { border-top: 1px solid #141414; @@ -519,6 +536,12 @@ QScrollBar::sub-line:horizontal, QScrollBar::sub-line:vertical { border: none; } +QScrollArea { + border: 1px solid #1A1A1A; + background-color: #222222; +} + + /******************************************************************************* ** END LIBRARY ***************************************************************** *******************************************************************************/ @@ -582,10 +605,13 @@ QScrollBar::sub-line:horizontal, QScrollBar::sub-line:vertical { WWidget, QLabel { font-family: "Open Sans"; - font-size: 8px; text-transform: uppercase; } +WWidget { + font-size: 8px; +} + /* Start spacing for Deck overview row (small waveform, option grid) */ #OptionGrid { background-color: #333333; @@ -1077,17 +1103,17 @@ WWidgetGroup { ** Buttons ******************************************************************* *******************************************************************************/ -WPushButton { +WPushButton, QToolButton { color: #D2D2D2; background-color: qlineargradient(x1: 0, y1: 1, x2: 0, y2: 0, stop: 0 #4B4B4B, stop: 1 #4B4B4B); - border: 1px solid #4B4B4B; + border: 0px solid #4B4B4B; border-radius: 2px; outline: none; } -WPushButton:hover { +WPushButton:hover, QToolButton:hover { color: #D2D2D2; background-color: qlineargradient(x1: 0, y1: 1, x2: 0, y2: 0, stop: 0 #5F5F5F, @@ -1096,7 +1122,7 @@ WPushButton:hover { } /*"Pressed" state*/ -WPushButton[value="1"] { +WPushButton[value="1"], QToolButton:pressed { /*color: #FDFDFD;*/ color: #FDFDFD; background-color: qlineargradient(x1: 0, y1: 1, x2: 0, y2: 0, diff --git a/res/skins/LateNight/library.xml b/res/skins/LateNight/library.xml index 4c1c1d4ad664..aeedf94cc3da 100644 --- a/res/skins/LateNight/library.xml +++ b/res/skins/LateNight/library.xml @@ -13,32 +13,36 @@ horizontal + + LibrarySidebarButtons + LibrarySplitter me,me - 1,12 + 1,6,6 [LateNight],LibrarySidebarSplitSize vertical - - CoverArtSplitter me,me - 1,1 + 30,30 + 2,8 [LateNight],CoverArtSplitSize vertical 0,0 - + + LibrarySidebarExpanded + me,me - 40,40 + 30,30, [Library],show_coverart visible @@ -49,14 +53,31 @@ - - vertical - - #585858 - #eece33 - + + 1 + + + 1 + + + 1 + + + + + vertical + + + 2 + + + 2 + + + 2 + diff --git a/res/skins/LateNight/style.qss b/res/skins/LateNight/style.qss index 048b542d72ea..6574cba37f21 100644 --- a/res/skins/LateNight/style.qss +++ b/res/skins/LateNight/style.qss @@ -49,19 +49,6 @@ background-color: #0f0f0f; } -#LibrarySingleton { - padding-top: 5px; - padding-bottom: 2px; -} - -#Library { - border-left: 0px solid #585858; - background-color: #0e0e0e; - padding-top: 5px; - padding-bottom: 2px; - /*border-top: 1px solid #585858;*/ -} - #DeckRowOne { qproperty-layoutAlignment: 'AlignLeft | AlignTop'; border-bottom: 1px solid #585858; @@ -1156,17 +1143,142 @@ /* Library styling is hard */ -QTableView, QTextBrowser, QTreeView { +#LibrarySingleton { + padding-top: 5px; + padding-bottom: 2px; +} + +#Library { + border-left: 0px solid #585858; + background-color: #0e0e0e; + padding-top: 5px; + padding-bottom: 2px; + /*border-top: 1px solid #585858;*/ +} + +#LibrarySidebarExpanded QWidget { + background-color: #0f0f0f; +} + +#LibrarySidebarExpanded QScrollArea { + border: 1px solid #585858; +} + +#LibrarySidebarExpanded QTabWidget QTreeView, #DlgAutoDJ QScrollArea { + margin: 0; + border: none; +} + +#LibrarySidebarExpanded QTabWidget { + border: none +} + +#LibrarySidebarExpanded QTabWidget::pane, +#LibrarySidebarExpanded #DlgRecording, +#LibrarySidebarExpanded #DlgAnalysis { + border: 1px solid #585858; +} + +#LibrarySidebarExpanded QTabBar::tab { + padding: 5px; + border: none; + color: #cfb32c; + background-color: #191919; + border-top-left-radius: 2px; + border-top-right-radius: 2px; +} + +#LibrarySidebarExpanded QTabBar::tab:hover { + border-top: 2px solid #585858; + border-right: 2px solid #585858; + border-left: 2px solid #585858; + background-color: #232323; +} + +#LibrarySidebarExpanded QTabBar::tab:!enabled { + color: #7A6919; + background-color: #191919; +} + + +#LibrarySidebarExpanded QTabBar::tab:focus { + border-top: 2px solid #8E5C00; + border-right: 2px solid #8E5C00; + border-left: 2px solid #8E5C00; + background-color: #232323; +} + +#LibrarySidebarExpanded QTabBar::tab:selected { + background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #585858, stop:1 #0f0f0f); +} + +#LibrarySidebarExpanded QWidget:!enabled { + color: #666666; +} + +#LibrarySidebarButtons QToolButton { + margin: 0px 0px 0px 0px; + padding: 0px 0px 0px 0px; + border: none; + color: #cfb32c; + background-color: #191919; +} + +#LibrarySidebarExpanded QPushButton { + margin: 2px 3px 2px 3px; + padding: 4px 6px; + min-width: 65px; + border: none; + color: #cfb32c; + background-color: #191919; +} + +#LibrarySidebarButtons QToolButton:hover, +#LibrarySidebarExpanded QPushButton:hover { + border: 1px solid #585858; + background-color: #232323; + color: #cfb32c; +} + +#LibrarySidebarButtons QToolButton:hover { + border-radius: 10px; +} + +#LibrarySidebarButtons QToolButton:focus { + border-radius: 10px; +} + +#LibrarySidebarButtons:focus, +#LibrarySidebarButtons QToolButton:focus, +#LibrarySidebarExpanded > QWidget:focus, +#LibrarySidebarExpanded QAbstractScrollArea > QWidget:focus { + border: 1px solid #8E5C00; +} + +#LibrarySidebarButtons WButtonBar { + background-color: #191919; + margin-right: 11px; +} + +#LibrarySidebarButtons { + margin-right: 5px; +} + +#LibrarySidebarButtons, QTableView, QTextBrowser, QTreeView { border: 1px solid #585858; /*font: 15px/18px;*/ color: #cfb32c; - background-color: #0f0f0f; + background-color: #191919; alternate-background-color: #1a1a1a; selection-color: #cfb32c; selection-background-color: #725309; } +WLibraryBreadCrumb { + margin: 4px 2px; +} + /* checkbox in library "Played" column */ QTableView::indicator { width: 12px; height: 12px;} QTableView::indicator:checked { background: url(skin:/style/style_checkbox_checked.png);} @@ -1286,30 +1398,13 @@ WCoverArt { background: transparent; color: #ACACAC; } QLabel, QRadioButton { background: transparent; color: #cfb32c; } /* Additional space for QRadionButtons and QLabels */ -WLibrary QRadioButton, WLibrary QLabel { margin: 9px 3px 6px 3px; } - -/* Additional space for the first QRadionButton in the row */ -WLibrary QRadioButton#radioButtonRecentlyAdded { margin: 9px 3px 6px 12px; } +WBaseLibrary QRadioButton, WBaseLibrary QLabel { margin: 9px 3px 6px 3px; } -/* Additional space for the QPushButtons */ -#DlgMissing > QPushButton, -#DlgHidden > QPushButton, -#DlgAutoDJ > QPushButton, -#DlgRecording > QPushButton, -#DlgAnalysis > QPushButton { margin: 9px 3px 6px 3px; padding: 3px 8px; min-width: 65px; } - -/* Additional space for the first QPushButton in the row */ -#DlgMissing > QPushButton#btnPurge, -#DlgHidden > QPushButton#btnUnhide, -#DlgAutoDJ > QPushButton#pushButtonAutoDJ, -#DlgRecording > QPushButton#pushButtonRecording, -#DlgAnalysis > QPushButton#pushButtonAnalyze { margin: 9px 12px 6px 3px; } - -/* Additional space for the last QPushButton in the row */ -#DlgAutoDJ > QPushButton#pushButtonShuffle { margin: 9px 3px 6px 12px; } +/* Additional space for the first QRadionButton in the row +WBaseLibrary QRadioButton#radioButtonRecentlyAdded { margin: 9px 3px 6px 12px; } */ /* Spacing between treeview and searchbar */ -QTreeView { margin: 10px 0px 0px 0px; } +QTreeView { margin: 0px 0px 0px 0px; } /* triangle for closed/opened branches in treeview */ QTreeView { show-decoration-selected: 0; background-color: #151515; } /* Suppresses that selected sidebar items branch indicator shows wrong color when out of focus ; lp:880588 */ @@ -1333,3 +1428,13 @@ QTreeView::item:selected { background-color:qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #585858, stop:1 #0f0f0f); color: #cfb32c; } + +WLibrary[showFocus="0"] { + padding: 1px 0 0 0; + border: none; +} + +WLibrary[showFocus="1"] { + padding: 1px 0 0 0; + border-top: 1px solid #FF7100 !important; +} \ No newline at end of file diff --git a/res/skins/Shade/skin.xml b/res/skins/Shade/skin.xml index e61033ff9b00..3cf830d730b1 100644 --- a/res/skins/Shade/skin.xml +++ b/res/skins/Shade/skin.xml @@ -1,3 +1,4 @@ + QLabel, QRadioButton { @@ -460,32 +461,65 @@ background: transparent; color: #C1C1C1;} - - WLibrary QRadioButton, WLibrary QLabel { margin: 9px 3px 6px 3px; } + + WBaseLibrary QPushButton { + margin: 2px 3px; + padding: 3px 3px; + } + + WBaseLibrary QScrollArea, + QScrollArea QWidget { + background-color: transparent; + } - - WLibrary QRadioButton#radioButtonRecentlyAdded { margin: 9px 3px 6px 12px; } + WBaseLibrary QTabWidget QTreeView, #DlgAutoDJ QScrollArea { + background-color: #191919; + border: none; + } - - #DlgMissing > QPushButton, - #DlgHidden > QPushButton, - #DlgAutoDJ > QPushButton, - #DlgRecording > QPushButton, - #DlgAnalysis > QPushButton { - margin: 9px 3px 6px 3px; - padding: 3px 8px; - min-width: 65px; + WBaseLibrary QTabWidget::pane { + background-color: #191919; + border: 1px solid #646464; + } + + WBaseLibrary QPushButton { + border: 1px solid black; + background-color: #aab2b7; + } + + #LibrarySidebarButtons, #LibrarySidebarButtons WButtonBar { + background-color: #8d98a3; + margin-right: 13px; + } + + #LibrarySidebarButtons QScrollArea { + background-color: #8d98a3; } - - #DlgMissing > QPushButton#btnPurge, - #DlgHidden > QPushButton#btnUnhide, - #DlgAutoDJ > QPushButton#pushButtonAutoDJ, - #DlgRecording > QPushButton#pushButtonRecording, - #DlgAnalysis > QPushButton#pushButtonAnalyze { margin: 9px 12px 6px 3px; } + WButtonBar QToolButton { + padding: 2px 2px 2px 2px; + color: black; + border: none; + background-color: #aab2b7; + } + + WButtonBar QToolButton:hover { + border: 2px solid #626f87; + } + + WBaseLibrary[showFocus="0"] { + padding: 2px 0 0 0; + border: none; + } + + WBaseLibrary[showFocus="1"] { + padding: 2px 1px 1px 1px; + border-top: 2px solid aqua; + } - - #DlgAutoDJ > QPushButton#pushButtonShuffle { margin: 9px 3px 6px 12px; } + WLibraryBreadCrumb { + margin: 2px 4px; + } QTreeView { margin: 0px 0px 0px 5px; } @@ -506,11 +540,10 @@ me,me - + [Shade],LibrarySidebarSplitSize 1,e - vertical @@ -527,138 +560,138 @@ - vertical - - - - horizontal + vertical - - - text - - [PreviewDeck1] - - 50me,15f - right - - - - eject - - 1 - - 0 - btn_eject1_over.png - btn_eject1.png - - - - [PreviewDeck1],eject - true - LeftButton - false - - - - - - - - horizontal - - - play_start - - 2 - true - - 0 - btn_play_sampler_down.png - btn_play_sampler.png - - - 1 - btn_play_sampler_overdown.png - btn_play_sampler_over.png - - 2,2 - - [PreviewDeck1],play - true - LeftButton - - - [PreviewDeck1],start - true - RightButton - false - - - - waveform_overview - - [PreviewDeck1] - me,30f - #8D98A3 - #FFE300 - #0099FF - #FF0035 - #FF8000 - #00FF00 - - bottom - #FFFFFF - #00FF00 - %1 - - - cue_point - C - top - #FF001C - #00FF00 - - - [PreviewDeck1],playposition - false - - + + horizontal + + + + text + + [PreviewDeck1] + + 50me,15f + right + + + + eject + + 1 + + 0 + btn_eject1_over.png + btn_eject1.png + + + + [PreviewDeck1],eject + true + LeftButton + false + + + + + + + + horizontal + + + play_start + + 2 + true + + 0 + btn_play_sampler_down.png + btn_play_sampler.png + + + 1 + btn_play_sampler_overdown.png + btn_play_sampler_over.png + + 2,2 + + [PreviewDeck1],play + true + LeftButton + + + [PreviewDeck1],start + true + RightButton + false + + + + waveform_overview + + [PreviewDeck1] + me,30f + #8D98A3 + #FFE300 + #0099FF + #FF0035 + #FF8000 + #00FF00 + + bottom + #FFFFFF + #00FF00 + %1 + + + cue_point + C + top + #FF001C + #00FF00 + + + [PreviewDeck1],playposition + false + + + + - - @@ -708,7 +741,7 @@ ********************************************** --> - pregain + [Deere],LibrarySidebarSplitSizepregain knob_volume_previewdeck.png slider_volume_previewdeck.png @@ -727,35 +760,62 @@ visible - - - - e,me - - 1,e - 0,0 + + horizontal - - - - 16,16 + + LibrarySidebarButtons + + + vertical + 8,2 me,me - - [Library],show_coverart - visible - - + + + LibrarySidebarExpanded + + + + 30,30 + me,me + + [Library],show_coverart + visible + + + + - + + + + + vertical + + + 1 + + + 1 + + + 1 + - - vertical - + + 2 + + + 2 + + + 2 + diff --git a/src/library/analysisfeature.cpp b/src/library/analysisfeature.cpp index ab925a9a2b7a..3d0ac1ec1642 100644 --- a/src/library/analysisfeature.cpp +++ b/src/library/analysisfeature.cpp @@ -4,29 +4,31 @@ #include +#include "analyzer/analyzerqueue.h" +#include "controllers/keyboard/keyboardeventfilter.h" #include "library/analysisfeature.h" +#include "library/dlganalysis.h" +#include "library/library.h" #include "library/librarytablemodel.h" #include "library/trackcollection.h" -#include "library/dlganalysis.h" -#include "widget/wlibrary.h" -#include "controllers/keyboard/keyboardeventfilter.h" -#include "analyzer/analyzerqueue.h" +#include "library/treeitem.h" #include "sources/soundsourceproxy.h" -#include "util/dnd.h" #include "util/debug.h" +#include "util/dnd.h" +#include "widget/wanalysislibrarytableview.h" +#include "widget/wlibrary.h" +#include "widget/wtracktableview.h" -const QString AnalysisFeature::m_sAnalysisViewName = QString("Analysis"); - -AnalysisFeature::AnalysisFeature(QObject* parent, - UserSettingsPointer pConfig, - TrackCollection* pTrackCollection) : - LibraryFeature(parent), - m_pConfig(pConfig), - m_pTrackCollection(pTrackCollection), - m_pAnalyzerQueue(NULL), +AnalysisFeature::AnalysisFeature(UserSettingsPointer pConfig, + Library* pLibrary, TrackCollection* pTrackCollection, + QObject* parent) : + LibraryFeature(pConfig, pLibrary, pTrackCollection, parent), + m_pAnalyzerQueue(nullptr), m_iOldBpmEnabled(0), m_analysisTitleName(tr("Analyze")), - m_pAnalysisView(NULL) { + m_pAnalysisView(nullptr){ + + m_childModel.setRootItem(new TreeItem("$root", "$root", this, nullptr)); setTitleDefault(); } @@ -58,35 +60,36 @@ QIcon AnalysisFeature::getIcon() { return QIcon(":/images/library/ic_library_prepare.png"); } -void AnalysisFeature::bindWidget(WLibrary* libraryWidget, - KeyboardEventFilter* keyboard) { - m_pAnalysisView = new DlgAnalysis(libraryWidget, - m_pConfig, - m_pTrackCollection); - connect(m_pAnalysisView, SIGNAL(loadTrack(TrackPointer)), - this, SIGNAL(loadTrack(TrackPointer))); - connect(m_pAnalysisView, SIGNAL(loadTrackToPlayer(TrackPointer, QString)), - this, SIGNAL(loadTrackToPlayer(TrackPointer, QString))); - connect(m_pAnalysisView, SIGNAL(analyzeTracks(QList)), - this, SLOT(analyzeTracks(QList))); - connect(m_pAnalysisView, SIGNAL(stopAnalysis()), - this, SLOT(stopAnalysis())); - - connect(m_pAnalysisView, SIGNAL(trackSelected(TrackPointer)), - this, SIGNAL(trackSelected(TrackPointer))); +QWidget* AnalysisFeature::createPaneWidget(KeyboardEventFilter* pKeyboard, + int paneId) { + WTrackTableView* pTable = LibraryFeature::createTableWidget(pKeyboard, paneId); + pTable->loadTrackModel(getAnalysisTableModel()); + connect(pTable->selectionModel(), + SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), + this, + SLOT(tableSelectionChanged(const QItemSelection&, const QItemSelection&))); + + return pTable; +} +QWidget* AnalysisFeature::createInnerSidebarWidget(KeyboardEventFilter* pKeyboard) { + m_pAnalysisView = new DlgAnalysis(nullptr, this, m_pTrackCollection); + + m_pAnalysisView->setTableModel(getAnalysisTableModel()); + connect(this, SIGNAL(analysisActive(bool)), m_pAnalysisView, SLOT(analysisActive(bool))); connect(this, SIGNAL(trackAnalysisStarted(int)), m_pAnalysisView, SLOT(trackAnalysisStarted(int))); - m_pAnalysisView->installEventFilter(keyboard); - + m_pAnalysisView->installEventFilter(pKeyboard); + // Let the DlgAnalysis know whether or not analysis is active. - bool bAnalysisActive = m_pAnalyzerQueue != NULL; + bool bAnalysisActive = m_pAnalyzerQueue != nullptr; emit(analysisActive(bAnalysisActive)); - - libraryWidget->registerView(m_sAnalysisViewName, m_pAnalysisView); + m_pAnalysisView->onShow(); + + return m_pAnalysisView; } TreeItemModel* AnalysisFeature::getChildModel() { @@ -94,22 +97,32 @@ TreeItemModel* AnalysisFeature::getChildModel() { } void AnalysisFeature::refreshLibraryModels() { - if (m_pAnalysisView) { + if (!m_pAnalysisView.isNull()) { m_pAnalysisView->onShow(); } } +void AnalysisFeature::selectAll() { + QPointer pTable = LibraryFeature::getFocusedTable(); + if (!pTable.isNull()) { + pTable->selectAll(); + } +} + void AnalysisFeature::activate() { //qDebug() << "AnalysisFeature::activate()"; - emit(switchToView(m_sAnalysisViewName)); - if (m_pAnalysisView) { - emit(restoreSearch(m_pAnalysisView->currentSearch())); + //m_pLibrary->switchToView(m_sAnalysisViewName); + m_pLibrary->switchToFeature(this); + m_pLibrary->showBreadCrumb(m_childModel.getItem(QModelIndex())); + + if (!m_pAnalysisView.isNull()) { + m_pLibrary->restoreSearch(m_pAnalysisView->currentSearch()); } emit(enableCoverArtDisplay(true)); } void AnalysisFeature::analyzeTracks(QList trackIds) { - if (m_pAnalyzerQueue == NULL) { + if (m_pAnalyzerQueue == nullptr) { // Save the old BPM detection prefs setting (on or off) m_iOldBpmEnabled = m_pConfig->getValueString(ConfigKey("[BPM]","BPMDetectionEnabled")).toInt(); // Force BPM detection to be on. @@ -153,7 +166,7 @@ void AnalysisFeature::slotProgressUpdate(int num_left) { void AnalysisFeature::stopAnalysis() { //qDebug() << this << "stopAnalysis()"; - if (m_pAnalyzerQueue != NULL) { + if (m_pAnalyzerQueue != nullptr) { m_pAnalyzerQueue->stop(); } } @@ -161,15 +174,27 @@ void AnalysisFeature::stopAnalysis() { void AnalysisFeature::cleanupAnalyzer() { setTitleDefault(); emit(analysisActive(false)); - if (m_pAnalyzerQueue != NULL) { + if (m_pAnalyzerQueue != nullptr) { m_pAnalyzerQueue->stop(); m_pAnalyzerQueue->deleteLater(); - m_pAnalyzerQueue = NULL; + m_pAnalyzerQueue = nullptr; // Restore old BPM detection setting for preferences... m_pConfig->set(ConfigKey("[BPM]","BPMDetectionEnabled"), ConfigValue(m_iOldBpmEnabled)); } } +void AnalysisFeature::tableSelectionChanged(const QItemSelection&, + const QItemSelection&) { + //qDebug() << "AnalysisFeature::tableSelectionChanged" << sender(); + QPointer pTable = LibraryFeature::getFocusedTable(); + if (pTable.isNull()) { + return; + } + + const QModelIndexList &indexes = pTable->selectionModel()->selectedIndexes(); + m_pAnalysisView->setSelectedIndexes(indexes); +} + bool AnalysisFeature::dropAccept(QList urls, QObject* pSource) { Q_UNUSED(pSource); QList files = DragAndDropHelper::supportedTracksFromUrls(urls, false, true); @@ -182,3 +207,12 @@ bool AnalysisFeature::dropAccept(QList urls, QObject* pSource) { bool AnalysisFeature::dragMoveAccept(QUrl url) { return SoundSourceProxy::isUrlSupported(url); } + +AnalysisLibraryTableModel* AnalysisFeature::getAnalysisTableModel() { + if (m_pAnalysisLibraryTableModel.isNull()) { + m_pAnalysisLibraryTableModel = + new AnalysisLibraryTableModel(this, m_pTrackCollection); + } + + return m_pAnalysisLibraryTableModel; +} diff --git a/src/library/analysisfeature.h b/src/library/analysisfeature.h index bbdd853fc45c..2516c8c9efbf 100644 --- a/src/library/analysisfeature.h +++ b/src/library/analysisfeature.h @@ -23,9 +23,10 @@ class TrackCollection; class AnalysisFeature : public LibraryFeature { Q_OBJECT public: - AnalysisFeature(QObject* parent, - UserSettingsPointer pConfig, - TrackCollection* pTrackCollection); + AnalysisFeature(UserSettingsPointer pConfig, + Library* pLibrary, + TrackCollection* pTrackCollection, + QObject* parent); virtual ~AnalysisFeature(); QVariant title(); @@ -33,24 +34,28 @@ class AnalysisFeature : public LibraryFeature { bool dropAccept(QList urls, QObject* pSource); bool dragMoveAccept(QUrl url); - void bindWidget(WLibrary* libraryWidget, - KeyboardEventFilter* keyboard); - + + QWidget* createPaneWidget(KeyboardEventFilter* pKeyboard, int paneId) override; + QWidget* createInnerSidebarWidget(KeyboardEventFilter* pKeyboard) override; + TreeItemModel* getChildModel(); void refreshLibraryModels(); + void stopAnalysis(); signals: void analysisActive(bool bActive); void trackAnalysisStarted(int size); public slots: + void selectAll(); void activate(); void analyzeTracks(QList trackIds); private slots: void slotProgressUpdate(int num_left); - void stopAnalysis(); void cleanupAnalyzer(); + void tableSelectionChanged(const QItemSelection&, + const QItemSelection&); private: // Sets the title of this feature to the default name, given by @@ -61,18 +66,18 @@ class AnalysisFeature : public LibraryFeature { // where x is the current track being analyzed and y is the total number of // tracks in the job void setTitleProgress(int trackNum, int totalNum); + + AnalysisLibraryTableModel* getAnalysisTableModel(); - UserSettingsPointer m_pConfig; - TrackCollection* m_pTrackCollection; AnalyzerQueue* m_pAnalyzerQueue; // Used to temporarily enable BPM detection in the prefs before we analyse int m_iOldBpmEnabled; // The title returned by title() QVariant m_Title; TreeItemModel m_childModel; - const static QString m_sAnalysisViewName; QString m_analysisTitleName; - DlgAnalysis* m_pAnalysisView; + QPointer m_pAnalysisView; + QPointer m_pAnalysisLibraryTableModel; }; diff --git a/src/library/autodj/autodjfeature.cpp b/src/library/autodj/autodjfeature.cpp index 78ce04f40a5c..27f04b6b003d 100644 --- a/src/library/autodj/autodjfeature.cpp +++ b/src/library/autodj/autodjfeature.cpp @@ -5,37 +5,38 @@ #include #include #include +#include +#include #include "library/autodj/autodjfeature.h" #include "library/library.h" #include "library/parser.h" -#include "mixer/playermanager.h" #include "library/autodj/autodjprocessor.h" #include "library/trackcollection.h" #include "library/autodj/dlgautodj.h" #include "library/treeitem.h" +#include "mixer/playermanager.h" #include "widget/wlibrary.h" +#include "widget/wlibrarysidebar.h" #include "controllers/keyboard/keyboardeventfilter.h" #include "sources/soundsourceproxy.h" #include "util/dnd.h" -const QString AutoDJFeature::m_sAutoDJViewName = QString("Auto DJ"); static const int kMaxRetrieveAttempts = 3; -AutoDJFeature::AutoDJFeature(Library* pLibrary, - UserSettingsPointer pConfig, +AutoDJFeature::AutoDJFeature(UserSettingsPointer pConfig, + Library* pLibrary, + QObject* parent, PlayerManagerInterface* pPlayerManager, TrackCollection* pTrackCollection) - : LibraryFeature(pLibrary), - m_pConfig(pConfig), - m_pLibrary(pLibrary), + : LibraryFeature(pConfig, pLibrary, pTrackCollection, parent), m_pTrackCollection(pTrackCollection), m_crateDao(pTrackCollection->getCrateDAO()), m_playlistDao(pTrackCollection->getPlaylistDAO()), m_iAutoDJPlaylistId(-1), - m_pAutoDJProcessor(NULL), - m_pAutoDJView(NULL), + m_pAutoDJProcessor(nullptr), + m_pAutoDJView(nullptr), m_autoDjCratesDao(pTrackCollection->getDatabase(), pTrackCollection->getTrackDAO(), pTrackCollection->getCrateDAO(), @@ -57,6 +58,8 @@ AutoDJFeature::AutoDJFeature(Library* pLibrary, // Create the "Crates" tree-item under the root item. TreeItem* root = m_childModel.getItem(QModelIndex()); + root->setLibraryFeature(this); + m_pCratesTreeItem = new TreeItem(tr("Crates"), "", this, root); m_pCratesTreeItem->setIcon(QIcon(":/images/library/ic_library_crates.png")); root->appendChild(m_pCratesTreeItem); @@ -96,28 +99,60 @@ QIcon AutoDJFeature::getIcon() { return QIcon(":/images/library/ic_library_autodj.png"); } -void AutoDJFeature::bindWidget(WLibrary* libraryWidget, - KeyboardEventFilter* keyboard) { - m_pAutoDJView = new DlgAutoDJ(libraryWidget, - m_pConfig, - m_pLibrary, - m_pAutoDJProcessor, - m_pTrackCollection, - keyboard); - libraryWidget->registerView(m_sAutoDJViewName, m_pAutoDJView); - connect(m_pAutoDJView, SIGNAL(loadTrack(TrackPointer)), - this, SIGNAL(loadTrack(TrackPointer))); - connect(m_pAutoDJView, SIGNAL(loadTrackToPlayer(TrackPointer, QString, bool)), - this, SIGNAL(loadTrackToPlayer(TrackPointer, QString, bool))); +QWidget* AutoDJFeature::createPaneWidget(KeyboardEventFilter* pKeyboard, int paneId) { + WTrackTableView* pTrackTableView = + new WTrackTableView(nullptr, m_pConfig, m_pTrackCollection, false); + + pTrackTableView->installEventFilter(pKeyboard); + + connect(m_pLibrary, SIGNAL(setTrackTableFont(const QFont&)), + pTrackTableView, SLOT(setTrackTableFont(const QFont&))); + connect(m_pLibrary, SIGNAL(setTrackTableRowHeight(int)), + pTrackTableView, SLOT(setTrackTableRowHeight(int))); + + connect(pTrackTableView, SIGNAL(trackSelected(TrackPointer)), + m_pLibrary, SIGNAL(trackSelected(TrackPointer))); + + m_trackTables[paneId] = pTrackTableView; + + pTrackTableView->loadTrackModel(m_pAutoDJProcessor->getTableModel()); + + connect(pTrackTableView->selectionModel(), + SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), + this, + SLOT(selectionChanged(const QItemSelection&, const QItemSelection&))); + + return pTrackTableView; +} - connect(m_pAutoDJView, SIGNAL(trackSelected(TrackPointer)), - this, SIGNAL(trackSelected(TrackPointer))); +QWidget* AutoDJFeature::createInnerSidebarWidget(KeyboardEventFilter* pKeyboard) { + QTabWidget* pContainer = new QTabWidget(nullptr); + + // Add controls + m_pAutoDJView = new DlgAutoDJ(pContainer, m_pLibrary, m_pAutoDJProcessor); + m_pAutoDJView->installEventFilter(pKeyboard); + QScrollArea* pScroll = new QScrollArea(pContainer); + pScroll->setWidget(m_pAutoDJView); + pScroll->setWidgetResizable(true); + pContainer->addTab(pScroll, tr("Controls")); + + // Add drop target + WLibrarySidebar* pSidebar = new WLibrarySidebar(pContainer); + pSidebar->setModel(&m_childModel); + pSidebar->installEventFilter(pKeyboard); + + connect(pSidebar, SIGNAL(rightClicked(const QPoint&, const QModelIndex&)), + this, SLOT(onRightClickChild(const QPoint&, const QModelIndex&))); + + pContainer->addTab(pSidebar, tr("Track source")); // Be informed when the user wants to add another random track. connect(m_pAutoDJProcessor,SIGNAL(randomTrackRequested(int)), this,SLOT(slotRandomQueue(int))); connect(m_pAutoDJView, SIGNAL(addRandomButton(bool)), this, SLOT(slotAddRandomTrack(bool))); + + return pContainer; } TreeItemModel* AutoDJFeature::getChildModel() { @@ -126,8 +161,16 @@ TreeItemModel* AutoDJFeature::getChildModel() { void AutoDJFeature::activate() { //qDebug() << "AutoDJFeature::activate()"; - emit(switchToView(m_sAutoDJViewName)); - emit(restoreSearch(QString())); //Null String disables search box + DEBUG_ASSERT_AND_HANDLE(!m_pAutoDJView.isNull()) { + return; + } + + m_pAutoDJView->onShow(); + + m_pLibrary->switchToFeature(this); + m_pLibrary->showBreadCrumb(m_childModel.getItem(QModelIndex())); + m_pLibrary->restoreSearch(QString()); //Null String disables search box + emit(enableCoverArtDisplay(true)); } @@ -264,6 +307,10 @@ void AutoDJFeature::slotAddRandomTrack(bool) { TrackPointer addedTrack = (m_pTrackCollection->getTrackDAO()).getTrack(trackId); if(addedTrack->exists()) { playlistDao.appendTrackToPlaylist(trackId, m_iAutoDJPlaylistId); + DEBUG_ASSERT_AND_HANDLE(!m_pAutoDJView.isNull()) { + return; + } + m_pAutoDJView->onShow(); return; } else { @@ -284,6 +331,10 @@ void AutoDJFeature::slotAddRandomTrack(bool) { if(addedTrack->exists()) { if(!addedTrack->getPlayCounter().isPlayed()) { playlistDao.appendTrackToPlaylist(trackId, m_iAutoDJPlaylistId); + DEBUG_ASSERT_AND_HANDLE(!m_pAutoDJView.isNull()) { + return; + } + m_pAutoDJView->onShow(); return; } @@ -334,21 +385,31 @@ void AutoDJFeature::constructCrateChildModel() { void AutoDJFeature::onRightClickChild(const QPoint& globalPos, QModelIndex index) { //Save the model index so we can get it in the action slots... - m_lastRightClickedIndex = index; - - TreeItem *item = static_cast(index.internalPointer()); - QString crateName = item->dataPath().toString(); - if (crateName.length() > 0) { + QString crateName; + if (index.isValid()) { + m_lastRightClickedIndex = index; + + TreeItem* item = static_cast(index.internalPointer()); + DEBUG_ASSERT_AND_HANDLE(item) { + return; + } + + crateName = item->dataPath().toString(); + } + + if (!crateName.isEmpty()) { // A crate was right-clicked. // Bring up the context menu. - QMenu menu(NULL); + QMenu menu(nullptr); menu.addAction(m_pRemoveCrateFromAutoDj); menu.exec(globalPos); - } else { + return; + } + else { // The "Crates" tree-item was right-clicked. // Bring up the context menu. - QMenu menu(NULL); - QMenu crateMenu(NULL); + QMenu menu(nullptr); + QMenu crateMenu(nullptr); crateMenu.setTitle(tr("Add Crate as Track Source")); QMap crateMap; m_crateDao.getAutoDjCrates(false, &crateMap); @@ -375,3 +436,17 @@ void AutoDJFeature::slotRandomQueue(int tracksToAdd) { tracksToAdd -= 1; } } + +void AutoDJFeature::selectionChanged(const QItemSelection&, const QItemSelection&) { + DEBUG_ASSERT_AND_HANDLE(!m_pAutoDJView.isNull()) { + return; + } + + auto it = m_trackTables.find(m_featureFocus); + if (it == m_trackTables.end() || it->isNull()) { + return; + } + + const QModelIndexList& selectedRows = (*it)->selectionModel()->selectedRows(); + m_pAutoDJView->setSelectedRows(selectedRows); +} diff --git a/src/library/autodj/autodjfeature.h b/src/library/autodj/autodjfeature.h index 7cc7d2803c89..a433ec07237c 100644 --- a/src/library/autodj/autodjfeature.h +++ b/src/library/autodj/autodjfeature.h @@ -21,6 +21,7 @@ #include "library/treeitemmodel.h" #include "library/dao/autodjcratesdao.h" +#include "widget/wtracktableview.h" class DlgAutoDJ; class Library; @@ -31,8 +32,9 @@ class AutoDJProcessor; class AutoDJFeature : public LibraryFeature { Q_OBJECT public: - AutoDJFeature(Library* pLibrary, - UserSettingsPointer pConfig, + AutoDJFeature(UserSettingsPointer pConfig, + Library* pLibrary, + QObject* parent, PlayerManagerInterface* pPlayerManager, TrackCollection* pTrackCollection); virtual ~AutoDJFeature(); @@ -43,8 +45,8 @@ class AutoDJFeature : public LibraryFeature { bool dropAccept(QList urls, QObject* pSource); bool dragMoveAccept(QUrl url); - void bindWidget(WLibrary* libraryWidget, - KeyboardEventFilter* keyboard); + QWidget* createPaneWidget(KeyboardEventFilter* pKeyboard, int paneId) override; + QWidget* createInnerSidebarWidget(KeyboardEventFilter* pKeyboard) override; TreeItemModel* getChildModel(); @@ -55,17 +57,15 @@ class AutoDJFeature : public LibraryFeature { void onRightClickChild(const QPoint& globalPos, QModelIndex index); private: - UserSettingsPointer m_pConfig; - Library* m_pLibrary; TrackCollection* m_pTrackCollection; CrateDAO& m_crateDao; PlaylistDAO& m_playlistDao; // The id of the AutoDJ playlist. int m_iAutoDJPlaylistId; AutoDJProcessor* m_pAutoDJProcessor; - const static QString m_sAutoDJViewName; TreeItemModel m_childModel; - DlgAutoDJ* m_pAutoDJView; + QPointer m_pAutoDJView; + QHash > m_trackTables; // Initialize the list of crates loaded into the auto-DJ queue. void constructCrateChildModel(); @@ -118,6 +118,7 @@ class AutoDJFeature : public LibraryFeature { // of tracks in the playlist void slotRandomQueue(int); + void selectionChanged(const QItemSelection&, const QItemSelection&); }; diff --git a/src/library/autodj/dlgautodj.cpp b/src/library/autodj/dlgautodj.cpp index 9b2197e422e8..30a449dc4308 100644 --- a/src/library/autodj/dlgautodj.cpp +++ b/src/library/autodj/dlgautodj.cpp @@ -8,67 +8,31 @@ #include "util/duration.h" DlgAutoDJ::DlgAutoDJ(QWidget* parent, - UserSettingsPointer pConfig, Library* pLibrary, - AutoDJProcessor* pProcessor, - TrackCollection* pTrackCollection, - KeyboardEventFilter* pKeyboard) - : QWidget(parent), + AutoDJProcessor* pProcessor) + : QFrame(parent), Ui::DlgAutoDJ(), m_pAutoDJProcessor(pProcessor), // no sorting - m_pTrackTableView(new WTrackTableView(this, pConfig, - pTrackCollection, false)), - m_pAutoDJTableModel(NULL) { + m_pAutoDJTableModel(nullptr), + m_pLibrary(pLibrary) { setupUi(this); - m_pTrackTableView->installEventFilter(pKeyboard); - connect(m_pTrackTableView, SIGNAL(loadTrack(TrackPointer)), - this, SIGNAL(loadTrack(TrackPointer))); - connect(m_pTrackTableView, SIGNAL(loadTrackToPlayer(TrackPointer, QString, bool)), - this, SIGNAL(loadTrackToPlayer(TrackPointer, QString, bool))); - connect(m_pTrackTableView, SIGNAL(trackSelected(TrackPointer)), - this, SIGNAL(trackSelected(TrackPointer))); - connect(pLibrary, SIGNAL(setTrackTableFont(QFont)), - m_pTrackTableView, SLOT(setTrackTableFont(QFont))); - connect(pLibrary, SIGNAL(setTrackTableRowHeight(int)), - m_pTrackTableView, SLOT(setTrackTableRowHeight(int))); - connect(m_pTrackTableView, SIGNAL(trackSelected(TrackPointer)), - this, SLOT(updateSelectionInfo())); - - - QBoxLayout* box = dynamic_cast(layout()); - DEBUG_ASSERT_AND_HANDLE(box) { //Assumes the form layout is a QVBox/QHBoxLayout! - } else { - box->removeWidget(m_pTrackTablePlaceholder); - m_pTrackTablePlaceholder->hide(); - box->insertWidget(1, m_pTrackTableView); - } - // We do _NOT_ take ownership of this from AutoDJProcessor. m_pAutoDJTableModel = m_pAutoDJProcessor->getTableModel(); - m_pTrackTableView->loadTrackModel(m_pAutoDJTableModel); // Override some playlist-view properties: - // Do not set this because it disables auto-scrolling - //m_pTrackTableView->setDragDropMode(QAbstractItemView::InternalMove); - connect(pushButtonShuffle, SIGNAL(clicked(bool)), this, SLOT(shufflePlaylistButton(bool))); - connect(pushButtonSkipNext, SIGNAL(clicked(bool)), this, SLOT(skipNextButton(bool))); - connect(pushButtonAddRandom, SIGNAL(clicked(bool)), this, SIGNAL(addRandomButton(bool))); - connect(pushButtonFadeNow, SIGNAL(clicked(bool)), this, SLOT(fadeNowButton(bool))); - connect(spinBoxTransition, SIGNAL(valueChanged(int)), this, SLOT(transitionSliderChanged(int))); - connect(pushButtonAutoDJ, SIGNAL(toggled(bool)), this, SLOT(toggleAutoDJButton(bool))); @@ -81,45 +45,30 @@ DlgAutoDJ::DlgAutoDJ(QWidget* parent, connect(m_pAutoDJProcessor, SIGNAL(autoDJStateChanged(AutoDJProcessor::AutoDJState)), this, SLOT(autoDJStateChanged(AutoDJProcessor::AutoDJState))); autoDJStateChanged(m_pAutoDJProcessor->getState()); - - updateSelectionInfo(); } DlgAutoDJ::~DlgAutoDJ() { - qDebug() << "~DlgAutoDJ()"; - - // Delete m_pTrackTableView before the table model. This is because the - // table view saves the header state using the model. - delete m_pTrackTableView; + //qDebug() << "~DlgAutoDJ()"; } void DlgAutoDJ::onShow() { m_pAutoDJTableModel->select(); } -void DlgAutoDJ::onSearch(const QString& text) { - // Do not allow filtering the Auto DJ playlist, because - // Auto DJ will work from the filtered table - Q_UNUSED(text); -} - -void DlgAutoDJ::loadSelectedTrack() { - m_pTrackTableView->loadSelectedTrack(); -} - -void DlgAutoDJ::loadSelectedTrackToGroup(QString group, bool play) { - m_pTrackTableView->loadSelectedTrackToGroup(group, play); -} - -void DlgAutoDJ::moveSelection(int delta) { - m_pTrackTableView->moveSelection(delta); +void DlgAutoDJ::setSelectedRows(const QModelIndexList& selectedRows) { + m_selectedRows = selectedRows; + updateSelectionInfo(); } -void DlgAutoDJ::shufflePlaylistButton(bool) { - QModelIndexList indexList = m_pTrackTableView->selectionModel()->selectedRows(); - - // Activate regardless of button being checked - m_pAutoDJProcessor->shufflePlaylist(indexList); +void DlgAutoDJ::shufflePlaylistButton(bool) { + LibraryView* pView = m_pLibrary->getActiveView(); + WTrackTableView* pTrackTable = dynamic_cast(pView); + + if (pView) { + QModelIndexList indexList = pTrackTable->selectionModel()->selectedRows(); + // Activate regardless of button being checked + m_pAutoDJProcessor->shufflePlaylist(indexList); + } } void DlgAutoDJ::skipNextButton(bool) { @@ -191,35 +140,24 @@ void DlgAutoDJ::autoDJStateChanged(AutoDJProcessor::AutoDJState state) { } } -void DlgAutoDJ::setTrackTableFont(const QFont& font) { - m_pTrackTableView->setTrackTableFont(font); -} - -void DlgAutoDJ::setTrackTableRowHeight(int rowHeight) { - m_pTrackTableView->setTrackTableRowHeight(rowHeight); -} - void DlgAutoDJ::updateSelectionInfo() { + if (m_selectedRows.isEmpty()) { + labelSelectionInfo->setText(""); + labelSelectionInfo->setEnabled(false); + return; + } + double duration = 0.0; - - QModelIndexList indices = m_pTrackTableView->selectionModel()->selectedRows(); - - for (int i = 0; i < indices.size(); ++i) { - TrackPointer pTrack = m_pAutoDJTableModel->getTrack(indices.at(i)); + for (const QModelIndex& mIndex : m_selectedRows) { + TrackPointer pTrack = m_pAutoDJTableModel->getTrack(mIndex); if (pTrack) { duration += pTrack->getDuration(); } } QString label; - - if (!indices.isEmpty()) { - label.append(mixxx::Duration::formatSeconds(duration)); - label.append(QString(" (%1)").arg(indices.size())); - labelSelectionInfo->setText(label); - labelSelectionInfo->setEnabled(true); - } else { - labelSelectionInfo->setText(""); - labelSelectionInfo->setEnabled(false); - } + label.append(mixxx::Duration::formatSeconds(duration)); + label.append(QString(" (%1)").arg(m_selectedRows.size())); + labelSelectionInfo->setText(label); + labelSelectionInfo->setEnabled(true); } diff --git a/src/library/autodj/dlgautodj.h b/src/library/autodj/dlgautodj.h index fda36bcdcbbe..f7ac02d61968 100644 --- a/src/library/autodj/dlgautodj.h +++ b/src/library/autodj/dlgautodj.h @@ -3,6 +3,7 @@ #include #include +#include #include "library/autodj/ui_dlgautodj.h" #include "preferences/usersettings.h" @@ -16,20 +17,16 @@ class PlaylistTableModel; class WTrackTableView; -class DlgAutoDJ : public QWidget, public Ui::DlgAutoDJ, public LibraryView { +class DlgAutoDJ : public QFrame, public Ui::DlgAutoDJ { Q_OBJECT public: - DlgAutoDJ(QWidget* parent, UserSettingsPointer pConfig, - Library* pLibrary, - AutoDJProcessor* pProcessor, TrackCollection* pTrackCollection, - KeyboardEventFilter* pKeyboard); + DlgAutoDJ(QWidget* parent, Library *pLibrary, AutoDJProcessor* pProcessor); virtual ~DlgAutoDJ(); - + void onShow(); - void onSearch(const QString& text); - void loadSelectedTrack(); - void loadSelectedTrackToGroup(QString group, bool play); - void moveSelection(int delta); + + // These seleced rows are always from the focused pane + void setSelectedRows(const QModelIndexList& selectedRows); public slots: void shufflePlaylistButton(bool buttonChecked); @@ -39,20 +36,17 @@ class DlgAutoDJ : public QWidget, public Ui::DlgAutoDJ, public LibraryView { void transitionTimeChanged(int time); void transitionSliderChanged(int value); void autoDJStateChanged(AutoDJProcessor::AutoDJState state); - void setTrackTableFont(const QFont& font); - void setTrackTableRowHeight(int rowHeight); void updateSelectionInfo(); signals: void addRandomButton(bool buttonChecked); - void loadTrack(TrackPointer tio); - void loadTrackToPlayer(TrackPointer tio, QString group, bool); - void trackSelected(TrackPointer pTrack); - + private: AutoDJProcessor* m_pAutoDJProcessor; - WTrackTableView* m_pTrackTableView; PlaylistTableModel* m_pAutoDJTableModel; + Library* m_pLibrary; + + QModelIndexList m_selectedRows; }; #endif //DLGAUTODJ_H diff --git a/src/library/autodj/dlgautodj.ui b/src/library/autodj/dlgautodj.ui index f71a2a1cbd6d..b8c2bb67a1c4 100644 --- a/src/library/autodj/dlgautodj.ui +++ b/src/library/autodj/dlgautodj.ui @@ -6,7 +6,7 @@ 0 0 - 560 + 507 399 @@ -14,81 +14,15 @@ Auto DJ - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Shuffle the content of the Auto DJ playlist. - - - Shuffle - - - false - - - - - - - Add a random track from track sources (crates) or Library to the Auto DJ playlist. - - - Add Random - - - - - - - Skip the next track in the Auto DJ playlist. - - - Skip Track - - - false - - - - + - Trigger the transition to the next track. + Seconds - Fade Now + sec. @@ -114,16 +48,6 @@ - - - - Seconds - - - sec. - - - @@ -131,45 +55,95 @@ - 1 + 40 20 - - - - - - - - - - - Turn Auto DJ on or off. - - - Enable Auto DJ - - - true - - - - - + + + Turn Auto DJ on or off. + + + Enable Auto DJ + + true + + + + + + + + + + + Trigger the transition to the next track. + + + Fade Now + + + + + + + Skip the next track in the Auto DJ playlist. + + + Skip Track + + + false + + + + + + + Add a random track from track sources (crates) or Library to the Auto DJ playlist. + + + Add Random + + + + + + + Shuffle the content of the Auto DJ playlist. + + + Shuffle + + + false + + + + + + + Qt::Vertical + + + + 20 + 172 + + + + - - - + diff --git a/src/library/banshee/bansheefeature.cpp b/src/library/banshee/bansheefeature.cpp index 05ee9c67a3dc..6e90b8b67bbb 100644 --- a/src/library/banshee/bansheefeature.cpp +++ b/src/library/banshee/bansheefeature.cpp @@ -8,15 +8,17 @@ #include "library/dao/settingsdao.h" #include "library/baseexternalplaylistmodel.h" #include "library/banshee/bansheeplaylistmodel.h" +#include "library/library.h" const QString BansheeFeature::BANSHEE_MOUNT_KEY = "mixxx.BansheeFeature.mount"; QString BansheeFeature::m_databaseFile; -BansheeFeature::BansheeFeature(QObject* parent, - TrackCollection* pTrackCollection, - UserSettingsPointer pConfig) - : BaseExternalLibraryFeature(parent, pTrackCollection), +BansheeFeature::BansheeFeature(UserSettingsPointer pConfig, + Library* pLibrary, + QObject* parent, + TrackCollection* pTrackCollection) + : BaseExternalLibraryFeature(pConfig, pLibrary, parent, pTrackCollection), m_pTrackCollection(pTrackCollection), m_cancelImport(false) { Q_UNUSED(pConfig); @@ -91,7 +93,8 @@ void BansheeFeature::activate() { m_isActivated = true; - TreeItem* playlist_root = new TreeItem(); + TreeItem* playlistRoot = new TreeItem(); + playlistRoot->setLibraryFeature(this); QList list = m_connection.getPlaylists(); @@ -99,12 +102,12 @@ void BansheeFeature::activate() { foreach (playlist, list) { qDebug() << playlist.name; // append the playlist to the child model - TreeItem *item = new TreeItem(playlist.name, playlist.playlistId, this, playlist_root); - playlist_root->appendChild(item); + TreeItem *item = new TreeItem(playlist.name, playlist.playlistId, this, playlistRoot); + playlistRoot->appendChild(item); } - if (playlist_root) { - m_childModel.setRootItem(playlist_root); + if (playlistRoot) { + m_childModel.setRootItem(playlistRoot); if (m_isActivated) { activate(); } @@ -117,7 +120,9 @@ void BansheeFeature::activate() { } m_pBansheePlaylistModel->setTableModel(0); // Gets the master playlist - emit(showTrackModel(m_pBansheePlaylistModel)); + + showTrackModel(m_pBansheePlaylistModel); + m_pLibrary->showBreadCrumb(m_childModel.getItem(QModelIndex())); emit(enableCoverArtDisplay(false)); } @@ -129,7 +134,9 @@ void BansheeFeature::activateChild(const QModelIndex& index) { if (playlistID > 0) { qDebug() << "Activating " << item->data().toString(); m_pBansheePlaylistModel->setTableModel(playlistID); - emit(showTrackModel(m_pBansheePlaylistModel)); + + showTrackModel(m_pBansheePlaylistModel); + m_pLibrary->showBreadCrumb(item); emit(enableCoverArtDisplay(false)); } } diff --git a/src/library/banshee/bansheefeature.h b/src/library/banshee/bansheefeature.h index 566835896290..096a3b924cdf 100644 --- a/src/library/banshee/bansheefeature.h +++ b/src/library/banshee/bansheefeature.h @@ -20,7 +20,10 @@ class BansheePlaylistModel; class BansheeFeature : public BaseExternalLibraryFeature { Q_OBJECT public: - BansheeFeature(QObject* parent, TrackCollection* pTrackCollection, UserSettingsPointer pConfig); + BansheeFeature(UserSettingsPointer pConfig, + Library* pLibrary, + QObject* parent, + TrackCollection* pTrackCollection); virtual ~BansheeFeature(); static bool isSupported(); static void prepareDbPath(UserSettingsPointer pConfig); @@ -55,7 +58,7 @@ class BansheeFeature : public BaseExternalLibraryFeature { bool m_cancelImport; static QString m_databaseFile; - + static const QString BANSHEE_MOUNT_KEY; }; diff --git a/src/library/baseexternallibraryfeature.cpp b/src/library/baseexternallibraryfeature.cpp index 27d69ee48e34..450a9277689f 100644 --- a/src/library/baseexternallibraryfeature.cpp +++ b/src/library/baseexternallibraryfeature.cpp @@ -4,10 +4,11 @@ #include "library/basesqltablemodel.h" -BaseExternalLibraryFeature::BaseExternalLibraryFeature(QObject* pParent, +BaseExternalLibraryFeature::BaseExternalLibraryFeature(UserSettingsPointer pConfig, + Library* pLibrary, + QObject* pParent, TrackCollection* pCollection) - : LibraryFeature(pParent), - m_pTrackCollection(pCollection) { + : LibraryFeature(pConfig, pLibrary, pCollection, pParent) { m_pAddToAutoDJAction = new QAction(tr("Add to Auto DJ Queue (bottom)"), this); connect(m_pAddToAutoDJAction, SIGNAL(triggered()), this, SLOT(slotAddToAutoDJ())); diff --git a/src/library/baseexternallibraryfeature.h b/src/library/baseexternallibraryfeature.h index 7a13f5d47d20..43ce15bbbbb8 100644 --- a/src/library/baseexternallibraryfeature.h +++ b/src/library/baseexternallibraryfeature.h @@ -12,7 +12,10 @@ class TrackCollection; class BaseExternalLibraryFeature : public LibraryFeature { Q_OBJECT public: - BaseExternalLibraryFeature(QObject* pParent, TrackCollection* pCollection); + BaseExternalLibraryFeature(UserSettingsPointer pConfig, + Library* pLibrary, + QObject* pParent, + TrackCollection* pCollection); virtual ~BaseExternalLibraryFeature(); public slots: @@ -38,7 +41,6 @@ class BaseExternalLibraryFeature : public LibraryFeature { private: void addToAutoDJ(bool bTop); - TrackCollection* m_pTrackCollection; QAction* m_pAddToAutoDJAction; QAction* m_pAddToAutoDJTopAction; QAction* m_pImportAsMixxxPlaylistAction; diff --git a/src/library/baseplaylistfeature.cpp b/src/library/baseplaylistfeature.cpp index f210b08d031a..33221f1cf7e3 100644 --- a/src/library/baseplaylistfeature.cpp +++ b/src/library/baseplaylistfeature.cpp @@ -16,19 +16,18 @@ #include "library/treeitem.h" #include "controllers/keyboard/keyboardeventfilter.h" #include "widget/wlibrary.h" +#include "widget/wlibrarystack.h" #include "widget/wlibrarytextbrowser.h" #include "util/assert.h" -BasePlaylistFeature::BasePlaylistFeature(QObject* parent, - UserSettingsPointer pConfig, - TrackCollection* pTrackCollection, - QString rootViewName) - : LibraryFeature(pConfig, parent), - m_pTrackCollection(pTrackCollection), +BasePlaylistFeature::BasePlaylistFeature(UserSettingsPointer pConfig, + Library* pLibrary, + QObject* parent, + TrackCollection* pTrackCollection) + : LibraryFeature(pConfig, pLibrary, pTrackCollection, parent), m_playlistDao(pTrackCollection->getPlaylistDAO()), m_trackDao(pTrackCollection->getTrackDAO()), - m_pPlaylistTableModel(NULL), - m_rootViewName(rootViewName) { + m_pPlaylistTableModel(nullptr) { m_pCreatePlaylistAction = new QAction(tr("Create New Playlist"),this); connect(m_pCreatePlaylistAction, SIGNAL(triggered()), this, SLOT(slotCreatePlaylist())); @@ -92,7 +91,6 @@ BasePlaylistFeature::BasePlaylistFeature(QObject* parent, connect(&m_playlistDao, SIGNAL(lockChanged(int)), this, SLOT(slotPlaylistTableChanged(int))); - Library* pLibrary = static_cast(parent); connect(pLibrary, SIGNAL(trackSelected(TrackPointer)), this, SLOT(slotTrackSelected(TrackPointer))); connect(pLibrary, SIGNAL(switchToView(const QString&)), @@ -100,7 +98,8 @@ BasePlaylistFeature::BasePlaylistFeature(QObject* parent, } BasePlaylistFeature::~BasePlaylistFeature() { - delete m_pPlaylistTableModel; + qDeleteAll(m_playlistTableModel); + m_playlistTableModel.clear(); delete m_pCreatePlaylistAction; delete m_pDeletePlaylistAction; delete m_pImportPlaylistAction; @@ -130,18 +129,58 @@ int BasePlaylistFeature::playlistIdFromIndex(QModelIndex index) { return playlistId; } +QPointer BasePlaylistFeature::getPlaylistTableModel(int paneId) { + if (paneId < 0) { + paneId = m_focusedPane; + } + auto it = m_playlistTableModel.find(paneId); + if (it == m_playlistTableModel.end() || it->isNull()) { + it = m_playlistTableModel.insert(paneId, constructTableModel()); + } + return *it; +} + void BasePlaylistFeature::activate() { - emit(switchToView(m_rootViewName)); - emit(restoreSearch(QString())); // Null String disables search box + auto it = m_panes.find(m_featureFocus); + auto itId = m_idBrowse.find(m_featureFocus); + if (it == m_panes.end() || it->isNull() || itId == m_idBrowse.end()) { + return; + } + + (*it)->setCurrentIndex(*itId); + switchToFeature(); + showBreadCrumb(m_childModel.getItem(QModelIndex())); + + restoreSearch(QString()); // Null String disables search box emit(enableCoverArtDisplay(true)); + m_featureFocus = -1; } void BasePlaylistFeature::activateChild(const QModelIndex& index) { //qDebug() << "BasePlaylistFeature::activateChild()" << index; int playlistId = playlistIdFromIndex(index); + m_pPlaylistTableModel = getPlaylistTableModel(m_focusedPane); + if (playlistId != -1 && m_pPlaylistTableModel) { m_pPlaylistTableModel->setTableModel(playlistId); - emit(showTrackModel(m_pPlaylistTableModel)); + + auto it = m_panes.find(m_focusedPane); + auto itId = m_idTable.find(m_focusedPane); + if (it == m_panes.end() || it->isNull() || itId == m_idTable.end()) { + return; + } + + (*it)->setCurrentIndex(*itId); + + // Set the feature Focus for a moment to allow the LibraryFeature class + // to find the focused WTrackTable + m_featureFocus = m_focusedPane; + showTrackModel(m_pPlaylistTableModel); + m_featureFocus = -1; + + restoreSearch(""); + showBreadCrumb(static_cast(index.internalPointer())); + emit(enableCoverArtDisplay(true)); } } @@ -151,7 +190,8 @@ void BasePlaylistFeature::activatePlaylist(int playlistId) { QModelIndex index = indexFromPlaylistId(playlistId); if (playlistId != -1 && index.isValid() && m_pPlaylistTableModel) { m_pPlaylistTableModel->setTableModel(playlistId); - emit(showTrackModel(m_pPlaylistTableModel)); + showTrackModel(m_pPlaylistTableModel); + //m_pPlaylistTableModel->select(); emit(enableCoverArtDisplay(true)); // Update selection emit(featureSelect(this, m_lastRightClickedIndex)); @@ -310,6 +350,11 @@ void BasePlaylistFeature::slotCreatePlaylist() { } } +void BasePlaylistFeature::setFeatureFocus(int focus) { + m_pPlaylistTableModel = getPlaylistTableModel(focus); + LibraryFeature::setFeatureFocus(focus); +} + void BasePlaylistFeature::slotDeletePlaylist() { //qDebug() << "slotDeletePlaylist() row:" << m_lastRightClickedIndex.data(); int playlistId = playlistIdFromIndex(m_lastRightClickedIndex); @@ -593,15 +638,24 @@ TreeItemModel* BasePlaylistFeature::getChildModel() { return &m_childModel; } -void BasePlaylistFeature::bindWidget(WLibrary* libraryWidget, - KeyboardEventFilter* keyboard) { - Q_UNUSED(keyboard); - WLibraryTextBrowser* edit = new WLibraryTextBrowser(libraryWidget); +QWidget* BasePlaylistFeature::createPaneWidget(KeyboardEventFilter* pKeyboard, + int paneId) { + WLibraryStack* pStack = new WLibraryStack(nullptr); + m_panes[paneId] = pStack; + + WLibraryTextBrowser* edit = new WLibraryTextBrowser(pStack); edit->setHtml(getRootViewHtml()); edit->setOpenLinks(false); + edit->installEventFilter(pKeyboard); connect(edit, SIGNAL(anchorClicked(const QUrl)), this, SLOT(htmlLinkClicked(const QUrl))); - libraryWidget->registerView(m_rootViewName, edit); + m_idBrowse[paneId] = pStack->addWidget(edit); + + QWidget* pTable = LibraryFeature::createPaneWidget(pKeyboard, paneId); + pTable->setParent(pStack); + m_idTable[paneId] = pStack->addWidget(pTable); + + return pStack; } void BasePlaylistFeature::htmlLinkClicked(const QUrl& link) { @@ -619,6 +673,7 @@ void BasePlaylistFeature::htmlLinkClicked(const QUrl& link) { */ QModelIndex BasePlaylistFeature::constructChildModel(int selected_id) { buildPlaylistList(); + m_childModel.setRootItem(new TreeItem("$root", "$root", this, nullptr)); QList data_list; int selected_row = -1; // Access the invisible root item diff --git a/src/library/baseplaylistfeature.h b/src/library/baseplaylistfeature.h index 4b8583cc22d0..73391ef52fc1 100644 --- a/src/library/baseplaylistfeature.h +++ b/src/library/baseplaylistfeature.h @@ -16,25 +16,25 @@ #include "library/dao/trackdao.h" #include "track/track.h" -class WLibrary; class KeyboardEventFilter; class PlaylistTableModel; class TrackCollection; class TreeItem; +class WLibrary; +class WLibraryStack; class BasePlaylistFeature : public LibraryFeature { Q_OBJECT public: - BasePlaylistFeature(QObject* parent, - UserSettingsPointer pConfig, - TrackCollection* pTrackCollection, - QString rootViewName); + BasePlaylistFeature(UserSettingsPointer pConfig, + Library* pLibrary, + QObject* parent, + TrackCollection* pTrackCollection); virtual ~BasePlaylistFeature(); TreeItemModel* getChildModel(); - void bindWidget(WLibrary* libraryWidget, - KeyboardEventFilter* keyboard); + QWidget* createPaneWidget(KeyboardEventFilter*pKeyboard, int paneId) override; signals: void showPage(const QUrl& page); @@ -50,6 +50,7 @@ class BasePlaylistFeature : public LibraryFeature { virtual void slotPlaylistContentChanged(int playlistId) = 0; virtual void slotPlaylistTableRenamed(int playlistId, QString a_strName) = 0; void slotCreatePlaylist(); + void setFeatureFocus(int focus); protected slots: void slotDeletePlaylist(); @@ -75,14 +76,17 @@ class BasePlaylistFeature : public LibraryFeature { virtual void addToAutoDJ(bool bTop); int playlistIdFromIndex(QModelIndex index); + QPointer getPlaylistTableModel(int paneId); + virtual PlaylistTableModel* constructTableModel() = 0; + // Get the QModelIndex of a playlist based on its id. Returns QModelIndex() // on failure. QModelIndex indexFromPlaylistId(int playlistId); - TrackCollection* m_pTrackCollection; PlaylistDAO &m_playlistDao; TrackDAO &m_trackDao; - PlaylistTableModel* m_pPlaylistTableModel; + QPointer m_pPlaylistTableModel; + QHash > m_playlistTableModel; QAction *m_pCreatePlaylistAction; QAction *m_pDeletePlaylistAction; QAction *m_pAddToAutoDJAction; @@ -108,7 +112,10 @@ class BasePlaylistFeature : public LibraryFeature { virtual QString getRootViewHtml() const = 0; QSet m_playlistsSelectedTrackIsIn; - QString m_rootViewName; + + QHash > m_panes; + QHash m_idBrowse; + QHash m_idTable; }; #endif /* BASEPLAYLISTFEATURE_H */ diff --git a/src/library/browse/browsefeature.cpp b/src/library/browse/browsefeature.cpp index 9f6231b78c0c..cf8c9e586fc7 100644 --- a/src/library/browse/browsefeature.cpp +++ b/src/library/browse/browsefeature.cpp @@ -1,33 +1,36 @@ // browsefeature.cpp // Created 9/8/2009 by RJ Ryan (rryan@mit.edu) -#include -#include +#include +#include #include -#include #include -#include -#include #include #include +#include +#include +#include -#include "track/track.h" -#include "library/treeitem.h" +#include "controllers/keyboard/keyboardeventfilter.h" #include "library/browse/browsefeature.h" +#include "library/library.h" #include "library/trackcollection.h" -#include "widget/wlibrarytextbrowser.h" -#include "widget/wlibrary.h" -#include "controllers/keyboard/keyboardeventfilter.h" +#include "library/treeitem.h" +#include "track/track.h" #include "util/sandbox.h" +#include "widget/wlibrary.h" +#include "widget/wlibrarystack.h" +#include "widget/wlibrarytextbrowser.h" + const QString kQuickLinksSeparator = "-+-"; -BrowseFeature::BrowseFeature(QObject* parent, - UserSettingsPointer pConfig, +BrowseFeature::BrowseFeature(UserSettingsPointer pConfig, + Library* pLibrary, + QObject* parent, TrackCollection* pTrackCollection, RecordingManager* pRecordingManager) - : LibraryFeature(parent), - m_pConfig(pConfig), + : LibraryFeature(pConfig, pLibrary, pTrackCollection, parent), m_browseModel(this, pTrackCollection, pRecordingManager), m_proxyModel(&m_browseModel), m_pTrackCollection(pTrackCollection), @@ -55,6 +58,7 @@ BrowseFeature::BrowseFeature(QObject* parent, // The invisible root item of the child model TreeItem* rootItem = new TreeItem(); + rootItem->setLibraryFeature(this); m_pQuickLinkItem = new TreeItem(tr("Quick Links"), QUICK_LINK_NODE, this, rootItem); rootItem->appendChild(m_pQuickLinkItem); @@ -207,17 +211,35 @@ TreeItemModel* BrowseFeature::getChildModel() { return &m_childModel; } -void BrowseFeature::bindWidget(WLibrary* libraryWidget, - KeyboardEventFilter* keyboard) { - Q_UNUSED(keyboard); - WLibraryTextBrowser* edit = new WLibraryTextBrowser(libraryWidget); - edit->setHtml(getRootViewHtml()); - libraryWidget->registerView("BROWSEHOME", edit); +QWidget* BrowseFeature::createPaneWidget(KeyboardEventFilter* pKeyboard, + int paneId) { + WLibraryStack* pStack = new WLibraryStack(nullptr); + m_panes[paneId] = pStack; + + WLibraryTextBrowser* pEdit = new WLibraryTextBrowser(nullptr); + pEdit->setHtml(getRootViewHtml()); + pEdit->installEventFilter(pKeyboard); + m_idBrowse[paneId] = pStack->addWidget(pEdit); + + QWidget* pTable = LibraryFeature::createPaneWidget(pKeyboard, paneId); + pTable->setParent(pStack); + m_idTable[paneId] = pStack->addWidget(pTable); + + return pStack; } void BrowseFeature::activate() { - emit(switchToView("BROWSEHOME")); - emit(restoreSearch(QString())); + auto it = m_panes.find(m_featureFocus); + auto itId = m_idBrowse.find(m_featureFocus); + if (it == m_panes.end() || it->isNull() || itId == m_idBrowse.end()) { + return; + } + + (*it)->setCurrentIndex(*itId); + switchToFeature(); + m_pLibrary->showBreadCrumb(m_childModel.getItem(QModelIndex())); + m_pLibrary->restoreSearch(QString()); + emit(enableCoverArtDisplay(false)); } @@ -246,8 +268,20 @@ void BrowseFeature::activateChild(const QModelIndex& index) { } m_browseModel.setPath(dir); } - emit(showTrackModel(&m_proxyModel)); - emit(enableCoverArtDisplay(false)); + + auto itId = m_idTable.find(m_featureFocus); + auto it = m_panes.find(m_featureFocus); + + if (it == m_panes.end() || it->isNull() || itId == m_idTable.end()) { + qDebug() << "BrowseFeature::activateChild item not found"; + return; + } + + (*it)->setCurrentIndex(*itId); + showTrackModel(&m_proxyModel); + m_pLibrary->showBreadCrumb(item); + + emit(enableCoverArtDisplay(true)); } void BrowseFeature::onRightClickChild(const QPoint& globalPos, QModelIndex index) { diff --git a/src/library/browse/browsefeature.h b/src/library/browse/browsefeature.h index 94188ee6b0d0..46fdce22f4d8 100644 --- a/src/library/browse/browsefeature.h +++ b/src/library/browse/browsefeature.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -23,12 +24,14 @@ #define DEVICE_NODE "::mixxx_device_node::" class TrackCollection; +class WLibraryStack; class BrowseFeature : public LibraryFeature { Q_OBJECT public: - BrowseFeature(QObject* parent, - UserSettingsPointer pConfig, + BrowseFeature(UserSettingsPointer pConfig, + Library* pLibrary, + QObject* parent, TrackCollection* pTrackCollection, RecordingManager* pRec); virtual ~BrowseFeature(); @@ -36,8 +39,7 @@ class BrowseFeature : public LibraryFeature { QVariant title(); QIcon getIcon(); - void bindWidget(WLibrary* libraryWidget, - KeyboardEventFilter* keyboard); + QWidget* createPaneWidget(KeyboardEventFilter*pKeyboard, int paneId) override; TreeItemModel* getChildModel(); @@ -64,7 +66,6 @@ class BrowseFeature : public LibraryFeature { void saveQuickLinks(); void loadQuickLinks(); - UserSettingsPointer m_pConfig; BrowseTableModel m_browseModel; ProxyTrackModel m_proxyModel; TrackCollection* m_pTrackCollection; @@ -75,6 +76,10 @@ class BrowseFeature : public LibraryFeature { TreeItem* m_pLastRightClickedItem; TreeItem* m_pQuickLinkItem; QStringList m_quickLinkList; + + QHash > m_panes; + QHash m_idBrowse; + QHash m_idTable; }; #endif // BROWSEFEATURE_H diff --git a/src/library/browse/foldertreemodel.cpp b/src/library/browse/foldertreemodel.cpp index 62b28afc4124..6776e394ecfc 100644 --- a/src/library/browse/foldertreemodel.cpp +++ b/src/library/browse/foldertreemodel.cpp @@ -33,11 +33,19 @@ FolderTreeModel::~FolderTreeModel() { * is only called if necessary. */ bool FolderTreeModel::hasChildren(const QModelIndex& parent) const { + if (!parent.isValid()) { + return true; + } + TreeItem *item = static_cast(parent.internalPointer()); /* Usually the child count is 0 becuase we do lazy initalization * However, for, buid-in items such as 'Quick Links' there exist * child items at init time */ + if (item == nullptr) { + return false; + } + if(item->dataPath().toString() == QUICK_LINK_NODE) return true; //Can only happen on Windows diff --git a/src/library/cratefeature.cpp b/src/library/cratefeature.cpp index 0d53d49e321e..f56d5a1452a9 100644 --- a/src/library/cratefeature.cpp +++ b/src/library/cratefeature.cpp @@ -7,31 +7,34 @@ #include #include + +#include "controllers/keyboard/keyboardeventfilter.h" #include "library/cratefeature.h" +#include "library/cratetablemodel.h" #include "library/export/trackexportwizard.h" +#include "library/parsercsv.h" #include "library/parser.h" #include "library/parserm3u.h" #include "library/parserpls.h" -#include "library/parsercsv.h" - -#include "library/cratetablemodel.h" -#include "library/trackcollection.h" #include "library/queryutil.h" -#include "widget/wlibrarytextbrowser.h" -#include "widget/wlibrary.h" -#include "controllers/keyboard/keyboardeventfilter.h" -#include "treeitem.h" +#include "library/trackcollection.h" #include "sources/soundsourceproxy.h" +#include "treeitem.h" #include "util/dnd.h" #include "util/duration.h" +#include "util/time.h" +#include "widget/wlibrary.h" +#include "widget/wlibrarytextbrowser.h" +#include "widget/wlibrarystack.h" -CrateFeature::CrateFeature(Library* pLibrary, - TrackCollection* pTrackCollection, - UserSettingsPointer pConfig) - : LibraryFeature(pConfig), +CrateFeature::CrateFeature(UserSettingsPointer pConfig, + Library* pLibrary, + QObject* parent, + TrackCollection* pTrackCollection) + : LibraryFeature(pConfig, pLibrary, pTrackCollection, parent), m_pTrackCollection(pTrackCollection), m_crateDao(pTrackCollection->getCrateDAO()), - m_crateTableModel(this, pTrackCollection) { + m_pCrateTableModel(nullptr) { m_pCreateCrateAction = new QAction(tr("Create New Crate"),this); connect(m_pCreateCrateAction, SIGNAL(triggered()), @@ -94,14 +97,13 @@ CrateFeature::CrateFeature(Library* pLibrary, this, SLOT(slotCrateTableChanged(int))); // construct child model - TreeItem *rootItem = new TreeItem(); - m_childModel.setRootItem(rootItem); + TreeItem *pRootItem = new TreeItem(); + pRootItem->setLibraryFeature(this); + m_childModel.setRootItem(pRootItem); constructChildModel(-1); connect(pLibrary, SIGNAL(trackSelected(TrackPointer)), this, SLOT(slotTrackSelected(TrackPointer))); - connect(pLibrary, SIGNAL(switchToView(const QString&)), - this, SLOT(slotResetSelectedTrack())); } CrateFeature::~CrateFeature() { @@ -127,7 +129,7 @@ QIcon CrateFeature::getIcon() { int CrateFeature::crateIdFromIndex(QModelIndex index) { TreeItem* item = static_cast(index.internalPointer()); - if (item == NULL) { + if (item == nullptr) { return -1; } @@ -140,6 +142,12 @@ int CrateFeature::crateIdFromIndex(QModelIndex index) { return playlistId; } +bool CrateFeature::dragMoveAccept(QUrl url) { + return SoundSourceProxy::isUrlSupported(url) || + Parser::isPlaylistFilenameSupported(url.toLocalFile()); +} + + bool CrateFeature::dropAcceptChild(const QModelIndex& index, QList urls, QObject* pSource) { int crateId = crateIdFromIndex(index); @@ -178,15 +186,24 @@ bool CrateFeature::dragMoveAcceptChild(const QModelIndex& index, QUrl url) { return !locked && formatSupported; } -void CrateFeature::bindWidget(WLibrary* libraryWidget, - KeyboardEventFilter* keyboard) { - Q_UNUSED(keyboard); - WLibraryTextBrowser* edit = new WLibraryTextBrowser(libraryWidget); - edit->setHtml(getRootViewHtml()); - edit->setOpenLinks(false); - connect(edit, SIGNAL(anchorClicked(const QUrl)), +QWidget* CrateFeature::createPaneWidget(KeyboardEventFilter *pKeyboard, + int paneId) { + WLibraryStack* pContainer = new WLibraryStack(nullptr); + m_panes[paneId] = pContainer; + + WLibraryTextBrowser* pEdit = new WLibraryTextBrowser(pContainer); + pEdit->setHtml(getRootViewHtml()); + pEdit->setOpenLinks(false); + pEdit->installEventFilter(pKeyboard); + connect(pEdit, SIGNAL(anchorClicked(const QUrl)), this, SLOT(htmlLinkClicked(const QUrl))); - libraryWidget->registerView("CRATEHOME", edit); + + m_idBrowse[paneId] = pContainer->addWidget(pEdit); + + QWidget* pTable = LibraryFeature::createPaneWidget(pKeyboard, paneId); + m_idTable[paneId] = pContainer->addWidget(pTable); + + return pContainer; } TreeItemModel* CrateFeature::getChildModel() { @@ -194,29 +211,59 @@ TreeItemModel* CrateFeature::getChildModel() { } void CrateFeature::activate() { - emit(switchToView("CRATEHOME")); - emit(restoreSearch(QString())); //disable search on crate home + auto it = m_panes.find(m_featureFocus); + auto itId = m_idBrowse.find(m_featureFocus); + if (it == m_panes.end() || it->isNull() || itId == m_idBrowse.end()) { + return; + } + + (*it)->setCurrentIndex(*itId); + + m_pLibrary->switchToFeature(this); + m_pLibrary->showBreadCrumb(m_childModel.getItem(QModelIndex())); + m_pLibrary->restoreSearch(QString()); //disable search on crate home + m_featureFocus = -1; emit(enableCoverArtDisplay(true)); } void CrateFeature::activateChild(const QModelIndex& index) { - if (!index.isValid()) + if (!index.isValid()) { return; + } int crateId = crateIdFromIndex(index); if (crateId == -1) { return; } - m_crateTableModel.setTableModel(crateId); - emit(showTrackModel(&m_crateTableModel)); + + m_pCrateTableModel = getTableModel(m_focusedPane); + auto it = m_panes.find(m_focusedPane); + auto itId = m_idTable.find(m_focusedPane); + if (it == m_panes.end() || it->isNull() || itId == m_idTable.end()) { + return; + } + + (*it)->setCurrentIndex(*itId); + m_pCrateTableModel->setTableModel(crateId); + + // Set the feature Focus for a moment to allow the LibraryFeature class + // to find the focused WTrackTable + m_featureFocus = m_focusedPane; + showTrackModel(m_pCrateTableModel); + m_featureFocus = -1; + + m_pLibrary->restoreSearch(""); + m_pLibrary->showBreadCrumb(static_cast(index.internalPointer())); emit(enableCoverArtDisplay(true)); } void CrateFeature::activateCrate(int crateId) { //qDebug() << "CrateFeature::activateCrate()" << crateId; + m_pCrateTableModel = getTableModel(m_focusedPane); + QModelIndex index = indexFromCrateId(crateId); if (crateId != -1 && index.isValid()) { - m_crateTableModel.setTableModel(crateId); - emit(showTrackModel(&m_crateTableModel)); + m_pCrateTableModel->setTableModel(crateId); + showTrackModel(m_pCrateTableModel); emit(enableCoverArtDisplay(true)); // Update selection emit(featureSelect(this, m_lastRightClickedIndex)); @@ -227,7 +274,7 @@ void CrateFeature::activateCrate(int crateId) { void CrateFeature::onRightClick(const QPoint& globalPos) { m_lastRightClickedIndex = QModelIndex(); - QMenu menu(NULL); + QMenu menu(nullptr); menu.addAction(m_pCreateCrateAction); menu.addSeparator(); menu.addAction(m_pCreateImportPlaylistAction); @@ -238,9 +285,6 @@ void CrateFeature::onRightClickChild(const QPoint& globalPos, QModelIndex index) //Save the model index so we can get it in the action slots... m_lastRightClickedIndex = index; int crateId = crateIdFromIndex(index); - if (crateId == -1) { - return; - } bool locked = m_crateDao.isCrateLocked(crateId); @@ -610,7 +654,7 @@ void CrateFeature::slotImportPlaylistFile(const QString &playlist_file) { //qDebug() << "Size of Imported Playlist: " << entries.size(); //Iterate over the List that holds URLs of playlist entires - m_crateTableModel.addTracks(QModelIndex(), entries); + m_pCrateTableModel->addTracks(QModelIndex(), entries); //delete the parser object delete playlist_parser; @@ -658,7 +702,7 @@ void CrateFeature::slotCreateImportCrate() { lastCrateId = m_crateDao.createCrate(name); if (lastCrateId != -1) { - m_crateTableModel.setTableModel(lastCrateId); + m_pCrateTableModel->setTableModel(lastCrateId); } else { QMessageBox::warning(NULL, @@ -684,7 +728,7 @@ void CrateFeature::slotAnalyzeCrate() { } void CrateFeature::slotExportPlaylist() { - int crateId = m_crateTableModel.getCrate(); + int crateId = m_pCrateTableModel->getCrate(); QString crateName = m_crateDao.crateName(crateId); qDebug() << "Export crate" << crateId << crateName; @@ -722,7 +766,7 @@ void CrateFeature::slotExportPlaylist() { // Create a new table model since the main one might have an active search. QScopedPointer pCrateTableModel( new CrateTableModel(this, m_pTrackCollection)); - pCrateTableModel->setTableModel(m_crateTableModel.getCrate()); + pCrateTableModel->setTableModel(m_pCrateTableModel->getCrate()); pCrateTableModel->select(); if (file_location.endsWith(".csv", Qt::CaseInsensitive)) { @@ -734,8 +778,8 @@ void CrateFeature::slotExportPlaylist() { QList playlist_items; int rows = pCrateTableModel->rowCount(); for (int i = 0; i < rows; ++i) { - QModelIndex index = m_crateTableModel.index(i, 0); - playlist_items << m_crateTableModel.getTrackLocation(index); + QModelIndex index = m_pCrateTableModel->index(i, 0); + playlist_items << m_pCrateTableModel->getTrackLocation(index); } if (file_location.endsWith(".pls", Qt::CaseInsensitive)) { @@ -759,14 +803,14 @@ void CrateFeature::slotExportTrackFiles() { // Create a new table model since the main one might have an active search. QScopedPointer pCrateTableModel( new CrateTableModel(this, m_pTrackCollection)); - pCrateTableModel->setTableModel(m_crateTableModel.getCrate()); + pCrateTableModel->setTableModel(m_pCrateTableModel->getCrate()); pCrateTableModel->select(); int rows = pCrateTableModel->rowCount(); QList trackpointers; for (int i = 0; i < rows; ++i) { - QModelIndex index = m_crateTableModel.index(i, 0); - trackpointers.push_back(m_crateTableModel.getTrack(index)); + QModelIndex index = m_pCrateTableModel->index(i, 0); + trackpointers.push_back(m_pCrateTableModel->getTrack(index)); } TrackExportWizard track_export(nullptr, m_pConfig, trackpointers); @@ -863,3 +907,12 @@ QModelIndex CrateFeature::indexFromCrateId(int crateId) { } return QModelIndex(); } + +QPointer CrateFeature::getTableModel(int paneId) { + auto it = m_crateTableModel.find(paneId); + if (it == m_crateTableModel.end() || it->isNull()) { + it = m_crateTableModel.insert(paneId, + new CrateTableModel(this, m_pTrackCollection)); + } + return *it; +} diff --git a/src/library/cratefeature.h b/src/library/cratefeature.h index 495b74ad7c33..fd5870f3ddc1 100644 --- a/src/library/cratefeature.h +++ b/src/library/cratefeature.h @@ -24,21 +24,24 @@ class TrackCollection; class CrateFeature : public LibraryFeature { Q_OBJECT public: - CrateFeature(Library* pLibrary, - TrackCollection* pTrackCollection, - UserSettingsPointer pConfig); + CrateFeature(UserSettingsPointer pConfig, + Library* pLibrary, + QObject* parent, + TrackCollection* pTrackCollection); virtual ~CrateFeature(); QVariant title(); QIcon getIcon(); + + void onSearch(QString&) {} + bool dragMoveAccept(QUrl url); bool dropAcceptChild(const QModelIndex& index, QList urls, QObject* pSource); bool dragMoveAcceptChild(const QModelIndex& index, QUrl url); - void bindWidget(WLibrary* libraryWidget, - KeyboardEventFilter* keyboard); - + QWidget* createPaneWidget(KeyboardEventFilter* pKeyboard, int paneId) override; + TreeItemModel* getChildModel(); signals: @@ -83,6 +86,8 @@ class CrateFeature : public LibraryFeature { // Get the QModelIndex of a crate based on its id. Returns QModelIndex() // on failure. QModelIndex indexFromCrateId(int crateId); + + QPointer getTableModel(int paneId); TrackCollection* m_pTrackCollection; CrateDAO& m_crateDao; @@ -98,11 +103,15 @@ class CrateFeature : public LibraryFeature { QAction *m_pExportTrackFilesAction; QAction *m_pAnalyzeCrateAction; QList > m_crateList; - CrateTableModel m_crateTableModel; + QHash > m_crateTableModel; + CrateTableModel* m_pCrateTableModel; QModelIndex m_lastRightClickedIndex; TreeItemModel m_childModel; TrackPointer m_pSelectedTrack; QSet m_cratesSelectedTrackIsIn; + QHash > m_panes; + QHash m_idBrowse; + QHash m_idTable; }; #endif /* CRATEFEATURE_H */ diff --git a/src/library/dlganalysis.cpp b/src/library/dlganalysis.cpp index 6d8ec9f38012..e552827aaad0 100644 --- a/src/library/dlganalysis.cpp +++ b/src/library/dlganalysis.cpp @@ -1,66 +1,32 @@ #include -#include "widget/wwidget.h" -#include "widget/wskincolor.h" -#include "widget/wanalysislibrarytableview.h" -#include "library/trackcollection.h" +#include "library/analysisfeature.h" #include "library/dlganalysis.h" +#include "library/trackcollection.h" #include "util/assert.h" +#include "widget/wanalysislibrarytableview.h" +#include "widget/wskincolor.h" +#include "widget/wwidget.h" -DlgAnalysis::DlgAnalysis(QWidget* parent, - UserSettingsPointer pConfig, - TrackCollection* pTrackCollection) - : QWidget(parent), - m_pConfig(pConfig), +DlgAnalysis::DlgAnalysis(QWidget* parent, AnalysisFeature *pAnalysis, + TrackCollection* pTrackCollection) + : QFrame(parent), m_pTrackCollection(pTrackCollection), m_bAnalysisActive(false), + m_pAnalysis(pAnalysis), m_tracksInQueue(0), m_currentTrack(0) { setupUi(this); m_songsButtonGroup.addButton(radioButtonRecentlyAdded); m_songsButtonGroup.addButton(radioButtonAllSongs); - m_pAnalysisLibraryTableView = new WAnalysisLibraryTableView(this, pConfig, pTrackCollection); - connect(m_pAnalysisLibraryTableView, SIGNAL(loadTrack(TrackPointer)), - this, SIGNAL(loadTrack(TrackPointer))); - connect(m_pAnalysisLibraryTableView, SIGNAL(loadTrackToPlayer(TrackPointer, QString)), - this, SIGNAL(loadTrackToPlayer(TrackPointer, QString))); - - connect(m_pAnalysisLibraryTableView, SIGNAL(trackSelected(TrackPointer)), - this, SIGNAL(trackSelected(TrackPointer))); - - QBoxLayout* box = dynamic_cast(layout()); - DEBUG_ASSERT_AND_HANDLE(box) { // Assumes the form layout is a QVBox/QHBoxLayout! - } else { - box->removeWidget(m_pTrackTablePlaceholder); - m_pTrackTablePlaceholder->hide(); - box->insertWidget(1, m_pAnalysisLibraryTableView); - } - - m_pAnalysisLibraryTableModel = new AnalysisLibraryTableModel(this, pTrackCollection); - m_pAnalysisLibraryTableView->loadTrackModel(m_pAnalysisLibraryTableModel); - - connect(radioButtonRecentlyAdded, SIGNAL(clicked()), - this, SLOT(showRecentSongs())); - connect(radioButtonAllSongs, SIGNAL(clicked()), - this, SLOT(showAllSongs())); - - // TODO(rryan): This triggers a library search before the UI has even - // started up. Accounts for 0.2% of skin creation time. Get rid of this! - radioButtonRecentlyAdded->click(); - labelProgress->setText(""); pushButtonAnalyze->setEnabled(false); connect(pushButtonAnalyze, SIGNAL(clicked()), this, SLOT(analyze())); connect(pushButtonSelectAll, SIGNAL(clicked()), - this, SLOT(selectAll())); - - connect(m_pAnalysisLibraryTableView->selectionModel(), - SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection&)), - this, - SLOT(tableSelectionChanged(const QItemSelection &, const QItemSelection&))); + m_pAnalysis, SLOT(selectAll())); } DlgAnalysis::~DlgAnalysis() { @@ -70,54 +36,19 @@ void DlgAnalysis::onShow() { // Refresh table // There might be new tracks dropped to other views m_pAnalysisLibraryTableModel->select(); -} - -void DlgAnalysis::onSearch(const QString& text) { - m_pAnalysisLibraryTableModel->search(text); -} - -void DlgAnalysis::loadSelectedTrack() { - m_pAnalysisLibraryTableView->loadSelectedTrack(); -} - -void DlgAnalysis::loadSelectedTrackToGroup(QString group, bool play) { - m_pAnalysisLibraryTableView->loadSelectedTrackToGroup(group, play); -} - -void DlgAnalysis::slotSendToAutoDJ() { - // append to auto DJ - m_pAnalysisLibraryTableView->slotSendToAutoDJ(); -} - -void DlgAnalysis::slotSendToAutoDJTop() { - m_pAnalysisLibraryTableView->slotSendToAutoDJTop(); -} - -void DlgAnalysis::moveSelection(int delta) { - m_pAnalysisLibraryTableView->moveSelection(delta); -} - -void DlgAnalysis::tableSelectionChanged(const QItemSelection& selected, - const QItemSelection& deselected) { - Q_UNUSED(selected); - Q_UNUSED(deselected); - bool tracksSelected = m_pAnalysisLibraryTableView->selectionModel()->hasSelection(); - pushButtonAnalyze->setEnabled(tracksSelected || m_bAnalysisActive); -} - -void DlgAnalysis::selectAll() { - m_pAnalysisLibraryTableView->selectAll(); + + // TODO(rryan): This triggers a library search before the UI has even + // started up. Accounts for 0.2% of skin creation time. Get rid of this! + radioButtonRecentlyAdded->click(); } void DlgAnalysis::analyze() { //qDebug() << this << "analyze()"; if (m_bAnalysisActive) { - emit(stopAnalysis()); + m_pAnalysis->stopAnalysis(); } else { QList trackIds; - - QModelIndexList selectedIndexes = m_pAnalysisLibraryTableView->selectionModel()->selectedRows(); - foreach(QModelIndex selectedIndex, selectedIndexes) { + for (QModelIndex selectedIndex : m_selectedIndexes) { TrackId trackId(selectedIndex.sibling( selectedIndex.row(), m_pAnalysisLibraryTableModel->fieldIndex(LIBRARYTABLE_ID)).data()); @@ -126,12 +57,12 @@ void DlgAnalysis::analyze() { } } m_currentTrack = 1; - emit(analyzeTracks(trackIds)); + m_pAnalysis->analyzeTracks(trackIds); } } void DlgAnalysis::analysisActive(bool bActive) { - qDebug() << this << "analysisActive" << bActive; + //qDebug() << this << "analysisActive" << bActive; m_bAnalysisActive = bActive; if (bActive) { pushButtonAnalyze->setEnabled(true); @@ -146,7 +77,7 @@ void DlgAnalysis::analysisActive(bool bActive) { // slot void DlgAnalysis::trackAnalysisFinished(int size) { - qDebug() << "Analysis finished" << size << "tracks left"; + //qDebug() << "Analysis finished" << size << "tracks left"; if (size > 0) { m_currentTrack = m_tracksInQueue - size + 1; } @@ -167,19 +98,22 @@ int DlgAnalysis::getNumTracks() { return m_tracksInQueue; } -void DlgAnalysis::trackAnalysisStarted(int size) { - m_tracksInQueue = size; -} - -void DlgAnalysis::showRecentSongs() { - m_pAnalysisLibraryTableModel->showRecentSongs(); +void DlgAnalysis::setSelectedIndexes(const QModelIndexList& selectedIndexes) { + //qDebug() << "DlgAnalysis::setSelectedIndexes" << selectedIndexes; + m_selectedIndexes = selectedIndexes; + pushButtonAnalyze->setEnabled(m_selectedIndexes.size() > 0 || + m_bAnalysisActive); } -void DlgAnalysis::showAllSongs() { - m_pAnalysisLibraryTableModel->showAllSongs(); +void DlgAnalysis::setTableModel(AnalysisLibraryTableModel *pTableModel) { + m_pAnalysisLibraryTableModel = pTableModel; + + connect(radioButtonRecentlyAdded, SIGNAL(clicked()), + m_pAnalysisLibraryTableModel, SLOT(showRecentSongs())); + connect(radioButtonAllSongs, SIGNAL(clicked()), + m_pAnalysisLibraryTableModel, SLOT(showAllSongs())); } -void DlgAnalysis::installEventFilter(QObject* pFilter) { - QWidget::installEventFilter(pFilter); - m_pAnalysisLibraryTableView->installEventFilter(pFilter); +void DlgAnalysis::trackAnalysisStarted(int size) { + m_tracksInQueue = size; } diff --git a/src/library/dlganalysis.h b/src/library/dlganalysis.h index 84520b38cf66..9a83b0491ef5 100644 --- a/src/library/dlganalysis.h +++ b/src/library/dlganalysis.h @@ -2,6 +2,7 @@ #define DLGANALYSIS_H #include +#include #include "preferences/usersettings.h" #include "library/analysislibrarytablemodel.h" @@ -11,57 +12,48 @@ class AnalysisLibraryTableModel; class WAnalysisLibraryTableView; +class AnalysisFeature; -class DlgAnalysis : public QWidget, public Ui::DlgAnalysis, public virtual LibraryView { +class DlgAnalysis : public QFrame, public Ui::DlgAnalysis { + Q_OBJECT + public: + DlgAnalysis(QWidget *parent, - UserSettingsPointer pConfig, - TrackCollection* pTrackCollection); + AnalysisFeature* pAnalysis, + TrackCollection* pTrackCollection); virtual ~DlgAnalysis(); - virtual void onSearch(const QString& text); virtual void onShow(); - virtual void loadSelectedTrack(); - virtual void loadSelectedTrackToGroup(QString group, bool play); - virtual void slotSendToAutoDJ(); - virtual void slotSendToAutoDJTop(); - virtual void moveSelection(int delta); inline const QString currentSearch() { return m_pAnalysisLibraryTableModel->currentSearch(); } int getNumTracks(); + + // The selected indexes are always from the focused pane + void setSelectedIndexes(const QModelIndexList& selectedIndexes); + void setTableModel(AnalysisLibraryTableModel* pTableModel); public slots: - void tableSelectionChanged(const QItemSelection& selected, - const QItemSelection& deselected); - void selectAll(); + void analyze(); void trackAnalysisFinished(int size); void trackAnalysisProgress(int progress); void trackAnalysisStarted(int size); - void showRecentSongs(); - void showAllSongs(); - void installEventFilter(QObject* pFilter); void analysisActive(bool bActive); - - signals: - void loadTrack(TrackPointer pTrack); - void loadTrackToPlayer(TrackPointer pTrack, QString player); - void analyzeTracks(QList trackIds); - void stopAnalysis(); - void trackSelected(TrackPointer pTrack); - + private: //Note m_pTrackTablePlaceholder is defined in the .ui file - UserSettingsPointer m_pConfig; TrackCollection* m_pTrackCollection; bool m_bAnalysisActive; QButtonGroup m_songsButtonGroup; - WAnalysisLibraryTableView* m_pAnalysisLibraryTableView; AnalysisLibraryTableModel* m_pAnalysisLibraryTableModel; + AnalysisFeature* m_pAnalysis; int m_tracksInQueue; int m_currentTrack; + + QModelIndexList m_selectedIndexes; }; #endif //DLGTRIAGE_H diff --git a/src/library/dlganalysis.ui b/src/library/dlganalysis.ui index de46af0a67d5..48e7249aba27 100644 --- a/src/library/dlganalysis.ui +++ b/src/library/dlganalysis.ui @@ -14,107 +14,66 @@ Analyze - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - 0 + + + Shows tracks added to the library within the last 7 days. - - 0 + + New - - 0 + + + + + + Shows all tracks in the library. - - 0 + + All - - 0 + + + + + + Progress - - - - Shows tracks added to the library within the last 7 days. - - - New - - - - - - - Shows all tracks in the library. - - - All - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Progress - - - - - - - Selects all tracks in the table below. - - - Select All - - - - - - - Runs beatgrid, key, and ReplayGain detection on the selected tracks. Does not generate waveforms for the selected tracks to save disk space. - - - Analyze - - - - + - - - true + + + Selects all tracks in the table below. + + + Select All + + + + Runs beatgrid, key, and ReplayGain detection on the selected tracks. Does not generate waveforms for the selected tracks to save disk space. + + + Analyze + + + + + + + Qt::Vertical + + + + 20 + 235 + + + + diff --git a/src/library/dlghidden.cpp b/src/library/dlghidden.cpp index 9cddafb9912d..7adcc47323d0 100644 --- a/src/library/dlghidden.cpp +++ b/src/library/dlghidden.cpp @@ -5,55 +5,20 @@ #include "widget/wtracktableview.h" #include "util/assert.h" -DlgHidden::DlgHidden(QWidget* parent, UserSettingsPointer pConfig, - Library* pLibrary, TrackCollection* pTrackCollection, - KeyboardEventFilter* pKeyboard) - : QWidget(parent), - Ui::DlgHidden(), - m_pTrackTableView( - new WTrackTableView(this, pConfig, pTrackCollection, false)) { +DlgHidden::DlgHidden(QWidget* parent) + : QFrame(parent), + Ui::DlgHidden() { setupUi(this); - m_pTrackTableView->installEventFilter(pKeyboard); - - // Install our own trackTable - QBoxLayout* box = dynamic_cast(layout()); - DEBUG_ASSERT_AND_HANDLE(box) { //Assumes the form layout is a QVBox/QHBoxLayout! - } else { - box->removeWidget(m_pTrackTablePlaceholder); - m_pTrackTablePlaceholder->hide(); - box->insertWidget(1, m_pTrackTableView); - } - - m_pHiddenTableModel = new HiddenTableModel(this, pTrackCollection); - m_pTrackTableView->loadTrackModel(m_pHiddenTableModel); - - connect(btnUnhide, SIGNAL(clicked()), - m_pTrackTableView, SLOT(slotUnhide())); - connect(btnUnhide, SIGNAL(clicked()), - this, SLOT(clicked())); - connect(btnPurge, SIGNAL(clicked()), - m_pTrackTableView, SLOT(slotPurge())); - connect(btnPurge, SIGNAL(clicked()), - this, SLOT(clicked())); - connect(btnSelect, SIGNAL(clicked()), - this, SLOT(selectAll())); - connect(m_pTrackTableView->selectionModel(), - SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), - this, - SLOT(selectionChanged(const QItemSelection&, const QItemSelection&))); - - connect(m_pTrackTableView, SIGNAL(trackSelected(TrackPointer)), - this, SIGNAL(trackSelected(TrackPointer))); - connect(pLibrary, SIGNAL(setTrackTableFont(QFont)), - m_pTrackTableView, SLOT(setTrackTableFont(QFont))); - connect(pLibrary, SIGNAL(setTrackTableRowHeight(int)), - m_pTrackTableView, SLOT(setTrackTableRowHeight(int))); + + connect(btnPurge, SIGNAL(clicked()), this, SLOT(onShow())); + connect(btnSelect, SIGNAL(clicked()), this, SIGNAL(selectAll())); + connect(btnPurge, SIGNAL(clicked()), this, SIGNAL(purge())); + connect(btnUnhide, SIGNAL(clicked()), this, SIGNAL(unhide())); } DlgHidden::~DlgHidden() { // Delete m_pTrackTableView before the table model. This is because the // table view saves the header state using the model. - delete m_pTrackTableView; delete m_pHiddenTableModel; } @@ -63,34 +28,15 @@ void DlgHidden::onShow() { activateButtons(false); } -void DlgHidden::onSearch(const QString& text) { - m_pHiddenTableModel->search(text); -} - -void DlgHidden::clicked() { - // all marked tracks are gone now anyway - onShow(); +void DlgHidden::setSelectedIndexes(const QModelIndexList& selectedIndexes) { + activateButtons(!selectedIndexes.empty()); } -void DlgHidden::selectAll() { - m_pTrackTableView->selectAll(); +void DlgHidden::setTableModel(HiddenTableModel* pTableModel) { + m_pHiddenTableModel = pTableModel; } void DlgHidden::activateButtons(bool enable) { btnPurge->setEnabled(enable); btnUnhide->setEnabled(enable); } - -void DlgHidden::selectionChanged(const QItemSelection &selected, - const QItemSelection &deselected) { - Q_UNUSED(deselected); - activateButtons(!selected.indexes().isEmpty()); -} - -void DlgHidden::setTrackTableFont(const QFont& font) { - m_pTrackTableView->setTrackTableFont(font); -} - -void DlgHidden::setTrackTableRowHeight(int rowHeight) { - m_pTrackTableView->setTrackTableRowHeight(rowHeight); -} diff --git a/src/library/dlghidden.h b/src/library/dlghidden.h index 6b78d85f1fca..a084ea450e37 100644 --- a/src/library/dlghidden.h +++ b/src/library/dlghidden.h @@ -12,30 +12,27 @@ class WTrackTableView; class HiddenTableModel; class QItemSelection; -class DlgHidden : public QWidget, public Ui::DlgHidden, public LibraryView { +class DlgHidden : public QFrame, public Ui::DlgHidden { Q_OBJECT public: - DlgHidden(QWidget* parent, UserSettingsPointer pConfig, - Library* pLibrary, TrackCollection* pTrackCollection, - KeyboardEventFilter* pKeyboard); + DlgHidden(QWidget* parent); virtual ~DlgHidden(); - void onShow(); - void onSearch(const QString& text); + // The indexes are always from the focused pane + void setSelectedIndexes(const QModelIndexList& selectedIndexes); + void setTableModel(HiddenTableModel* pTableModel); public slots: - void clicked(); - void selectAll(); - void selectionChanged(const QItemSelection&, const QItemSelection&); - void setTrackTableFont(const QFont& font); - void setTrackTableRowHeight(int rowHeight); + void onShow(); signals: + void selectAll(); + void unhide(); + void purge(); void trackSelected(TrackPointer pTrack); private: void activateButtons(bool enable); - WTrackTableView* m_pTrackTableView; HiddenTableModel* m_pHiddenTableModel; }; diff --git a/src/library/dlghidden.ui b/src/library/dlghidden.ui index d80de64e4210..2b96be8becce 100644 --- a/src/library/dlghidden.ui +++ b/src/library/dlghidden.ui @@ -14,103 +14,60 @@ Hidden Tracks - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - 0 + + + Selects all tracks in the table below. - - 0 + + Select All - - 0 + + + + + + Purge selected tracks from the library. - - 0 + + Purge - - 0 + + false - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Selects all tracks in the table below. - - - Select All - - - - - - - Purge selected tracks from the library. - - - Purge - - - false - - - - - - - Unhide selected tracks from the library. - - - Unhide - - - Ctrl+S - - - false - - - - + - - - true + + + Unhide selected tracks from the library. + + + Unhide + + + Ctrl+S + + + false + + + + Qt::Vertical + + + + 20 + 285 + + + + - - - + diff --git a/src/library/dlgmissing.cpp b/src/library/dlgmissing.cpp index 3f5918bfb3ed..4eb22b2bcacb 100644 --- a/src/library/dlgmissing.cpp +++ b/src/library/dlgmissing.cpp @@ -4,51 +4,19 @@ #include "widget/wtracktableview.h" #include "util/assert.h" -DlgMissing::DlgMissing(QWidget* parent, UserSettingsPointer pConfig, - Library* pLibrary, - TrackCollection* pTrackCollection, KeyboardEventFilter* pKeyboard) - : QWidget(parent), - Ui::DlgMissing(), - m_pTrackTableView( - new WTrackTableView(this, pConfig, pTrackCollection, false)) { - setupUi(this); - m_pTrackTableView->installEventFilter(pKeyboard); - - // Install our own trackTable - QBoxLayout* box = dynamic_cast(layout()); - DEBUG_ASSERT_AND_HANDLE(box) { //Assumes the form layout is a QVBox/QHBoxLayout! - } else { - box->removeWidget(m_pTrackTablePlaceholder); - m_pTrackTablePlaceholder->hide(); - box->insertWidget(1, m_pTrackTableView); - } - - m_pMissingTableModel = new MissingTableModel(this, pTrackCollection); - m_pTrackTableView->loadTrackModel(m_pMissingTableModel); - - connect(btnPurge, SIGNAL(clicked()), - m_pTrackTableView, SLOT(slotPurge())); - connect(btnPurge, SIGNAL(clicked()), - this, SLOT(clicked())); - connect(btnSelect, SIGNAL(clicked()), - this, SLOT(selectAll())); - connect(m_pTrackTableView->selectionModel(), - SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), - this, - SLOT(selectionChanged(const QItemSelection&, const QItemSelection&))); - connect(pLibrary, SIGNAL(setTrackTableFont(QFont)), - m_pTrackTableView, SLOT(setTrackTableFont(QFont))); - connect(pLibrary, SIGNAL(setTrackTableRowHeight(int)), - m_pTrackTableView, SLOT(setTrackTableRowHeight(int))); - - connect(m_pTrackTableView, SIGNAL(trackSelected(TrackPointer)), - this, SIGNAL(trackSelected(TrackPointer))); +DlgMissing::DlgMissing(QWidget* parent) + : QFrame(parent), + Ui::DlgMissing() { + setupUi(this); + + connect(btnPurge, SIGNAL(clicked()), this, SLOT(onShow())); + connect(btnPurge, SIGNAL(clicked()), this, SIGNAL(purge())); + connect(btnSelect, SIGNAL(clicked()), this, SIGNAL(selectAll())); } DlgMissing::~DlgMissing() { // Delete m_pTrackTableView before the table model. This is because the // table view saves the header state using the model. - delete m_pTrackTableView; delete m_pMissingTableModel; } @@ -57,33 +25,14 @@ void DlgMissing::onShow() { activateButtons(false); } -void DlgMissing::clicked() { - // all marked tracks are gone now anyway - onShow(); -} - -void DlgMissing::onSearch(const QString& text) { - m_pMissingTableModel->search(text); +void DlgMissing::setSelectedIndexes(const QModelIndexList& selectedIndexes) { + activateButtons(!selectedIndexes.isEmpty()); } -void DlgMissing::selectAll() { - m_pTrackTableView->selectAll(); +void DlgMissing::setTableModel(MissingTableModel* pTableModel) { + m_pMissingTableModel = pTableModel; } void DlgMissing::activateButtons(bool enable) { btnPurge->setEnabled(enable); } - -void DlgMissing::selectionChanged(const QItemSelection &selected, - const QItemSelection &deselected) { - Q_UNUSED(deselected); - activateButtons(!selected.indexes().isEmpty()); -} - -void DlgMissing::setTrackTableFont(const QFont& font) { - m_pTrackTableView->setTrackTableFont(font); -} - -void DlgMissing::setTrackTableRowHeight(int rowHeight) { - m_pTrackTableView->setTrackTableRowHeight(rowHeight); -} diff --git a/src/library/dlgmissing.h b/src/library/dlgmissing.h index fe287e8e8ef3..6438e035b36e 100644 --- a/src/library/dlgmissing.h +++ b/src/library/dlgmissing.h @@ -11,30 +11,27 @@ class WTrackTableView; class MissingTableModel; -class DlgMissing : public QWidget, public Ui::DlgMissing, public LibraryView { +class DlgMissing : public QFrame, public Ui::DlgMissing { Q_OBJECT public: - DlgMissing(QWidget* parent, UserSettingsPointer pConfig, - Library* pLibrary, TrackCollection* pTrackCollection, - KeyboardEventFilter* pKeyboard); + DlgMissing(QWidget* parent); virtual ~DlgMissing(); - void onShow(); - void onSearch(const QString& text); + // The indexes are always from the Focused pane + void setSelectedIndexes(const QModelIndexList& selectedIndexes); + void setTableModel(MissingTableModel* pTableModel); public slots: - void clicked(); - void selectAll(); - void selectionChanged(const QItemSelection&, const QItemSelection&); - void setTrackTableFont(const QFont& font); - void setTrackTableRowHeight(int rowHeight); + void onShow(); signals: + void purge(); + void selectAll(); void trackSelected(TrackPointer pTrack); private: void activateButtons(bool enable); - WTrackTableView* m_pTrackTableView; + MissingTableModel* m_pMissingTableModel; }; diff --git a/src/library/dlgmissing.ui b/src/library/dlgmissing.ui index 8db680a895ce..bc3a8805537c 100644 --- a/src/library/dlgmissing.ui +++ b/src/library/dlgmissing.ui @@ -14,87 +14,44 @@ Missing Tracks - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - 0 + + + Selects all tracks in the table below. - - 0 + + Select All - - 0 + + + + + + Purge selected tracks from the library. - - 0 + + Purge - - 0 + + false - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Selects all tracks in the table below. - - - Select All - - - - - - - Purge selected tracks from the library. - - - Purge - - - false - - - - + - - - true + + + Qt::Vertical - + + + 20 + 316 + + + - - - + diff --git a/src/library/setlogfeature.cpp b/src/library/historyfeature.cpp similarity index 88% rename from src/library/setlogfeature.cpp rename to src/library/historyfeature.cpp index 01e2ba7919d0..14e8c222b992 100644 --- a/src/library/setlogfeature.cpp +++ b/src/library/historyfeature.cpp @@ -2,7 +2,7 @@ #include #include -#include "library/setlogfeature.h" +#include "library/historyfeature.h" #include "control/controlobject.h" #include "library/playlisttablemodel.h" @@ -11,14 +11,12 @@ #include "mixer/playerinfo.h" #include "mixer/playermanager.h" -SetlogFeature::SetlogFeature(QObject* parent, - UserSettingsPointer pConfig, +HistoryFeature::HistoryFeature(UserSettingsPointer pConfig, + Library* pLibrary, + QObject* parent, TrackCollection* pTrackCollection) - : BasePlaylistFeature(parent, pConfig, pTrackCollection, "SETLOGHOME"), + : BasePlaylistFeature(pConfig, pLibrary, parent, pTrackCollection), m_playlistId(-1) { - m_pPlaylistTableModel = new PlaylistTableModel(this, pTrackCollection, - "mixxx.db.model.setlog", - true); //show all tracks m_pJoinWithPreviousAction = new QAction(tr("Join with previous"), this); connect(m_pJoinWithPreviousAction, SIGNAL(triggered()), this, SLOT(slotJoinWithPrevious())); @@ -33,9 +31,12 @@ SetlogFeature::SetlogFeature(QObject* parent, TreeItem *rootItem = new TreeItem(); m_childModel.setRootItem(rootItem); constructChildModel(-1); + + connect(&PlayerInfo::instance(), SIGNAL(currentPlayingTrackChanged(TrackPointer)), + this, SLOT(slotPlayingTrackChanged(TrackPointer))); } -SetlogFeature::~SetlogFeature() { +HistoryFeature::~HistoryFeature() { // If the history playlist we created doesn't have any tracks in it then // delete it so we don't end up with tons of empty playlists. This is mostly // for developers since they regularly open Mixxx without loading a track. @@ -45,24 +46,15 @@ SetlogFeature::~SetlogFeature() { } } -QVariant SetlogFeature::title() { +QVariant HistoryFeature::title() { return tr("History"); } -QIcon SetlogFeature::getIcon() { +QIcon HistoryFeature::getIcon() { return QIcon(":/images/library/ic_library_history.png"); } -void SetlogFeature::bindWidget(WLibrary* libraryWidget, - KeyboardEventFilter* keyboard) { - BasePlaylistFeature::bindWidget(libraryWidget, - keyboard); - connect(&PlayerInfo::instance(), SIGNAL(currentPlayingTrackChanged(TrackPointer)), - this, SLOT(slotPlayingTrackChanged(TrackPointer))); -} - -void SetlogFeature::onRightClick(const QPoint& globalPos) { - Q_UNUSED(globalPos); +void HistoryFeature::onRightClick(const QPoint&) { m_lastRightClickedIndex = QModelIndex(); // Create the right-click menu @@ -72,7 +64,7 @@ void SetlogFeature::onRightClick(const QPoint& globalPos) { // menu.exec(globalPos); } -void SetlogFeature::onRightClickChild(const QPoint& globalPos, QModelIndex index) { +void HistoryFeature::onRightClickChild(const QPoint& globalPos, QModelIndex index) { //Save the model index so we can get it in the action slots... m_lastRightClickedIndex = index; QString playlistName = index.data().toString(); @@ -113,7 +105,7 @@ void SetlogFeature::onRightClickChild(const QPoint& globalPos, QModelIndex index } -void SetlogFeature::buildPlaylistList() { +void HistoryFeature::buildPlaylistList() { m_playlistList.clear(); // Setup the sidebar playlist model QSqlTableModel playlistTableModel(this, m_pTrackCollection->getDatabase()); @@ -138,7 +130,7 @@ void SetlogFeature::buildPlaylistList() { } } -void SetlogFeature::decorateChild(TreeItem* item, int playlist_id) { +void HistoryFeature::decorateChild(TreeItem* item, int playlist_id) { if (playlist_id == m_playlistId) { item->setIcon(QIcon(":/images/library/ic_library_history_current.png")); } else if (m_playlistDao.isPlaylistLocked(playlist_id)) { @@ -148,7 +140,12 @@ void SetlogFeature::decorateChild(TreeItem* item, int playlist_id) { } } -void SetlogFeature::slotGetNewPlaylist() { +PlaylistTableModel* HistoryFeature::constructTableModel() { + return new PlaylistTableModel(this, m_pTrackCollection, + "mixxx.db.model.setlog", true); +} + +void HistoryFeature::slotGetNewPlaylist() { //qDebug() << "slotGetNewPlaylist() succesfully triggered !"; // create a new playlist for today @@ -174,10 +171,10 @@ void SetlogFeature::slotGetNewPlaylist() { } slotPlaylistTableChanged(m_playlistId); // For moving selection - emit(showTrackModel(m_pPlaylistTableModel)); + showTrackModel(m_pPlaylistTableModel); } -void SetlogFeature::slotJoinWithPrevious() { +void HistoryFeature::slotJoinWithPrevious() { //qDebug() << "slotJoinWithPrevious() row:" << m_lastRightClickedIndex.data(); if (m_lastRightClickedIndex.isValid()) { @@ -222,14 +219,14 @@ void SetlogFeature::slotJoinWithPrevious() { if (m_playlistDao.copyPlaylistTracks(currentPlaylistId, previousPlaylistId)) { m_playlistDao.deletePlaylist(currentPlaylistId); slotPlaylistTableChanged(previousPlaylistId); // For moving selection - emit(showTrackModel(m_pPlaylistTableModel)); + showTrackModel(m_pPlaylistTableModel); } } } } } -void SetlogFeature::slotPlayingTrackChanged(TrackPointer currentPlayingTrack) { +void HistoryFeature::slotPlayingTrackChanged(TrackPointer currentPlayingTrack) { if (!currentPlayingTrack) { return; } @@ -264,6 +261,8 @@ void SetlogFeature::slotPlayingTrackChanged(TrackPointer currentPlayingTrack) { if (!currentPlayingTrackId.isValid()) { return; } + + m_pPlaylistTableModel = getPlaylistTableModel(-1); if (m_pPlaylistTableModel->getPlaylist() == m_playlistId) { // View needs a refresh @@ -275,7 +274,7 @@ void SetlogFeature::slotPlayingTrackChanged(TrackPointer currentPlayingTrack) { } } -void SetlogFeature::slotPlaylistTableChanged(int playlistId) { +void HistoryFeature::slotPlaylistTableChanged(int playlistId) { if (!m_pPlaylistTableModel) { return; } @@ -289,7 +288,7 @@ void SetlogFeature::slotPlaylistTableChanged(int playlistId) { } } -void SetlogFeature::slotPlaylistContentChanged(int playlistId) { +void HistoryFeature::slotPlaylistContentChanged(int playlistId) { if (!m_pPlaylistTableModel) { return; } @@ -302,7 +301,7 @@ void SetlogFeature::slotPlaylistContentChanged(int playlistId) { } } -void SetlogFeature::slotPlaylistTableRenamed(int playlistId, +void HistoryFeature::slotPlaylistTableRenamed(int playlistId, QString /* a_strName */) { if (!m_pPlaylistTableModel) { return; @@ -320,7 +319,7 @@ void SetlogFeature::slotPlaylistTableRenamed(int playlistId, } } -QString SetlogFeature::getRootViewHtml() const { +QString HistoryFeature::getRootViewHtml() const { QString playlistsTitle = tr("History"); QString playlistsSummary = tr("The history section automatically keeps a list of tracks you play in your DJ sets."); QString playlistsSummary2 = tr("This is handy for remembering what worked in your DJ sets, posting set-lists, or reporting your plays to licensing organizations."); diff --git a/src/library/setlogfeature.h b/src/library/historyfeature.h similarity index 78% rename from src/library/setlogfeature.h rename to src/library/historyfeature.h index d02b25e4e6ea..dffda06d2456 100644 --- a/src/library/setlogfeature.h +++ b/src/library/historyfeature.h @@ -13,21 +13,20 @@ class TrackCollection; class TreeItem; -class SetlogFeature : public BasePlaylistFeature { +class HistoryFeature : public BasePlaylistFeature { Q_OBJECT public: - SetlogFeature(QObject* parent, UserSettingsPointer pConfig, + HistoryFeature(UserSettingsPointer pConfig, + Library* pLibrary, + QObject* parent, TrackCollection* pTrackCollection); - virtual ~SetlogFeature(); + virtual ~HistoryFeature(); QVariant title(); QIcon getIcon(); - virtual void bindWidget(WLibrary* libraryWidget, - KeyboardEventFilter* keyboard); - public slots: - void onRightClick(const QPoint& globalPos); + void onRightClick(const QPoint&); void onRightClickChild(const QPoint& globalPos, QModelIndex index); void slotJoinWithPrevious(); void slotGetNewPlaylist(); @@ -35,6 +34,7 @@ class SetlogFeature : public BasePlaylistFeature { protected: void buildPlaylistList(); void decorateChild(TreeItem *pChild, int playlist_id); + PlaylistTableModel* constructTableModel(); private slots: void slotPlayingTrackChanged(TrackPointer currentPlayingTrack); diff --git a/src/library/itunes/itunesfeature.cpp b/src/library/itunes/itunesfeature.cpp index 24a4655a31a5..0b3dac8aeaf2 100644 --- a/src/library/itunes/itunesfeature.cpp +++ b/src/library/itunes/itunesfeature.cpp @@ -14,6 +14,7 @@ #include "library/dao/settingsdao.h" #include "library/baseexternaltrackmodel.h" #include "library/baseexternalplaylistmodel.h" +#include "library/library.h" #include "library/queryutil.h" #include "util/lcs.h" #include "util/sandbox.h" @@ -28,8 +29,11 @@ QString localhost_token() { #endif } -ITunesFeature::ITunesFeature(QObject* parent, TrackCollection* pTrackCollection) - : BaseExternalLibraryFeature(parent, pTrackCollection), +ITunesFeature::ITunesFeature(UserSettingsPointer pConfig, + Library* pLibrary, + QObject* parent, + TrackCollection* pTrackCollection) + : BaseExternalLibraryFeature(pConfig, pLibrary, parent, pTrackCollection), m_pTrackCollection(pTrackCollection), m_cancelImport(false) { QString tableName = "itunes_library"; @@ -143,7 +147,8 @@ void ITunesFeature::activate(bool forceReload) { NULL, tr("Select your iTunes library"), QDir::homePath(), "*.xml"); QFileInfo dbFile(m_dbfile); if (m_dbfile.isEmpty() || !dbFile.exists()) { - emit(showTrackModel(m_pITunesTrackModel)); + m_pLibrary->showBreadCrumb(m_childModel.getItem(QModelIndex())); + showTrackModel(m_pITunesTrackModel); return; } @@ -173,7 +178,8 @@ void ITunesFeature::activate(bool forceReload) { emit (featureIsLoading(this, true)); } - emit(showTrackModel(m_pITunesTrackModel)); + showTrackModel(m_pITunesTrackModel); + m_pLibrary->showBreadCrumb(m_childModel.getItem(QModelIndex())); emit(enableCoverArtDisplay(false)); } @@ -182,7 +188,9 @@ void ITunesFeature::activateChild(const QModelIndex& index) { QString playlist = index.data().toString(); qDebug() << "Activating " << playlist; m_pITunesPlaylistModel->setPlaylist(playlist); - emit(showTrackModel(m_pITunesPlaylistModel)); + + showTrackModel(m_pITunesPlaylistModel); + m_pLibrary->showBreadCrumb(static_cast(index.internalPointer())); emit(enableCoverArtDisplay(false)); } @@ -735,6 +743,7 @@ void ITunesFeature::clearTable(QString table_name) { void ITunesFeature::onTrackCollectionLoaded() { TreeItem* root = m_future.result(); + root->setLibraryFeature(this); if (root) { m_childModel.setRootItem(root); @@ -742,7 +751,7 @@ void ITunesFeature::onTrackCollectionLoaded() { m_trackSource->buildIndex(); //m_pITunesTrackModel->select(); - emit(showTrackModel(m_pITunesTrackModel)); + showTrackModel(m_pITunesTrackModel); qDebug() << "Itunes library loaded: success"; } else { QMessageBox::warning( diff --git a/src/library/itunes/itunesfeature.h b/src/library/itunes/itunesfeature.h index 3dcf3baa5ec6..10818eca0f45 100644 --- a/src/library/itunes/itunesfeature.h +++ b/src/library/itunes/itunesfeature.h @@ -21,7 +21,10 @@ class BaseExternalPlaylistModel; class ITunesFeature : public BaseExternalLibraryFeature { Q_OBJECT public: - ITunesFeature(QObject* parent, TrackCollection* pTrackCollection); + ITunesFeature(UserSettingsPointer pConfig, + Library* pLibrary, + QObject* parent, + TrackCollection* pTrackCollection); virtual ~ITunesFeature(); static bool isSupported(); diff --git a/src/library/library.cpp b/src/library/library.cpp index 99520e58e8c8..625102aa005e 100644 --- a/src/library/library.cpp +++ b/src/library/library.cpp @@ -5,12 +5,15 @@ #include #include #include +#include #include "mixer/playermanager.h" #include "library/library.h" #include "library/library_preferences.h" #include "library/libraryfeature.h" #include "library/librarytablemodel.h" +#include "library/librarypanemanager.h" +#include "library/librarysidebarexpandedmanager.h" #include "library/sidebarmodel.h" #include "library/trackcollection.h" #include "library/trackmodel.h" @@ -25,20 +28,15 @@ #include "library/playlistfeature.h" #include "library/traktor/traktorfeature.h" #include "library/librarycontrol.h" -#include "library/setlogfeature.h" +#include "library/historyfeature.h" #include "util/sandbox.h" #include "util/assert.h" -#include "widget/wtracktableview.h" -#include "widget/wlibrary.h" #include "widget/wlibrarysidebar.h" +#include "widget/wbuttonbar.h" #include "controllers/keyboard/keyboardeventfilter.h" -// This is is the name which we use to register the WTrackTableView with the -// WLibrary -const QString Library::m_sTrackViewName = QString("WTrackTableView"); - // The default row height of the library. const int Library::kDefaultRowHeightPx = 20; @@ -50,7 +48,8 @@ Library::Library(QObject* parent, UserSettingsPointer pConfig, m_pTrackCollection(new TrackCollection(pConfig)), m_pLibraryControl(new LibraryControl(this)), m_pRecordingManager(pRecordingManager), - m_scanner(m_pTrackCollection, pConfig) { + m_scanner(m_pTrackCollection, pConfig), + m_pSidebarExpanded(nullptr) { qRegisterMetaType("Library::RemovalType"); connect(&m_scanner, SIGNAL(scanStarted()), @@ -60,56 +59,8 @@ Library::Library(QObject* parent, UserSettingsPointer pConfig, // Refresh the library models when the library (re)scan is finished. connect(&m_scanner, SIGNAL(scanFinished()), this, SLOT(slotRefreshLibraryModels())); - - // TODO(rryan) -- turn this construction / adding of features into a static - // method or something -- CreateDefaultLibrary - m_pMixxxLibraryFeature = new MixxxLibraryFeature(this, m_pTrackCollection,m_pConfig); - addFeature(m_pMixxxLibraryFeature); - - addFeature(new AutoDJFeature(this, pConfig, pPlayerManager, m_pTrackCollection)); - m_pPlaylistFeature = new PlaylistFeature(this, m_pTrackCollection, m_pConfig); - addFeature(m_pPlaylistFeature); - m_pCrateFeature = new CrateFeature(this, m_pTrackCollection, m_pConfig); - addFeature(m_pCrateFeature); - BrowseFeature* browseFeature = new BrowseFeature( - this, pConfig, m_pTrackCollection, m_pRecordingManager); - connect(browseFeature, SIGNAL(scanLibrary()), - &m_scanner, SLOT(scan())); - connect(&m_scanner, SIGNAL(scanStarted()), - browseFeature, SLOT(slotLibraryScanStarted())); - connect(&m_scanner, SIGNAL(scanFinished()), - browseFeature, SLOT(slotLibraryScanFinished())); - - addFeature(browseFeature); - addFeature(new RecordingFeature(this, pConfig, m_pTrackCollection, m_pRecordingManager)); - addFeature(new SetlogFeature(this, pConfig, m_pTrackCollection)); - m_pAnalysisFeature = new AnalysisFeature(this, pConfig, m_pTrackCollection); - connect(m_pPlaylistFeature, SIGNAL(analyzeTracks(QList)), - m_pAnalysisFeature, SLOT(analyzeTracks(QList))); - connect(m_pCrateFeature, SIGNAL(analyzeTracks(QList)), - m_pAnalysisFeature, SLOT(analyzeTracks(QList))); - addFeature(m_pAnalysisFeature); - //iTunes and Rhythmbox should be last until we no longer have an obnoxious - //messagebox popup when you select them. (This forces you to reach for your - //mouse or keyboard if you're using MIDI control and you scroll through them...) - if (RhythmboxFeature::isSupported() && - pConfig->getValueString(ConfigKey("[Library]","ShowRhythmboxLibrary"),"1").toInt()) { - addFeature(new RhythmboxFeature(this, m_pTrackCollection)); - } - if (pConfig->getValueString(ConfigKey("[Library]","ShowBansheeLibrary"),"1").toInt()) { - BansheeFeature::prepareDbPath(pConfig); - if (BansheeFeature::isSupported()) { - addFeature(new BansheeFeature(this, m_pTrackCollection, pConfig)); - } - } - if (ITunesFeature::isSupported() && - pConfig->getValueString(ConfigKey("[Library]","ShowITunesLibrary"),"1").toInt()) { - addFeature(new ITunesFeature(this, m_pTrackCollection)); - } - if (TraktorFeature::isSupported() && - pConfig->getValueString(ConfigKey("[Library]","ShowTraktorLibrary"),"1").toInt()) { - addFeature(new TraktorFeature(this, m_pTrackCollection)); - } + + createFeatures(pConfig, pPlayerManager); // On startup we need to check if all of the user's library folders are // accessible to us. If the user is using a database from <1.12.0 with @@ -137,13 +88,9 @@ Library::Library(QObject* parent, UserSettingsPointer pConfig, Library::~Library() { // Delete the sidebar model first since it depends on the LibraryFeatures. delete m_pSidebarModel; - - QMutableListIterator features_it(m_features); - while(features_it.hasNext()) { - LibraryFeature* feature = features_it.next(); - features_it.remove(); - delete feature; - } + + qDeleteAll(m_features); + m_features.clear(); delete m_pLibraryControl; //IMPORTANT: m_pTrackCollection gets destroyed via the QObject hierarchy somehow. @@ -154,106 +101,113 @@ Library::~Library() { delete m_pTrackCollection; } -void Library::bindSidebarWidget(WLibrarySidebar* pSidebarWidget) { - m_pLibraryControl->bindSidebarWidget(pSidebarWidget); - - // Setup the sources view - pSidebarWidget->setModel(m_pSidebarModel); - connect(m_pSidebarModel, SIGNAL(selectIndex(const QModelIndex&)), - pSidebarWidget, SLOT(selectIndex(const QModelIndex&))); - connect(pSidebarWidget, SIGNAL(pressed(const QModelIndex&)), - m_pSidebarModel, SLOT(clicked(const QModelIndex&))); - // Lazy model: Let triangle symbol increment the model - connect(pSidebarWidget, SIGNAL(expanded(const QModelIndex&)), - m_pSidebarModel, SLOT(doubleClicked(const QModelIndex&))); - - connect(pSidebarWidget, SIGNAL(rightClicked(const QPoint&, const QModelIndex&)), - m_pSidebarModel, SLOT(rightClicked(const QPoint&, const QModelIndex&))); - - pSidebarWidget->slotSetFont(m_trackTableFont); - connect(this, SIGNAL(setTrackTableFont(QFont)), - pSidebarWidget, SLOT(slotSetFont(QFont))); +void Library::bindSearchBar(WSearchLineEdit* searchLine, int id) { + // Get the value once to avoid searching again in the hash + LibraryPaneManager* pPane = getPane(id); + pPane->bindSearchBar(searchLine); } -void Library::bindWidget(WLibrary* pLibraryWidget, - KeyboardEventFilter* pKeyboard) { - WTrackTableView* pTrackTableView = - new WTrackTableView(pLibraryWidget, m_pConfig, m_pTrackCollection); - pTrackTableView->installEventFilter(pKeyboard); - connect(this, SIGNAL(showTrackModel(QAbstractItemModel*)), - pTrackTableView, SLOT(loadTrackModel(QAbstractItemModel*))); - connect(pTrackTableView, SIGNAL(loadTrack(TrackPointer)), - this, SLOT(slotLoadTrack(TrackPointer))); - connect(pTrackTableView, SIGNAL(loadTrackToPlayer(TrackPointer, QString, bool)), - this, SLOT(slotLoadTrackToPlayer(TrackPointer, QString, bool))); - pLibraryWidget->registerView(m_sTrackViewName, pTrackTableView); - - connect(this, SIGNAL(switchToView(const QString&)), - pLibraryWidget, SLOT(switchToView(const QString&))); - - connect(pTrackTableView, SIGNAL(trackSelected(TrackPointer)), - this, SIGNAL(trackSelected(TrackPointer))); - - connect(this, SIGNAL(setTrackTableFont(QFont)), - pTrackTableView, SLOT(setTrackTableFont(QFont))); - connect(this, SIGNAL(setTrackTableRowHeight(int)), - pTrackTableView, SLOT(setTrackTableRowHeight(int))); - - connect(this, SIGNAL(searchStarting()), - pTrackTableView, SLOT(onSearchStarting())); - connect(this, SIGNAL(searchCleared()), - pTrackTableView, SLOT(onSearchCleared())); - - m_pLibraryControl->bindWidget(pLibraryWidget, pKeyboard); - - QListIterator feature_it(m_features); - while(feature_it.hasNext()) { - LibraryFeature* feature = feature_it.next(); - feature->bindWidget(pLibraryWidget, pKeyboard); +void Library::bindSidebarWidget(WButtonBar* sidebar) { + for (LibraryFeature* f : m_features) { + WFeatureClickButton* button = sidebar->addButton(f); + + connect(button, SIGNAL(clicked(LibraryFeature*)), + this, SLOT(slotActivateFeature(LibraryFeature*))); + connect(button, SIGNAL(hoverShow(LibraryFeature*)), + this, SLOT(slotHoverFeature(LibraryFeature*))); + connect(button, SIGNAL(rightClicked(const QPoint&)), + f, SLOT(onRightClick(const QPoint&))); } +} +void Library::bindPaneWidget(WLibrary* pLibraryWidget, + KeyboardEventFilter* pKeyboard, int paneId) { + + // Get the value once to avoid searching again in the hash + LibraryPaneManager* pPane = getPane(paneId); + pPane->bindPaneWidget(pLibraryWidget, pKeyboard); + + connect(pPane, SIGNAL(search(const QString&)), + pLibraryWidget, SLOT(search(const QString&))); + // Set the current font and row height on all the WTrackTableViews that were // just connected to us. emit(setTrackTableFont(m_trackTableFont)); emit(setTrackTableRowHeight(m_iTrackTableRowHeight)); } +void Library::bindSidebarExpanded(WBaseLibrary* expandedPane, + KeyboardEventFilter* pKeyboard) { + //qDebug() << "Library::bindSidebarExpanded"; + m_pSidebarExpanded = new LibrarySidebarExpandedManager(this); + m_pSidebarExpanded->addFeatures(m_features); + m_pSidebarExpanded->bindPaneWidget(expandedPane, pKeyboard); +} + +void Library::bindBreadCrumb(WLibraryBreadCrumb* pBreadCrumb, int paneId) { + // Get the value once to avoid searching again in the hash + LibraryPaneManager* pPane = getPane(paneId); + pPane->setBreadCrumb(pBreadCrumb); +} + +void Library::destroyInterface() { + m_pSidebarExpanded->deleteLater(); + + for (LibraryPaneManager* p : m_panes) { + p->deleteLater(); + } + + for (LibraryFeature* f : m_features) { + f->setFeatureFocus(-1); + } + m_panes.clear(); +} + +LibraryView *Library::getActiveView() { + WBaseLibrary* pPane = m_panes[m_focusedPane]->getPaneWidget(); + WLibrary* pLibrary = qobject_cast(pPane); + DEBUG_ASSERT_AND_HANDLE(pLibrary) { + return nullptr; + } + return pLibrary->getActiveView(); +} + + void Library::addFeature(LibraryFeature* feature) { DEBUG_ASSERT_AND_HANDLE(feature) { return; } - m_features.push_back(feature); + m_features.append(feature); + m_pSidebarModel->addLibraryFeature(feature); - connect(feature, SIGNAL(showTrackModel(QAbstractItemModel*)), - this, SLOT(slotShowTrackModel(QAbstractItemModel*))); - connect(feature, SIGNAL(switchToView(const QString&)), - this, SLOT(slotSwitchToView(const QString&))); + + // TODO(jmigual): this should be removed and add a direct interaction + // between the LibraryFeature and the Library connect(feature, SIGNAL(loadTrack(TrackPointer)), this, SLOT(slotLoadTrack(TrackPointer))); connect(feature, SIGNAL(loadTrackToPlayer(TrackPointer, QString, bool)), this, SLOT(slotLoadTrackToPlayer(TrackPointer, QString, bool))); - connect(feature, SIGNAL(restoreSearch(const QString&)), - this, SLOT(slotRestoreSearch(const QString&))); connect(feature, SIGNAL(enableCoverArtDisplay(bool)), this, SIGNAL(enableCoverArtDisplay(bool))); connect(feature, SIGNAL(trackSelected(TrackPointer)), this, SIGNAL(trackSelected(TrackPointer))); } -void Library::slotShowTrackModel(QAbstractItemModel* model) { - //qDebug() << "Library::slotShowTrackModel" << model; - TrackModel* trackModel = dynamic_cast(model); - DEBUG_ASSERT_AND_HANDLE(trackModel) { - return; +void Library::switchToFeature(LibraryFeature* pFeature) { + m_pSidebarExpanded->switchToFeature(pFeature); + slotUpdateFocus(pFeature); + + WBaseLibrary* pWLibrary = m_panes[m_focusedPane]->getPaneWidget(); + // Only change the current pane if it's not shown already + if (pWLibrary->getCurrentFeature() != pFeature) { + m_panes[m_focusedPane]->switchToFeature(pFeature); } - emit(showTrackModel(model)); - emit(switchToView(m_sTrackViewName)); - emit(restoreSearch(trackModel->currentSearch())); + + handleFocus(); } -void Library::slotSwitchToView(const QString& view) { - //qDebug() << "Library::slotSwitchToView" << view; - emit(switchToView(view)); +void Library::showBreadCrumb(TreeItem *pTree) { + m_panes[m_focusedPane]->showBreadCrumb(pTree); } void Library::slotLoadTrack(TrackPointer pTrack) { @@ -272,8 +226,12 @@ void Library::slotLoadTrackToPlayer(TrackPointer pTrack, QString group, bool pla emit(loadTrackToPlayer(pTrack, group, play)); } -void Library::slotRestoreSearch(const QString& text) { - emit(restoreSearch(text)); +void Library::restoreSearch(const QString& text) { + LibraryPaneManager* pane = getFocusedPane(); + DEBUG_ASSERT_AND_HANDLE(pane) { + return; + } + pane->restoreSearch(text); } void Library::slotRefreshLibraryModels() { @@ -291,7 +249,32 @@ void Library::slotCreateCrate() { void Library::onSkinLoadFinished() { // Enable the default selection when a new skin is loaded. - m_pSidebarModel->activateDefaultSelection(); + //m_pSidebarModel->activateDefaultSelection(); + if (m_panes.size() > 0) { + + auto itF = m_features.begin(); + auto itP = m_panes.begin(); + + // Assign a feature to show on each pane unless there are more panes + // than features + while (itP != m_panes.end() && itF != m_features.end()) { + m_focusedPane = itP.key(); + + (*itF)->setFeatureFocus(itP.key()); + (*itF)->activate(); + + ++itP; + ++itF; + } + + // The first pane always shows the Mixxx Library feature on start + m_focusedPane = m_panes.begin().key(); + (*m_features.begin())->setFeatureFocus(m_focusedPane); + slotActivateFeature(*m_features.begin()); + } + else { + qDebug() << "Library::onSkinLoadFinished No Panes loaded!"; + } } void Library::slotRequestAddDir(QString dir) { @@ -368,6 +351,64 @@ QStringList Library::getDirs() { return m_pTrackCollection->getDirectoryDAO().getDirs(); } +void Library::paneCollapsed(int paneId) { + m_collapsedPanes.insert(paneId); + + // Automatically switch the focus to a non collapsed pane + m_panes[paneId]->clearFocus(); + + for (LibraryPaneManager* pPane : m_panes) { + if (!m_collapsedPanes.contains(pPane->getPaneId())) { + m_focusedPane = pPane->getPaneId(); + setFocusedPane(); + pPane->setFocus(); + break; + } + } +} + +void Library::paneUncollapsed(int paneId) { + m_collapsedPanes.remove(paneId); +} + +void Library::slotActivateFeature(LibraryFeature *pFeature) { + // The feature is being shown currently in the focused pane + if (m_panes[m_focusedPane]->getCurrentFeature() == pFeature) { + m_pSidebarExpanded->switchToFeature(pFeature); + handleFocus(); + return; + } + int featureFocus = pFeature->getFeatureFocus(); + + // The feature is not focused anywhere + if (featureFocus < 0 || m_collapsedPanes.contains(featureFocus)) { + // Remove the previous focused feature in this pane + for (LibraryFeature* f : m_features) { + if (f->getFeatureFocus() == m_focusedPane) { + f->setFeatureFocus(-1); + } + } + } else { + // The feature is shown in some not collapsed pane + m_focusedPane = featureFocus; + setFocusedPane(); + m_pSidebarExpanded->switchToFeature(pFeature); + handleFocus(); + return; + } + + m_panes[m_focusedPane]->setCurrentFeature(pFeature); + pFeature->setFeatureFocus(m_focusedPane); + pFeature->activate(); + handleFocus(); +} + +void Library::slotHoverFeature(LibraryFeature *pFeature) { + // This function only changes the sidebar expanded to allow dropping items + // directly in some features sidebar panes + m_pSidebarExpanded->switchToFeature(pFeature); +} + void Library::slotSetTrackTableFont(const QFont& font) { m_trackTableFont = font; emit(setTrackTableFont(font)); @@ -377,3 +418,126 @@ void Library::slotSetTrackTableRowHeight(int rowHeight) { m_iTrackTableRowHeight = rowHeight; emit(setTrackTableRowHeight(rowHeight)); } + +void Library::slotPaneFocused(LibraryPaneManager* pPane) { + DEBUG_ASSERT_AND_HANDLE(pPane) { + return; + } + + if (pPane != m_pSidebarExpanded) { + m_focusedPane = pPane->getPaneId(); + pPane->getCurrentFeature()->setFeatureFocus(m_focusedPane); + DEBUG_ASSERT_AND_HANDLE(m_focusedPane != -1) { + return; + } + setFocusedPane(); + handleFocus(); + } + + //qDebug() << "Library::slotPaneFocused" << m_focusedPane; +} + +void Library::slotUpdateFocus(LibraryFeature *pFeature) { + if (pFeature->getFeatureFocus() >= 0) { + m_focusedPane = pFeature->getFeatureFocus(); + setFocusedPane(); + } +} + + +LibraryPaneManager* Library::getPane(int paneId) { + //qDebug() << "Library::createPane" << id; + // Get the value once to avoid searching again in the hash + auto it = m_panes.find(paneId); + if (it != m_panes.end()) { + return *it; + } + + LibraryPaneManager* pPane = new LibraryPaneManager(paneId, this); + pPane->addFeatures(m_features); + m_panes.insert(paneId, pPane); + + m_focusedPane = paneId; + setFocusedPane(); + return pPane; +} + +LibraryPaneManager *Library::getFocusedPane() { + //qDebug() << "Focused" << m_focusedPane; + if (m_focusedPane == -1) { + return m_pSidebarExpanded; + } + else if (m_panes.contains(m_focusedPane)) { + return m_panes[m_focusedPane]; + } + return nullptr; +} + +void Library::createFeatures(UserSettingsPointer pConfig, PlayerManagerInterface* pPlayerManager) { + m_pMixxxLibraryFeature = new MixxxLibraryFeature(pConfig, this, this, m_pTrackCollection); + addFeature(m_pMixxxLibraryFeature); + + addFeature(new AutoDJFeature(pConfig, this, this, pPlayerManager, m_pTrackCollection)); + m_pPlaylistFeature = new PlaylistFeature(pConfig, this, this, m_pTrackCollection); + addFeature(m_pPlaylistFeature); + m_pCrateFeature = new CrateFeature(pConfig, this, this, m_pTrackCollection); + addFeature(m_pCrateFeature); + BrowseFeature* browseFeature = new BrowseFeature( + pConfig, this, this, m_pTrackCollection, m_pRecordingManager); + connect(browseFeature, SIGNAL(scanLibrary()), + &m_scanner, SLOT(scan())); + connect(&m_scanner, SIGNAL(scanStarted()), + browseFeature, SLOT(slotLibraryScanStarted())); + connect(&m_scanner, SIGNAL(scanFinished()), + browseFeature, SLOT(slotLibraryScanFinished())); + + addFeature(browseFeature); + addFeature(new RecordingFeature(pConfig, this, this, m_pTrackCollection, m_pRecordingManager)); + addFeature(new HistoryFeature(pConfig, this, this, m_pTrackCollection)); + m_pAnalysisFeature = new AnalysisFeature(pConfig, this, m_pTrackCollection, this); + connect(m_pPlaylistFeature, SIGNAL(analyzeTracks(QList)), + m_pAnalysisFeature, SLOT(analyzeTracks(QList))); + connect(m_pCrateFeature, SIGNAL(analyzeTracks(QList)), + m_pAnalysisFeature, SLOT(analyzeTracks(QList))); + addFeature(m_pAnalysisFeature); + //iTunes and Rhythmbox should be last until we no longer have an obnoxious + //messagebox popup when you select them. (This forces you to reach for your + //mouse or keyboard if you're using MIDI control and you scroll through them...) + if (RhythmboxFeature::isSupported() && + pConfig->getValueString(ConfigKey("[Library]","ShowRhythmboxLibrary"),"1").toInt()) { + addFeature(new RhythmboxFeature(pConfig, this, this, m_pTrackCollection)); + } + + if (pConfig->getValueString(ConfigKey("[Library]","ShowBansheeLibrary"),"1").toInt()) { + BansheeFeature::prepareDbPath(pConfig); + if (BansheeFeature::isSupported()) { + addFeature(new BansheeFeature(pConfig, this, this, m_pTrackCollection)); + } + } + if (ITunesFeature::isSupported() && + pConfig->getValueString(ConfigKey("[Library]","ShowITunesLibrary"),"1").toInt()) { + addFeature(new ITunesFeature(pConfig, this, this, m_pTrackCollection)); + } + if (TraktorFeature::isSupported() && + pConfig->getValueString(ConfigKey("[Library]","ShowTraktorLibrary"),"1").toInt()) { + addFeature(new TraktorFeature(pConfig, this, this, m_pTrackCollection)); + } +} + +void Library::setFocusedPane() { + for (LibraryFeature* pFeature : m_features) { + pFeature->setFocusedPane(m_focusedPane); + } +} + +void Library::handleFocus() { + // Changes the visual focus effect, removes the existing one and adds the + // new focus + m_panes[m_focusedPane]->setFocus(); + for (auto it = m_panes.begin(); it != m_panes.end(); ++it) { + // Remove the focus from not focused panes + if (it.key() != m_focusedPane) { + it.value()->clearFocus(); + } + } +} diff --git a/src/library/library.h b/src/library/library.h index dbc8c8323e3c..092eb42d6768 100644 --- a/src/library/library.h +++ b/src/library/library.h @@ -11,45 +11,71 @@ #include #include #include +#include #include "preferences/usersettings.h" #include "track/track.h" #include "recording/recordingmanager.h" #include "analysisfeature.h" #include "library/coverartcache.h" -#include "library/setlogfeature.h" +#include "library/historyfeature.h" #include "library/scanner/libraryscanner.h" -class TrackModel; -class TrackCollection; -class SidebarModel; +#include "widget/wtracktableview.h" +#include "widget/wfeatureclickbutton.h" + +class CrateFeature; +class KeyboardEventFilter; +class LibraryPaneManager; +class LibraryControl; class LibraryFeature; class LibraryTableModel; -class WLibrarySidebar; -class WLibrary; -class WSearchLineEdit; +class LibrarySidebarExpandedManager; class MixxxLibraryFeature; class PlaylistFeature; -class CrateFeature; -class LibraryControl; -class KeyboardEventFilter; class PlayerManagerInterface; +class SidebarModel; +class TrackModel; +class TrackCollection; +class WLibrary; +class WLibrarySidebar; +class WLibraryBreadCrumb; +class WButtonBar; +class WSearchLineEdit; class Library : public QObject { Q_OBJECT public: + enum RemovalType { + LeaveTracksUnchanged = 0, + HideTracks, + PurgeTracks + }; + + static const int kDefaultRowHeightPx; + Library(QObject* parent, UserSettingsPointer pConfig, PlayerManagerInterface* pPlayerManager, RecordingManager* pRecordingManager); virtual ~Library(); - - void bindWidget(WLibrary* libraryWidget, - KeyboardEventFilter* pKeyboard); - void bindSidebarWidget(WLibrarySidebar* sidebarWidget); - + + void bindSearchBar(WSearchLineEdit* searchLine, int id); + void bindSidebarWidget(WButtonBar* sidebar); + void bindPaneWidget(WLibrary* libraryWidget, + KeyboardEventFilter* pKeyboard, int paneId); + void bindSidebarExpanded(WBaseLibrary* expandedPane, + KeyboardEventFilter* pKeyboard); + void bindBreadCrumb(WLibraryBreadCrumb *pBreadCrumb, int paneId); + + void destroyInterface(); + LibraryView* getActiveView(); + void addFeature(LibraryFeature* feature); QStringList getDirs(); + + void paneCollapsed(int paneId); + void paneUncollapsed(int paneId); // TODO(rryan) Transitionary only -- the only reason this is here is so the // waveform widgets can signal to a player to load a track. This can be @@ -66,24 +92,20 @@ class Library : public QObject { inline const QFont& getTrackTableFont() const { return m_trackTableFont; } - - //static Library* buildDefaultLibrary(); - - enum RemovalType { - LeaveTracksUnchanged = 0, - HideTracks, - PurgeTracks - }; - - static const int kDefaultRowHeightPx; + + void switchToFeature(LibraryFeature* pFeature); + void showBreadCrumb(TreeItem* pTree); + void restoreSearch(const QString& text); public slots: - void slotShowTrackModel(QAbstractItemModel* model); - void slotSwitchToView(const QString& view); + + void slotActivateFeature(LibraryFeature* pFeature); + void slotHoverFeature(LibraryFeature* pFeature); + + // Updates the focus from the feature before changing the view void slotLoadTrack(TrackPointer pTrack); void slotLoadTrackToPlayer(TrackPointer pTrack, QString group, bool play); void slotLoadLocationToPlayer(QString location, QString group); - void slotRestoreSearch(const QString& text); void slotRefreshLibraryModels(); void slotCreatePlaylist(); void slotCreateCrate(); @@ -93,20 +115,19 @@ class Library : public QObject { void onSkinLoadFinished(); void slotSetTrackTableFont(const QFont& font); void slotSetTrackTableRowHeight(int rowHeight); + void slotPaneFocused(LibraryPaneManager *pPane); + + // Updates with the focus feature + void slotUpdateFocus(LibraryFeature* pFeature); void scan() { m_scanner.scan(); } signals: - void showTrackModel(QAbstractItemModel* model); - void switchToView(const QString& view); void loadTrack(TrackPointer pTrack); void loadTrackToPlayer(TrackPointer pTrack, QString group, bool play = false); - void restoreSearch(const QString&); - void search(const QString& text); - void searchCleared(); - void searchStarting(); + // emit this signal to enable/disable the cover art widget void enableCoverArtDisplay(bool); void trackSelected(TrackPointer pTrack); @@ -119,12 +140,19 @@ class Library : public QObject { void scanFinished(); private: + + // If the pane exists returns it, otherwise it creates the pane + LibraryPaneManager *getPane(int paneId); + LibraryPaneManager* getFocusedPane(); + + void createFeatures(UserSettingsPointer pConfig, PlayerManagerInterface *pPlayerManager); + void setFocusedPane(); + + void handleFocus(); + UserSettingsPointer m_pConfig; SidebarModel* m_pSidebarModel; TrackCollection* m_pTrackCollection; - QList m_features; - const static QString m_sTrackViewName; - const static QString m_sAutoDJViewName; MixxxLibraryFeature* m_pMixxxLibraryFeature; PlaylistFeature* m_pPlaylistFeature; CrateFeature* m_pCrateFeature; @@ -134,6 +162,14 @@ class Library : public QObject { LibraryScanner m_scanner; QFont m_trackTableFont; int m_iTrackTableRowHeight; + + QHash m_panes; + LibraryPaneManager* m_pSidebarExpanded; + QList m_features; + QSet m_collapsedPanes; + + // Can be any integer as it's used with a HashMap + int m_focusedPane; }; #endif /* LIBRARY_H */ diff --git a/src/library/librarycontrol.cpp b/src/library/librarycontrol.cpp index 577b9fac1724..a162fdaa8308 100644 --- a/src/library/librarycontrol.cpp +++ b/src/library/librarycontrol.cpp @@ -50,7 +50,6 @@ void LoadToGroupController::slotLoadToGroupAndPlay(double v) { LibraryControl::LibraryControl(Library* pLibrary) : QObject(pLibrary), m_pLibrary(pLibrary), - m_pLibraryWidget(NULL), m_pSidebarWidget(NULL), m_numDecks("[Master]", "num_decks", this), m_numSamplers("[Master]", "num_samplers", this), @@ -194,30 +193,13 @@ void LibraryControl::bindSidebarWidget(WLibrarySidebar* pSidebarWidget) { this, SLOT(sidebarWidgetDeleted())); } -void LibraryControl::bindWidget(WLibrary* pLibraryWidget, KeyboardEventFilter* pKeyboard) { - Q_UNUSED(pKeyboard); - if (m_pLibraryWidget != NULL) { - disconnect(m_pLibraryWidget, 0, this, 0); - } - m_pLibraryWidget = pLibraryWidget; - connect(m_pLibraryWidget, SIGNAL(destroyed()), - this, SLOT(libraryWidgetDeleted())); -} - -void LibraryControl::libraryWidgetDeleted() { - m_pLibraryWidget = NULL; -} - void LibraryControl::sidebarWidgetDeleted() { m_pSidebarWidget = NULL; } void LibraryControl::slotLoadSelectedTrackToGroup(QString group, bool play) { - if (m_pLibraryWidget == NULL) { - return; - } - LibraryView* activeView = m_pLibraryWidget->getActiveView(); + LibraryView* activeView = m_pLibrary->getActiveView(); if (!activeView) { return; } @@ -225,12 +207,9 @@ void LibraryControl::slotLoadSelectedTrackToGroup(QString group, bool play) { } void LibraryControl::slotLoadSelectedIntoFirstStopped(double v) { - if (m_pLibraryWidget == NULL) { - return; - } - + if (v > 0) { - LibraryView* activeView = m_pLibraryWidget->getActiveView(); + LibraryView* activeView = m_pLibrary->getActiveView(); if (!activeView) { return; } @@ -239,12 +218,8 @@ void LibraryControl::slotLoadSelectedIntoFirstStopped(double v) { } void LibraryControl::slotAutoDjAddTop(double v) { - if (m_pLibraryWidget == NULL) { - return; - } - if (v > 0) { - LibraryView* activeView = m_pLibraryWidget->getActiveView(); + LibraryView* activeView = m_pLibrary->getActiveView(); if (!activeView) { return; } @@ -253,12 +228,8 @@ void LibraryControl::slotAutoDjAddTop(double v) { } void LibraryControl::slotAutoDjAddBottom(double v) { - if (m_pLibraryWidget == NULL) { - return; - } - if (v > 0) { - LibraryView* activeView = m_pLibraryWidget->getActiveView(); + LibraryView* activeView = m_pLibrary->getActiveView(); if (!activeView) { return; } @@ -279,13 +250,9 @@ void LibraryControl::slotSelectPrevTrack(double v) { } void LibraryControl::slotSelectTrack(double v) { - if (m_pLibraryWidget == NULL) { - return; - } - int i = (int)v; - LibraryView* activeView = m_pLibraryWidget->getActiveView(); + LibraryView* activeView = m_pLibrary->getActiveView(); if (!activeView) { return; } diff --git a/src/library/librarycontrol.h b/src/library/librarycontrol.h index 7b449d9c5a01..2356595e11cc 100644 --- a/src/library/librarycontrol.h +++ b/src/library/librarycontrol.h @@ -36,12 +36,10 @@ class LibraryControl : public QObject { public: LibraryControl(Library* pLibrary); virtual ~LibraryControl(); - - void bindWidget(WLibrary* pLibrary, KeyboardEventFilter* pKeyboard); + void bindSidebarWidget(WLibrarySidebar* pLibrarySidebar); private slots: - void libraryWidgetDeleted(); void sidebarWidgetDeleted(); void slotLoadSelectedTrackToGroup(QString group, bool play); void slotSelectNextTrack(double v); @@ -84,7 +82,6 @@ class LibraryControl : public QObject { ControlPushButton* m_pFontSizeIncrement; ControlPushButton* m_pFontSizeDecrement; - WLibrary* m_pLibraryWidget; WLibrarySidebar* m_pSidebarWidget; ControlProxy m_numDecks; ControlProxy m_numSamplers; diff --git a/src/library/libraryfeature.cpp b/src/library/libraryfeature.cpp index 19575cf46235..f35974d0ec53 100644 --- a/src/library/libraryfeature.cpp +++ b/src/library/libraryfeature.cpp @@ -1,23 +1,148 @@ // libraryfeature.cpp // Created 8/17/2009 by RJ Ryan (rryan@mit.edu) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "library/library.h" #include "library/libraryfeature.h" +#include "widget/wbaselibrary.h" +#include "widget/wlibrarysidebar.h" +#include "widget/wtracktableview.h" // KEEP THIS cpp file to tell scons that moc should be called on the class!!! // The reason for this is that LibraryFeature uses slots/signals and for this -// to work the code has to be precompiles by moc -LibraryFeature::LibraryFeature(QObject *parent) - : QObject(parent) { +// to work the code has to be precompiled by moc +LibraryFeature::LibraryFeature(UserSettingsPointer pConfig, + Library* pLibrary, + TrackCollection* pTrackCollection, + QObject* parent) + : QObject(parent), + m_pConfig(pConfig), + m_pLibrary(pLibrary), + m_pTrackCollection(pTrackCollection), + m_featureFocus(-1) { +} +LibraryFeature::~LibraryFeature() { + } -LibraryFeature::LibraryFeature(UserSettingsPointer pConfig, QObject* parent) - : QObject(parent), - m_pConfig(pConfig) { +QWidget* LibraryFeature::createPaneWidget(KeyboardEventFilter* pKeyboard, + int paneId) { + return createTableWidget(pKeyboard, paneId); } -LibraryFeature::~LibraryFeature() { +QWidget *LibraryFeature::createSidebarWidget(KeyboardEventFilter* pKeyboard) { + //qDebug() << "LibraryFeature::bindSidebarWidget"; + QFrame* pContainer = new QFrame(nullptr); + pContainer->setContentsMargins(0,0,0,0); + + QVBoxLayout* pLayout = new QVBoxLayout(pContainer); + pLayout->setContentsMargins(0,0,0,0); + pLayout->setSpacing(0); + pContainer->setLayout(pLayout); + + QLabel* pTitle = new QLabel(title().toString(), pContainer); + pLayout->addWidget(pTitle); + + QWidget* pSidebar = createInnerSidebarWidget(pKeyboard); + pSidebar->setParent(pContainer); + pLayout->addWidget(pSidebar); + + return pContainer; +} + +void LibraryFeature::setFeatureFocus(int focus) { + m_featureFocus = focus; +} + +int LibraryFeature::getFeatureFocus() { + return m_featureFocus; +} + +void LibraryFeature::setFocusedPane(int paneId) { + m_focusedPane = paneId; +} + +WTrackTableView* LibraryFeature::createTableWidget(KeyboardEventFilter* pKeyboard, + int paneId) { + WTrackTableView* pTrackTableView = + new WTrackTableView(nullptr, m_pConfig, m_pTrackCollection); + + pTrackTableView->installEventFilter(pKeyboard); + + connect(pTrackTableView, SIGNAL(loadTrack(TrackPointer)), + this, SIGNAL(loadTrack(TrackPointer))); + connect(pTrackTableView, SIGNAL(loadTrackToPlayer(TrackPointer, QString, bool)), + this, SIGNAL(loadTrackToPlayer(TrackPointer, QString, bool))); + connect(pTrackTableView, SIGNAL(trackSelected(TrackPointer)), + this, SIGNAL(trackSelected(TrackPointer))); + + connect(m_pLibrary, SIGNAL(setTrackTableFont(QFont)), + pTrackTableView, SLOT(setTrackTableFont(QFont))); + connect(m_pLibrary, SIGNAL(setTrackTableRowHeight(int)), + pTrackTableView, SLOT(setTrackTableRowHeight(int))); + m_trackTables[paneId] = pTrackTableView; + + return pTrackTableView; +} + +QWidget* LibraryFeature::createInnerSidebarWidget(KeyboardEventFilter *pKeyboard) { + return createLibrarySidebarWidget(pKeyboard); +} + +WLibrarySidebar *LibraryFeature::createLibrarySidebarWidget(KeyboardEventFilter *pKeyboard) { + WLibrarySidebar* pSidebar = new WLibrarySidebar(nullptr); + pSidebar->installEventFilter(pKeyboard); + pSidebar->setModel(getChildModel()); + + connect(pSidebar, SIGNAL(clicked(const QModelIndex&)), + this, SLOT(activateChild(const QModelIndex&))); + connect(pSidebar, SIGNAL(doubleClicked(const QModelIndex&)), + this, SLOT(onLazyChildExpandation(const QModelIndex&))); + connect(pSidebar, SIGNAL(rightClicked(const QPoint&, const QModelIndex&)), + this, SLOT(onRightClickChild(const QPoint&, const QModelIndex&))); + connect(pSidebar, SIGNAL(expanded(const QModelIndex&)), + this, SLOT(onLazyChildExpandation(const QModelIndex&))); + return pSidebar; +} + +void LibraryFeature::showTrackModel(QAbstractItemModel *model) { + auto it = m_trackTables.find(m_featureFocus); + if (it == m_trackTables.end() || it->isNull()) { + return; + } + (*it)->loadTrackModel(model); + switchToFeature(); +} + +void LibraryFeature::switchToFeature() { + m_pLibrary->switchToFeature(this); +} + +void LibraryFeature::restoreSearch(const QString& search) { + m_pLibrary->restoreSearch(search); +} + +void LibraryFeature::showBreadCrumb(TreeItem* pTree) { + m_pLibrary->showBreadCrumb(pTree); +} +WTrackTableView *LibraryFeature::getFocusedTable() { + auto it = m_trackTables.find(m_featureFocus); + if (it == m_trackTables.end() || it->isNull()) { + return nullptr; + } + return *it; } QStringList LibraryFeature::getPlaylistFiles(QFileDialog::FileMode mode) { @@ -25,15 +150,17 @@ QStringList LibraryFeature::getPlaylistFiles(QFileDialog::FileMode mode) { ConfigKey("[Library]", "LastImportExportPlaylistDirectory"), QDesktopServices::storageLocation(QDesktopServices::MusicLocation)); - QFileDialog dialogg(NULL, - tr("Import Playlist"), - lastPlaylistDirectory, - tr("Playlist Files (*.m3u *.m3u8 *.pls *.csv)")); - dialogg.setAcceptMode(QFileDialog::AcceptOpen); - dialogg.setFileMode(mode); - dialogg.setModal(true); + QFileDialog dialog(nullptr, + tr("Import Playlist"), + lastPlaylistDirectory, + tr("Playlist Files (*.m3u *.m3u8 *.pls *.csv)")); + dialog.setAcceptMode(QFileDialog::AcceptOpen); + dialog.setFileMode(mode); + dialog.setModal(true); // If the user refuses return - if (! dialogg.exec()) return QStringList(); - return dialogg.selectedFiles(); + if (!dialog.exec()) { + return QStringList(); + } + return dialog.selectedFiles(); } diff --git a/src/library/libraryfeature.h b/src/library/libraryfeature.h index f750bf6cda16..e9c0b647cf56 100644 --- a/src/library/libraryfeature.h +++ b/src/library/libraryfeature.h @@ -4,113 +4,141 @@ #ifndef LIBRARYFEATURE_H #define LIBRARYFEATURE_H -#include -#include -#include -#include +#include +#include +#include #include -#include -#include #include -#include -#include -#include "track/track.h" -#include "treeitemmodel.h" +#include "controllers/keyboard/keyboardeventfilter.h" #include "library/coverartcache.h" #include "library/dao/trackdao.h" +#include "preferences/usersettings.h" +#include "track/track.h" +#include "treeitemmodel.h" +class Library; +class TrackCollection; class TrackModel; -class WLibrarySidebar; +class WBaseLibrary; class WLibrary; -class KeyboardEventFilter; +class WLibrarySidebar; +class WTrackTableView; // pure virtual (abstract) class to provide an interface for libraryfeatures class LibraryFeature : public QObject { - Q_OBJECT + Q_OBJECT public: - LibraryFeature(QObject* parent = NULL); + // The parent does not necessary be the Library LibraryFeature(UserSettingsPointer pConfig, - QObject* parent = NULL); + Library* pLibrary, TrackCollection *pTrackCollection, + QObject* parent = nullptr); + virtual ~LibraryFeature(); virtual QVariant title() = 0; virtual QIcon getIcon() = 0; - virtual bool dropAccept(QList urls, QObject* pSource) { - Q_UNUSED(urls); - Q_UNUSED(pSource); + virtual bool dropAccept(QList /* urls */, + QObject* /* pSource */) { return false; } - virtual bool dropAcceptChild(const QModelIndex& index, - QList urls, QObject* pSource) { - Q_UNUSED(index); - Q_UNUSED(urls); - Q_UNUSED(pSource); + virtual bool dropAcceptChild(const QModelIndex& /* index */, + QList /* urls */, + QObject* /* pSource */) { return false; } - virtual bool dragMoveAccept(QUrl url) { - Q_UNUSED(url); + virtual bool dragMoveAccept(QUrl /* url */) { return false; } - virtual bool dragMoveAcceptChild(const QModelIndex& index, QUrl url) { - Q_UNUSED(index); - Q_UNUSED(url); + virtual bool dragMoveAcceptChild(const QModelIndex& /* index */, + QUrl /* url */) { return false; } - - // Reimplement this to register custom views with the library widget. - virtual void bindWidget(WLibrary* /* libraryWidget */, - KeyboardEventFilter* /* keyboard */) {} + + // Reimplement this to register custom views with the library widget + // at the right pane. + virtual QWidget* createPaneWidget(KeyboardEventFilter* pKeyboard, + int paneId); + + // Reimplement this to register custom views with the library widget, + // at the sidebar expanded pane + virtual QWidget* createSidebarWidget(KeyboardEventFilter* pKeyboard); + virtual TreeItemModel* getChildModel() = 0; - - protected: - inline QStringList getPlaylistFiles() { return getPlaylistFiles(QFileDialog::ExistingFiles); } - inline QString getPlaylistFile() { return getPlaylistFiles(QFileDialog::ExistingFile).first(); } - UserSettingsPointer m_pConfig; + + virtual void setFeatureFocus(int focus); + virtual int getFeatureFocus(); + + virtual void setFocusedPane(int paneId); public slots: // called when you single click on the root item virtual void activate() = 0; // called when you single click on a child item, e.g., a concrete playlist or crate - virtual void activateChild(const QModelIndex& index) { - Q_UNUSED(index); + virtual void activateChild(const QModelIndex&) { } // called when you right click on the root item - virtual void onRightClick(const QPoint& globalPos) { - Q_UNUSED(globalPos); + virtual void onRightClick(const QPoint&) { } // called when you right click on a child item, e.g., a concrete playlist or crate - virtual void onRightClickChild(const QPoint& globalPos, QModelIndex index) { - Q_UNUSED(globalPos); - Q_UNUSED(index); + virtual void onRightClickChild(const QPoint& /* globalPos */, + QModelIndex /* index */) { } // Only implement this, if using incremental or lazy childmodels, see BrowseFeature. // This method is executed whenever you **double** click child items - virtual void onLazyChildExpandation(const QModelIndex& index) { - Q_UNUSED(index); + virtual void onLazyChildExpandation(const QModelIndex&) { } + signals: - void showTrackModel(QAbstractItemModel* model); - void switchToView(const QString& view); - void loadTrack(TrackPointer pTrack); + + void loadTrack(TrackPointer); void loadTrackToPlayer(TrackPointer pTrack, QString group, bool play = false); - void restoreSearch(const QString&); // emit this signal before you parse a large music collection, e.g., iTunes, Traktor. // The second arg indicates if the feature should be "selected" when loading starts void featureIsLoading(LibraryFeature*, bool selectFeature); // emit this signal if the foreign music collection has been imported/parsed. - void featureLoadingFinished(LibraryFeature*s); + void featureLoadingFinished(LibraryFeature*); // emit this signal to select pFeature void featureSelect(LibraryFeature* pFeature, const QModelIndex& index); // emit this signal to enable/disable the cover art widget void enableCoverArtDisplay(bool); - void trackSelected(TrackPointer pTrack); + void trackSelected(TrackPointer); + + protected: + inline QStringList getPlaylistFiles() { return getPlaylistFiles(QFileDialog::ExistingFiles); } + inline QString getPlaylistFile() { return getPlaylistFiles(QFileDialog::ExistingFile).first(); } + + // Creates a table widget with no model + WTrackTableView* createTableWidget(KeyboardEventFilter* pKeyboard, + int paneId); + + // Creates a WLibrarySidebar widget with the getChildModel() function as + // model + WLibrarySidebar* createLibrarySidebarWidget(KeyboardEventFilter* pKeyboard); + + // Override this function to create a custom inner widget for the sidebar, + // the default widget is a WLibrarySidebar widget + virtual QWidget* createInnerSidebarWidget(KeyboardEventFilter* pKeyboard); + + void showTrackModel(QAbstractItemModel *model); + void switchToFeature(); + void restoreSearch(const QString& search); + void showBreadCrumb(TreeItem *pTree); + + WTrackTableView* getFocusedTable(); + + UserSettingsPointer m_pConfig; + Library* m_pLibrary; + TrackCollection* m_pTrackCollection; + + int m_featureFocus; + int m_focusedPane; private: QStringList getPlaylistFiles(QFileDialog::FileMode mode); - + QHash > m_trackTables; }; #endif /* LIBRARYFEATURE_H */ diff --git a/src/library/librarypanemanager.cpp b/src/library/librarypanemanager.cpp new file mode 100644 index 000000000000..34c59a935700 --- /dev/null +++ b/src/library/librarypanemanager.cpp @@ -0,0 +1,155 @@ +#include + +#include "library/libraryfeature.h" +#include "library/library.h" +#include "library/librarypanemanager.h" +#include "util/assert.h" +#include "widget/wbuttonbar.h" +#include "widget/wlibrarybreadcrumb.h" +#include "widget/wtracktableview.h" + +LibraryPaneManager::LibraryPaneManager(int paneId, Library *pLibrary, QObject* parent) + : QObject(parent), + m_pPaneWidget(nullptr), + m_pBreadCrumb(nullptr), + m_paneId(paneId), + m_pLibrary(pLibrary) { + qApp->installEventFilter(this); +} + +LibraryPaneManager::~LibraryPaneManager() { +} + +void LibraryPaneManager::bindPaneWidget(WBaseLibrary* pLibraryWidget, + KeyboardEventFilter* pKeyboard) { + //qDebug() << "LibraryPaneManager::bindLibraryWidget" << libraryWidget; + m_pPaneWidget = pLibraryWidget; + + connect(m_pPaneWidget, SIGNAL(focused()), + this, SLOT(slotPaneFocused())); + connect(m_pPaneWidget, SIGNAL(collapsed()), + this, SLOT(slotPaneCollapsed())); + connect(m_pPaneWidget, SIGNAL(uncollapsed()), + this, SLOT(slotPaneUncollapsed())); + + WLibrary* lib = qobject_cast(m_pPaneWidget); + if (lib == nullptr) { + return; + } + for (LibraryFeature* f : m_features) { + //f->bindPaneWidget(lib, pKeyboard, m_paneId); + + QWidget* pPane = f->createPaneWidget(pKeyboard, m_paneId); + if (pPane == nullptr) { + continue; + } + pPane->setParent(lib); + lib->registerView(f, pPane); + } +} + +void LibraryPaneManager::bindSearchBar(WSearchLineEdit* pSearchBar) { + pSearchBar->installEventFilter(this); + + connect(pSearchBar, SIGNAL(search(const QString&)), + this, SIGNAL(search(const QString&))); + connect(pSearchBar, SIGNAL(searchCleared()), + this, SIGNAL(searchCleared())); + connect(pSearchBar, SIGNAL(searchStarting()), + this, SIGNAL(searchStarting())); + + m_pSearchBar = pSearchBar; +} + +void LibraryPaneManager::setBreadCrumb(WLibraryBreadCrumb *pBreadCrumb) { + m_pBreadCrumb = pBreadCrumb; +} + +void LibraryPaneManager::addFeature(LibraryFeature* feature) { + DEBUG_ASSERT_AND_HANDLE(feature) { + return; + } + + m_features.append(feature); +} + +void LibraryPaneManager::addFeatures(const QList& features) { + m_features.append(features); +} + +WBaseLibrary* LibraryPaneManager::getPaneWidget() { + return m_pPaneWidget; +} + +void LibraryPaneManager::setCurrentFeature(LibraryFeature* pFeature) { + m_pCurrentFeature = pFeature; +} + +LibraryFeature *LibraryPaneManager::getCurrentFeature() const { + return m_pCurrentFeature; +} + +void LibraryPaneManager::setFocus() { + //qDebug() << "LibraryPaneManager::setFocus"; + DEBUG_ASSERT_AND_HANDLE(m_pPaneWidget) { + return; + } + + m_pPaneWidget->setProperty("showFocus", 1); +} + +void LibraryPaneManager::clearFocus() { + //qDebug() << "LibraryPaneManager::clearFocus"; + m_pPaneWidget->setProperty("showFocus", 0); +} + +void LibraryPaneManager::switchToFeature(LibraryFeature* pFeature) { + DEBUG_ASSERT_AND_HANDLE(!m_pPaneWidget.isNull() && pFeature) { + return; + } + + m_pCurrentFeature = pFeature; + m_pPaneWidget->switchToFeature(pFeature); +} + +void LibraryPaneManager::restoreSearch(const QString& text) { + if (!m_pSearchBar.isNull()) { + m_pSearchBar->restoreSearch(text); + } +} + +void LibraryPaneManager::showBreadCrumb(TreeItem *pTree) { + DEBUG_ASSERT_AND_HANDLE(!m_pBreadCrumb.isNull()) { + return; + } + + m_pBreadCrumb->showBreadCrumb(pTree); +} + +void LibraryPaneManager::slotPaneCollapsed() { + m_pLibrary->paneCollapsed(m_paneId); +} + +void LibraryPaneManager::slotPaneUncollapsed() { + m_pLibrary->paneUncollapsed(m_paneId); +} + +void LibraryPaneManager::slotPaneFocused() { + m_pLibrary->slotPaneFocused(this); +} + +bool LibraryPaneManager::eventFilter(QObject*, QEvent* event) { + if (m_pPaneWidget.isNull()) { + return false; + } + + if (event->type() == QEvent::MouseButtonPress && + m_pPaneWidget->underMouse()) { + m_pLibrary->slotPaneFocused(this); + } + + // Since this event filter is for the entire application (to handle the + // mouse event), NEVER return true. If true is returned I will block all + // application events and will block the entire application. + return false; +} diff --git a/src/library/librarypanemanager.h b/src/library/librarypanemanager.h new file mode 100644 index 000000000000..f5a6d412ddf2 --- /dev/null +++ b/src/library/librarypanemanager.h @@ -0,0 +1,84 @@ +#ifndef LIBRARYVIEWMANAGER_H +#define LIBRARYVIEWMANAGER_H + +#include +#include + +#include "widget/wlibrary.h" +#include "widget/wsearchlineedit.h" +#include "widget/wtracktableview.h" + +class LibraryFeature; +class WButtonBar; +class WLibraryBreadCrumb; +class TreeItem; +class Library; + +class LibraryPaneManager : public QObject { + Q_OBJECT + + public: + + LibraryPaneManager(int paneId, Library* pLibrary, QObject* parent = nullptr); + + ~LibraryPaneManager(); + + bool initialize(); + + // All features must be added before adding a pane + virtual void bindPaneWidget(WBaseLibrary* pLibraryWidget, + KeyboardEventFilter* pKeyboard); + void bindSearchBar(WSearchLineEdit* pSearchBar); + void setBreadCrumb(WLibraryBreadCrumb* pBreadCrumb); + + void addFeature(LibraryFeature* feature); + void addFeatures(const QList& features); + + WBaseLibrary* getPaneWidget(); + + void setCurrentFeature(LibraryFeature* pFeature); + LibraryFeature* getCurrentFeature() const; + + void setFocus(); + void clearFocus(); + + void restoreSearch(const QString& text); + void switchToFeature(LibraryFeature* pFeature); + void showBreadCrumb(TreeItem* pTree); + + inline int getPaneId() { + return m_paneId; + } + + signals: + + void search(const QString& text); + void searchCleared(); + void searchStarting(); + + public slots: + + void slotPaneCollapsed(); + void slotPaneUncollapsed(); + void slotPaneFocused(); + + protected: + + QPointer m_pPaneWidget; + QList m_features; + QPointer m_pBreadCrumb; + QPointer m_pSearchBar; + + private: + + LibraryFeature* m_pCurrentFeature; + int m_paneId; + Library* m_pLibrary; + + private slots: + + // Used to handle focus change + bool eventFilter(QObject*, QEvent* event); +}; + +#endif // LIBRARYVIEWMANAGER_H diff --git a/src/library/librarysidebarexpandedmanager.cpp b/src/library/librarysidebarexpandedmanager.cpp new file mode 100644 index 000000000000..60da9906c79f --- /dev/null +++ b/src/library/librarysidebarexpandedmanager.cpp @@ -0,0 +1,23 @@ +#include "librarysidebarexpandedmanager.h" +#include "library/libraryfeature.h" + +LibrarySidebarExpandedManager::LibrarySidebarExpandedManager(Library *pLibrary, + QObject* parent) + : LibraryPaneManager(-1, pLibrary, parent) { + +} + +void LibrarySidebarExpandedManager::bindPaneWidget(WBaseLibrary* sidebarWidget, + KeyboardEventFilter* pKeyboard) { + m_pPaneWidget = sidebarWidget; + + for (LibraryFeature* f : m_features) { + QWidget* pPane = f->createSidebarWidget(pKeyboard); + if (pPane == nullptr) { + continue; + } + pPane->setParent(sidebarWidget); + sidebarWidget->registerView(f, pPane); + } +} + diff --git a/src/library/librarysidebarexpandedmanager.h b/src/library/librarysidebarexpandedmanager.h new file mode 100644 index 000000000000..7a29eb9ada91 --- /dev/null +++ b/src/library/librarysidebarexpandedmanager.h @@ -0,0 +1,14 @@ +#ifndef LIBRARYSIDEBAREXPANDEDMANAGER_H +#define LIBRARYSIDEBAREXPANDEDMANAGER_H +#include "library/librarypanemanager.h" + +class LibrarySidebarExpandedManager : public LibraryPaneManager +{ + public: + LibrarySidebarExpandedManager(Library* pLibrary, QObject* parent = nullptr); + + void bindPaneWidget(WBaseLibrary* sidebarWidget, + KeyboardEventFilter* pKeyboard) override; +}; + +#endif // LIBRARYSIDEBAREXPANDEDMANAGER_H diff --git a/src/library/libraryview.h b/src/library/libraryview.h index 5ae34aee2fe6..5eab3c40ec49 100644 --- a/src/library/libraryview.h +++ b/src/library/libraryview.h @@ -11,7 +11,7 @@ class LibraryView { public: - virtual ~LibraryView() {}; + virtual ~LibraryView() {} virtual void onShow() = 0; // reimplement if LibraryView should be able to search @@ -19,10 +19,10 @@ class LibraryView { // If applicable, requests that the LibraryView load the selected // track. Does nothing otherwise. - virtual void loadSelectedTrack() {}; + virtual void loadSelectedTrack() {} - virtual void slotSendToAutoDJ() {}; - virtual void slotSendToAutoDJTop() {}; + virtual void slotSendToAutoDJ() {} + virtual void slotSendToAutoDJTop() {} // If applicable, requests that the LibraryView load the selected track to // the specified group. Does nothing otherwise. diff --git a/src/library/mixxxlibraryfeature.cpp b/src/library/mixxxlibraryfeature.cpp index 60271e3a38e0..f485bcafad17 100644 --- a/src/library/mixxxlibraryfeature.cpp +++ b/src/library/mixxxlibraryfeature.cpp @@ -13,24 +13,34 @@ #include "library/hiddentablemodel.h" #include "library/queryutil.h" #include "library/trackcollection.h" +#include "library/dlghidden.h" +#include "library/dlgmissing.h" #include "treeitem.h" #include "sources/soundsourceproxy.h" #include "widget/wlibrary.h" +#include "widget/wlibrarystack.h" +#include "widget/wlibrarysidebar.h" #include "util/dnd.h" -#include "library/dlghidden.h" -#include "library/dlgmissing.h" -MixxxLibraryFeature::MixxxLibraryFeature(Library* pLibrary, - TrackCollection* pTrackCollection, - UserSettingsPointer pConfig) - : LibraryFeature(pLibrary), - kMissingTitle(tr("Missing Tracks")), +MixxxLibraryFeature::MixxxLibraryFeature(UserSettingsPointer pConfig, + Library* pLibrary, + QObject* parent, + TrackCollection* pTrackCollection) + : LibraryFeature(pConfig, pLibrary, pTrackCollection, parent), + kLibraryTitle(tr("Library")), kHiddenTitle(tr("Hidden Tracks")), - m_pLibrary(pLibrary), - m_pMissingView(NULL), - m_pHiddenView(NULL), + kMissingTitle(tr("Missing Tracks")), + m_pHiddenView(nullptr), + m_pMissingView(nullptr), + m_idExpandedHidden(-1), + m_idExpandedMissing(-1), + m_idExpandedControls(-1), + m_idExpandedTree(-1), + m_pHiddenTableModel(nullptr), + m_pMissingTableModel(nullptr), + m_pExpandedStack(nullptr), + m_pSidebarTab(nullptr), m_trackDao(pTrackCollection->getTrackDAO()), - m_pConfig(pConfig), m_pTrackCollection(pTrackCollection) { QStringList columns; columns << "library." + LIBRARYTABLE_ID @@ -109,10 +119,16 @@ MixxxLibraryFeature::MixxxLibraryFeature(Library* pLibrary, m_pLibraryTableModel = new LibraryTableModel(this, pTrackCollection, "mixxx.db.model.library"); TreeItem* pRootItem = new TreeItem(); + pRootItem->setLibraryFeature(this); + + TreeItem* pLibraryChildItem = new TreeItem(kLibraryTitle, kLibraryTitle, + this, pRootItem); + pLibraryChildItem->setIcon(getIcon()); TreeItem* pmissingChildItem = new TreeItem(kMissingTitle, kMissingTitle, this, pRootItem); TreeItem* phiddenChildItem = new TreeItem(kHiddenTitle, kHiddenTitle, this, pRootItem); + pRootItem->appendChild(pLibraryChildItem); pRootItem->appendChild(pmissingChildItem); pRootItem->appendChild(phiddenChildItem); @@ -123,23 +139,53 @@ MixxxLibraryFeature::~MixxxLibraryFeature() { delete m_pLibraryTableModel; } -void MixxxLibraryFeature::bindWidget(WLibrary* pLibraryWidget, - KeyboardEventFilter* pKeyboard) { - m_pHiddenView = new DlgHidden(pLibraryWidget, m_pConfig, m_pLibrary, - m_pTrackCollection, pKeyboard); - pLibraryWidget->registerView(kHiddenTitle, m_pHiddenView); - connect(m_pHiddenView, SIGNAL(trackSelected(TrackPointer)), - this, SIGNAL(trackSelected(TrackPointer))); +QWidget *MixxxLibraryFeature::createPaneWidget(KeyboardEventFilter* pKeyboard, + int paneId) { + WTrackTableView* pTable = LibraryFeature::createTableWidget(pKeyboard, paneId); + + connect(this, SIGNAL(unhideHidden()), pTable, SLOT(slotUnhide())); + connect(this, SIGNAL(purgeHidden()), pTable, SLOT(slotPurge())); + connect(this, SIGNAL(purgeMissing()), pTable, SLOT(slotPurge())); + + return pTable; +} + +QWidget* MixxxLibraryFeature::createInnerSidebarWidget(KeyboardEventFilter* pKeyboard) { + m_pSidebarTab = new QTabWidget(nullptr); + + // Create tree + WLibrarySidebar* pSidebar = createLibrarySidebarWidget(pKeyboard); + m_idExpandedTree = m_pSidebarTab->addTab(pSidebar, tr("Tree")); + + // Create tabs + m_pExpandedStack = new QStackedWidget(m_pSidebarTab); + + // Create Hidden View controls + m_pHiddenView = new DlgHidden(pSidebar); + m_pHiddenView->setTableModel(getHiddenTableModel()); + m_pHiddenView->installEventFilter(pKeyboard); + + connect(m_pHiddenView, SIGNAL(unhide()), this, SIGNAL(unhideHidden())); + connect(m_pHiddenView, SIGNAL(purge()), this, SIGNAL(purgeHidden())); + connect(m_pHiddenView, SIGNAL(selectAll()), this, SLOT(selectAll())); + m_idExpandedHidden = m_pExpandedStack->addWidget(m_pHiddenView); - m_pMissingView = new DlgMissing(pLibraryWidget, m_pConfig, m_pLibrary, - m_pTrackCollection, pKeyboard); - pLibraryWidget->registerView(kMissingTitle, m_pMissingView); - connect(m_pMissingView, SIGNAL(trackSelected(TrackPointer)), - this, SIGNAL(trackSelected(TrackPointer))); + // Create Missing View controls + m_pMissingView = new DlgMissing(pSidebar); + m_pMissingView->setTableModel(getMissingTableModel()); + m_pMissingView->installEventFilter(pKeyboard); + + connect(m_pMissingView, SIGNAL(purge()), this, SIGNAL(purgeMissing())); + connect(m_pMissingView, SIGNAL(selectAll()), this, SLOT(selectAll())); + m_idExpandedMissing = m_pExpandedStack->addWidget(m_pMissingView); + + m_idExpandedControls = m_pSidebarTab->addTab(m_pExpandedStack, tr("Controls")); + + return m_pSidebarTab; } QVariant MixxxLibraryFeature::title() { - return tr("Library"); + return kLibraryTitle; } QIcon MixxxLibraryFeature::getIcon() { @@ -154,23 +200,117 @@ void MixxxLibraryFeature::refreshLibraryModels() { if (m_pLibraryTableModel) { m_pLibraryTableModel->select(); } - if (m_pMissingView) { + if (!m_pMissingView.isNull()) { m_pMissingView->onShow(); } - if (m_pHiddenView) { + if (!m_pHiddenView.isNull()) { m_pHiddenView->onShow(); } } +void MixxxLibraryFeature::selectionChanged(const QItemSelection&, + const QItemSelection&) { + WTrackTableView* pTable = getFocusedTable(); + if (pTable == nullptr) { + return; + } + + auto it = m_idPaneCurrent.find(m_featureFocus); + if (it == m_idPaneCurrent.end()) { + return; + } + + const QModelIndexList& selection = pTable->selectionModel()->selectedIndexes(); + switch (*it) { + case Panes::Hidden: + m_pHiddenView->setSelectedIndexes(selection); + break; + case Panes::Missing: + m_pMissingView->setSelectedIndexes(selection); + break; + default: + break; + } +} + +void MixxxLibraryFeature::selectAll() { + QPointer pTable = getFocusedTable(); + if (!pTable.isNull()) { + pTable->selectAll(); + } +} + + void MixxxLibraryFeature::activate() { - qDebug() << "MixxxLibraryFeature::activate()"; - emit(showTrackModel(m_pLibraryTableModel)); + //qDebug() << "MixxxLibraryFeature::activate()"; + + m_idPaneCurrent[m_featureFocus] = Panes::MixxxLibrary; + QPointer pTable = getFocusedTable(); + if (pTable.isNull()) { + return; + } + + m_pSidebarTab->setTabEnabled(m_idExpandedControls, false); + pTable->setSortingEnabled(true); + showTrackModel(m_pLibraryTableModel); + m_pLibrary->restoreSearch(""); + m_pLibrary->showBreadCrumb(m_childModel.getItem(QModelIndex())); + emit(enableCoverArtDisplay(true)); } void MixxxLibraryFeature::activateChild(const QModelIndex& index) { - QString itemName = index.data().toString(); - emit(switchToView(itemName)); + QString itemName = index.data(TreeItemModel::kDataPathRole).toString(); + TreeItem* pTree = static_cast (index.internalPointer()); + QPointer pTable = getFocusedTable(); + + if (itemName == kLibraryTitle) { + activate(); + return; + } + + DEBUG_ASSERT_AND_HANDLE(!m_pExpandedStack.isNull() && !pTable.isNull()) { + return; + } + pTable->setSortingEnabled(false); + + if (itemName == kHiddenTitle) { + DEBUG_ASSERT_AND_HANDLE(!m_pHiddenView.isNull()) { + return; + } + + m_idPaneCurrent[m_featureFocus] = Panes::Hidden; + pTable->loadTrackModel(getHiddenTableModel()); + + m_pHiddenView->onShow(); + m_pExpandedStack->setCurrentIndex(m_idExpandedHidden); + + } else if (itemName == kMissingTitle) { + DEBUG_ASSERT_AND_HANDLE(!m_pMissingView.isNull()) { + return; + } + + m_idPaneCurrent[m_featureFocus] = Panes::Missing; + pTable->loadTrackModel(getMissingTableModel()); + + m_pMissingView->onShow(); + m_pExpandedStack->setCurrentIndex(m_idExpandedMissing); + } else { + return; + } + + // This is the only way to get the selection signal changing the track + // models, every time the model changes the selection model changes too + // so we need to reconnect + connect(pTable->selectionModel(), + SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), + this, + SLOT(selectionChanged(const QItemSelection&, const QItemSelection&))); + + m_pSidebarTab->setTabEnabled(m_idExpandedControls, true); + switchToFeature(); + m_pLibrary->restoreSearch(""); + m_pLibrary->showBreadCrumb(pTree); emit(enableCoverArtDisplay(true)); } @@ -190,3 +330,17 @@ bool MixxxLibraryFeature::dragMoveAccept(QUrl url) { return SoundSourceProxy::isUrlSupported(url) || Parser::isPlaylistFilenameSupported(url.toLocalFile()); } + +HiddenTableModel* MixxxLibraryFeature::getHiddenTableModel() { + if (m_pHiddenTableModel.isNull()) { + m_pHiddenTableModel = new HiddenTableModel(this, m_pTrackCollection); + } + return m_pHiddenTableModel; +} + +MissingTableModel* MixxxLibraryFeature::getMissingTableModel() { + if (m_pMissingTableModel.isNull()) { + m_pMissingTableModel = new MissingTableModel(this, m_pTrackCollection); + } + return m_pMissingTableModel; +} diff --git a/src/library/mixxxlibraryfeature.h b/src/library/mixxxlibraryfeature.h index 54d0a14f51e8..460e8b5a0b6f 100644 --- a/src/library/mixxxlibraryfeature.h +++ b/src/library/mixxxlibraryfeature.h @@ -10,9 +10,10 @@ #include #include #include -#include #include -#include +#include +#include +#include #include "library/libraryfeature.h" #include "library/dao/trackdao.h" @@ -25,39 +26,78 @@ class Library; class BaseTrackCache; class LibraryTableModel; class TrackCollection; +class WTrackTableView; +class HiddenTableModel; +class MissingTableModel; class MixxxLibraryFeature : public LibraryFeature { Q_OBJECT - public: - MixxxLibraryFeature(Library* pLibrary, - TrackCollection* pTrackCollection, - UserSettingsPointer pConfig); + + public: + MixxxLibraryFeature(UserSettingsPointer pConfig, + Library* pLibrary, + QObject* parent, + TrackCollection* pTrackCollection); virtual ~MixxxLibraryFeature(); QVariant title(); QIcon getIcon(); + bool dropAccept(QList urls, QObject* pSource); bool dragMoveAccept(QUrl url); TreeItemModel* getChildModel(); - void bindWidget(WLibrary* pLibrary, - KeyboardEventFilter* pKeyboard); + + QWidget* createPaneWidget(KeyboardEventFilter*pKeyboard, int paneId) override; + QWidget* createInnerSidebarWidget(KeyboardEventFilter* pKeyboard) override; public slots: void activate(); void activateChild(const QModelIndex& index); void refreshLibraryModels(); + + void selectionChanged(const QItemSelection&, const QItemSelection&); + + void selectAll(); + + + signals: + void unhideHidden(); + void purgeHidden(); + void purgeMissing(); private: - const QString kMissingTitle; + enum Panes { + MixxxLibrary = 1, + Hidden = 2, + Missing = 3 + }; + + HiddenTableModel* getHiddenTableModel(); + MissingTableModel* getMissingTableModel(); + + const QString kLibraryTitle; const QString kHiddenTitle; - Library* m_pLibrary; + const QString kMissingTitle; + QPointer m_pHiddenView; + QPointer m_pMissingView; + QHash m_idPaneCurrent; + + // SidebarExpanded pane's ids + int m_idExpandedHidden; + int m_idExpandedMissing; + int m_idExpandedControls; + int m_idExpandedTree; + + QPointer m_pHiddenTableModel; + QPointer m_pMissingTableModel; + + QPointer m_pExpandedStack; + QPointer m_pSidebarTab; + QSharedPointer m_pBaseTrackCache; LibraryTableModel* m_pLibraryTableModel; - DlgMissing* m_pMissingView; - DlgHidden* m_pHiddenView; TreeItemModel m_childModel; TrackDAO& m_trackDao; - UserSettingsPointer m_pConfig; TrackCollection* m_pTrackCollection; }; diff --git a/src/library/playlistfeature.cpp b/src/library/playlistfeature.cpp index fff0ed5bfbb9..2d99850a8e4c 100644 --- a/src/library/playlistfeature.cpp +++ b/src/library/playlistfeature.cpp @@ -18,14 +18,11 @@ #include "util/dnd.h" #include "util/duration.h" -PlaylistFeature::PlaylistFeature(QObject* parent, - TrackCollection* pTrackCollection, - UserSettingsPointer pConfig) - : BasePlaylistFeature(parent, pConfig, pTrackCollection, - "PLAYLISTHOME") { - m_pPlaylistTableModel = new PlaylistTableModel(this, pTrackCollection, - "mixxx.db.model.playlist"); - +PlaylistFeature::PlaylistFeature(UserSettingsPointer pConfig, + Library* pLibrary, + QObject* parent, + TrackCollection* pTrackCollection) + : BasePlaylistFeature(pConfig, pLibrary, parent, pTrackCollection) { //construct child model TreeItem *rootItem = new TreeItem(); m_childModel.setRootItem(rootItem); @@ -47,7 +44,7 @@ void PlaylistFeature::onRightClick(const QPoint& globalPos) { m_lastRightClickedIndex = QModelIndex(); //Create the right-click menu - QMenu menu(NULL); + QMenu menu(nullptr); menu.addAction(m_pCreatePlaylistAction); menu.addSeparator(); menu.addAction(m_pCreateImportPlaylistAction); @@ -85,6 +82,13 @@ void PlaylistFeature::onRightClickChild(const QPoint& globalPos, QModelIndex ind menu.exec(globalPos); } + +bool PlaylistFeature::dragMoveAccept(QUrl url) { + return SoundSourceProxy::isUrlSupported(url) || + Parser::isPlaylistFilenameSupported(url.toLocalFile()); +} + + bool PlaylistFeature::dropAcceptChild(const QModelIndex& index, QList urls, QObject* pSource) { int playlistId = playlistIdFromIndex(index); @@ -182,6 +186,10 @@ void PlaylistFeature::decorateChild(TreeItem* item, int playlist_id) { } } +PlaylistTableModel* PlaylistFeature::constructTableModel() { + return new PlaylistTableModel(this, m_pTrackCollection, "mixxx.db.model.playlist"); +} + void PlaylistFeature::slotPlaylistTableChanged(int playlistId) { if (!m_pPlaylistTableModel) { return; diff --git a/src/library/playlistfeature.h b/src/library/playlistfeature.h index 0d129eff74b3..a049d9c0987b 100644 --- a/src/library/playlistfeature.h +++ b/src/library/playlistfeature.h @@ -20,13 +20,16 @@ class TreeItem; class PlaylistFeature : public BasePlaylistFeature { Q_OBJECT public: - PlaylistFeature(QObject* parent, TrackCollection* pTrackCollection, - UserSettingsPointer pConfig); + PlaylistFeature(UserSettingsPointer pConfig, + Library* pLibrary, + QObject* parent, + TrackCollection* pTrackCollection); virtual ~PlaylistFeature(); QVariant title(); QIcon getIcon(); + bool dragMoveAccept(QUrl url); bool dropAcceptChild(const QModelIndex& index, QList urls, QObject* pSource); bool dragMoveAcceptChild(const QModelIndex& index, QUrl url); @@ -42,6 +45,7 @@ class PlaylistFeature : public BasePlaylistFeature { protected: void buildPlaylistList(); void decorateChild(TreeItem *pChild, int playlist_id); + PlaylistTableModel* constructTableModel(); private: QString getRootViewHtml() const; diff --git a/src/library/recording/dlgrecording.cpp b/src/library/recording/dlgrecording.cpp index 019a4c32b186..77c22d42d945 100644 --- a/src/library/recording/dlgrecording.cpp +++ b/src/library/recording/dlgrecording.cpp @@ -8,29 +8,16 @@ #include "widget/wtracktableview.h" #include "util/assert.h" -DlgRecording::DlgRecording(QWidget* parent, UserSettingsPointer pConfig, - Library* pLibrary, TrackCollection* pTrackCollection, - RecordingManager* pRecordingManager, KeyboardEventFilter* pKeyboard) - : QWidget(parent), - m_pConfig(pConfig), +DlgRecording::DlgRecording(QWidget* parent, TrackCollection* pTrackCollection, + RecordingManager* pRecordingManager) + : QFrame(parent), m_pTrackCollection(pTrackCollection), - m_browseModel(this, m_pTrackCollection, pRecordingManager), - m_proxyModel(&m_browseModel), + m_pBrowseModel(nullptr), + m_pProxyModel(nullptr), m_bytesRecordedStr("--"), m_durationRecordedStr("--:--"), m_pRecordingManager(pRecordingManager) { setupUi(this); - m_pTrackTableView = new WTrackTableView(this, pConfig, m_pTrackCollection, false); // No sorting - m_pTrackTableView->installEventFilter(pKeyboard); - - connect(m_pTrackTableView, SIGNAL(loadTrack(TrackPointer)), - this, SIGNAL(loadTrack(TrackPointer))); - connect(m_pTrackTableView, SIGNAL(loadTrackToPlayer(TrackPointer, QString, bool)), - this, SIGNAL(loadTrackToPlayer(TrackPointer, QString, bool))); - connect(pLibrary, SIGNAL(setTrackTableFont(QFont)), - m_pTrackTableView, SLOT(setTrackTableFont(QFont))); - connect(pLibrary, SIGNAL(setTrackTableRowHeight(int)), - m_pTrackTableView, SLOT(setTrackTableRowHeight(int))); connect(m_pRecordingManager, SIGNAL(isRecording(bool)), this, SLOT(slotRecordingEnabled(bool))); @@ -39,22 +26,8 @@ DlgRecording::DlgRecording(QWidget* parent, UserSettingsPointer pConfig, connect(m_pRecordingManager, SIGNAL(durationRecorded(QString)), this, SLOT(slotDurationRecorded(QString))); - QBoxLayout* box = dynamic_cast(layout()); - DEBUG_ASSERT_AND_HANDLE(box) { //Assumes the form layout is a QVBox/QHBoxLayout! - } else { - box->removeWidget(m_pTrackTablePlaceholder); - m_pTrackTablePlaceholder->hide(); - box->insertWidget(1, m_pTrackTableView); - } - m_recordingDir = m_pRecordingManager->getRecordingDir(); - m_proxyModel.setFilterCaseSensitivity(Qt::CaseInsensitive); - m_proxyModel.setSortCaseSensitivity(Qt::CaseInsensitive); - - m_browseModel.setPath(m_recordingDir); - m_pTrackTableView->loadTrackModel(&m_proxyModel); - connect(pushButtonRecording, SIGNAL(toggled(bool)), this, SLOT(toggleRecording(bool))); label->setText(""); @@ -66,39 +39,23 @@ DlgRecording::~DlgRecording() { void DlgRecording::onShow() { m_recordingDir = m_pRecordingManager->getRecordingDir(); - m_browseModel.setPath(m_recordingDir); -} - -void DlgRecording::refreshBrowseModel() { - m_browseModel.setPath(m_recordingDir); -} - -void DlgRecording::onSearch(const QString& text) { - m_proxyModel.search(text); + m_pBrowseModel->setPath(m_recordingDir); } -void DlgRecording::slotRestoreSearch() { - emit(restoreSearch(currentSearch())); +void DlgRecording::setProxyTrackModel(ProxyTrackModel* pProxyModel) { + m_pProxyModel = pProxyModel; + + m_pProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + m_pProxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); } -void DlgRecording::loadSelectedTrack() { - m_pTrackTableView->loadSelectedTrack(); +void DlgRecording::setBrowseTableModel(BrowseTableModel* pBrowseModel) { + m_pBrowseModel = pBrowseModel; + m_pBrowseModel->setPath(m_recordingDir); } -void DlgRecording::slotSendToAutoDJ() { - m_pTrackTableView->slotSendToAutoDJ(); -} - -void DlgRecording::slotSendToAutoDJTop() { - m_pTrackTableView->slotSendToAutoDJTop(); -} - -void DlgRecording::loadSelectedTrackToGroup(QString group, bool play) { - m_pTrackTableView->loadSelectedTrackToGroup(group, play); -} - -void DlgRecording::moveSelection(int delta) { - m_pTrackTableView->moveSelection(delta); +void DlgRecording::refreshBrowseModel() { + m_pBrowseModel->setPath(m_recordingDir); } void DlgRecording::toggleRecording(bool toggle) { @@ -125,7 +82,7 @@ void DlgRecording::slotRecordingEnabled(bool isRecording) { label->setEnabled(false); } //This will update the recorded track table view - m_browseModel.setPath(m_recordingDir); + m_pBrowseModel->setPath(m_recordingDir); } // gets number of recorded bytes and update label @@ -149,11 +106,3 @@ void DlgRecording::refreshLabel() { .arg(m_durationRecordedStr); label->setText(text); } - -void DlgRecording::setTrackTableFont(const QFont& font) { - m_pTrackTableView->setTrackTableFont(font); -} - -void DlgRecording::setTrackTableRowHeight(int rowHeight) { - m_pTrackTableView->setTrackTableRowHeight(rowHeight); -} diff --git a/src/library/recording/dlgrecording.h b/src/library/recording/dlgrecording.h index 0f8db1f42ade..cd9c1c05d78c 100644 --- a/src/library/recording/dlgrecording.h +++ b/src/library/recording/dlgrecording.h @@ -16,47 +16,32 @@ class PlaylistTableModel; class QSqlTableModel; class WTrackTableView; -class DlgRecording : public QWidget, public Ui::DlgRecording, public virtual LibraryView { +class DlgRecording : public QFrame, public Ui::DlgRecording { Q_OBJECT public: - DlgRecording(QWidget *parent, UserSettingsPointer pConfig, - Library* pLibrary, TrackCollection* pTrackCollection, - RecordingManager* pRecManager, KeyboardEventFilter* pKeyboard); + DlgRecording(QWidget *parent, TrackCollection* pTrackCollection, + RecordingManager* pRecManager); virtual ~DlgRecording(); - virtual void onSearch(const QString& text); virtual void onShow(); - virtual void loadSelectedTrack(); - virtual void slotSendToAutoDJ(); - virtual void slotSendToAutoDJTop(); - virtual void loadSelectedTrackToGroup(QString group, bool play); - virtual void moveSelection(int delta); - inline const QString currentSearch() { return m_proxyModel.currentSearch(); } + void setProxyTrackModel(ProxyTrackModel* pProxyModel); + void setBrowseTableModel(BrowseTableModel* pBrowseModel); public slots: void toggleRecording(bool toggle); void slotRecordingEnabled(bool); void slotBytesRecorded(long); void refreshBrowseModel(); - void slotRestoreSearch(); void slotDurationRecorded(QString durationRecorded); - void setTrackTableFont(const QFont& font); - void setTrackTableRowHeight(int rowHeight); - - signals: - void loadTrack(TrackPointer tio); - void loadTrackToPlayer(TrackPointer tio, QString group, bool play); - void restoreSearch(QString search); - + private: - UserSettingsPointer m_pConfig; + void refreshLabel(); + TrackCollection* m_pTrackCollection; - WTrackTableView* m_pTrackTableView; - BrowseTableModel m_browseModel; - ProxyTrackModel m_proxyModel; + BrowseTableModel* m_pBrowseModel; + ProxyTrackModel* m_pProxyModel; QString m_recordingDir; - void refreshLabel(); QString m_bytesRecordedStr; QString m_durationRecordedStr; diff --git a/src/library/recording/dlgrecording.ui b/src/library/recording/dlgrecording.ui index b94205936376..895adc9ac486 100644 --- a/src/library/recording/dlgrecording.ui +++ b/src/library/recording/dlgrecording.ui @@ -6,85 +6,47 @@ 0 0 - 582 - 399 + 391 + 322 Recordings - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - 0 + + + Start Recording - - 0 - - - 0 - - - 0 - - - 0 + + true - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Status: - - - - - - - Start Recording - - - true - - - - + - - + + + Status: + + true + + + + Qt::Vertical + + + + 20 + 247 + + + + diff --git a/src/library/recording/recordingfeature.cpp b/src/library/recording/recordingfeature.cpp index e3a3c0d8c3aa..7b3b369d3939 100644 --- a/src/library/recording/recordingfeature.cpp +++ b/src/library/recording/recordingfeature.cpp @@ -10,17 +10,21 @@ #include "widget/wlibrary.h" #include "controllers/keyboard/keyboardeventfilter.h" -const QString RecordingFeature::m_sRecordingViewName = QString("Recording"); - -RecordingFeature::RecordingFeature(Library* pLibrary, - UserSettingsPointer pConfig, +RecordingFeature::RecordingFeature(UserSettingsPointer pConfig, + Library* pLibrary, + QObject* parent, TrackCollection* pTrackCollection, RecordingManager* pRecordingManager) - : LibraryFeature(pLibrary), - m_pConfig(pConfig), - m_pLibrary(pLibrary), + : LibraryFeature(pConfig, pLibrary, pTrackCollection, parent), m_pTrackCollection(pTrackCollection), - m_pRecordingManager(pRecordingManager) { + m_pRecordingManager(pRecordingManager), + m_pRecordingView(nullptr), + m_pBrowseModel(nullptr), + m_pProxyModel(nullptr) { + + TreeItem* pRoot = new TreeItem(); + pRoot->setLibraryFeature(this); + m_childModel.setRootItem(pRoot); } RecordingFeature::~RecordingFeature() { @@ -38,35 +42,60 @@ QIcon RecordingFeature::getIcon() { TreeItemModel* RecordingFeature::getChildModel() { return &m_childModel; } -void RecordingFeature::bindWidget(WLibrary* pLibraryWidget, - KeyboardEventFilter *keyboard) { - //The view will be deleted by LibraryWidget - DlgRecording* pRecordingView = new DlgRecording(pLibraryWidget, - m_pConfig, - m_pLibrary, - m_pTrackCollection, - m_pRecordingManager, - keyboard); - pRecordingView->installEventFilter(keyboard); - pLibraryWidget->registerView(m_sRecordingViewName, pRecordingView); - connect(pRecordingView, SIGNAL(loadTrack(TrackPointer)), - this, SIGNAL(loadTrack(TrackPointer))); - connect(pRecordingView, SIGNAL(loadTrackToPlayer(TrackPointer, QString, bool)), - this, SIGNAL(loadTrackToPlayer(TrackPointer, QString, bool))); - connect(this, SIGNAL(refreshBrowseModel()), - pRecordingView, SLOT(refreshBrowseModel())); - connect(this, SIGNAL(requestRestoreSearch()), - pRecordingView, SLOT(slotRestoreSearch())); - connect(pRecordingView, SIGNAL(restoreSearch(QString)), - this, SIGNAL(restoreSearch(QString))); +QWidget* RecordingFeature::createPaneWidget(KeyboardEventFilter* pKeyboard, int) { + WTrackTableView* pTrackTableView = new WTrackTableView(nullptr, + m_pConfig, + m_pTrackCollection, + false); // No sorting + pTrackTableView->installEventFilter(pKeyboard); + + connect(m_pLibrary, SIGNAL(setTrackTableFont(QFont)), + pTrackTableView, SLOT(setTrackTableFont(QFont))); + connect(m_pLibrary, SIGNAL(setTrackTableRowHeight(int)), + pTrackTableView, SLOT(setTrackTableRowHeight(int))); + pTrackTableView->loadTrackModel(getProxyTrackModel()); + + return pTrackTableView; +} + +QWidget *RecordingFeature::createInnerSidebarWidget(KeyboardEventFilter* pKeyboard) { + m_pRecordingView = new DlgRecording(nullptr, + m_pTrackCollection, + m_pRecordingManager); + m_pRecordingView->installEventFilter(pKeyboard); + m_pRecordingView->setBrowseTableModel(getBrowseTableModel()); + m_pRecordingView->setProxyTrackModel(getProxyTrackModel()); + + return m_pRecordingView; } void RecordingFeature::activate() { - emit(refreshBrowseModel()); - emit(switchToView(m_sRecordingViewName)); - // Ask the view to emit a restoreSearch signal. - emit(requestRestoreSearch()); + DEBUG_ASSERT_AND_HANDLE(!m_pRecordingView.isNull()) { + return; + } + + m_pRecordingView->refreshBrowseModel(); + m_pLibrary->switchToFeature(this); + m_pLibrary->showBreadCrumb(m_childModel.getItem(QModelIndex())); + m_pLibrary->restoreSearch(""); + emit(enableCoverArtDisplay(false)); } + +BrowseTableModel* RecordingFeature::getBrowseTableModel() { + if (m_pBrowseModel.isNull()) { + m_pBrowseModel = new BrowseTableModel(this, m_pTrackCollection, m_pRecordingManager); + } + + return m_pBrowseModel; +} + +ProxyTrackModel* RecordingFeature::getProxyTrackModel() { + if (m_pProxyModel.isNull()) { + m_pProxyModel = new ProxyTrackModel(getBrowseTableModel()); + } + + return m_pProxyModel; +} diff --git a/src/library/recording/recordingfeature.h b/src/library/recording/recordingfeature.h index 7ff7e43ac469..01e241092a35 100644 --- a/src/library/recording/recordingfeature.h +++ b/src/library/recording/recordingfeature.h @@ -14,14 +14,16 @@ #include "library/proxytrackmodel.h" #include "recording/recordingmanager.h" -class Library; class TrackCollection; +class WTrackTableView; +class DlgRecording; class RecordingFeature : public LibraryFeature { Q_OBJECT public: - RecordingFeature(Library* parent, - UserSettingsPointer pConfig, + RecordingFeature(UserSettingsPointer pConfig, + Library* pLibrary, + QObject* parent, TrackCollection* pTrackCollection, RecordingManager* pRecordingManager); virtual ~RecordingFeature(); @@ -29,8 +31,8 @@ class RecordingFeature : public LibraryFeature { QVariant title(); QIcon getIcon(); - void bindWidget(WLibrary* libraryWidget, - KeyboardEventFilter* keyboard); + QWidget* createPaneWidget(KeyboardEventFilter *pKeyboard, int) override; + QWidget* createInnerSidebarWidget(KeyboardEventFilter* pKeyboard) override; TreeItemModel* getChildModel(); @@ -39,16 +41,19 @@ class RecordingFeature : public LibraryFeature { signals: void setRootIndex(const QModelIndex&); - void requestRestoreSearch(); - void refreshBrowseModel(); private: - UserSettingsPointer m_pConfig; - Library* m_pLibrary; + + BrowseTableModel* getBrowseTableModel(); + ProxyTrackModel* getProxyTrackModel(); + TrackCollection* m_pTrackCollection; FolderTreeModel m_childModel; - const static QString m_sRecordingViewName; RecordingManager* m_pRecordingManager; + + QPointer m_pRecordingView; + QPointer m_pBrowseModel; + QPointer m_pProxyModel; }; #endif diff --git a/src/library/rhythmbox/rhythmboxfeature.cpp b/src/library/rhythmbox/rhythmboxfeature.cpp index 9d9f7d331d2c..25f7f4ff6263 100644 --- a/src/library/rhythmbox/rhythmboxfeature.cpp +++ b/src/library/rhythmbox/rhythmboxfeature.cpp @@ -7,11 +7,15 @@ #include "library/baseexternaltrackmodel.h" #include "library/baseexternalplaylistmodel.h" -#include "library/treeitem.h" +#include "library/library.h" #include "library/queryutil.h" +#include "library/treeitem.h" -RhythmboxFeature::RhythmboxFeature(QObject* parent, TrackCollection* pTrackCollection) - : BaseExternalLibraryFeature(parent, pTrackCollection), +RhythmboxFeature::RhythmboxFeature(UserSettingsPointer pConfig, + Library* pLibrary, + QObject* parent, + TrackCollection* pTrackCollection) + : BaseExternalLibraryFeature(pConfig, pLibrary, parent, pTrackCollection), m_pTrackCollection(pTrackCollection), m_cancelImport(false) { QString tableName = "rhythmbox_library"; @@ -108,7 +112,7 @@ TreeItemModel* RhythmboxFeature::getChildModel() { } void RhythmboxFeature::activate() { - qDebug() << "RhythmboxFeature::activate()"; + //qDebug() << "RhythmboxFeature::activate()"; if (!m_isActivated) { m_isActivated = true; @@ -127,8 +131,9 @@ void RhythmboxFeature::activate() { //calls a slot in the sidebar model such that 'Rhythmbox (isLoading)' is displayed. emit (featureIsLoading(this, true)); } - - emit(showTrackModel(m_pRhythmboxTrackModel)); + + showTrackModel(m_pRhythmboxTrackModel); + m_pLibrary->showBreadCrumb(m_childModel.getItem(QModelIndex())); emit(enableCoverArtDisplay(false)); } @@ -137,7 +142,9 @@ void RhythmboxFeature::activateChild(const QModelIndex& index) { QString playlist = index.data().toString(); qDebug() << "Activating " << playlist; m_pRhythmboxPlaylistModel->setPlaylist(playlist); - emit(showTrackModel(m_pRhythmboxPlaylistModel)); + + showTrackModel(m_pRhythmboxPlaylistModel); + m_pLibrary->showBreadCrumb(static_cast(index.internalPointer())); emit(enableCoverArtDisplay(false)); } @@ -149,12 +156,12 @@ TreeItem* RhythmboxFeature::importMusicCollection() { if (!db.exists()) { db.setFileName(QDir::homePath() + "/.local/share/rhythmbox/rhythmdb.xml"); if (!db.exists()) { - return NULL; + return nullptr; } } if (!db.open(QIODevice::ReadOnly | QIODevice::Text)) - return NULL; + return nullptr; //Delete all table entries of Traktor feature ScopedTransaction transaction(m_database); @@ -436,6 +443,7 @@ void RhythmboxFeature::clearTable(QString table_name) { void RhythmboxFeature::onTrackCollectionLoaded() { TreeItem* root = m_track_future.result(); + root->setLibraryFeature(this); if (root) { m_childModel.setRootItem(root); diff --git a/src/library/rhythmbox/rhythmboxfeature.h b/src/library/rhythmbox/rhythmboxfeature.h index b2649959b525..4ea8a6601bd6 100644 --- a/src/library/rhythmbox/rhythmboxfeature.h +++ b/src/library/rhythmbox/rhythmboxfeature.h @@ -21,7 +21,10 @@ class BaseExternalPlaylistModel; class RhythmboxFeature : public BaseExternalLibraryFeature { Q_OBJECT public: - RhythmboxFeature(QObject* parent, TrackCollection*); + RhythmboxFeature(UserSettingsPointer pConfig, + Library* pLibrary, + QObject* parent, + TrackCollection* pTrackCollection); virtual ~RhythmboxFeature(); static bool isSupported(); diff --git a/src/library/traktor/traktorfeature.cpp b/src/library/traktor/traktorfeature.cpp index 2cf48b224edc..3b3b697f88e7 100644 --- a/src/library/traktor/traktorfeature.cpp +++ b/src/library/traktor/traktorfeature.cpp @@ -10,6 +10,7 @@ #include "library/traktor/traktorfeature.h" +#include "library/library.h" #include "library/librarytablemodel.h" #include "library/missingtablemodel.h" #include "library/queryutil.h" @@ -50,8 +51,10 @@ bool TraktorPlaylistModel::isColumnHiddenByDefault(int column) { return BaseSqlTableModel::isColumnHiddenByDefault(column); } -TraktorFeature::TraktorFeature(QObject* parent, TrackCollection* pTrackCollection) - : BaseExternalLibraryFeature(parent, pTrackCollection), +TraktorFeature::TraktorFeature(UserSettingsPointer pConfig, + Library* pLibrary, + QObject* parent, TrackCollection* pTrackCollection) + : BaseExternalLibraryFeature(pConfig, pLibrary, parent, pTrackCollection), m_pTrackCollection(pTrackCollection), m_cancelImport(false) { QString tableName = "traktor_library"; @@ -135,7 +138,7 @@ void TraktorFeature::refreshLibraryModels() { } void TraktorFeature::activate() { - qDebug() << "TraktorFeature::activate()"; + //qDebug() << "TraktorFeature::activate()"; if (!m_isActivated) { m_isActivated = true; @@ -156,8 +159,9 @@ void TraktorFeature::activate() { //calls a slot in the sidebar model such that 'iTunes (isLoading)' is displayed. emit (featureIsLoading(this, true)); } - - emit(showTrackModel(m_pTraktorTableModel)); + + showTrackModel(m_pTraktorTableModel); + m_pLibrary->showBreadCrumb(m_childModel.getItem(QModelIndex())); emit(enableCoverArtDisplay(false)); } @@ -171,7 +175,9 @@ void TraktorFeature::activateChild(const QModelIndex& index) { if (item->isPlaylist()) { qDebug() << "Activate Traktor Playlist: " << item->dataPath().toString(); m_pTraktorPlaylistModel->setPlaylist(item->dataPath().toString()); - emit(showTrackModel(m_pTraktorPlaylistModel)); + + showTrackModel(m_pTraktorPlaylistModel); + m_pLibrary->showBreadCrumb(item); emit(enableCoverArtDisplay(false)); } } @@ -602,17 +608,19 @@ QString TraktorFeature::getTraktorMusicDatabase() { void TraktorFeature::onTrackCollectionLoaded() { TreeItem* root = m_future.result(); + root->setLibraryFeature(this); if (root) { m_childModel.setRootItem(root); // Tell the traktor track source that it should re-build its index. m_trackSource->buildIndex(); //m_pTraktorTableModel->select(); - emit(showTrackModel(m_pTraktorTableModel)); + showTrackModel(m_pTraktorTableModel); + m_pLibrary->showBreadCrumb(root); qDebug() << "Traktor library loaded successfully"; } else { QMessageBox::warning( - NULL, + nullptr, tr("Error Loading Traktor Library"), tr("There was an error loading your Traktor library. Some of " "your Traktor tracks or playlists may not have loaded.")); diff --git a/src/library/traktor/traktorfeature.h b/src/library/traktor/traktorfeature.h index edf04e426646..d210ae335eb1 100644 --- a/src/library/traktor/traktorfeature.h +++ b/src/library/traktor/traktorfeature.h @@ -38,7 +38,10 @@ class TraktorPlaylistModel : public BaseExternalPlaylistModel { class TraktorFeature : public BaseExternalLibraryFeature { Q_OBJECT public: - TraktorFeature(QObject* parent, TrackCollection*); + TraktorFeature(UserSettingsPointer pConfig, + Library* pLibrary, + QObject* parent, + TrackCollection* pTrackCollection); virtual ~TraktorFeature(); QVariant title(); diff --git a/src/library/treeitem.cpp b/src/library/treeitem.cpp index 2985373fcb92..dc4575579e0b 100644 --- a/src/library/treeitem.cpp +++ b/src/library/treeitem.cpp @@ -34,15 +34,15 @@ TreeItem::TreeItem(const QString &data, const QString &data_path, m_data = data; m_dataPath = data_path; m_parentItem = parent; - m_feature = feature; + m_pFeature = feature; m_bold = false; } TreeItem::TreeItem() { m_data = "$root"; m_dataPath = "$root"; - m_parentItem = NULL; - m_feature = NULL; + m_parentItem = nullptr; + m_pFeature = nullptr; m_bold = false; } @@ -95,7 +95,11 @@ int TreeItem::row() const { } LibraryFeature* TreeItem::getFeature() { - return m_feature; + return m_pFeature; +} + +void TreeItem::setLibraryFeature(LibraryFeature *pFeature) { + m_pFeature = pFeature; } bool TreeItem::insertChildren(QList &data, int position, int count) { diff --git a/src/library/treeitem.h b/src/library/treeitem.h index d56c38cc243d..6b7be0594a64 100644 --- a/src/library/treeitem.h +++ b/src/library/treeitem.h @@ -7,6 +7,7 @@ #include #include #include +#include #include "library/libraryfeature.h" @@ -22,7 +23,7 @@ class TreeItem { void appendChild(TreeItem *child); /** remove a child item at the given index **/ void removeChild(int index); - /** returns the tree item at position 'row' in the childlist **/ + /** returns the tree item at position 'row' in the child list **/ TreeItem *child(int row); /** returns the number of childs of this tree item **/ int childCount() const; @@ -48,6 +49,8 @@ class TreeItem { bool isFolder() const; /* Returns the Library feature object to which an item belongs to */ LibraryFeature* getFeature(); + + void setLibraryFeature(LibraryFeature* pFeature); void setBold(bool bold) { m_bold = bold; @@ -64,7 +67,7 @@ class TreeItem { QList m_childItems; QString m_dataPath; QString m_data; - LibraryFeature* m_feature; + LibraryFeature* m_pFeature; bool m_bold; TreeItem *m_parentItem; diff --git a/src/library/treeitemmodel.cpp b/src/library/treeitemmodel.cpp index cd9824c3a67e..3270c598a0c5 100644 --- a/src/library/treeitemmodel.cpp +++ b/src/library/treeitemmodel.cpp @@ -45,8 +45,10 @@ QVariant TreeItemModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); - if (role != Qt::DisplayRole && role != kDataPathRole && role != kBoldRole) + if (role != Qt::DisplayRole && role != kDataPathRole && + role != kBoldRole && role != Qt::DecorationRole) { return QVariant(); + } TreeItem *item = static_cast(index.internalPointer()); @@ -55,6 +57,8 @@ QVariant TreeItemModel::data(const QModelIndex &index, int role) const { return item->dataPath(); } else if (role == kBoldRole) { return item->isBold(); + } else if (role == Qt::DecorationRole) { + return item->getIcon(); } return item->data(); } @@ -204,3 +208,43 @@ void TreeItemModel::triggerRepaint() { QModelIndex right = index(rowCount() - 1, columnCount() - 1); emit(dataChanged(left, right)); } + +bool TreeItemModel::dropAccept(const QModelIndex& index, QList urls, + QObject* pSource) { + //qDebug() << "TreeItemModel::dropAccept() index=" << index << urls; + bool result = false; + if (index.isValid()) { + LibraryFeature* pFeature; + if (index.internalPointer() == this) { + pFeature = m_pRootItem->getFeature(); + } else { + TreeItem* treeItem = (TreeItem*) index.internalPointer(); + if (!treeItem) { + return false; + } + pFeature = treeItem->getFeature(); + } + + result = pFeature->dropAcceptChild(index, urls, pSource); + } + return result; +} + +bool TreeItemModel::dragMoveAccept(const QModelIndex& index, QUrl url) { + //qDebug() << "TreeItemModel::dragMoveAccept() index=" << index << url; + bool result = false; + if (index.isValid()) { + LibraryFeature* pFeature; + if (index.internalPointer() == this) { + pFeature = m_pRootItem->getFeature(); + } else { + TreeItem* treeItem = (TreeItem*) index.internalPointer(); + if (treeItem) { + pFeature = treeItem->getFeature(); + } + } + + result = pFeature->dragMoveAcceptChild(index, url); + } + return result; +} diff --git a/src/library/treeitemmodel.h b/src/library/treeitemmodel.h index 3aa28de27bf2..f67ba9e2014c 100644 --- a/src/library/treeitemmodel.h +++ b/src/library/treeitemmodel.h @@ -5,6 +5,7 @@ #include #include #include +#include class TreeItem; @@ -36,6 +37,9 @@ class TreeItemModel : public QAbstractItemModel { TreeItem* getItem(const QModelIndex &index) const; void triggerRepaint(); + + bool dropAccept(const QModelIndex& index, QList urls, QObject* pSource); + bool dragMoveAccept(const QModelIndex& index, QUrl url); private: TreeItem *m_pRootItem; diff --git a/src/mixer/basetrackplayer.cpp b/src/mixer/basetrackplayer.cpp index cc49de5c852d..4fbcac3b4cc4 100644 --- a/src/mixer/basetrackplayer.cpp +++ b/src/mixer/basetrackplayer.cpp @@ -242,13 +242,6 @@ void BaseTrackPlayerImpl::slotTrackLoaded(TrackPointer pNewTrack, m_pKey->set(m_pLoadedTrack->getKey()); setReplayGain(m_pLoadedTrack->getReplayGain().getRatio()); - // Clear loop - // It seems that the trick is to first clear the loop out point, and then - // the loop in point. If we first clear the loop in point, the loop out point - // does not get cleared. - m_pLoopOutPoint->set(-1); - m_pLoopInPoint->set(-1); - const QList trackCues(pNewTrack->getCuePoints()); QListIterator it(trackCues); while (it.hasNext()) { diff --git a/src/mixxx.cpp b/src/mixxx.cpp index d7511d7d4780..58713548f181 100644 --- a/src/mixxx.cpp +++ b/src/mixxx.cpp @@ -267,7 +267,7 @@ void MixxxMainWindow::initialize(QApplication* pApp, const CmdlineArgs& args) { m_pPlayerManager, m_pRecordingManager); m_pPlayerManager->bindToLibrary(m_pLibrary); - + launchProgress(35); // Get Music dir @@ -1043,6 +1043,7 @@ void MixxxMainWindow::rebootMixxxView() { // that need to be deleted -- otherwise we can't tell what features the skin // supports since the controls from the previous skin will be left over. m_pMenuBar->onNewSkinAboutToLoad(); + m_pLibrary->destroyInterface(); if (m_pWidgetParent) { m_pWidgetParent->hide(); diff --git a/src/mixxx.h b/src/mixxx.h index a1793d56b541..5ca96f3b4b08 100644 --- a/src/mixxx.h +++ b/src/mixxx.h @@ -37,6 +37,7 @@ class EngineMaster; class GuiTick; class LaunchImage; class Library; +class LibraryPaneManager; class KeyboardEventFilter; class PlayerManager; class RecordingManager; diff --git a/src/skin/legacyskinparser.cpp b/src/skin/legacyskinparser.cpp index 9fabb0cc05e0..76db23eefe63 100644 --- a/src/skin/legacyskinparser.cpp +++ b/src/skin/legacyskinparser.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -65,6 +66,8 @@ #include "widget/wsearchlineedit.h" #include "widget/wlibrary.h" #include "widget/wlibrarysidebar.h" +#include "widget/wlibrarybreadcrumb.h" +#include "widget/wbuttonbar.h" #include "widget/wskincolor.h" #include "widget/wpixmapstore.h" #include "widget/wwidgetstack.h" @@ -143,7 +146,8 @@ LegacySkinParser::LegacySkinParser() m_pVCManager(NULL), m_pEffectsManager(NULL), m_pParent(NULL), - m_pContext(NULL) { + m_pContext(NULL), + m_paneId(0) { } LegacySkinParser::LegacySkinParser(UserSettingsPointer pConfig, @@ -161,7 +165,8 @@ LegacySkinParser::LegacySkinParser(UserSettingsPointer pConfig, m_pVCManager(pVCMan), m_pEffectsManager(pEffectsManager), m_pParent(NULL), - m_pContext(NULL) { + m_pContext(NULL), + m_paneId(0) { } LegacySkinParser::~LegacySkinParser() { @@ -551,10 +556,18 @@ QList LegacySkinParser::parseNode(const QDomElement& node) { result = wrapWidget(parseLabelWidget(node)); } else if (nodeName == "Splitter") { result = wrapWidget(parseSplitter(node)); + } else if (nodeName == "LibrarySidebarButtons") { + result = wrapWidget(parseLibrarySidebarButtons(node)); } else if (nodeName == "LibrarySidebar") { result = wrapWidget(parseLibrarySidebar(node)); + } else if (nodeName == "LibrarySidebarExpanded") { + result = wrapWidget(parseLibrarySidebarExpanded(node)); + } else if (nodeName == "LibraryPane") { + result = wrapWidget(parseLibraryPane(node)); + } else if (nodeName == "LibraryBreadCrumb") { + result = wrapWidget(parseLibraryBreadCrumb(node)); } else if (nodeName == "Library") { - result = wrapWidget(parseLibrary(node)); + result = wrapWidget(parseLibrary(node)); } else if (nodeName == "Key") { result = wrapWidget(parseEngineKey(node)); } else if (nodeName == "Battery") { @@ -1135,21 +1148,20 @@ QWidget* LegacySkinParser::parseSpinny(const QDomElement& node) { } QWidget* LegacySkinParser::parseSearchBox(const QDomElement& node) { - WSearchLineEdit* pLineEditSearch = new WSearchLineEdit(m_pParent); - commonWidgetSetup(node, pLineEditSearch, false); - pLineEditSearch->setup(node, *m_pContext); - - // Connect search box signals to the library - connect(pLineEditSearch, SIGNAL(search(const QString&)), - m_pLibrary, SIGNAL(search(const QString&))); - connect(pLineEditSearch, SIGNAL(searchCleared()), - m_pLibrary, SIGNAL(searchCleared())); - connect(pLineEditSearch, SIGNAL(searchStarting()), - m_pLibrary, SIGNAL(searchStarting())); - connect(m_pLibrary, SIGNAL(restoreSearch(const QString&)), - pLineEditSearch, SLOT(restoreSearch(const QString&))); - - return pLineEditSearch; + WSearchLineEdit* pSearchLineEdit = new WSearchLineEdit(m_pParent); + + int id = -1; + if (m_pContext->hasNodeSelectInt(node, "Id", &id)) { + //qDebug() << "SearchBox ID:" << id; + m_pLibrary->bindSearchBar(pSearchLineEdit, id); + } + else { + SKIN_WARNING(node, *m_pContext) << "SearchBox Id not found"; + } + pSearchLineEdit->setup(node, *m_pContext); + commonWidgetSetup(node, pSearchLineEdit, false); + + return pSearchLineEdit; } QWidget* LegacySkinParser::parseCoverArt(const QDomElement& node) { @@ -1163,8 +1175,6 @@ QWidget* LegacySkinParser::parseCoverArt(const QDomElement& node) { // If no group was provided, hook the widget up to the Library. if (channel.isEmpty()) { // Connect cover art signals to the library - connect(m_pLibrary, SIGNAL(switchToView(const QString&)), - pCoverArt, SLOT(slotReset())); connect(m_pLibrary, SIGNAL(enableCoverArtDisplay(bool)), pCoverArt, SLOT(slotEnable(bool))); connect(m_pLibrary, SIGNAL(trackSelected(TrackPointer)), @@ -1237,31 +1247,122 @@ void LegacySkinParser::parseSingletonDefinition(const QDomElement& node) { pChild->hide(); } -QWidget* LegacySkinParser::parseLibrary(const QDomElement& node) { +QWidget* LegacySkinParser::parseLibraryPane(const QDomElement& node) { WLibrary* pLibraryWidget = new WLibrary(m_pParent); pLibraryWidget->installEventFilter(m_pKeyboard); pLibraryWidget->installEventFilter(m_pControllerManager->getControllerLearningEventFilter()); - // Connect Library search signals to the WLibrary - connect(m_pLibrary, SIGNAL(search(const QString&)), - pLibraryWidget, SLOT(search(const QString&))); - - m_pLibrary->bindWidget(pLibraryWidget, m_pKeyboard); - + int id = -1; + if (m_pContext->hasNodeSelectInt(node, "Id", &id)) { + //qDebug() << "LegacySkinParser::parseLibrary:ID" << id; + m_pLibrary->bindPaneWidget(pLibraryWidget, m_pKeyboard, id); + } + else { + SKIN_WARNING(node, *m_pContext) << "No Id found"; + } + // This must come after the bindWidget or we will not style any of the // LibraryView's because they have not been added yet. commonWidgetSetup(node, pLibraryWidget, false); - return pLibraryWidget; } -QWidget* LegacySkinParser::parseLibrarySidebar(const QDomElement& node) { - WLibrarySidebar* pLibrarySidebar = new WLibrarySidebar(m_pParent); +QWidget* LegacySkinParser::parseLibrary(const QDomElement& node) { + // Must add both a SearchBox and a LibraryPane + QFrame* pContainer = new QFrame(m_pParent); + QVBoxLayout* pLayout = new QVBoxLayout(pContainer); + pContainer->setLayout(pLayout); + + WSearchLineEdit* pSearchBox = new WSearchLineEdit(pContainer); + pSearchBox->setup(node, *m_pContext); + m_pLibrary->bindSearchBar(pSearchBox, m_paneId); + commonWidgetSetup(node, pSearchBox); + pLayout->addWidget(pSearchBox); + + WLibraryBreadCrumb* pBreadCrumb = new WLibraryBreadCrumb(pContainer); + m_pLibrary->bindBreadCrumb(pBreadCrumb, m_paneId); + setupWidget(node, pBreadCrumb); + pLayout->addWidget(pBreadCrumb); + + WLibrary* pLibraryWidget = new WLibrary(pContainer); + pLibraryWidget->installEventFilter(m_pKeyboard); + pLibraryWidget->installEventFilter(m_pControllerManager->getControllerLearningEventFilter()); + pLayout->addWidget(pLibraryWidget); + + m_pLibrary->bindPaneWidget(pLibraryWidget, m_pKeyboard, m_paneId); + commonWidgetSetup(node, pLibraryWidget, false); + qDebug() << "LegacySkinParser::parseLibrary"; + + ++m_paneId; + return pContainer; +} + + +QWidget *LegacySkinParser::parseLibrarySidebar(const QDomElement& node) { + // We must create both LibrarySidebarButtons and LibrarySidebarExpanded + // to allow support for old skins + QFrame* pContainer = new QFrame(m_pParent); + QHBoxLayout* pLayout = new QHBoxLayout(pContainer); + pContainer->setLayout(pLayout); + + QScrollArea* scroll = new QScrollArea(pContainer); + scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + scroll->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + + WButtonBar* pLibrarySidebar = new WButtonBar(pContainer); + pLibrarySidebar->installEventFilter(m_pKeyboard); + m_pLibrary->bindSidebarWidget(pLibrarySidebar); + scroll->setWidget(pLibrarySidebar); + pLayout->addWidget(scroll); + + WBaseLibrary* pLibrarySidebarExpanded = new WBaseLibrary(pContainer); + pLibrarySidebarExpanded->installEventFilter(m_pKeyboard); + pLibrarySidebarExpanded->installEventFilter(m_pControllerManager->getControllerLearningEventFilter()); + m_pLibrary->bindSidebarExpanded(pLibrarySidebarExpanded, m_pKeyboard); + pLayout->addWidget(pLibrarySidebarExpanded); + + setupWidget(node, pLibrarySidebar); + commonWidgetSetup(node, pLibrarySidebarExpanded, false); + return pContainer; +} + +QWidget* LegacySkinParser::parseLibrarySidebarButtons(const QDomElement& node) { + QScrollArea* scroll = new QScrollArea(m_pParent); + scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + scroll->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + + WButtonBar* pLibrarySidebar = new WButtonBar(scroll); pLibrarySidebar->installEventFilter(m_pKeyboard); - pLibrarySidebar->installEventFilter(m_pControllerManager->getControllerLearningEventFilter()); m_pLibrary->bindSidebarWidget(pLibrarySidebar); - commonWidgetSetup(node, pLibrarySidebar, false); - return pLibrarySidebar; + scroll->setWidget(pLibrarySidebar); + + setupWidget(node, scroll); + return scroll; +} + +QWidget *LegacySkinParser::parseLibrarySidebarExpanded(const QDomElement &node) { + WBaseLibrary* pLibrarySidebarExpanded = new WBaseLibrary(m_pParent); + pLibrarySidebarExpanded->installEventFilter(m_pKeyboard); + pLibrarySidebarExpanded->installEventFilter(m_pControllerManager->getControllerLearningEventFilter()); + m_pLibrary->bindSidebarExpanded(pLibrarySidebarExpanded, m_pKeyboard); + commonWidgetSetup(node, pLibrarySidebarExpanded, false); + return pLibrarySidebarExpanded; +} + +QWidget* LegacySkinParser::parseLibraryBreadCrumb(const QDomElement& node) { + WLibraryBreadCrumb* pLibraryBreacrumb = new WLibraryBreadCrumb(m_pParent); + + int id = -1; + if (m_pContext->hasNodeSelectInt(node, "Id", &id)) { + //qDebug() << "LegacySkinParser::parseLibrary:ID" << id; + m_pLibrary->bindBreadCrumb(pLibraryBreacrumb, id); + } + else { + SKIN_WARNING(node, *m_pContext) << "No Id found"; + } + setupWidget(node, pLibraryBreacrumb); + + return pLibraryBreacrumb; } QWidget* LegacySkinParser::parseTableView(const QDomElement& node) { @@ -1289,11 +1390,11 @@ QWidget* LegacySkinParser::parseTableView(const QDomElement& node) { QWidget* oldParent = m_pParent; m_pParent = pSplitter; - QWidget* pLibraryWidget = parseLibrary(node); + QWidget* pLibraryWidget = parseLibraryPane(node); QWidget* pLibrarySidebarPage = new QWidget(pSplitter); m_pParent = pLibrarySidebarPage; - QWidget* pLibrarySidebar = parseLibrarySidebar(node); + QWidget* pLibrarySidebar = parseLibrarySidebarButtons(node); QWidget* pLineEditSearch = parseSearchBox(node); m_pParent = oldParent; diff --git a/src/skin/legacyskinparser.h b/src/skin/legacyskinparser.h index bd7d4b2eaa51..a05ef2e5264e 100644 --- a/src/skin/legacyskinparser.h +++ b/src/skin/legacyskinparser.h @@ -15,6 +15,7 @@ class WBaseWidget; class Library; +class LibraryPaneManager; class KeyboardEventFilter; class PlayerManager; class EffectsManager; @@ -98,8 +99,12 @@ class LegacySkinParser : public QObject, public SkinParser { // Library widgets. QWidget* parseTableView(const QDomElement& node); QWidget* parseSearchBox(const QDomElement& node); - QWidget* parseLibrary(const QDomElement& node); + QWidget* parseLibraryPane(const QDomElement& node); QWidget* parseLibrarySidebar(const QDomElement& node); + QWidget* parseLibrarySidebarButtons(const QDomElement& node); + QWidget* parseLibrarySidebarExpanded(const QDomElement& node); + QWidget* parseLibraryBreadCrumb(const QDomElement& node); + QWidget* parseLibrary(const QDomElement& node); QWidget* parseBattery(const QDomElement& node); QWidget* parseCoverArt(const QDomElement& node); @@ -139,6 +144,7 @@ class LegacySkinParser : public QObject, public SkinParser { QHash m_templateCache; static QList s_channelStrs; static QMutex s_safeStringMutex; + int m_paneId; }; diff --git a/src/skin/skinloader.cpp b/src/skin/skinloader.cpp index 2cc4bb3bf1de..426a19dfe984 100644 --- a/src/skin/skinloader.cpp +++ b/src/skin/skinloader.cpp @@ -125,7 +125,7 @@ QWidget* SkinLoader::loadDefaultSkin(QWidget* pParent, } LegacySkinParser legacy(m_pConfig, pKeyboard, pPlayerManager, - pControllerManager, pLibrary, pVCMan, + pControllerManager, pLibrary, pVCMan, pEffectsManager); return legacy.parseSkin(skinPath, pParent); } diff --git a/src/skin/skinloader.h b/src/skin/skinloader.h index ac76b0c99785..0f594eb80527 100644 --- a/src/skin/skinloader.h +++ b/src/skin/skinloader.h @@ -11,6 +11,7 @@ class KeyboardEventFilter; class PlayerManager; class ControllerManager; class Library; +class LibraryPaneManager; class VinylControlManager; class EffectsManager; class LaunchImage; diff --git a/src/util/dnd.h b/src/util/dnd.h index 542b0ecdf6d0..8cab28e375c8 100644 --- a/src/util/dnd.h +++ b/src/util/dnd.h @@ -26,6 +26,7 @@ class DragAndDropHelper { bool firstOnly, bool acceptPlaylists) { QList fileLocations; + qDebug() << urls; foreach (const QUrl& url, urls) { // XXX: Possible WTF alert - Previously we thought we needed diff --git a/src/widget/wbaselibrary.cpp b/src/widget/wbaselibrary.cpp new file mode 100644 index 000000000000..92bfe51b8b94 --- /dev/null +++ b/src/widget/wbaselibrary.cpp @@ -0,0 +1,84 @@ +#include +#include +#include +#include +#include + +#include "library/libraryfeature.h" +#include "widget/wbaselibrary.h" + +WBaseLibrary::WBaseLibrary(QWidget* parent) + : QStackedWidget(parent), + WBaseWidget(this), + m_mutex(QMutex::Recursive), + m_showFocus(0) { + +} + +bool WBaseLibrary::registerView(LibraryFeature *pFeature, QWidget *view) { + QMutexLocker lock(&m_mutex); + if (m_featureMap.contains(pFeature)) { + return false; + } + + view->installEventFilter(this); + int index = addWidget(view); + setCurrentIndex(index); + m_pCurrentFeature = pFeature; + m_featureMap[pFeature] = view; + return true; +} + +LibraryFeature *WBaseLibrary::getCurrentFeature() { + return m_pCurrentFeature; +} + +int WBaseLibrary::getShowFocus() { + return m_showFocus; +} + +void WBaseLibrary::setShowFocus(int sFocus) { + //qDebug() << "WBaseLibrary::setShowFocus" << sFocus << this; + m_showFocus = sFocus; + + style()->unpolish(this); + style()->polish(this); + update(); +} + +void WBaseLibrary::switchToFeature(LibraryFeature *pFeature) { + auto it = m_featureMap.find(pFeature); + if (it != m_featureMap.end() && currentWidget() != (*it)) { + m_pCurrentFeature = pFeature; + setCurrentWidget(*it); + } +} + +bool WBaseLibrary::eventFilter(QObject*, QEvent* pEvent) { + if (pEvent->type() == QEvent::FocusIn) { + //qDebug() << "WBaseLibrary::eventFilter FocusIn"; + emit(focused()); + } + return false; +} + +bool WBaseLibrary::event(QEvent* pEvent) { + if (pEvent->type() == QEvent::ToolTip) { + updateTooltip(); + } + + return QStackedWidget::event(pEvent); +} + +void WBaseLibrary::resizeEvent(QResizeEvent *pEvent) { + + // Detect whether the library is collapsed to change the focus behaviour + if (pEvent->size().isEmpty()) { + m_isCollapsed = true; + emit(collapsed()); + } else if (m_isCollapsed) { + m_isCollapsed = false; + emit(uncollapsed()); + } +} + diff --git a/src/widget/wbaselibrary.h b/src/widget/wbaselibrary.h new file mode 100644 index 000000000000..9975aa324b7b --- /dev/null +++ b/src/widget/wbaselibrary.h @@ -0,0 +1,55 @@ +#ifndef WLIBRARYSIDEBAREXPANDED_H +#define WLIBRARYSIDEBAREXPANDED_H +#include +#include +#include + +#include "widget/wbasewidget.h" + +class LibraryFeature; + +class WBaseLibrary : public QStackedWidget, public WBaseWidget +{ + Q_OBJECT + public: + + WBaseLibrary(QWidget* parent = nullptr); + + virtual bool registerView(LibraryFeature* pFeature, QWidget* view); + + LibraryFeature* getCurrentFeature(); + + Q_PROPERTY(int showFocus READ getShowFocus WRITE setShowFocus) + + int getShowFocus(); + + // Sets the widget to the focused state, it's not the same as Qt focus + void setShowFocus(int sFocus); + + signals: + + void focused(); + void collapsed(); + void uncollapsed(); + + public slots: + + virtual void switchToFeature(LibraryFeature* pFeature); + + protected: + + bool eventFilter(QObject*, QEvent* pEvent); + bool event(QEvent* pEvent) override; + void resizeEvent(QResizeEvent* pEvent); + + QHash m_featureMap; + + private: + + LibraryFeature* m_pCurrentFeature; + QMutex m_mutex; + int m_showFocus; + bool m_isCollapsed; +}; + +#endif // WLIBRARYSIDEBAREXPANDED_H diff --git a/src/widget/wbuttonbar.cpp b/src/widget/wbuttonbar.cpp new file mode 100644 index 000000000000..24a38d9c46eb --- /dev/null +++ b/src/widget/wbuttonbar.cpp @@ -0,0 +1,24 @@ +#include + +#include "wbuttonbar.h" +#include "library/libraryfeature.h" + +WButtonBar::WButtonBar(QWidget* parent) + : QFrame(parent) { + + m_pLayout = new QVBoxLayout(this); + m_pLayout->setContentsMargins(0,0,0,0); + m_pLayout->setSpacing(0); + setLayout(m_pLayout); + setFocusPolicy(Qt::NoFocus); +} + +WFeatureClickButton* WButtonBar::addButton(LibraryFeature* pFeature) { + WFeatureClickButton* button = new WFeatureClickButton(pFeature, this); + button->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); + button->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); + + m_pLayout->addWidget(button); + return button; +} + diff --git a/src/widget/wbuttonbar.h b/src/widget/wbuttonbar.h new file mode 100644 index 000000000000..35c5908357d0 --- /dev/null +++ b/src/widget/wbuttonbar.h @@ -0,0 +1,25 @@ +#ifndef WBUTTONBAR_H +#define WBUTTONBAR_H + +#include +#include +#include +#include +#include + +#include "widget/wfeatureclickbutton.h" + +class WButtonBar : public QFrame +{ + Q_OBJECT + public: + WButtonBar(QWidget* parent = nullptr); + + WFeatureClickButton* addButton(LibraryFeature *pFeature); + + private: + + QLayout* m_pLayout; +}; + +#endif // WBUTTONBAR_H diff --git a/src/widget/wfeatureclickbutton.cpp b/src/widget/wfeatureclickbutton.cpp new file mode 100644 index 000000000000..8b94eff7f14a --- /dev/null +++ b/src/widget/wfeatureclickbutton.cpp @@ -0,0 +1,67 @@ +#include "wfeatureclickbutton.h" +#include "util/assert.h" + +const int WFeatureClickButton::kHoverTime = 250; // milliseconds + +WFeatureClickButton::WFeatureClickButton(LibraryFeature* pFeature, QWidget* parent) + : QToolButton(parent), + m_pFeature(pFeature) { + DEBUG_ASSERT_AND_HANDLE(pFeature != nullptr) { + return; + } + + setIcon(m_pFeature->getIcon()); + setText(m_pFeature->title().toString()); + + connect(this, SIGNAL(clicked()), this, SLOT(slotClicked())); + setAcceptDrops(true); +} + +void WFeatureClickButton::mousePressEvent(QMouseEvent* event) { + if (event->button() == Qt::RightButton) { + emit(rightClicked(event->globalPos())); + } + QToolButton::mousePressEvent(event); +} + +void WFeatureClickButton::dragEnterEvent(QDragEnterEvent* event) { + qDebug() << "WFeatureClickButton::dragEnterEvent" << event; + if (!event->mimeData()->hasUrls() || event->source() == this) { + event->ignore(); + return; + } + if (m_pFeature->dragMoveAccept(event->mimeData()->urls().first())) { + event->acceptProposedAction(); + m_hoverTimer.start(kHoverTime, this); + } +} + +void WFeatureClickButton::dragLeaveEvent(QDragLeaveEvent*) { + m_hoverTimer.stop(); +} + +void WFeatureClickButton::dropEvent(QDropEvent* event) { + m_hoverTimer.stop(); + event->acceptProposedAction(); + if (!event->mimeData()->hasUrls() || event->source() == this) { + event->ignore(); + return; + } + + if (m_pFeature->dropAccept(event->mimeData()->urls(), event->source())) { + event->acceptProposedAction(); + } +} + +void WFeatureClickButton::timerEvent(QTimerEvent* event) { + if (event->timerId() != m_hoverTimer.timerId()) { + QToolButton::timerEvent(event); + return; + } + emit(hoverShow(m_pFeature)); +} + +void WFeatureClickButton::slotClicked() { + emit(clicked(m_pFeature)); +} + diff --git a/src/widget/wfeatureclickbutton.h b/src/widget/wfeatureclickbutton.h new file mode 100644 index 000000000000..110e8984661b --- /dev/null +++ b/src/widget/wfeatureclickbutton.h @@ -0,0 +1,45 @@ +#ifndef WRIGHTCLICKBUTTON_H +#define WRIGHTCLICKBUTTON_H + +#include +#include +#include +#include + +class WFeatureClickButton : public QToolButton +{ + Q_OBJECT + +public: + WFeatureClickButton(LibraryFeature* pFeature = nullptr, + QWidget* parent = nullptr); + +signals: + + void clicked(LibraryFeature*); + void rightClicked(const QPoint&); + void hoverShow(LibraryFeature*); + +protected: + + void mousePressEvent(QMouseEvent* event); + + void dragEnterEvent(QDragEnterEvent* event); + void dragLeaveEvent(QDragLeaveEvent*); + void dropEvent(QDropEvent* event); + + void timerEvent(QTimerEvent* event); + +private slots: + + void slotClicked(); + +private: + + static const int kHoverTime; + + LibraryFeature* m_pFeature; + QBasicTimer m_hoverTimer; +}; + +#endif // WRIGHTCLICKBUTTON_H diff --git a/src/widget/wlibrary.cpp b/src/widget/wlibrary.cpp index bcd01d52edd9..238632a1a4da 100644 --- a/src/widget/wlibrary.cpp +++ b/src/widget/wlibrary.cpp @@ -9,44 +9,35 @@ #include "controllers/keyboard/keyboardeventfilter.h" WLibrary::WLibrary(QWidget* parent) - : QStackedWidget(parent), - WBaseWidget(this), + : WBaseLibrary(parent), m_mutex(QMutex::Recursive) { + } -bool WLibrary::registerView(QString name, QWidget* view) { +bool WLibrary::registerView(LibraryFeature *pFeature, QWidget* pView) { QMutexLocker lock(&m_mutex); - if (m_viewMap.contains(name)) { - return false; - } - if (dynamic_cast(view) == nullptr) { + if (pFeature == nullptr || dynamic_cast(pView) == nullptr) { qDebug() << "WARNING: Attempted to register a view with WLibrary " << "that does not implement the LibraryView interface. " << "Ignoring."; return false; } - addWidget(view); - m_viewMap[name] = view; - return true; + return WBaseLibrary::registerView(pFeature, pView); } -void WLibrary::switchToView(const QString& name) { +void WLibrary::switchToFeature(LibraryFeature *pFeature) { QMutexLocker lock(&m_mutex); - //qDebug() << "WLibrary::switchToView" << name; - QWidget* widget = m_viewMap.value(name, nullptr); - if (widget != nullptr) { - LibraryView * lview = dynamic_cast(widget); - if (lview == nullptr) { + auto it = m_featureMap.find(pFeature); + if (it != m_featureMap.end()) { + LibraryView* pView = dynamic_cast(*it); + if (pView == nullptr) { qDebug() << "WARNING: Attempted to register a view with WLibrary " << "that does not implement the LibraryView interface. " << "Ignoring."; return; } - if (currentWidget() != widget) { - //qDebug() << "WLibrary::setCurrentWidget" << name; - setCurrentWidget(widget); - lview->onShow(); - } + WBaseLibrary::switchToFeature(pFeature); + pView->onShow(); } } @@ -66,10 +57,3 @@ void WLibrary::search(const QString& name) { LibraryView* WLibrary::getActiveView() const { return dynamic_cast(currentWidget()); } - -bool WLibrary::event(QEvent* pEvent) { - if (pEvent->type() == QEvent::ToolTip) { - updateTooltip(); - } - return QStackedWidget::event(pEvent); -} diff --git a/src/widget/wlibrary.h b/src/widget/wlibrary.h index 6c6ccb943ff9..7115b3933e06 100644 --- a/src/widget/wlibrary.h +++ b/src/widget/wlibrary.h @@ -11,11 +11,11 @@ #include #include "library/libraryview.h" -#include "widget/wbasewidget.h" +#include "widget/wbaselibrary.h" class KeyboardEventFilter; -class WLibrary : public QStackedWidget, public WBaseWidget { +class WLibrary : public WBaseLibrary { Q_OBJECT public: explicit WLibrary(QWidget* parent); @@ -26,24 +26,20 @@ class WLibrary : public QStackedWidget, public WBaseWidget { // the view and is in charge of deleting it. Returns whether or not the // registration was successful. Registered widget must implement the // LibraryView interface. - bool registerView(QString name, QWidget* view); + bool registerView(LibraryFeature* pFeature, QWidget *pView); LibraryView* getActiveView() const; - + public slots: // Show the view registered with the given name. Does nothing if the current // view is the specified view, or if the name does not specify any // registered view. - void switchToView(const QString& name); + void switchToFeature(LibraryFeature* pFeature); void search(const QString&); - protected: - bool event(QEvent* pEvent) override; - private: QMutex m_mutex; - QMap m_viewMap; }; #endif /* WLIBRARY_H */ diff --git a/src/widget/wlibrarybreadcrumb.cpp b/src/widget/wlibrarybreadcrumb.cpp new file mode 100644 index 000000000000..d76d334b3d06 --- /dev/null +++ b/src/widget/wlibrarybreadcrumb.cpp @@ -0,0 +1,54 @@ +#include + +#include + +namespace { + +QString getPathString(TreeItem* pTree) { + // Base case + if (pTree == nullptr || pTree->getFeature() == nullptr) { + return QString(); + } + else if (pTree->parent() == nullptr) { + return pTree->getFeature()->title().toString(); + } + + // Recursive case + QString text = pTree->data().toString(); + QString next = getPathString(pTree->parent()); + return next % QLatin1Literal(" > ") % text; +} + +} // NAMESPACE + + +WLibraryBreadCrumb::WLibraryBreadCrumb(QWidget* parent) + : QLabel(parent) { + setText("I'm a BreadCrumb"); +} + +void WLibraryBreadCrumb::setText(const QString &text) { + m_longText = text; + QFontMetrics metrics(font()); + + // Measure the text for the label width + QString elidedText = metrics.elidedText(m_longText, Qt::ElideRight, width() - 10); + QLabel::setText(elidedText); +} + +QString WLibraryBreadCrumb::text() const { + return m_longText; +} + +QSize WLibraryBreadCrumb::minimumSizeHint() const { + return QSize(0, height()); +} + +void WLibraryBreadCrumb::showBreadCrumb(TreeItem *pTree) { + setText(getPathString(pTree)); +} + +void WLibraryBreadCrumb::resizeEvent(QResizeEvent *pEvent) { + QLabel::resizeEvent(pEvent); + setText(m_longText); +} diff --git a/src/widget/wlibrarybreadcrumb.h b/src/widget/wlibrarybreadcrumb.h new file mode 100644 index 000000000000..f22fb801e3e3 --- /dev/null +++ b/src/widget/wlibrarybreadcrumb.h @@ -0,0 +1,32 @@ +#ifndef SRC_WIDGET_WBREADCRUMB_H_ +#define SRC_WIDGET_WBREADCRUMB_H_ + +#include +#include "library/treeitem.h" + +class WLibraryBreadCrumb : public QLabel { + Q_OBJECT + + public: + + WLibraryBreadCrumb(QWidget* parent = nullptr); + + void setText(const QString& text); + QString text() const; + + virtual QSize minimumSizeHint() const; + + public slots: + + void showBreadCrumb(TreeItem* pTree); + + protected: + + virtual void resizeEvent(QResizeEvent* pEvent); + + private: + + QString m_longText; +}; + +#endif /* SRC_WIDGET_WBREADCRUMB_H_ */ diff --git a/src/widget/wlibrarysidebar.cpp b/src/widget/wlibrarysidebar.cpp index 5a3c6b16c4f1..3638159466c7 100644 --- a/src/widget/wlibrarysidebar.cpp +++ b/src/widget/wlibrarysidebar.cpp @@ -7,6 +7,7 @@ #include #include "library/sidebarmodel.h" +#include "library/treeitemmodel.h" #include "util/dnd.h" const int expand_time = 250; @@ -38,7 +39,7 @@ void WLibrarySidebar::contextMenuEvent(QContextMenuEvent *event) { // Drag enter event, happens when a dragged item enters the track sources view void WLibrarySidebar::dragEnterEvent(QDragEnterEvent * event) { - qDebug() << "WLibrarySidebar::dragEnterEvent" << event->mimeData()->formats(); + //qDebug() << "WLibrarySidebar::dragEnterEvent" << event->mimeData()->formats(); if (event->mimeData()->hasUrls()) { // We don't have a way to ask the LibraryFeatures whether to accept a // drag so for now we accept all drags. Since almost every @@ -94,6 +95,20 @@ void WLibrarySidebar::dragMoveEvent(QDragMoveEvent * event) { break; } } + } else { + TreeItemModel* treeModel = dynamic_cast(model()); + if (treeModel) { + accepted = false; + + for (const QUrl& url : urls) { + QModelIndex destIndex = this->indexAt(event->pos()); + if (treeModel->dragMoveAccept(destIndex, url)) { + accepted = true; + break; + } + } + + } } if (accepted) { event->acceptProposedAction(); @@ -135,6 +150,7 @@ void WLibrarySidebar::dropEvent(QDropEvent * event) { //Drag-and-drop from an external application or the track table widget //eg. dragging a track from Windows Explorer onto the sidebar SidebarModel* sidebarModel = dynamic_cast(model()); + if (sidebarModel) { QModelIndex destIndex = indexAt(event->pos()); // event->source() will return NULL if something is droped from @@ -145,6 +161,17 @@ void WLibrarySidebar::dropEvent(QDropEvent * event) { } else { event->ignore(); } + } else { + TreeItemModel* pTreeModel = dynamic_cast(model()); + if (pTreeModel) { + QModelIndex destIndex = indexAt(event->pos()); + QList urls(event->mimeData()->urls()); + if (pTreeModel->dropAccept(destIndex, urls, event->source())) { + event->acceptProposedAction(); + } else { + event->ignore(); + } + } } } //emit(trackDropped(name)); diff --git a/src/widget/wlibrarysidebar.h b/src/widget/wlibrarysidebar.h index 68490afa3d81..07ce76915929 100644 --- a/src/widget/wlibrarysidebar.h +++ b/src/widget/wlibrarysidebar.h @@ -13,6 +13,7 @@ #include #include "widget/wbasewidget.h" +#include "library/libraryview.h" class WLibrarySidebar : public QTreeView, public WBaseWidget { Q_OBJECT diff --git a/src/widget/wlibrarystack.cpp b/src/widget/wlibrarystack.cpp new file mode 100644 index 000000000000..1a7c431990a5 --- /dev/null +++ b/src/widget/wlibrarystack.cpp @@ -0,0 +1,69 @@ +#include + +#include "widget/wlibrarystack.h" + +WLibraryStack::WLibraryStack(QWidget* parent) + : QStackedWidget(parent) { + // TODO Auto-generated constructor stub + +} + +WLibraryStack::~WLibraryStack() { + // TODO Auto-generated destructor stub +} + +int WLibraryStack::addWidget(QWidget* w) { + //qDebug() << "WLibraryStack::addWidget" << w; + checkAndWarning(w); + return QStackedWidget::addWidget(w); +} + +int WLibraryStack::insertWidget(int index, QWidget* w) { + checkAndWarning(w); + return QStackedWidget::insertWidget(index, w); +} + +void WLibraryStack::onShow() { + +} + +void WLibraryStack::onSearch(const QString& text) { + LibraryView* pView = dynamic_cast(currentWidget()); + + if (pView) { + pView->onSearch(text); + } +} + +void WLibraryStack::loadSelectedTrack() { + LibraryView* pView = dynamic_cast(currentWidget()); + + if (pView) { + pView->loadSelectedTrack(); + } +} + +void WLibraryStack::slotSendToAutoDJ() { + LibraryView* pView = dynamic_cast(currentWidget()); + + if (pView) { + pView->slotSendToAutoDJ(); + } +} + +void WLibraryStack::slotSendToAutoDJTop() { + LibraryView* pView = dynamic_cast(currentWidget()); + + if (pView) { + pView->slotSendToAutoDJTop(); + } +} + +bool WLibraryStack::checkAndWarning(QWidget* w) { + if (!dynamic_cast(w)) { + qDebug() << "WARNING: Attempted to register a view with WLibraryStack" + << "that does not implement the LibraryView interface."; + return false; + } + return true; +} diff --git a/src/widget/wlibrarystack.h b/src/widget/wlibrarystack.h new file mode 100644 index 000000000000..d37a1d62ecf7 --- /dev/null +++ b/src/widget/wlibrarystack.h @@ -0,0 +1,34 @@ +#ifndef WLIBRARYSTACK_H +#define WLIBRARYSTACK_H + +#include + +#include "library/libraryview.h" + +/* This is a stacked widget that contains LibraryViews inside to, it can be + * added to the WLibrary and allows compatibility + */ + +class WLibraryStack : public QStackedWidget, public LibraryView { + Q_OBJECT + + public: + WLibraryStack(QWidget* parent = nullptr); + ~WLibraryStack(); + + int addWidget(QWidget* w); + int insertWidget(int index, QWidget *w); + + void onShow(); + void onSearch(const QString& text); + + void loadSelectedTrack(); + void slotSendToAutoDJ(); + void slotSendToAutoDJTop(); + + private: + + bool checkAndWarning(QWidget *w); +}; + +#endif /* WLIBRARYSTACK_H */ diff --git a/src/widget/wsearchlineedit.h b/src/widget/wsearchlineedit.h index 81a64ca217a8..7f681ab8374b 100644 --- a/src/widget/wsearchlineedit.h +++ b/src/widget/wsearchlineedit.h @@ -16,7 +16,7 @@ class WSearchLineEdit : public QLineEdit, public WBaseWidget { Q_OBJECT public: - explicit WSearchLineEdit(QWidget* pParent); + explicit WSearchLineEdit(QWidget* pParent = nullptr); void setup(const QDomNode& node, const SkinContext& context); diff --git a/src/widget/wtracktableview.cpp b/src/widget/wtracktableview.cpp index 6b7e3edc4310..de4e3e075ccf 100644 --- a/src/widget/wtracktableview.cpp +++ b/src/widget/wtracktableview.cpp @@ -211,8 +211,8 @@ void WTrackTableView::loadTrackModel(QAbstractItemModel *model) { * this will cause a small GUI freeze */ if (getTrackModel() == trackModel) { - // Re-sort the table even if the track model is the same. This triggers - // a select() if the table is dirty. + // Re-sort the table even if the track model is the same. + trackModel->select(); doSortByColumn(horizontalHeader()->sortIndicatorSection()); return; } @@ -355,6 +355,7 @@ void WTrackTableView::loadTrackModel(QAbstractItemModel *model) { // target though, so my hax above may not be completely unjustified. setVisible(true); + trackModel->select(); } void WTrackTableView::createActions() { @@ -1029,7 +1030,7 @@ void WTrackTableView::dragEnterEvent(QDragEnterEvent * event) { return; } } else if (DragAndDropHelper::dragEnterAccept(*event->mimeData(), - "library", true, true)) { + "", true, true)) { event->acceptProposedAction(); return; }