1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-09-06 04:02:54 +02:00

Compare commits

...

88 Commits

Author SHA1 Message Date
Christian Muehlhaeuser
f7e82368bd * Unbreak OSX compile. 2011-08-30 02:44:24 +02:00
Christian Muehlhaeuser
b54670ad48 * Prepare for 0.2.3. 2011-08-30 02:00:14 +02:00
Christian Muehlhaeuser
1a869762f6 * Fixed TWK-451: Properly parent the friends auth / add dialog. 2011-08-30 00:54:01 +02:00
Christian Muehlhaeuser
8af49897a9 * Fixed TWK-482: Don't close after showing a jabber friend request. 2011-08-30 00:27:46 +02:00
Jason Herskowitz
6571a0b88e Added fix for Spotify links 2011-08-30 00:19:45 -03:00
Jeff Mitchell
70b350510f Do please check your debug for null pointers :-) 2011-08-29 13:13:44 -04:00
Christian Muehlhaeuser
72471f70d3 * Added more debug output for playlist errors. 2011-08-29 17:13:06 +02:00
Jeff Mitchell
6fe16be23b Remove dependency on XComposite on X11. It wasn't actually a dependency
on the library, just the development headers (used to check for th
existence of compositing at runtime, but not necessary for us).
2011-08-29 11:04:02 -04:00
Leo Franchi
a3833c1da5 Fix rdio links 2011-08-29 10:51:44 -04:00
Leo Franchi
053652756a Fix rdio links 2011-08-29 10:49:29 -04:00
Leo Franchi
33ee2a4724 Sync master and stable 0.2.3 changelog 2011-08-29 10:16:38 -04:00
Leo Franchi
c15c1531f8 Changelog 2011-08-29 10:14:50 -04:00
Leo Franchi
e3d1f2ea8c Don't depend on sender() as it can be null in queued events
(cherry picked from commit 1994ab33fe)
2011-08-29 10:13:51 -04:00
Leo Franchi
ecc9502c2c TWK-441: Try to fix crash where sourcetreeitem would hold on to a dangling parent
(cherry picked from commit 3fa9fa337e)
2011-08-29 10:13:42 -04:00
Christian Muehlhaeuser
446dd993a8 * Be safer. 2011-08-29 16:08:40 +02:00
Christian Muehlhaeuser
2e19306458 * Some debug and safety meassures. 2011-08-29 16:05:04 +02:00
Christian Muehlhaeuser
b256141f74 * Updated ChangeLog. 2011-08-28 06:06:07 +02:00
Jeff Mitchell
61ddd9bfa1 Changelogify Twitter issue 2011-08-27 22:10:06 -04:00
Jeff Mitchell
bce77397a7 Make public/mentions timelines check less often to offset bumping up the dm timer 2011-08-27 22:06:40 -04:00
Jeff Mitchell
47f0895efb Some further cleanup and make dms poll a bit more often while keeping the timelines longer 2011-08-27 22:06:33 -04:00
Jeff Mitchell
a16139266b Hopefully really fix the twitter issue 2011-08-27 22:06:27 -04:00
Jeff Mitchell
e050b99550 Fix twitter saved offer key issue, maybe 2011-08-27 22:06:21 -04:00
Jeff Mitchell
92b2fdcb2e Move libqnetwm down one level and hopefully fix building for leo 2011-08-27 16:25:10 -04:00
Jeff Mitchell
ab72133c15 Fix include path 2011-08-27 16:08:14 -04:00
Jeff Mitchell
44a9c35413 Fix listen along menu not resetting when you stop listening along 2011-08-27 14:05:56 -04:00
Jeff Mitchell
e7e2a7775d Changelogify++ 2011-08-27 14:02:37 -04:00
Jeff Mitchell
f0d677ce52 Doh, smart pointers -- fix compile 2011-08-27 17:15:40 -04:00
Jeff Mitchell
4687e73aa9 Hells. Yes. Make TH window come to front when launched with a URL. (X11, not sure if Win works yet.) 2011-08-27 17:14:00 -04:00
Jeff Mitchell
fcb70b551b Fix compile on mingw32 2011-08-27 17:13:53 -04:00
Jeff Mitchell
5975d45ca4 Add definitions for bringToFront. Can't test on X11 as TH crashes with database errors before I get the chance. 2011-08-27 17:13:46 -04:00
Jeff Mitchell
95b99f4e8c None of this works, but maybe it's a start since Qt directly isn't
working
2011-08-27 17:13:36 -04:00
Jeff Mitchell
4047588086 Better structuring 2011-08-27 17:13:19 -04:00
Jeff Mitchell
cdd1c05f22 Draw the battle lines 2011-08-27 17:13:06 -04:00
Jeff Mitchell
c4b595d446 Base64 encode our twitter credentials like elsewhere 2011-08-27 17:12:53 -04:00
Jeff Mitchell
0732ce120a Fix crash; pointer safety! 2011-08-27 17:12:13 -04:00
Christian Muehlhaeuser
e62a868562 * Fixed TWK-461: Always pre-sort searches by score column. 2011-08-26 00:54:46 +02:00
Christian Muehlhaeuser
67c9e79dac * Remove obsolete debug output. 2011-08-26 00:53:36 +02:00
Christian Muehlhaeuser
037f7a4f57 * Guard pipeline against dupe queries. 2011-08-26 00:53:36 +02:00
Leo Franchi
bedf36c3f6 Don't resolve a query twice. EchonestGenerator no longer gives out auto-resolving queries, resolve your own. 2011-08-26 00:53:36 +02:00
Christian Muehlhaeuser
fcc4cbbc55 * Add debug output. 2011-08-26 00:53:36 +02:00
Christian Muehlhaeuser
5e95567db0 * Refactor pipeline. 2011-08-26 00:53:36 +02:00
Leo Franchi
f9591e800c TWK-340: smooth transform for more pixmaps 2011-08-26 00:53:36 +02:00
Leo Franchi
c887bbdd57 TWK-447: Clear albums model before appending to it 2011-08-26 00:53:36 +02:00
Christian Muehlhaeuser
0a0c8607fc * Make sure onResolvingFinished gets called. 2011-08-26 00:53:36 +02:00
Christian Muehlhaeuser
756ff7c869 * Cleaned up debugging in Servent. 2011-08-26 00:53:36 +02:00
Christian Muehlhaeuser
5374d65a05 * Don't assert out on too late results. 2011-08-26 00:53:36 +02:00
Michael Zanetti
4327d61df7 make trackURLs work again 2011-08-24 08:51:44 +02:00
Jason Herskowitz
5a9fb8d1cc Changed "added" to "fixed Ogg Vorbis support for OS X". 2011-08-23 03:00:19 -03:00
Jason Herskowitz
a169972723 Noted Ogg Vorbis (OS X) support in changelog. 2011-08-23 02:26:13 -03:00
Leo Franchi
1279b1fa12 Changelog 2011-08-22 19:17:31 -04:00
Leo Franchi
fd99fa84be TWK-431: Don't re-use the same infosystem caller id for every artistinfowidget.
(cherry picked from commit ac317eb0a6)
2011-08-22 19:17:03 -04:00
Jason Herskowitz
13ab10adea Edited ChangeLog via GitHub 2011-08-22 12:29:31 -04:00
Christian Muehlhaeuser
20d2a5ff59 * Bumped version to 0.2.2. 2011-08-22 17:46:55 +02:00
Leo Franchi
6b9eec00fd Changelog 2011-08-22 07:34:47 -04:00
Leo Franchi
d7416ddd57 TWK-415: Don't crash if the trackproxymodel has an invalid current track
(cherry picked from commit 498d7052de)
2011-08-22 07:32:48 -04:00
Leo Franchi
2e9aa7f3b3 Changelog. 2011-08-21 21:41:31 -04:00
Leo Franchi
208393deb0 TWK-401: Only update source stats after addfiles dbcmd is finished, instead of playing a race condition game. Also, refresh recent albums on stats change
(cherry picked from commit 6521198127)
2011-08-21 21:34:26 -04:00
Leo Franchi
b923d4f994 TWK-420: Show resolver wrench immediately after enabling a resolver.
(cherry picked from commit f0a6d4e8bb)
2011-08-21 21:34:20 -04:00
Christian Muehlhaeuser
edbee5a922 * Fixed filtering out unwanted dupes when viewing a local collection. 2011-08-21 22:52:53 +02:00
Michael Zanetti
e0bdc4b8bc TWK-411: Dragging an album/artist to "New Station" creates a station based on that artist instead of all the tracks 2011-08-21 16:00:21 +02:00
Christian Muehlhaeuser
4b53d20663 * Updated ChangeLog. 2011-08-21 04:51:52 +02:00
Christian Muehlhaeuser
74c4f4ddc2 * Fixed TWK-442: Provide stable sort ordering in Track- / TreeProxyModel. 2011-08-21 03:53:42 +02:00
Christian Muehlhaeuser
ec03ece35c * Fixed TWK-435: Don't block pipeline if a resolver times out. 2011-08-21 03:33:32 +02:00
Dominik Schmidt
d217e7fae0 TWK-410: possible fix 2011-08-21 03:33:25 +02:00
Christian Muehlhaeuser
bc3bcc708f * Don't filter out some wanted tracks, e.g. when multiple tracks of an album have the same name, but different tracks numbers. 2011-08-21 02:59:09 +02:00
Christian Muehlhaeuser
7a90ba11bb * Fixed TWK-387: Stop showing a loading spinner, if there are no items to load. 2011-08-21 02:58:59 +02:00
Michael Zanetti
ed219a56cd don't add duplicates when dropping albums or artists and fix losing items when combining synchronous and asynchrounous drop operations 2011-08-20 14:38:43 +02:00
Christian Muehlhaeuser
1537d5b08a * Updated ChangeLog. 2011-08-18 00:05:45 +02:00
Christian Muehlhaeuser
263340a270 * No need for clucene in Tomahawk binary. 2011-08-17 22:13:24 +02:00
Jason Herskowitz
bf54ebf7fe De-decapitate avatar placeholder
(cherry picked from commit 77d60fa02d)
2011-08-17 14:40:04 -04:00
Leo Franchi
bfbac296b0 Fix resolvers on qt 4.8, baseUrl is required for html5 security policy stuff for localStorage
(cherry picked from commit 4abb53ccdf)
2011-08-17 14:36:02 -04:00
Christian Muehlhaeuser
7971e7aadc * More verbose debug output in DBSyncConnection. 2011-08-17 04:15:28 +02:00
Christian Muehlhaeuser
4cc1690dea * Fixed sorting of identical items in Tree- & TrackProxyModel. 2011-08-17 03:34:21 +02:00
Christian Muehlhaeuser
2090d76955 * Fixed sidebar sorting for items with identical names. 2011-08-17 03:20:23 +02:00
Christian Muehlhaeuser
d09397a8ab * Moved the AudioControl QSlider stylesheets into SeekSlider. 2011-08-17 03:20:12 +02:00
Christian Muehlhaeuser
a734cad4e1 * No need for mouse tracking. 2011-08-17 02:27:39 +02:00
Christian Muehlhaeuser
9b433379ee * Fixed TWK-212: Sliders should jump to the position they are clicked on. 2011-08-17 02:27:25 +02:00
Christian Muehlhaeuser
2e11376d01 * Fix sorting for real. 2011-08-17 01:10:56 +02:00
Christian Muehlhaeuser
9ba11290ae * Fixed sidebar sorting. 2011-08-17 01:10:45 +02:00
Leo Franchi
97e0bbaf38 Allow to show the setting dialog always, and disable +/- buttons till loaded
(cherry picked from commit 713243cc5d)
2011-08-16 19:08:03 -04:00
Leo Franchi
eb4242622d Show spinner on initial load while loading sip plugins in preferences. Disable Settings action till servent is loaded
(cherry picked from commit ee8da33e06)
2011-08-16 19:07:57 -04:00
Michael Zanetti
469bea43ba added drag capabilities to albumview ins sourceinfowidget and welcomewidget 2011-08-16 23:49:24 +02:00
Michael Zanetti
f9f3a45a31 fix copyright headers in SettingsListDelegate 2011-08-16 19:53:50 +02:00
Michael Zanetti
0579d63013 fixed copyright headers 2011-08-16 19:53:41 +02:00
Michael Zanetti
1bb115ec6f Created DropJob which will start a query for the dropped items and wait for the results 2011-08-16 19:53:30 +02:00
Michael Zanetti
5f320028b8 more work to be able to drag and drop mixed mimetypes (e.g. an artist and some tracks) 2011-08-16 19:53:15 +02:00
Michael Zanetti
b0d445c262 added drag and drop to playlists for albums and artists 2011-08-16 19:53:06 +02:00
Christian Muehlhaeuser
479a0c469f * Un-regress listen-along. 2011-08-16 03:25:24 +02:00
78 changed files with 2558 additions and 542 deletions

View File

@@ -16,7 +16,7 @@ SET( TOMAHAWK_DESCRIPTION_SUMMARY "The social media player" )
SET( TOMAHAWK_VERSION_MAJOR 0 )
SET( TOMAHAWK_VERSION_MINOR 2 )
SET( TOMAHAWK_VERSION_PATCH 1 )
SET( TOMAHAWK_VERSION_PATCH 3 )
#SET( TOMAHAWK_VERSION_RC 0 )
@@ -72,6 +72,10 @@ ELSE()
LIST(APPEND NEEDED_QT4_COMPONENTS "QtGui" "QtWebkit" )
ENDIF()
IF( BUILD_GUI AND UNIX AND NOT APPLE )
FIND_PACKAGE( X11 )
ENDIF()
macro_optional_find_package(Qt4 4.7.0 COMPONENTS ${NEEDED_QT4_COMPONENTS} )
macro_log_feature(QT4_FOUND "Qt" "A cross-platform application and UI framework" "http://qt.nokia.com" TRUE "" "If you see this, although libqt4-devel is installed, check whether \n the qtwebkit-devel package is installed as well")

View File

@@ -203,7 +203,7 @@ Function PageReinstall
IntCmp $R0 ${VER_MINOR} build_check new_version older_version
build_check:
ReadRegDWORD $R0 HKLM "Software\Tomahawk" "VersionBuild"
IntCmp $R0 ${VER_BUILD} new_version older_version
IntCmp $R0 ${VER_BUILD} same_version new_version older_version
new_version:
!insertmacro INSTALLOPTIONS_WRITE "NSIS.InstallOptions.ini" "Field 1" "Text" "An older version of Tomahawk is installed on your system. It is recommended that you uninstall the current version before installing. Select the operation you want to perform and click Next to continue."

View File

@@ -1,3 +1,26 @@
Version 0.2.3:
* Fixed opening Rdio and Spotify links.
* Fixed potential crash in sidebar during syncing of sources.
* When Listening Along, the last song a peer plays is no longer duplicated.
* Fixed an issue where the Twitter plugin could get out of sync if the
database was cleared, leading to eventual crashes when re-connecting.
* Fixed duplicate albums showing up on Dashboard.
* Automatically sort search results by score.
* Fixed stations being stuck not fetching more songs.
* Fixed issue where artist bio could be referring to a different artist.
* Opening a "tomahawk" URL (or other URL with Tomahawk) brings the Tomahawk
window to the foreground.
Version 0.2.2:
* Fixed crash pressing previous and next when playing a song from the Queue.
* Fixed issue where wrench for newly added resolvers would not show up.
* Fixed sidebar statistics not updating after collection scan finished.
* Fixed omitting a few tracks in the Collection tree-view.
* Fixed sidebar & track sorting issues.
* Seek- & volume sliders now directly jump to the position you clicked on.
* Added ability to drag artists and albums within Tomahawk.
* (OS X) Fixed Ogg Vorbis support.
Version 0.2.1:
* Fixed crashing trying to play an unavailable track.
* Fixed a crash caused by using Javascript resolvers.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -237,7 +237,6 @@ TARGET_LINK_LIBRARIES( tomahawk
${QXTWEB_LIBRARIES}
${QJSON_LIBRARIES}
${TAGLIB_LIBRARIES}
${CLUCENE_LIBRARIES}
)

View File

@@ -35,6 +35,7 @@
#include "utils/tomahawkutils.h"
#include "utils/logger.h"
#include <globalactionmanager.h>
#include "dropjob.h"
using namespace Tomahawk;
@@ -90,56 +91,9 @@ AudioControls::AudioControls( QWidget* parent )
ui->ownerLabel->setForegroundRole( QPalette::Dark );
ui->metaDataArea->setStyleSheet( "QWidget#metaDataArea {\nborder-width: 4px;\nborder-image: url(" RESPATH "images/now-playing-panel.png) 4 4 4 4 stretch stretch; }" );
ui->seekSlider->setFixedHeight( 20 );
ui->seekSlider->setEnabled( true );
ui->seekSlider->setStyleSheet( "QSlider::groove::horizontal {"
"margin: 5px; border-width: 3px;"
"border-image: url(" RESPATH "images/seek-slider-bkg.png) 3 3 3 3 stretch stretch;"
"}"
"QSlider::sub-page:horizontal {"
"margin: 5px; border-width: 3px;"
"border-image: url(" RESPATH "images/seek-slider-level.png) 3 3 3 3 stretch stretch;"
"}"
"QSlider::handle::horizontal {"
"margin-bottom: -7px; margin-top: -7px;"
"margin-left: -4px; margin-right: -4px;"
"height: 17px; width: 16px;"
"background-image: url(" RESPATH "images/seek-and-volume-knob-rest.png);"
"background-repeat: no-repeat;"
"}" );
ui->volumeSlider->setFixedHeight( 20 );
ui->volumeSlider->setRange( 0, 100 );
ui->volumeSlider->setValue( AudioEngine::instance()->volume() );
ui->volumeSlider->setStyleSheet( "QSlider::groove::horizontal {"
"margin: 5px; border-width: 3px;"
"border-image: url(" RESPATH "images/volume-slider-bkg.png) 3 3 3 3 stretch stretch;"
"}"
"QSlider::sub-page:horizontal {"
"margin: 5px; border-width: 3px;"
"border-image: url(" RESPATH "images/seek-slider-level.png) 3 3 3 3 stretch stretch;"
"}"
"QSlider::handle::horizontal {"
"margin-bottom: -7px; margin-top: -7px;"
"margin-left: -4px; margin-right: -4px;"
"height: 17px; width: 16px;"
"background-image: url(" RESPATH "images/seek-and-volume-knob-rest.png);"
"background-repeat: no-repeat;"
"}" );
/* m_playAction = new QAction( this );
m_pauseAction = new QAction( this );
m_prevAction = new QAction( this );
m_nextAction = new QAction( this );
connect( m_playAction, SIGNAL( triggered() ), (QObject*)APP->audioEngine(), SLOT( play() ) );
connect( m_pauseAction, SIGNAL( triggered() ), (QObject*)APP->audioEngine(), SLOT( pause() ) );
connect( m_prevAction, SIGNAL( triggered() ), (QObject*)APP->audioEngine(), SLOT( previous() ) );
connect( m_nextAction, SIGNAL( triggered() ), (QObject*)APP->audioEngine(), SLOT( next() ) ); */
connect( ui->seekSlider, SIGNAL( valueChanged( int ) ), AudioEngine::instance(), SLOT( seek( int ) ) );
connect( ui->volumeSlider, SIGNAL( valueChanged( int ) ), AudioEngine::instance(), SLOT( setVolume( int ) ) );
@@ -179,7 +133,6 @@ AudioControls::AudioControls( QWidget* parent )
connect( Tomahawk::InfoSystem::InfoSystem::instance(), SIGNAL( finished( QString ) ), SLOT( infoSystemFinished( QString ) ) );
ui->buttonAreaLayout->setSpacing( 0 );
ui->stackedLayout->setSpacing( 0 );
ui->stackedLayout->setContentsMargins( 0, 0, 0, 0 );
@@ -305,9 +258,6 @@ AudioControls::onPlaybackLoading( const Tomahawk::result_ptr& result )
ui->seekSlider->setValue( 0 );
ui->seekSlider->setVisible( true );
/* m_playAction->setEnabled( false );
m_pauseAction->setEnabled( true ); */
ui->stackedLayout->setCurrentWidget( ui->pauseButton );
ui->loveButton->setEnabled( true );
@@ -343,18 +293,12 @@ AudioControls::socialActionsLoaded()
void
AudioControls::onPlaybackPaused()
{
/* m_pauseAction->setEnabled( false );
m_playAction->setEnabled( true ); */
ui->stackedLayout->setCurrentWidget( ui->playPauseButton );
}
void
AudioControls::onPlaybackResumed()
{
/* m_playAction->setEnabled( false );
m_pauseAction->setEnabled( true ); */
ui->stackedLayout->setCurrentWidget( ui->pauseButton );
ui->loveButton->setVisible( true );
}
@@ -376,9 +320,6 @@ AudioControls::onPlaybackStopped()
ui->stackedLayout->setCurrentWidget( ui->playPauseButton );
ui->loveButton->setEnabled( false );
ui->loveButton->setVisible( false );
/* m_pauseAction->setEnabled( false );
m_playAction->setEnabled( true ); */
}
@@ -521,7 +462,7 @@ AudioControls::onTrackClicked()
void
AudioControls::dragEnterEvent( QDragEnterEvent* e )
{
if ( GlobalActionManager::instance()->acceptsMimeData( e->mimeData() ) )
if ( DropJob::acceptsMimeData( e->mimeData() ) )
e->acceptProposedAction();
}
@@ -538,10 +479,11 @@ void
AudioControls::dropEvent( QDropEvent* e )
{
tDebug() << "AudioControls got drop:" << e->mimeData()->formats();
if ( GlobalActionManager::instance()->acceptsMimeData( e->mimeData() ) )
if ( DropJob::acceptsMimeData( e->mimeData() ) )
{
connect( GlobalActionManager::instance(), SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SLOT( droppedTracks( QList<Tomahawk::query_ptr> ) ) );
GlobalActionManager::instance()->tracksFromMimeData( e->mimeData() );
DropJob *dj = new DropJob();
connect( dj, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SLOT( droppedTracks( QList<Tomahawk::query_ptr> ) ) );
dj->tracksFromMimeData( e->mimeData() );
e->accept();
}
@@ -551,8 +493,6 @@ AudioControls::dropEvent( QDropEvent* e )
void
AudioControls::droppedTracks( QList< query_ptr > tracks )
{
disconnect( GlobalActionManager::instance(), SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SLOT( droppedTracks( QList<Tomahawk::query_ptr> ) ) );
if ( !tracks.isEmpty() )
{
// queue and play the first if nothign is playing

View File

@@ -82,11 +82,6 @@ private slots:
private:
Ui::AudioControls *ui;
QAction* m_playAction;
QAction* m_pauseAction;
QAction* m_prevAction;
QAction* m_nextAction;
QPixmap m_defaultCover;
Tomahawk::result_ptr m_currentTrack;

View File

@@ -337,7 +337,7 @@
</widget>
</item>
<item>
<widget class="QSlider" name="seekSlider">
<widget class="SeekSlider" name="seekSlider">
<property name="minimumSize">
<size>
<width>0</width>
@@ -489,7 +489,7 @@
</widget>
</item>
<item>
<widget class="QSlider" name="volumeSlider">
<widget class="SeekSlider" name="volumeSlider">
<property name="minimumSize">
<size>
<width>0</width>
@@ -529,6 +529,11 @@
</layout>
</widget>
<customwidgets>
<customwidget>
<class>SeekSlider</class>
<extends>QSlider</extends>
<header location="global">widgets/SeekSlider.h</header>
</customwidget>
<customwidget>
<class>ImageButton</class>
<extends>QPushButton</extends>

View File

@@ -31,6 +31,7 @@ set( libSources
viewmanager.cpp
globalactionmanager.cpp
contextmenu.cpp
dropjob.cpp
sip/SipPlugin.cpp
sip/SipHandler.cpp
@@ -176,6 +177,7 @@ set( libSources
widgets/newplaylistwidget.cpp
widgets/searchwidget.cpp
widgets/SeekSlider.cpp
widgets/playlisttypeselectordlg.cpp
widgets/welcomewidget.cpp
widgets/welcomeplaylistmodel.cpp
@@ -208,6 +210,7 @@ set( libHeaders
viewmanager.h
globalactionmanager.h
contextmenu.h
dropjob.h
artist.h
album.h
@@ -354,6 +357,7 @@ set( libHeaders
widgets/newplaylistwidget.h
widgets/searchwidget.h
widgets/SeekSlider.h
widgets/playlisttypeselectordlg.h
widgets/welcomewidget.h
widgets/welcomeplaylistmodel.h
@@ -406,7 +410,7 @@ include_directories( . ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/.
${LIBPORTFWD_INCLUDE_DIR}
${THIRDPARTY_DIR}/qxt/qxtweb-standalone/qxtweb
${CMAKE_BINARY_DIR}/thirdparty/liblastfm2/src
)
IF( UNIX AND NOT APPLE )
@@ -416,6 +420,12 @@ IF( UNIX AND NOT APPLE )
SET( libHeaders ${libHeaders}
infosystem/infoplugins/unix/fdonotifyplugin.h )
IF( BUILD_GUI AND X11_FOUND )
INCLUDE_DIRECTORIES( ${THIRDPARTY_DIR}/libqnetwm )
SET( libSources ${libSources} ${THIRDPARTY_DIR}/libqnetwm/libqnetwm/netwm.cpp )
SET( LINK_LIBRARIES ${LINK_LIBRARIES} ${X11_LIBRARIES} )
ENDIF()
ENDIF( UNIX AND NOT APPLE )
IF( WIN32 )

View File

@@ -212,7 +212,7 @@ AudioEngine::next()
return;
if ( !m_currentTrack.isNull() && !m_playlist.data()->hasNextItem() &&
m_currentTrack->id() == m_playlist.data()->currentItem()->id() )
( m_playlist.data()->currentItem().isNull() || ( m_currentTrack->id() == m_playlist.data()->currentItem()->id() ) ) )
{
//For instance, when doing a catch-up while listening along, but the person
//you're following hasn't started a new track yet...don't do anything
@@ -305,11 +305,17 @@ AudioEngine::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData,
return;
}
if ( m_currentTrack.isNull() ||
m_currentTrack.data()->track().isNull() ||
m_currentTrack.data()->artist().isNull() ||
m_currentTrack.data()->album().isNull() )
return;
QVariantMap playInfo;
playInfo["message"] = QString( "Tomahawk is playing \"%1\" by %2 on album %3." )
.arg( m_currentTrack->track() )
.arg( m_currentTrack->artist()->name() )
.arg( m_currentTrack->album()->name() );
.arg( m_currentTrack->album()->name() );
if ( !output.isNull() && output.isValid() )
{
QVariantMap returnedData = output.value< QVariantMap >();
@@ -487,8 +493,6 @@ AudioEngine::playItem( Tomahawk::PlaylistInterface* playlist, const Tomahawk::re
{
tDebug( LOGEXTRA ) << Q_FUNC_INFO << ( result.isNull() ? QString() : result->url() );
if ( !result->isOnline() )
return;
if ( !m_playlist.isNull() )
m_playlist.data()->reset();

View File

@@ -27,6 +27,7 @@
#include "databasecommand_collectionstats.h"
#include "databaseimpl.h"
#include "network/controlconnection.h"
#include "sourcelist.h"
#include "utils/logger.h"
@@ -72,7 +73,15 @@ DatabaseCommand_AddFiles::postCommitHook()
emit notify( m_queries );
if( source()->isLocal() )
{
Servent::instance()->triggerDBSync();
// Re-calculate local db stats
DatabaseCommand_CollectionStats* cmd = new DatabaseCommand_CollectionStats( SourceList::instance()->getLocal() );
connect( cmd, SIGNAL( done( QVariantMap ) ),
SourceList::instance()->getLocal().data(), SLOT( setStats( QVariantMap ) ), Qt::QueuedConnection );
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
}
}

View File

@@ -74,8 +74,7 @@ DatabaseCommand_AllAlbums::execForArtist( DatabaseImpl* dbi )
al << album;
}
if ( al.count() )
emit albums( al, data() );
emit albums( al, data() );
emit done();
}
@@ -124,8 +123,7 @@ DatabaseCommand_AllAlbums::execForCollection( DatabaseImpl* dbi )
al << album;
}
if ( al.count() )
emit albums( al, data() );
emit albums( al, data() );
emit done();
}

View File

@@ -26,6 +26,22 @@ DatabaseCommand_loadOps::exec( DatabaseImpl* dbi )
{
QList< dbop_ptr > ops;
if ( !m_since.isEmpty() )
{
TomahawkSqlQuery query = dbi->newquery();
query.prepare( QString( "SELECT id FROM oplog WHERE guid = ?" ) );
query.addBindValue( m_since );
query.exec();
if ( !query.next() )
{
tLog() << "Unknown oplog guid, requested, not replying:" << m_since;
Q_ASSERT( false );
emit done( m_since, m_since, ops );
return;
}
}
TomahawkSqlQuery query = dbi->newquery();
query.prepare( QString(
"SELECT guid, command, json, compressed, singleton "

View File

@@ -110,6 +110,7 @@ DatabaseWorker::doWork()
try
{
{
tDebug() << "Executing cmd:" << cmd->guid();
cmd->_exec( m_dbimpl ); // runs actual SQL stuff
if ( cmd->loggable() )

362
src/libtomahawk/dropjob.cpp Normal file
View File

@@ -0,0 +1,362 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2011, Michael Zanetti <mzanetti@kde.org>
* Copyright 2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dropjob.h"
#include "artist.h"
#include "album.h"
#include "utils/spotifyparser.h"
#include "utils/rdioparser.h"
#include "utils/shortenedlinkparser.h"
#include "utils/logger.h"
#include "globalactionmanager.h"
using namespace Tomahawk;
DropJob::DropJob( QObject *parent )
: QObject( parent )
, m_queryCount( 0 )
{
}
DropJob::~DropJob()
{
qDebug() << "destryong DropJob";
}
/// QMIMEDATA HANDLING
QStringList
DropJob::mimeTypes()
{
QStringList mimeTypes;
mimeTypes << "application/tomahawk.query.list"
<< "application/tomahawk.plentry.list"
<< "application/tomahawk.result.list"
<< "application/tomahawk.result"
<< "application/tomahawk.metadata.artist"
<< "application/tomahawk.metadata.album"
<< "application/tomahawk.mixed"
<< "text/plain";
return mimeTypes;
}
bool
DropJob::acceptsMimeData( const QMimeData* data, bool tracksOnly )
{
if ( data->hasFormat( "application/tomahawk.query.list" )
|| data->hasFormat( "application/tomahawk.plentry.list" )
|| data->hasFormat( "application/tomahawk.result.list" )
|| data->hasFormat( "application/tomahawk.result" )
|| data->hasFormat( "application/tomahawk.mixed" )
|| data->hasFormat( "application/tomahawk.metadata.album" )
|| data->hasFormat( "application/tomahawk.metadata.artist" ) )
{
return true;
}
// crude check for spotify tracks
if ( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "spotify" ) &&
( tracksOnly ? data->data( "text/plain" ).contains( "track" ) : true ) )
return true;
// crude check for rdio tracks
if ( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "rdio.com" ) &&
( tracksOnly ? data->data( "text/plain" ).contains( "track" ) : true ) )
return true;
// We whitelist t.co and bit.ly (and j.mp) since they do some link checking. Often playable (e.g. spotify..) links hide behind them,
// so we do an extra level of lookup
if ( ( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "bit.ly" ) ) ||
( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "j.mp" ) ) ||
( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "t.co" ) ) ||
( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "rd.io" ) ) )
return true;
return false;
}
void
DropJob::tracksFromMimeData( const QMimeData* data, bool allowDuplicates )
{
m_allowDuplicates = allowDuplicates;
parseMimeData( data );
if ( m_queryCount == 0 )
{
if ( !allowDuplicates )
removeDuplicates();
emit tracks( m_resultList );
deleteLater();
}
}
void
DropJob::parseMimeData( const QMimeData *data )
{
QList< query_ptr > results;
if ( data->hasFormat( "application/tomahawk.query.list" ) )
results = tracksFromQueryList( data );
else if ( data->hasFormat( "application/tomahawk.result.list" ) )
results = tracksFromResultList( data );
else if ( data->hasFormat( "application/tomahawk.metadata.album" ) )
results = tracksFromAlbumMetaData( data );
else if ( data->hasFormat( "application/tomahawk.metadata.artist" ) )
results = tracksFromArtistMetaData( data );
else if ( data->hasFormat( "application/tomahawk.mixed" ) )
tracksFromMixedData( data );
else if ( data->hasFormat( "text/plain" ) )
{
QString plainData = QString::fromUtf8( data->data( "text/plain" ).constData() );
tDebug() << "Got text/plain mime data:" << data->data( "text/plain" ) << "decoded to:" << plainData;
handleTrackUrls ( plainData );
}
m_resultList.append( results );
}
QList< query_ptr >
DropJob::tracksFromQueryList( const QMimeData* data )
{
QList< query_ptr > queries;
QByteArray itemData = data->data( "application/tomahawk.query.list" );
QDataStream stream( &itemData, QIODevice::ReadOnly );
while ( !stream.atEnd() )
{
qlonglong qptr;
stream >> qptr;
query_ptr* query = reinterpret_cast<query_ptr*>(qptr);
if ( query && !query->isNull() )
{
tDebug() << "Dropped query item:" << query->data()->artist() << "-" << query->data()->track();
queries << *query;
}
}
return queries;
}
QList< query_ptr >
DropJob::tracksFromResultList( const QMimeData* data )
{
QList< query_ptr > queries;
QByteArray itemData = data->data( "application/tomahawk.result.list" );
QDataStream stream( &itemData, QIODevice::ReadOnly );
while ( !stream.atEnd() )
{
qlonglong qptr;
stream >> qptr;
result_ptr* result = reinterpret_cast<result_ptr*>(qptr);
if ( result && !result->isNull() )
{
tDebug() << "Dropped result item:" << result->data()->artist()->name() << "-" << result->data()->track();
query_ptr q = result->data()->toQuery();
q->addResults( QList< result_ptr >() << *result );
queries << q;
}
}
return queries;
}
QList< query_ptr >
DropJob::tracksFromAlbumMetaData( const QMimeData *data )
{
QList<query_ptr> queries;
QByteArray itemData = data->data( "application/tomahawk.metadata.album" );
QDataStream stream( &itemData, QIODevice::ReadOnly );
while ( !stream.atEnd() )
{
QString artist;
stream >> artist;
QString album;
stream >> album;
artist_ptr artistPtr = Artist::get( artist );
album_ptr albumPtr = Album::get( artistPtr, album );
if ( albumPtr->tracks().isEmpty() )
{
connect( albumPtr.data(), SIGNAL( tracksAdded( QList<Tomahawk::query_ptr> ) ),
SLOT( onTracksAdded( QList<Tomahawk::query_ptr> ) ) );
m_queryCount++;
}
else
queries << albumPtr->tracks();
}
return queries;
}
QList< query_ptr >
DropJob::tracksFromArtistMetaData( const QMimeData *data )
{
QList<query_ptr> queries;
QByteArray itemData = data->data( "application/tomahawk.metadata.artist" );
QDataStream stream( &itemData, QIODevice::ReadOnly );
while ( !stream.atEnd() )
{
QString artist;
stream >> artist;
artist_ptr artistPtr = Artist::get( artist );
if ( artistPtr->tracks().isEmpty() )
{
connect( artistPtr.data(), SIGNAL( tracksAdded( QList<Tomahawk::query_ptr> ) ),
SLOT( onTracksAdded( QList<Tomahawk::query_ptr> ) ) );
m_queryCount++;
}
else
queries << artistPtr->tracks();
}
return queries;
}
QList< query_ptr >
DropJob::tracksFromMixedData( const QMimeData *data )
{
QList< query_ptr > queries;
QByteArray itemData = data->data( "application/tomahawk.mixed" );
QDataStream stream( &itemData, QIODevice::ReadOnly );
QString mimeType;
while ( !stream.atEnd() )
{
stream >> mimeType;
qDebug() << "mimetype is" << mimeType;
QByteArray singleData;
QDataStream singleStream( &singleData, QIODevice::WriteOnly );
QMimeData singleMimeData;
if ( mimeType == "application/tomahawk.query.list" || mimeType == "application/tomahawk.result.list" )
{
qlonglong query;
stream >> query;
singleStream << query;
}
else if ( mimeType == "application/tomahawk.metadata.album" )
{
QString artist;
stream >> artist;
singleStream << artist;
QString album;
stream >> album;
singleStream << album;
qDebug() << "got artist" << artist << "and album" << album;
}
else if ( mimeType == "application/tomahawk.metadata.artist" )
{
QString artist;
stream >> artist;
singleStream << artist;
qDebug() << "got artist" << artist;
}
singleMimeData.setData( mimeType, singleData );
parseMimeData( &singleMimeData );
}
return queries;
}
void
DropJob::handleTrackUrls( const QString& urls )
{
if ( urls.contains( "open.spotify.com/track") ||
urls.contains( "spotify:track" ) )
{
QStringList tracks = urls.split( "\n" );
tDebug() << "Got a list of spotify urls!" << tracks;
SpotifyParser* spot = new SpotifyParser( tracks, this );
connect( spot, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SLOT( onTracksAdded( QList< Tomahawk::query_ptr > ) ) );
m_queryCount++;
} else if ( urls.contains( "rdio.com" ) )
{
QStringList tracks = urls.split( "\n" );
tDebug() << "Got a list of rdio urls!" << tracks;
RdioParser* rdio = new RdioParser( this );
connect( rdio, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SLOT( onTracksAdded( QList< Tomahawk::query_ptr > ) ) );
m_queryCount++;
rdio->parse( tracks );
} else if ( urls.contains( "bit.ly" ) ||
urls.contains( "j.mp" ) ||
urls.contains( "t.co" ) ||
urls.contains( "rd.io" ) )
{
QStringList tracks = urls.split( "\n" );
tDebug() << "Got a list of shortened urls!" << tracks;
ShortenedLinkParser* parser = new ShortenedLinkParser( tracks, this );
connect( parser, SIGNAL( urls( QStringList ) ), this, SLOT( expandedUrls( QStringList ) ) );
m_queryCount++;
}
}
void
DropJob::expandedUrls( QStringList urls )
{
m_queryCount--;
handleTrackUrls( urls.join( "\n" ) );
}
void
DropJob::onTracksAdded( const QList<Tomahawk::query_ptr>& tracksList )
{
m_resultList.append( tracksList );
if ( --m_queryCount == 0 )
{
if ( !m_allowDuplicates )
removeDuplicates();
emit tracks( m_resultList );
deleteLater();
}
}
void
DropJob::removeDuplicates()
{
QList< Tomahawk::query_ptr > list;
foreach ( const Tomahawk::query_ptr& item, m_resultList )
{
bool contains = false;
foreach( const Tomahawk::query_ptr &tmpItem, list )
if ( item->album() == tmpItem->album()
&& item->artist() == tmpItem->artist()
&& item->track() == tmpItem->track() )
contains = true;
if ( !contains )
list.append( item );
}
m_resultList = list;
}

76
src/libtomahawk/dropjob.h Normal file
View File

@@ -0,0 +1,76 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2011, Michael Zanetti <mzanetti@kde.org>
* Copyright 2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DROPJOB_H
#define DROPJOB_H
#include "query.h"
#include <QObject>
#include <QStringList>
#include <QMimeData>
class DLLEXPORT DropJob : public QObject
{
Q_OBJECT
public:
explicit DropJob( QObject *parent = 0 );
~DropJob();
/**
* QMimeData helpers
*
* Call this to parse the tracks in a QMimeData object to query_ptrs. This will parse internal tomahawk
* data as well as all other formats supported (spotify, etc).
*
* Connect to tracks( QList< query_ptr> ); for the extracted tracks.
*/
static bool acceptsMimeData( const QMimeData* data, bool tracksOnly = true );
static QStringList mimeTypes();
void tracksFromMimeData( const QMimeData* data, bool allowDuplicates = false );
signals:
/// QMimeData parsing results
void tracks( const QList< Tomahawk::query_ptr >& tracks );
private slots:
void expandedUrls( QStringList );
void onTracksAdded( const QList<Tomahawk::query_ptr>& );
private:
/// handle parsing mime data
void parseMimeData( const QMimeData* data );
void handleTrackUrls( const QString& urls );
QList< Tomahawk::query_ptr > tracksFromQueryList( const QMimeData* d );
QList< Tomahawk::query_ptr > tracksFromResultList( const QMimeData* d );
QList< Tomahawk::query_ptr > tracksFromArtistMetaData( const QMimeData* d );
QList< Tomahawk::query_ptr > tracksFromAlbumMetaData( const QMimeData* d );
QList< Tomahawk::query_ptr > tracksFromMixedData( const QMimeData* d );
void removeDuplicates();
int m_queryCount;
bool m_allowDuplicates;
QList< Tomahawk::query_ptr > m_resultList;
};
#endif // DROPJOB_H

View File

@@ -64,7 +64,6 @@ GlobalActionManager::instance()
GlobalActionManager::GlobalActionManager( QObject* parent )
: QObject( parent )
{
m_mimeTypes << "application/tomahawk.query.list" << "application/tomahawk.plentry.list" << "application/tomahawk.result.list" << "text/plain";
}
GlobalActionManager::~GlobalActionManager()
@@ -779,149 +778,6 @@ GlobalActionManager::hostname() const
return QString( "http://toma.hk" );
}
/// QMIMEDATA HANDLING
QStringList
GlobalActionManager::mimeTypes() const
{
return m_mimeTypes;
}
bool
GlobalActionManager::acceptsMimeData( const QMimeData* data, bool tracksOnly )
{
if ( data->hasFormat( "application/tomahawk.query.list" )
|| data->hasFormat( "application/tomahawk.plentry.list" )
|| data->hasFormat( "application/tomahawk.result.list" ) )
{
return true;
}
// crude check for spotify tracks
if ( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "spotify" ) &&
( tracksOnly ? data->data( "text/plain" ).contains( "track" ) : true ) )
return true;
// crude check for rdio tracks
if ( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "rdio.com" ) &&
( tracksOnly ? data->data( "text/plain" ).contains( "track" ) : true ) )
return true;
// We whitelist t.co and bit.ly (and j.mp) since they do some link checking. Often playable (e.g. spotify..) links hide behind them,
// so we do an extra level of lookup
if ( ( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "bit.ly" ) ) ||
( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "j.mp" ) ) ||
( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "t.co" ) ) ||
( data->hasFormat( "text/plain" ) && data->data( "text/plain" ).contains( "rd.io" ) ) )
return true;
return false;
}
void
GlobalActionManager::tracksFromMimeData( const QMimeData* data )
{
if ( data->hasFormat( "application/tomahawk.query.list" ) )
emit tracks( tracksFromQueryList( data ) );
else if ( data->hasFormat( "application/tomahawk.result.list" ) )
emit tracks( tracksFromResultList( data ) );
else if ( data->hasFormat( "text/plain" ) )
{
QString plainData = QString::fromUtf8( data->data( "text/plain" ).constData() );
tDebug() << "Got text/plain mime data:" << data->data( "text/plain" ) << "decoded to:" << plainData;
handleTrackUrls ( plainData );
}
}
void
GlobalActionManager::handleTrackUrls( const QString& urls )
{
if ( urls.contains( "open.spotify.com/track") ||
urls.contains( "spotify:track" ) )
{
QStringList tracks = urls.split( "\n" );
tDebug() << "Got a list of spotify urls!" << tracks;
SpotifyParser* spot = new SpotifyParser( tracks, this );
connect( spot, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ) );
} else if ( urls.contains( "rdio.com" ) )
{
QStringList tracks = urls.split( "\n" );
tDebug() << "Got a list of rdio urls!" << tracks;
RdioParser* rdio = new RdioParser( this );
connect( rdio, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ), this, SIGNAL( tracks( QList<Tomahawk::query_ptr> ) ) );
rdio->parse( tracks );
} else if ( urls.contains( "bit.ly" ) ||
urls.contains( "j.mp" ) ||
urls.contains( "t.co" ) ||
urls.contains( "rd.io" ) )
{
QStringList tracks = urls.split( "\n" );
tDebug() << "Got a list of shortened urls!" << tracks;
ShortenedLinkParser* parser = new ShortenedLinkParser( tracks, this );
connect( parser, SIGNAL( urls( QStringList ) ), this, SLOT( expandedUrls( QStringList ) ) );
}
}
void
GlobalActionManager::expandedUrls( QStringList urls )
{
handleTrackUrls( urls.join( "\n" ) );
}
QList< query_ptr >
GlobalActionManager::tracksFromQueryList( const QMimeData* data )
{
QList< query_ptr > queries;
QByteArray itemData = data->data( "application/tomahawk.query.list" );
QDataStream stream( &itemData, QIODevice::ReadOnly );
while ( !stream.atEnd() )
{
qlonglong qptr;
stream >> qptr;
query_ptr* query = reinterpret_cast<query_ptr*>(qptr);
if ( query && !query->isNull() )
{
tDebug() << "Dropped query item:" << query->data()->artist() << "-" << query->data()->track();
queries << *query;
}
}
return queries;
}
QList< query_ptr >
GlobalActionManager::tracksFromResultList( const QMimeData* data )
{
QList< query_ptr > queries;
QByteArray itemData = data->data( "application/tomahawk.result.list" );
QDataStream stream( &itemData, QIODevice::ReadOnly );
while ( !stream.atEnd() )
{
qlonglong qptr;
stream >> qptr;
result_ptr* result = reinterpret_cast<result_ptr*>(qptr);
if ( result && !result->isNull() )
{
tDebug() << "Dropped result item:" << result->data()->artist()->name() << "-" << result->data()->track();
query_ptr q = result->data()->toQuery();
q->addResults( QList< result_ptr >() << *result );
queries << q;
}
}
return queries;
}
/// SPOTIFY URL HANDLING

View File

@@ -51,18 +51,6 @@ public:
QString copyPlaylistToClipboard( const Tomahawk::dynplaylist_ptr& playlist );
void savePlaylistToFile( const Tomahawk::playlist_ptr& playlist, const QString& filename );
/**
* QMimeData helpers
*
* Call this to parse the tracks in a QMimeData object to query_ptrs. This will parse internal tomahawk
* data as well as all other formats supported (spotify, etc).
*
* Connect to tracks( QList< query_ptr> ); for the extracted tracks.
*/
bool acceptsMimeData( const QMimeData* data, bool tracksOnly = true );
void tracksFromMimeData( const QMimeData* data );
QStringList mimeTypes() const;
public slots:
bool parseTomahawkLink( const QString& link );
void waitingForResolved( bool );
@@ -70,16 +58,12 @@ public slots:
Tomahawk::dynplaylist_ptr loadDynamicPlaylist( const QUrl& url, bool station );
void handleOpenTrack( const Tomahawk::query_ptr& qry );
signals:
/// QMimeData parsing results
void tracks( const QList< Tomahawk::query_ptr >& tracks );
private slots:
void bookmarkPlaylistCreated( const Tomahawk::playlist_ptr& pl );
void showPlaylist();
void xspfCreated( const QByteArray& xspf );
void expandedUrls( QStringList );
void spotifyToPlay( const Tomahawk::query_ptr& );
private:
@@ -101,18 +85,12 @@ private:
bool playSpotify( const QUrl& url );
bool queueSpotify( const QStringList& parts, const QList< QPair< QString, QString > >& queryItems );
/// handle parsing mime data
void handleTrackUrls( const QString& urls );
QList< Tomahawk::query_ptr > tracksFromQueryList( const QMimeData* d );
QList< Tomahawk::query_ptr > tracksFromResultList( const QMimeData* d );
QString hostname() const;
Tomahawk::playlist_ptr m_toShow;
Tomahawk::query_ptr m_waitingToBookmark;
Tomahawk::query_ptr m_waitingToPlay;
QStringList m_mimeTypes;
static GlobalActionManager* s_instance;
};

View File

@@ -283,7 +283,7 @@ DBSyncConnection::lastOpApplied()
void
DBSyncConnection::sendOps()
{
tLog() << "Will send peer all ops since" << m_uscache.value( "lastop" ).toString();
tLog() << "Will send peer" << m_source->id() << "all ops since" << m_uscache.value( "lastop" ).toString();
source_ptr src = SourceList::instance()->getLocal();

View File

@@ -60,6 +60,7 @@ Servent::Servent( QObject* parent )
: QTcpServer( parent )
, m_port( 0 )
, m_externalPort( 0 )
, m_ready( false )
, m_portfwd( 0 )
{
s_instance = this;
@@ -133,6 +134,7 @@ Servent::startListening( QHostAddress ha, bool upnp, int port )
tLog() << "Forcing static preferred host and port";
m_externalHostname = TomahawkSettings::instance()->externalHostname();
m_externalPort = TomahawkSettings::instance()->externalPort();
m_ready = true;
emit ready();
return true;
}
@@ -151,11 +153,14 @@ Servent::startListening( QHostAddress ha, bool upnp, int port )
if ( qApp->arguments().contains( "--lanhack" ) )
{
qDebug() << "LANHACK: set external address to lan address" << ha.toString();
tLog() << "LANHACK: set external address to lan address" << ha.toString();
QMetaObject::invokeMethod( this, "setExternalAddress", Qt::QueuedConnection, Q_ARG( QHostAddress, ha ), Q_ARG( unsigned int, m_port ) );
}
else
{
m_ready = true;
emit ready();
}
break;
}
break;
@@ -186,7 +191,7 @@ Servent::createConnectionKey( const QString& name, const QString &nodeid, const
cc->setId( nodeid );
cc->setOnceOnly( onceOnly );
qDebug() << "Creating connection key with name of " << cc->name() << " and id of " << cc->id() << " and key of " << _key << "; key is once only? : " << (onceOnly ? "true" : "false");
qDebug() << "Creating connection key with name of" << cc->name() << "and id of" << cc->id() << "and key of" << _key << "; key is once only? :" << (onceOnly ? "true" : "false");
registerOffer( _key, cc );
return _key;
}
@@ -224,9 +229,10 @@ Servent::setExternalAddress( QHostAddress ha, unsigned int port )
qDebug() << m_externalHostname << m_externalPort;
}
else
qDebug() << "No external access, LAN and outbound connections only!";
tLog() << "No external access, LAN and outbound connections only!";
}
m_ready = true;
emit ready();
}
@@ -348,7 +354,7 @@ Servent::readyRead()
// qDebug() << con->socket() << sock;
if( con->id() == nodeid )
{
qDebug() << "Duplicate control connection detected, dropping:" << nodeid << conntype;
tLog() << "Duplicate control connection detected, dropping:" << nodeid << conntype;
goto closeconnection;
}
}
@@ -372,7 +378,7 @@ Servent::readyRead()
Connection* conn = claimOffer( cc, nodeid, key, sock->peerAddress() );
if( !conn )
{
qDebug() << "claimOffer FAILED, key:" << key;
tLog() << "claimOffer FAILED, key:" << key;
goto closeconnection;
}
qDebug() << "claimOffer OK:" << key;
@@ -390,7 +396,7 @@ Servent::readyRead()
// fallthru to cleanup:
closeconnection:
qDebug() << "Closing incoming connection, something was wrong.";
tLog() << "Closing incoming connection, something was wrong.";
sock->_msg.clear();
sock->disconnectFromHost();
}
@@ -405,7 +411,7 @@ Servent::createParallelConnection( Connection* orig_conn, Connection* new_conn,
// if we can connect to them directly:
if( orig_conn && orig_conn->outbound() )
{
qDebug() << "Connecting directly";
tLog() << "Connecting directly";
connectToPeer( orig_conn->socket()->peerAddress().toString(),
orig_conn->peerPort(),
key,
@@ -414,7 +420,7 @@ Servent::createParallelConnection( Connection* orig_conn, Connection* new_conn,
else // ask them to connect to us:
{
QString tmpkey = uuid();
qDebug() << "Asking them to connect to us using" << tmpkey ;
tLog() << "Asking them to connect to us using" << tmpkey ;
registerOffer( tmpkey, new_conn );
QVariantMap m;
@@ -470,12 +476,18 @@ Servent::socketError( QAbstractSocket::SocketError e )
QTcpSocketExtra* sock = (QTcpSocketExtra*)sender();
if( !sock )
{
qDebug() << "SocketError, sock is null";
tLog() << "SocketError, sock is null";
return;
}
Connection* conn = sock->_conn;
qDebug() << "Servent::SocketError:" << e << conn->id() << conn->name();
if ( !conn )
{
tLog() << "SocketError, connection is null";
return;
}
tLog() << "Servent::SocketError:" << e << conn->id() << conn->name();
if( !sock->_disowned )
{
// connection will delete if we already transferred ownership, otherwise:
@@ -601,7 +613,7 @@ Servent::claimOffer( ControlConnection* cc, const QString &nodeid, const QString
}
if( !authed )
{
qDebug() << "File transfer request rejected, invalid source IP";
tLog() << "File transfer request rejected, invalid source IP";
return NULL;
}
}
@@ -635,7 +647,7 @@ Servent::claimOffer( ControlConnection* cc, const QString &nodeid, const QString
// This can happen if it's a streamconnection, but the audioengine has
// already closed the iodevice, causing the connection to be deleted before
// the peer connects and provides the first byte
qDebug() << Q_FUNC_INFO << "invalid/expired offer:" << key;
tLog() << Q_FUNC_INFO << "invalid/expired offer:" << key;
return NULL;
}
@@ -644,12 +656,12 @@ Servent::claimOffer( ControlConnection* cc, const QString &nodeid, const QString
// If there isn't a nodeid it's not the first connection and will already have been stopped
if( !checkACL( conn, nodeid, true ) )
{
qDebug() << "Connection not allowed due to ACL";
tLog() << "Connection not allowed due to ACL";
return NULL;
}
}
qDebug() << "ACL has allowed the connection";
tLog() << "ACL has allowed the connection";
if( conn->onceOnly() )
{
@@ -670,7 +682,7 @@ Servent::claimOffer( ControlConnection* cc, const QString &nodeid, const QString
}
else
{
qDebug() << "Invalid offer key:" << key;
tLog() << "Invalid offer key:" << key;
return NULL;
}
}
@@ -823,7 +835,6 @@ Servent::isIPWhitelisted( QHostAddress ip )
bool
Servent::connectedToSession( const QString& session )
{
qDebug() << Q_FUNC_INFO;
// qDebug() << "Checking against" << session;
foreach( ControlConnection* cc, m_controlconnections )
{
@@ -839,8 +850,6 @@ Servent::connectedToSession( const QString& session )
void
Servent::triggerDBSync()
{
qDebug() << Q_FUNC_INFO;
// tell peers we have new stuff they should sync
QList<source_ptr> sources = SourceList::instance()->sources();
foreach( const source_ptr& src, sources )
@@ -866,7 +875,6 @@ Servent::registerIODeviceFactory( const QString &proto, boost::function<QSharedP
QSharedPointer<QIODevice>
Servent::getIODeviceForUrl( const Tomahawk::result_ptr& result )
{
qDebug() << Q_FUNC_INFO << thread();
QSharedPointer<QIODevice> sp;
QRegExp rx( "^([a-zA-Z0-9]+)://(.+)$" );
@@ -897,7 +905,6 @@ Servent::localFileIODeviceFactory( const Tomahawk::result_ptr& result )
QSharedPointer<QIODevice>
Servent::httpIODeviceFactory( const Tomahawk::result_ptr& result )
{
qDebug() << Q_FUNC_INFO << result->url();
QNetworkRequest req( result->url() );
QNetworkReply* reply = TomahawkUtils::nam()->get( req );
return QSharedPointer<QIODevice>( reply, &QObject::deleteLater );

View File

@@ -1,5 +1,5 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
@@ -123,6 +123,8 @@ public:
QSharedPointer<QIODevice> localFileIODeviceFactory( const Tomahawk::result_ptr& result );
QSharedPointer<QIODevice> httpIODeviceFactory( const Tomahawk::result_ptr& result );
bool isReady() const { return m_ready; };
signals:
void streamStarted( StreamConnection* );
void streamFinished( StreamConnection* );
@@ -159,6 +161,7 @@ private:
int m_port, m_externalPort;
QHostAddress m_externalAddress;
QString m_externalHostname;
bool m_ready;
// currently active file transfers:
QList< StreamConnection* > m_scsessions;

View File

@@ -116,11 +116,13 @@ Pipeline::resolve( const QList<query_ptr>& qlist, bool prioritized, bool tempora
int i = 0;
foreach( const query_ptr& q, qlist )
{
if ( !m_qids.contains( q->id() ) )
m_qids.insert( q->id(), q );
if ( m_queries_pending.contains( q ) )
continue;
if ( m_qidsState.contains( q->id() ) )
continue;
if ( !m_qids.contains( q->id() ) )
m_qids.insert( q->id(), q );
if ( prioritized )
m_queries_pending.insert( i++, q );
@@ -169,8 +171,7 @@ Pipeline::reportResults( QID qid, const QList< result_ptr >& results )
if ( !m_qids.contains( qid ) )
{
qDebug() << "reportResults called for unknown QID" << qid;
Q_ASSERT( false );
tDebug() << "Result arrived too late for:" << qid;
return;
}
@@ -183,35 +184,14 @@ Pipeline::reportResults( QID qid, const QList< result_ptr >& results )
m_rids.insert( r->id(), r );
}
if ( q->solved() && !q->isFullTextQuery() )
if ( q->playable() && !q->isFullTextQuery() )
{
q->onResolvingFinished();
setQIDState( q, 0 );
if ( m_qidsTimeout.contains( q->id() ) )
m_qidsTimeout.remove( q->id() );
shuntNext();
return;
}
}
if ( decQIDState( q ) == 0 )
{
if ( !q->solved() || q->isFullTextQuery() )
q->onResolvingFinished();
if ( !m_queries_temporary.contains( q ) )
m_qids.remove( q->id() );
if ( m_qidsTimeout.contains( q->id() ) )
m_qidsTimeout.remove( q->id() );
shuntNext();
}
else
{
new FuncTimeout( 0, boost::bind( &Pipeline::timeoutShunt, this, q ), this );
}
decQIDState( q );
}
@@ -247,7 +227,6 @@ Pipeline::shuntNext()
}
setQIDState( q, rc );
new FuncTimeout( 0, boost::bind( &Pipeline::shunt, this, q ), this );
}
@@ -260,8 +239,7 @@ Pipeline::timeoutShunt( const query_ptr& q )
// are we still waiting for a timeout?
if ( m_qidsTimeout.contains( q->id() ) )
{
m_qidsTimeout.remove( q->id() );
shunt( q );
decQIDState( q );
}
}
@@ -278,7 +256,7 @@ Pipeline::shunt( const query_ptr& q )
if ( r )
{
qDebug() << "Dispatching to resolver" << r->name() << q->toString() << q->solved() << q->id();
tDebug() << "Dispatching to resolver" << r->name() << q->toString() << q->solved() << q->id();
q->setCurrentResolver( r );
r->resolve( q );
@@ -291,7 +269,9 @@ Pipeline::shunt( const query_ptr& q )
}
else
{
// we get here if we disable a resolver while a query is resolving
setQIDState( q, 0 );
return;
}
shuntNext();
@@ -327,14 +307,24 @@ Pipeline::setQIDState( const Tomahawk::query_ptr& query, int state )
{
QMutexLocker lock( &m_mut );
if ( m_qidsTimeout.contains( query->id() ) )
m_qidsTimeout.remove( query->id() );
if ( state > 0 )
{
m_qidsState.insert( query->id(), state );
new FuncTimeout( 0, boost::bind( &Pipeline::shunt, this, query ), this );
}
else
{
m_qidsState.remove( query->id() );
// qDebug() << "Queries running:" << m_qidsState.count();
query->onResolvingFinished();
if ( !m_queries_temporary.contains( query ) )
m_qids.remove( query->id() );
new FuncTimeout( 0, boost::bind( &Pipeline::shuntNext, this ), this );
}
}
@@ -358,22 +348,17 @@ Pipeline::incQIDState( const Tomahawk::query_ptr& query )
int
Pipeline::decQIDState( const Tomahawk::query_ptr& query )
{
QMutexLocker lock( &m_mut );
if ( !m_qidsState.contains( query->id() ) )
return 0;
int state = m_qidsState.value( query->id() ) - 1;
if ( state )
int state = 0;
{
m_qidsState.insert( query->id(), state );
}
else
{
m_qidsState.remove( query->id() );
// qDebug() << "Queries running:" << m_qidsState.count();
QMutexLocker lock( &m_mut );
if ( !m_qidsState.contains( query->id() ) )
return 0;
state = m_qidsState.value( query->id() ) - 1;
}
setQIDState( query, state );
return state;
}

View File

@@ -398,12 +398,6 @@ Playlist::setNewRevision( const QString& rev,
QList<plentry_ptr> entries;
foreach( const QString& id, neworderedguids )
{
/* qDebug() << "id:" << id;
qDebug() << "newordered:" << neworderedguids.count() << neworderedguids;
qDebug() << "entriesmap:" << entriesmap.count() << entriesmap;
qDebug() << "addedmap:" << addedmap.count() << addedmap;
qDebug() << "m_entries" << m_entries; */
if( entriesmap.contains( id ) )
{
entries.append( entriesmap.value( id ) );
@@ -416,6 +410,13 @@ Playlist::setNewRevision( const QString& rev,
}
else
{
/* qDebug() << "id:" << id;
* qDebug() << "newordered:" << neworderedguids.count() << neworderedguids;
* qDebug() << "entriesmap:" << entriesmap.count() << entriesmap;
* qDebug() << "addedmap:" << addedmap.count() << addedmap;
* qDebug() << "m_entries" << m_entries; */
tLog() << "Playlist error for playlist with guid" << guid() << "from source" << author()->friendlyName();
Q_ASSERT( false ); // XXX
}
}

View File

@@ -35,6 +35,7 @@ using namespace Tomahawk;
AlbumModel::AlbumModel( QObject* parent )
: QAbstractItemModel( parent )
, m_rootItem( new AlbumItem( 0, this ) )
, m_overwriteOnAdd( false )
{
qDebug() << Q_FUNC_INFO;
@@ -159,6 +160,7 @@ AlbumModel::headerData( int section, Qt::Orientation orientation, int role ) con
Qt::ItemFlags
AlbumModel::flags( const QModelIndex& index ) const
{
qDebug() << "asking for flags for index" << index;
Qt::ItemFlags defaultFlags = QAbstractItemModel::flags( index );
if ( index.isValid() && index.column() == 0 )
@@ -195,12 +197,13 @@ AlbumModel::mimeData( const QModelIndexList &indexes ) const
if ( item )
{
const album_ptr& album = item->album();
queryStream << qlonglong( &album );
queryStream << album->artist()->name();
queryStream << album->name();
}
}
QMimeData* mimeData = new QMimeData();
mimeData->setData( "application/tomahawk.query.list", queryData );
mimeData->setData( "application/tomahawk.metadata.album", queryData );
return mimeData;
}
@@ -235,13 +238,14 @@ AlbumModel::removeIndexes( const QList<QModelIndex>& indexes )
void
AlbumModel::addCollection( const collection_ptr& collection )
AlbumModel::addCollection( const collection_ptr& collection, bool overwrite )
{
qDebug() << Q_FUNC_INFO << collection->name()
<< collection->source()->id()
<< collection->source()->userName();
DatabaseCommand_AllAlbums* cmd = new DatabaseCommand_AllAlbums( collection );
m_overwriteOnAdd = overwrite;
connect( cmd, SIGNAL( albums( QList<Tomahawk::album_ptr>, QVariant ) ),
SLOT( addAlbums( QList<Tomahawk::album_ptr> ) ) );
@@ -253,7 +257,7 @@ AlbumModel::addCollection( const collection_ptr& collection )
void
AlbumModel::addFilteredCollection( const collection_ptr& collection, unsigned int amount, DatabaseCommand_AllAlbums::SortOrder order )
AlbumModel::addFilteredCollection( const collection_ptr& collection, unsigned int amount, DatabaseCommand_AllAlbums::SortOrder order, bool overwrite )
{
/* qDebug() << Q_FUNC_INFO << collection->name()
<< collection->source()->id()
@@ -264,6 +268,7 @@ AlbumModel::addFilteredCollection( const collection_ptr& collection, unsigned in
cmd->setLimit( amount );
cmd->setSortOrder( order );
cmd->setSortDescending( true );
m_overwriteOnAdd = overwrite;
connect( cmd, SIGNAL( albums( QList<Tomahawk::album_ptr>, QVariant ) ),
SLOT( addAlbums( QList<Tomahawk::album_ptr> ) ) );
@@ -283,6 +288,9 @@ AlbumModel::addAlbums( const QList<Tomahawk::album_ptr>& albums )
if ( !albums.count() )
return;
if ( m_overwriteOnAdd )
clear();
int c = rowCount( QModelIndex() );
QPair< int, int > crows;
crows.first = c;

View File

@@ -63,8 +63,8 @@ public:
virtual QStringList mimeTypes() const;
virtual Qt::ItemFlags flags( const QModelIndex& index ) const;
void addCollection( const Tomahawk::collection_ptr& collection );
void addFilteredCollection( const Tomahawk::collection_ptr& collection, unsigned int amount, DatabaseCommand_AllAlbums::SortOrder order );
void addCollection( const Tomahawk::collection_ptr& collection, bool overwrite = false );
void addFilteredCollection( const Tomahawk::collection_ptr& collection, unsigned int amount, DatabaseCommand_AllAlbums::SortOrder order, bool overwrite = false );
void clear();
@@ -109,6 +109,7 @@ private:
QString m_title;
QString m_description;
bool m_overwriteOnAdd;
};
#endif // ALBUMMODEL_H

View File

@@ -188,27 +188,6 @@ AlbumView::onScrollTimeout()
}
void
AlbumView::dragEnterEvent( QDragEnterEvent* event )
{
QListView::dragEnterEvent( event );
}
void
AlbumView::dragMoveEvent( QDragMoveEvent* event )
{
QListView::dragMoveEvent( event );
}
void
AlbumView::dropEvent( QDropEvent* event )
{
QListView::dropEvent( event );
}
void
AlbumView::paintEvent( QPaintEvent* event )
{
@@ -227,14 +206,30 @@ AlbumView::onFilterChanged( const QString& )
void
AlbumView::startDrag( Qt::DropActions supportedActions )
{
Q_UNUSED( supportedActions );
}
QList<QPersistentModelIndex> pindexes;
QModelIndexList indexes;
foreach( const QModelIndex& idx, selectedIndexes() )
{
if ( ( m_proxyModel->flags( idx ) & Qt::ItemIsDragEnabled ) )
{
indexes << idx;
pindexes << idx;
}
}
if ( indexes.count() == 0 )
return;
// Inspired from dolphin's draganddrophelper.cpp
QPixmap
AlbumView::createDragPixmap( int itemCount ) const
{
Q_UNUSED( itemCount );
return QPixmap();
qDebug() << "Dragging" << indexes.count() << "indexes";
QMimeData* data = m_proxyModel->mimeData( indexes );
if ( !data )
return;
QDrag* drag = new QDrag( this );
drag->setMimeData( data );
const QPixmap p = TomahawkUtils::createDragPixmap( indexes.count() );
drag->setPixmap( p );
drag->setHotSpot( QPoint( -20, -20 ) );
Qt::DropAction action = drag->exec( supportedActions, Qt::CopyAction );
}

View File

@@ -60,9 +60,6 @@ public slots:
protected:
virtual void startDrag( Qt::DropActions supportedActions );
virtual void dragEnterEvent( QDragEnterEvent* event );
virtual void dragMoveEvent( QDragMoveEvent* event );
virtual void dropEvent( QDropEvent* event );
void paintEvent( QPaintEvent* event );
@@ -73,8 +70,6 @@ private slots:
void onScrollTimeout();
private:
QPixmap createDragPixmap( int itemCount ) const;
AlbumModel* m_model;
AlbumProxyModel* m_proxyModel;
// PlaylistItemDelegate* m_delegate;

View File

@@ -412,7 +412,7 @@ query_ptr
EchonestGenerator::queryFromSong( const Echonest::Song& song )
{
// track[ "album" ] = song.release(); // TODO should we include it? can be quite specific
return Query::get( song.artistName(), song.title(), QString(), uuid() );
return Query::get( song.artistName(), song.title(), QString(), uuid(), false );
}

View File

@@ -1,5 +1,5 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
@@ -21,31 +21,33 @@
#include <QWidget>
#include "dllmacro.h"
class QMovie;
class QTimeLine;
/**
* A small widget that displays an animated loading spinner
*/
class LoadingSpinner : public QWidget {
class DLLEXPORT LoadingSpinner : public QWidget {
Q_OBJECT
public:
LoadingSpinner( QWidget* parent );
virtual ~LoadingSpinner();
virtual QSize sizeHint() const;
virtual void paintEvent( QPaintEvent* );
virtual void resizeEvent( QResizeEvent* );
public slots:
public slots:
void fadeIn();
void fadeOut();
private slots:
void hideFinished();
private:
void reposition();
QTimeLine* m_showHide;
QMovie* m_anim;
};

View File

@@ -27,7 +27,7 @@
#include "database/databasecommand_playbackhistory.h"
#include "dynamic/GeneratorInterface.h"
#include "utils/logger.h"
#include "globalactionmanager.h"
#include "dropjob.h"
using namespace Tomahawk;
@@ -358,14 +358,15 @@ PlaylistModel::dropMimeData( const QMimeData* data, Qt::DropAction action, int r
if ( action == Qt::IgnoreAction || isReadOnly() )
return true;
if ( !GlobalActionManager::instance()->acceptsMimeData( data ) )
if ( !DropJob::acceptsMimeData( data ) )
return false;
m_dropStorage.row = row;
m_dropStorage.parent = QPersistentModelIndex( parent );
m_dropStorage.action = action;
connect( GlobalActionManager::instance(), SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) );
GlobalActionManager::instance()->tracksFromMimeData( data );
DropJob *dj = new DropJob();
connect( dj, SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) );
dj->tracksFromMimeData( data );
return true;
}
@@ -377,8 +378,6 @@ PlaylistModel::parsedDroppedTracks( QList< query_ptr > tracks )
if ( m_dropStorage.row == -10 ) // nope
return;
disconnect( GlobalActionManager::instance(), SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) );
int beginRow;
if ( m_dropStorage.row != -1 )
beginRow = m_dropStorage.row;

View File

@@ -62,8 +62,10 @@ PlaylistView::setPlaylistModel( PlaylistModel* model )
TrackView::setTrackModel( m_model );
setColumnHidden( TrackModel::Age, true ); // Hide age column per default
if ( !m_model->playlist().isNull() )
if ( guid().isEmpty() && !m_model->playlist().isNull() )
{
setGuid( QString( "playlistview/%1/%2" ).arg( m_model->columnCount() ).arg( m_model->playlist()->guid() ) );
}
else
{
setGuid( QString( "playlistview/%1" ).arg( m_model->columnCount() ) );

View File

@@ -110,6 +110,7 @@ void SearchButton::paintEvent(QPaintEvent *event)
if (m_cache.isNull())
m_cache = generateSearchImage(m_showMenuTriangle);
QPainter painter(this);
painter.setRenderHint(QPainter::SmoothPixmapTransform);
painter.drawImage(QPoint(0, 0), m_cache);
}

View File

@@ -71,11 +71,11 @@ TrackHeader::visibleSectionCount() const
}
void
bool
TrackHeader::checkState()
{
if ( !count() || m_init )
return;
return false;
QByteArray state = TomahawkSettings::instance()->playlistColumnSizes( m_parent->guid() );
if ( !state.isEmpty() )
@@ -102,6 +102,7 @@ TrackHeader::checkState()
}
m_init = true;
return true;
}

View File

@@ -1,5 +1,5 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
@@ -40,7 +40,7 @@ public:
public slots:
void toggleVisibility( int index );
void checkState();
bool checkState();
protected:
void contextMenuEvent( QContextMenuEvent* e );

View File

@@ -177,7 +177,7 @@ Tomahawk::result_ptr
TrackProxyModel::currentItem() const
{
TrackModelItem* item = itemFromIndex( mapToSource( currentIndex() ) );
if ( item && item->query()->playable() )
if ( item && !item->query().isNull() && item->query()->playable() )
return item->query()->results().at( 0 );
return Tomahawk::result_ptr();
}
@@ -318,8 +318,8 @@ TrackProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) c
unsigned int albumpos1 = 0, albumpos2 = 0;
unsigned int bitrate1 = 0, bitrate2 = 0;
unsigned int mtime1 = 0, mtime2 = 0;
unsigned int id1 = 0, id2 = 0;
unsigned int size1 = 0, size2 = 0;
qint64 id1 = 0, id2 = 0;
if ( q1->numResults() )
{
@@ -346,6 +346,13 @@ TrackProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) c
size2 = r->size();
}
// This makes it a stable sorter and prevents items from randomly jumping about.
if ( id1 == id2 )
{
id1 = (qint64)&q1;
id2 = (qint64)&q2;
}
if ( left.column() == TrackModel::Artist ) // sort by artist
{
if ( artist1 == artist2 )
@@ -396,6 +403,11 @@ TrackProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) c
return size1 < size2;
}
return QString::localeAwareCompare( sourceModel()->data( left ).toString(),
sourceModel()->data( right ).toString() ) < 0;
const QString& lefts = sourceModel()->data( left ).toString();
const QString& rights = sourceModel()->data( right ).toString();
if ( lefts == rights )
return id1 < id2;
return QString::localeAwareCompare( lefts, rights ) < 0;
}

View File

@@ -33,7 +33,7 @@
#include "dynamic/widgets/LoadingSpinner.h"
#include "utils/tomahawkutils.h"
#include "utils/logger.h"
#include <globalactionmanager.h>
#include "dropjob.h"
using namespace Tomahawk;
@@ -62,8 +62,7 @@ TrackView::TrackView( QWidget* parent )
setVerticalScrollMode( QAbstractItemView::ScrollPerPixel );
setRootIsDecorated( false );
setUniformRowHeights( true );
setMinimumWidth( 300 );
// setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn );
setMinimumWidth( 200 );
setHeader( m_header );
setSortingEnabled( true );
@@ -204,7 +203,15 @@ void
TrackView::resizeEvent( QResizeEvent* event )
{
QTreeView::resizeEvent( event );
m_header->checkState();
int sortSection = m_header->sortIndicatorSection();
Qt::SortOrder sortOrder = m_header->sortIndicatorOrder();
if ( m_header->checkState() && sortSection >= 0 )
{
// restoreState keeps overwriting our previous sort-order
sortByColumn( sortSection, sortOrder );
}
}
@@ -214,7 +221,7 @@ TrackView::dragEnterEvent( QDragEnterEvent* event )
qDebug() << Q_FUNC_INFO;
QTreeView::dragEnterEvent( event );
if ( GlobalActionManager::instance()->acceptsMimeData( event->mimeData() ) )
if ( DropJob::acceptsMimeData( event->mimeData() ) )
{
m_dragging = true;
m_dropRect = QRect();
@@ -236,7 +243,7 @@ TrackView::dragMoveEvent( QDragMoveEvent* event )
return;
}
if ( GlobalActionManager::instance()->acceptsMimeData( event->mimeData() ) )
if ( DropJob::acceptsMimeData( event->mimeData() ) )
{
setDirtyRegion( m_dropRect );
const QPoint pos = event->pos();
@@ -278,7 +285,7 @@ TrackView::dropEvent( QDropEvent* event )
}
else
{
if ( GlobalActionManager::instance()->acceptsMimeData( event->mimeData() ) )
if ( DropJob::acceptsMimeData( event->mimeData() ) )
{
const QPoint pos = event->pos();
const QModelIndex index = indexAt( pos );

View File

@@ -293,6 +293,8 @@ TreeModel::flags( const QModelIndex& index ) const
TreeModelItem* item = itemFromIndex( index );
if ( item && !item->result().isNull() )
return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
if ( item && ( !item->album().isNull() || !item->artist().isNull() ) )
return Qt::ItemIsDragEnabled | defaultFlags;
}
return defaultFlags;
@@ -303,7 +305,7 @@ QStringList
TreeModel::mimeTypes() const
{
QStringList types;
types << "application/tomahawk.result.list";
types << "application/tomahawk.mixed";
return types;
}
@@ -316,23 +318,124 @@ TreeModel::mimeData( const QModelIndexList &indexes ) const
QByteArray resultData;
QDataStream resultStream( &resultData, QIODevice::WriteOnly );
foreach ( const QModelIndex& i, indexes )
// lets try with artist only
bool fail = false;
foreach ( const QModelIndex& i, indexes)
{
if ( i.column() > 0 )
if ( i.column() > 0 || indexes.contains( i.parent() ) )
continue;
QModelIndex idx = index( i.row(), 0, i.parent() );
TreeModelItem* item = itemFromIndex( idx );
if ( item && !item->result().isNull() )
TreeModelItem* item = itemFromIndex( i );
if ( !item )
continue;
if ( !item->artist().isNull() )
{
const artist_ptr& artist = item->artist();
resultStream << artist->name();
}
else
{
fail = true;
break;
}
}
if ( !fail )
{
QMimeData* mimeData = new QMimeData();
mimeData->setData( "application/tomahawk.metadata.artist", resultData );
return mimeData;
}
// lets try with album only
fail = false;
foreach ( const QModelIndex& i, indexes)
{
if ( i.column() > 0 || indexes.contains( i.parent() ) )
continue;
TreeModelItem* item = itemFromIndex( i );
if ( !item )
continue;
if ( !item->album().isNull() )
{
const album_ptr& album = item->album();
resultStream << album->artist()->name();
resultStream << album->name();
}
else
{
fail = true;
break;
}
}
if ( !fail )
{
QMimeData* mimeData = new QMimeData();
mimeData->setData( "application/tomahawk.metadata.album", resultData );
return mimeData;
}
// lets try with tracks only
fail = false;
foreach ( const QModelIndex& i, indexes)
{
if ( i.column() > 0 || indexes.contains( i.parent() ) )
continue;
TreeModelItem* item = itemFromIndex( i );
if ( !item )
continue;
if ( !item->result().isNull() )
{
const result_ptr& result = item->result();
resultStream << qlonglong( &result );
}
else
{
fail = true;
break;
}
}
if ( !fail )
{
QMimeData* mimeData = new QMimeData();
mimeData->setData( "application/tomahawk.result.list", resultData );
return mimeData;
}
// Ok... we have to use mixed
foreach ( const QModelIndex& i, indexes )
{
if ( i.column() > 0 || indexes.contains( i.parent() ) )
continue;
TreeModelItem* item = itemFromIndex( i );
if ( !item )
continue;
if ( !item->artist().isNull() )
{
const artist_ptr& artist = item->artist();
resultStream << QString( "application/tomahawk.metadata.artist" ) << artist->name();
}
else if ( !item->album().isNull() )
{
const album_ptr& album = item->album();
resultStream << QString( "application/tomahawk.metadata.album" ) << album->artist()->name() << album->name();
}
else if ( !item->result().isNull() )
{
const result_ptr& result = item->result();
resultStream << QString( "application/tomahawk.result.list" ) << qlonglong( &result );
}
}
QMimeData* mimeData = new QMimeData();
mimeData->setData( "application/tomahawk.result.list", resultData );
mimeData->setData( "application/tomahawk.mixed", resultData );
return mimeData;
}
@@ -513,6 +616,8 @@ void
TreeModel::onAlbumsAdded( const QList<Tomahawk::album_ptr>& albums, const QVariant& data )
{
qDebug() << Q_FUNC_INFO << albums.count() << data.toInt();
emit loadingFinished();
if ( !albums.count() )
return;
@@ -555,8 +660,6 @@ TreeModel::onAlbumsAdded( const QList<Tomahawk::album_ptr>& albums, const QVaria
emit endInsertRows();
else
emit dataChanged( albumitem->index, albumitem->index.sibling( albumitem->index.row(), columnCount( QModelIndex() ) - 1 ) );
emit loadingFinished();
}
@@ -564,6 +667,8 @@ void
TreeModel::onTracksAdded( const QList<Tomahawk::query_ptr>& tracks, const QVariant& data )
{
qDebug() << Q_FUNC_INFO << tracks.count();
emit loadingFinished();
if ( !tracks.count() )
return;
@@ -597,8 +702,6 @@ TreeModel::onTracksAdded( const QList<Tomahawk::query_ptr>& tracks, const QVaria
emit endInsertRows();
emit dataChanged( item->index.sibling( 0, 0 ), item->index.sibling( item->index.row(), columnCount( QModelIndex() ) - 1 ) );
emit loadingFinished();
}

View File

@@ -81,8 +81,11 @@ TreeProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent
QList< Tomahawk::result_ptr > rl = m_cache.values( sourceParent );
foreach ( const Tomahawk::result_ptr& result, rl )
{
if ( result->track() == pi->result()->track() )
if ( result->track() == pi->result()->track() &&
( result->albumpos() == pi->result()->albumpos() || result->albumpos() == 0 ) )
{
return ( result.data() == pi->result().data() );
}
}
for ( int i = 0; i < sourceModel()->rowCount( sourceParent ); i++ )
@@ -91,7 +94,9 @@ TreeProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent
continue;
TreeModelItem* ti = sourceModel()->itemFromIndex( sourceModel()->index( i, 0, sourceParent ) );
if ( ti->result()->track() == pi->result()->track() )
if ( ti->result()->track() == pi->result()->track() &&
( ti->result()->albumpos() == pi->result()->albumpos() || ti->result()->albumpos() == 0 ) )
{
if ( !pi->result()->isOnline() && ti->result()->isOnline() )
return false;
@@ -133,13 +138,19 @@ TreeProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) co
if ( !p2 )
return false;
const QString& lefts = textForItem( p1 );
const QString& rights = textForItem( p2 );
if ( !p1->result().isNull() )
{
if ( p1->result()->albumpos() != p2->result()->albumpos() )
return p1->result()->albumpos() < p2->result()->albumpos();
if ( lefts == rights )
return (qint64)&p1 < (qint64)&p2;
}
return QString::localeAwareCompare( textForItem( p1 ), textForItem( p2 ) ) < 0;
return QString::localeAwareCompare( lefts, rights ) < 0;
}

View File

@@ -176,6 +176,8 @@ Query::onResolvingFinished()
if ( !m_resolveFinished )
{
m_resolveFinished = true;
m_resolvers.clear();
emit resolvingFinished( m_solved );
}
}

View File

@@ -18,15 +18,17 @@
#include "tomahawkutils.h"
#include <QCoreApplication>
#include <QColor>
#include <QDateTime>
#include <QDir>
#include <QLayout>
#include <QPainter>
#include <QPixmap>
#include <QNetworkAccessManager>
#include <QNetworkProxy>
#include "headlesscheck.h"
#include <QtCore/QCoreApplication>
#include <QtGui/QColor>
#include <QtCore/QDateTime>
#include <QtCore/QDir>
#include <QtGui/QLayout>
#include <QtGui/QPainter>
#include <QtGui/QPixmap>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkProxy>
#ifdef WIN32
#include <windows.h>
@@ -38,6 +40,21 @@
#include <sys/sysctl.h>
#endif
#ifndef TOMAHAWK_HEADLESS
#include <QtGui/QApplication>
#include <QtGui/QWidget>
#ifdef Q_WS_X11
#include <QtGui/QX11Info>
#include <libqnetwm/netwm.h>
#endif
#ifdef Q_WS_WIN
#include <windows.h>
#include <windowsx.h>
#endif
#endif
#include <tomahawksettings.h>
#include "utils/logger.h"
#include "config.h"
@@ -506,13 +523,90 @@ setNam( QNetworkAccessManager* nam )
}
#ifndef TOMAHAWK_HEADLESS
QWidget*
tomahawkWindow()
{
QWidgetList widgetList = qApp->topLevelWidgets();
int i = 0;
while( i < widgetList.count() && widgetList.at( i )->objectName() != "TH_Main_Window" )
i++;
if ( i == widgetList.count() )
{
qDebug() << Q_FUNC_INFO << "could not find main Tomahawk mainwindow";
Q_ASSERT( false );
return 0;
}
QWidget *widget = widgetList.at( i );
return widget;
}
#ifndef Q_OS_MAC
void
bringToFront()
{
#if defined(Q_WS_X11)
{
qDebug() << Q_FUNC_INFO;
QWidget* widget = tomahawkWindow();
if ( !widget )
return;
widget->show();
widget->activateWindow();
widget->raise();
WId wid = widget->winId();
NETWM::init();
XEvent e;
e.xclient.type = ClientMessage;
e.xclient.message_type = NETWM::NET_ACTIVE_WINDOW;
e.xclient.display = QX11Info::display();
e.xclient.window = wid;
e.xclient.format = 32;
e.xclient.data.l[0] = 2;
e.xclient.data.l[1] = QX11Info::appTime();
e.xclient.data.l[2] = 0;
e.xclient.data.l[3] = 0l;
e.xclient.data.l[4] = 0l;
XSendEvent( QX11Info::display(), RootWindow( QX11Info::display(), DefaultScreen( QX11Info::display() ) ), False, SubstructureRedirectMask | SubstructureNotifyMask, &e );
}
#elif defined(Q_WS_WIN)
{
qDebug() << Q_FUNC_INFO;
QWidget* widget = tomahawkWindow();
if ( !widget )
return;
widget->show();
widget->activateWindow();
widget->raise();
WId wid = widget->winId();
HWND hwndActiveWin = GetForegroundWindow();
int idActive = GetWindowThreadProcessId(hwndActiveWin, NULL);
if ( AttachThreadInput(GetCurrentThreadId(), idActive, TRUE) )
{
SetForegroundWindow( wid );
SetFocus( wid );
AttachThreadInput(GetCurrentThreadId(), idActive, FALSE);
}
}
#endif
}
#endif
#endif
QPixmap
createAvatarFrame( const QPixmap &avatar )

View File

@@ -21,11 +21,11 @@
#include "dllmacro.h"
#include <QObject>
#include <QThread>
#include <QNetworkProxy>
#include <QStringList>
#include <QRect>
#include <QtCore/QObject>
#include <QtCore/QThread>
#include <QtNetwork/QNetworkProxy>
#include <QtCore/QStringList>
#include <QtCore/QRect>
#define RESPATH ":/data/"
@@ -86,6 +86,7 @@ namespace TomahawkUtils
DLLEXPORT void setProxyFactory( TomahawkUtils::NetworkProxyFactory* factory );
DLLEXPORT void setNam( QNetworkAccessManager* nam );
DLLEXPORT QWidget* tomahawkWindow();
/// Platform-specific bringing tomahawk mainwindow to front, b/c qt's activate() and such don't seem to work well enough for us
DLLEXPORT void bringToFront();

View File

@@ -0,0 +1,66 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "SeekSlider.h"
#include <QMouseEvent>
#include "utils/tomahawkutils.h"
#include "utils/logger.h"
SeekSlider::SeekSlider( QWidget* parent )
: QSlider( parent )
{
setFixedHeight( 20 );
setStyleSheet( "QSlider::groove::horizontal {"
"margin: 5px; border-width: 3px;"
"border-image: url(" RESPATH "images/seek-slider-bkg.png) 3 3 3 3 stretch stretch;"
"}"
"QSlider::sub-page:horizontal {"
"margin: 5px; border-width: 3px;"
"border-image: url(" RESPATH "images/seek-slider-level.png) 3 3 3 3 stretch stretch;"
"}"
"QSlider::handle::horizontal {"
"margin-bottom: -7px; margin-top: -7px;"
"margin-left: -4px; margin-right: -4px;"
"height: 17px; width: 16px;"
"background-image: url(" RESPATH "images/seek-and-volume-knob-rest.png);"
"background-repeat: no-repeat;"
"}" );
}
SeekSlider::~SeekSlider()
{
}
void
SeekSlider::mousePressEvent( QMouseEvent* event )
{
if ( event->button() == Qt::LeftButton )
{
QMouseEvent eventSwap( QEvent::MouseButtonRelease, event->pos(), event->globalPos(), Qt::MidButton, Qt::MidButton, event->modifiers() );
QSlider::mousePressEvent( &eventSwap );
}
else
QSlider::mousePressEvent( event );
}

View File

@@ -0,0 +1,38 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SEEKSLIDER_H
#define SEEKSLIDER_H
#include <QSlider>
#include "dllmacro.h"
class DLLEXPORT SeekSlider : public QSlider
{
Q_OBJECT
public:
SeekSlider( QWidget* parent = 0 );
~SeekSlider();
protected:
void mousePressEvent( QMouseEvent* event );
};
#endif // SEEKSLIDER_H

View File

@@ -31,15 +31,13 @@
#include "widgets/overlaywidget.h"
static QString s_aiInfoIdentifier = QString( "ArtistInfoWidget" );
using namespace Tomahawk;
ArtistInfoWidget::ArtistInfoWidget( const Tomahawk::artist_ptr& artist, QWidget* parent )
: QWidget( parent )
, ui( new Ui::ArtistInfoWidget )
, m_artist( artist )
, m_infoId( uuid() )
{
ui->setupUi( this );
@@ -95,7 +93,7 @@ ArtistInfoWidget::load( const artist_ptr& artist )
artistInfo["artist"] = artist->name();
Tomahawk::InfoSystem::InfoRequestData requestData;
requestData.caller = s_aiInfoIdentifier;
requestData.caller = m_infoId;
requestData.customData = QVariantMap();
requestData.input = artist->name();
@@ -121,7 +119,7 @@ ArtistInfoWidget::load( const artist_ptr& artist )
void
ArtistInfoWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestData, QVariant output )
{
if ( requestData.caller != s_aiInfoIdentifier )
if ( requestData.caller != m_infoId )
{
// qDebug() << "Info of wrong type or not with our identifier";
return;
@@ -139,6 +137,7 @@ ArtistInfoWidget::infoSystemInfo( Tomahawk::InfoSystem::InfoRequestData requestD
}
}
qDebug() << "ARTISTINFOWIDGET got infosystem info for:" << m_title << output.value< Tomahawk::InfoSystem::InfoGenericMap >();
QVariantMap returnedData = output.value< QVariantMap >();
switch ( requestData.type )
{

View File

@@ -102,6 +102,7 @@ private:
QString m_title;
QString m_description;
QString m_longDescription;
QString m_infoId;
QPixmap m_pixmap;
};

View File

@@ -40,6 +40,15 @@
<height>192</height>
</size>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragDrop</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
</widget>
</item>
</layout>
@@ -84,16 +93,16 @@
<extends>QLabel</extends>
<header location="global">widgets/HeaderLabel.h</header>
</customwidget>
<customwidget>
<class>PlaylistView</class>
<extends>QTreeView</extends>
<header>playlist/playlistview.h</header>
</customwidget>
<customwidget>
<class>AlbumView</class>
<extends>QListView</extends>
<header>playlist/albumview.h</header>
</customwidget>
<customwidget>
<class>PlaylistView</class>
<extends>QTreeView</extends>
<header>playlist/playlistview.h</header>
</customwidget>
<customwidget>
<class>CollectionView</class>
<extends>QTreeView</extends>

View File

@@ -39,6 +39,7 @@ SearchWidget::SearchWidget( const QString& search, QWidget* parent )
{
ui->setupUi( this );
ui->resultsView->setGuid( "searchwidget" );
m_resultsModel = new PlaylistModel( ui->resultsView );
ui->resultsView->setPlaylistModel( m_resultsModel );
ui->resultsView->overlay()->setEnabled( false );
@@ -96,7 +97,6 @@ SearchWidget::onResultsFound( const QList<Tomahawk::result_ptr>& results )
Tomahawk::query_ptr q = result->toQuery();
q->setResolveFinished( true );
q->addResults( rl );
qDebug() << result->toString();
m_resultsModel->append( q );
}

View File

@@ -86,7 +86,7 @@ WelcomeWidget::WelcomeWidget( QWidget* parent )
m_recentAlbumsModel = new AlbumModel( ui->additionsView );
ui->additionsView->setAlbumModel( m_recentAlbumsModel );
m_recentAlbumsModel->addFilteredCollection( collection_ptr(), 20, DatabaseCommand_AllAlbums::ModificationTime );
m_recentAlbumsModel->addFilteredCollection( collection_ptr(), 20, DatabaseCommand_AllAlbums::ModificationTime, true );
m_timer = new QTimer( this );
connect( m_timer, SIGNAL( timeout() ), SLOT( checkQueries() ) );
@@ -108,6 +108,14 @@ void
WelcomeWidget::updateRecentTracks()
{
m_tracksModel->loadHistory( Tomahawk::source_ptr(), HISTORY_TRACK_ITEMS );
connect( SourceList::instance()->getLocal().data(), SIGNAL( stats( QVariantMap ) ), this, SLOT( updateRecentAdditions() ) );
}
void
WelcomeWidget::updateRecentAdditions()
{
m_recentAlbumsModel->addFilteredCollection( collection_ptr(), 20, DatabaseCommand_AllAlbums::ModificationTime, true );
}

View File

@@ -103,6 +103,7 @@ signals:
public slots:
void updateRecentTracks();
void updatePlaylists();
void updateRecentAdditions();
private slots:
void onSourceAdded( const Tomahawk::source_ptr& source );

View File

@@ -30,7 +30,14 @@
</widget>
</item>
<item>
<widget class="AlbumView" name="additionsView"/>
<widget class="AlbumView" name="additionsView">
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
</widget>
</item>
</layout>
</widget>
@@ -49,7 +56,7 @@
</layout>
</widget>
</widget>
<widget class="QWidget" name="">
<widget class="QWidget" name="layoutWidget">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="HeaderLabel" name="label_3">
@@ -80,16 +87,16 @@
<extends>QLabel</extends>
<header location="global">widgets/HeaderLabel.h</header>
</customwidget>
<customwidget>
<class>PlaylistView</class>
<extends>QTreeView</extends>
<header>playlist/playlistview.h</header>
</customwidget>
<customwidget>
<class>AlbumView</class>
<extends>QListView</extends>
<header>playlist/albumview.h</header>
</customwidget>
<customwidget>
<class>PlaylistView</class>
<extends>QTreeView</extends>
<header>playlist/playlistview.h</header>
</customwidget>
<customwidget>
<class>PlaylistWidget</class>
<extends>QListWidget</extends>

View File

@@ -82,7 +82,7 @@ DirLister::scanDir( QDir dir, int depth, DirLister::Mode mode )
return;
}
tDebug( LOGVERBOSE ) << "DirLister::scanDir scanning: " << dir.canonicalPath() << " with mode " << mode;
tDebug( LOGVERBOSE ) << "DirLister::scanDir scanning:" << dir.canonicalPath() << "with mode" << mode;
if( !dir.exists() )
{
tDebug( LOGVERBOSE ) << "Dir no longer exists, not scanning";
@@ -283,14 +283,6 @@ MusicScanner::listerFinished( const QMap<QString, unsigned int>& newmtimes )
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>(cmd) );
}
// re-calculate source stats
{
DatabaseCommand_CollectionStats* cmd = new DatabaseCommand_CollectionStats( SourceList::instance()->getLocal() );
connect( cmd, SIGNAL( done( QVariantMap ) ),
SourceList::instance()->getLocal().data(), SLOT( setStats( QVariantMap ) ), Qt::QueuedConnection );
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>( cmd ) );
}
if ( !m_dirLister.isNull() )
{
m_dirListerThreadController->quit();;

View File

@@ -183,7 +183,7 @@ QtScriptResolver::init()
}
const QByteArray scriptContents = scriptFile.readAll();
m_engine->mainFrame()->setHtml( "<html><body></body></html>" );
m_engine->mainFrame()->setHtml( "<html><body></body></html>", QUrl( "file:///invalid/file/for/security/policy" ) );
// add c++ part of tomahawk javascript library
m_engine->mainFrame()->addToJavaScriptWindowObject( "Tomahawk", m_resolverHelper );

View File

@@ -109,6 +109,11 @@ ResolversModel::setData( const QModelIndex& index, const QVariant& value, int ro
m_enabledResolvers.append( resolver );
TomahawkApp::instance()->enableScriptResolver( resolver );
emit dataChanged( index, index );
if( Tomahawk::ExternalResolver* res = TomahawkApp::instance()->resolverForPath( resolver ) ) {
connect( res, SIGNAL( changed() ), this, SLOT( resolverChanged() ) );
}
} else if( state == Qt::Unchecked ) {
m_enabledResolvers.removeAll( resolver );

View File

@@ -51,6 +51,7 @@
#include "ui_proxydialog.h"
#include "ui_stackedsettingsdialog.h"
#include <playlist/dynamic/widgets/LoadingSpinner.h>
static QString
md5( const QByteArray& src )
@@ -66,6 +67,7 @@ SettingsDialog::SettingsDialog( QWidget *parent )
, m_rejected( false )
, m_sipModel( 0 )
, m_resolversModel( 0 )
, m_sipSpinner( 0 )
{
ui->setupUi( this );
TomahawkSettings* s = TomahawkSettings::instance();
@@ -107,6 +109,16 @@ SettingsDialog::SettingsDialog( QWidget *parent )
m_sipModel = new SipModel( this );
ui->accountsView->setModel( m_sipModel );
if ( !Servent::instance()->isReady() )
{
m_sipSpinner = new LoadingSpinner( ui->accountsView );
m_sipSpinner->fadeIn();
ui->addSipButton->setEnabled( false );
ui->removeSipButton->setEnabled( false );
connect( Servent::instance(), SIGNAL( ready() ), this, SLOT( serventReady() ) );
}
setupSipButtons();
ui->staticHostName->setText( s->externalHostname() );
@@ -228,6 +240,13 @@ SettingsDialog::~SettingsDialog()
delete ui;
}
void
SettingsDialog::serventReady()
{
m_sipSpinner->fadeOut();
ui->addSipButton->setEnabled( true );
ui->removeSipButton->setEnabled( true );
}
void
SettingsDialog::createIcons()

View File

@@ -22,6 +22,7 @@
#include <QDialog>
#include <QModelIndex>
class LoadingSpinner;
class QListWidgetItem;
class Ui_StackedSettingsDialog;
class SipPluginFactory;
@@ -100,6 +101,7 @@ private slots:
void sipCreateConfigClosed( int value );
void changePage( QListWidgetItem*, QListWidgetItem* );
void serventReady();
private:
void createIcons();
@@ -112,6 +114,7 @@ private:
bool m_rejected;
SipModel* m_sipModel;
ResolversModel* m_resolversModel;
LoadingSpinner* m_sipSpinner;
};
#endif // SETTINGSDIALOG_H

View File

@@ -1,3 +1,22 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2011, Michael Zanetti <mzanetti@kde.org>
* Copyright 2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "settingslistdelegate.h"
#include "utils/logger.h"

View File

@@ -1,3 +1,22 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2011, Michael Zanetti <mzanetti@kde.org>
* Copyright 2011, Leo Franchi <lfranchi@kde.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SETTINGSLISTDELEGATE_H
#define SETTINGSLISTDELEGATE_H

View File

@@ -82,8 +82,8 @@ void AvatarManager::onNewPresence(const Jreen::Presence& presence)
// qDebug() << presence.from().full() << "vcard: photo already cached no request necessary " << update->photoHash();
m_JidsAvatarHashes.insert( update->photoHash(), presence.from().bare() );
Q_ASSERT(!this->avatar(presence.from().bare()).isNull());
emit newAvatar(presence.from().bare());
if ( !this->avatar( presence.from().bare() ).isNull() )
emit newAvatar(presence.from().bare());
}
}
else

View File

@@ -20,6 +20,8 @@
#include "googlewrapper.h"
#include "ui_configwidget.h"
#include "utils/tomahawkutils.h"
#include <QtPlugin>
#include <QInputDialog>
@@ -69,9 +71,8 @@ void
GoogleWrapper::showAddFriendDialog()
{
bool ok;
QString id = QInputDialog::getText( 0, tr( "Add Friend" ),
tr( "Enter Google Address:" ), QLineEdit::Normal,
"", &ok );
QString id = QInputDialog::getText( TomahawkUtils::tomahawkWindow(), tr( "Add Friend" ),
tr( "Enter Google Address:" ), QLineEdit::Normal, "", &ok );
if ( !ok )
return;

View File

@@ -467,9 +467,8 @@ void
JabberPlugin::showAddFriendDialog()
{
bool ok;
QString id = QInputDialog::getText( 0, tr( "Add Friend" ),
tr( "Enter Jabber ID:" ), QLineEdit::Normal,
"", &ok );
QString id = QInputDialog::getText( TomahawkUtils::tomahawkWindow(), tr( "Add Friend" ),
tr( "Enter Jabber ID:" ), QLineEdit::Normal, "", &ok );
if ( !ok )
return;
@@ -712,7 +711,7 @@ void JabberPlugin::onSubscriptionReceived(const Jreen::RosterItem::Ptr& item, co
tr( "Authorize User" ),
QString( tr( "Do you want to grant <b>%1</b> access to your Collection?" ) ).arg(presence.from().bare()),
QMessageBox::Yes | QMessageBox::No,
0
TomahawkUtils::tomahawkWindow()
);
// add confirmBox to m_subscriptionConfirmBoxes

View File

@@ -6,7 +6,7 @@
TomahawkOAuthTwitter::TomahawkOAuthTwitter( QNetworkAccessManager *nam, QObject* parent )
: OAuthTwitter( QByteArray( "C4v4Wfa21rfIDck4HMR3A" ), QByteArray( "zXSjU6bjrvg6UVMJX4JufqHyjj3iextY14SR9uBEAo" ), parent )
: OAuthTwitter( QByteArray::fromBase64( "QzR2NFdmYTIxcmZJRGNrNEhNUjNB" ), QByteArray::fromBase64( "elhTalU2Ympydmc2VVZNSlg0SnVmcUh5amozaWV4dFkxNFNSOXVCRUFv" ), parent )
{
setNetworkAccessManager( nam );
}

View File

@@ -59,42 +59,43 @@ TwitterPlugin::TwitterPlugin( const QString& pluginId )
, m_isAuthed( false )
, m_checkTimer( this )
, m_connectTimer( this )
, m_dmPollTimer( this )
, m_cachedFriendsSinceId( 0 )
, m_cachedMentionsSinceId( 0 )
, m_cachedDirectMessagesSinceId( 0 )
, m_cachedPeers()
, m_keyCache()
, m_finishedFriends( false )
, m_finishedMentions( false )
, m_state( Disconnected )
{
qDebug() << Q_FUNC_INFO;
if ( !Database::instance() || Database::instance()->dbid() != twitterSavedDbid() )
if ( Database::instance()->dbid() != twitterSavedDbid() )
{
if ( !twitterSavedDbid().isEmpty() ) //remove eventually (post 0.2), here for migration purposes
{
setTwitterCachedDirectMessagesSinceId( 0 );
setTwitterCachedFriendsSinceId( 0 );
setTwitterCachedMentionsSinceId( 0 );
setTwitterCachedPeers( QHash< QString, QVariant >() );
}
setTwitterSavedDbid( Database::instance()->dbid() );
setTwitterCachedDirectMessagesSinceId( 0 );
setTwitterCachedFriendsSinceId( 0 );
setTwitterCachedMentionsSinceId( 0 );
setTwitterCachedPeers( QVariantHash() );
}
m_checkTimer.setInterval( 150000 );
setTwitterSavedDbid( Database::instance()->dbid() );
m_checkTimer.setInterval( 180000 );
m_checkTimer.setSingleShot( false );
connect( &m_checkTimer, SIGNAL( timeout() ), SLOT( checkTimerFired() ) );
m_connectTimer.setInterval( 150000 );
m_dmPollTimer.setInterval( 60000 );
m_dmPollTimer.setSingleShot( false );
connect( &m_dmPollTimer, SIGNAL( timeout() ), SLOT( pollDirectMessages() ) );
m_connectTimer.setInterval( 180000 );
m_connectTimer.setSingleShot( false );
connect( &m_connectTimer, SIGNAL( timeout() ), SLOT( connectTimerFired() ) );
m_configWidget = QWeakPointer< TwitterConfigWidget >( new TwitterConfigWidget( this, 0 ) );
connect( m_configWidget.data(), SIGNAL( twitterAuthed( bool ) ), SLOT( configDialogAuthedSignalSlot( bool ) ) );
}
void
TwitterPlugin::configDialogAuthedSignalSlot( bool authed )
{
@@ -171,10 +172,16 @@ TwitterPlugin::connectPlugin( bool startup )
qStableSort( peerlist.begin(), peerlist.end() );
foreach( QString screenName, peerlist )
{
QHash< QString, QVariant > cachedPeer = m_cachedPeers[screenName].toHash();
QVariantHash cachedPeer = m_cachedPeers[screenName].toHash();
if ( cachedPeer.contains( "onod" ) && cachedPeer["onod"] != Database::instance()->dbid() )
{
m_cachedPeers.remove( screenName );
syncConfig();
}
foreach( QString prop, cachedPeer.keys() )
qDebug() << "TwitterPlugin : " << screenName << ", key " << prop << ", value " << ( cachedPeer[prop].canConvert< QString >() ? cachedPeer[prop].toString() : QString::number( cachedPeer[prop].toInt() ) );
QMetaObject::invokeMethod( this, "registerOffer", Q_ARG( QString, screenName ), QGenericArgument( "QHash< QString, QVariant >", (const void*)&cachedPeer ) );
QMetaObject::invokeMethod( this, "registerOffer", Q_ARG( QString, screenName ), Q_ARG( QVariantHash, cachedPeer ) );
}
if ( twitterOAuthToken().isEmpty() || twitterOAuthTokenSecret().isEmpty() )
@@ -222,6 +229,7 @@ TwitterPlugin::disconnectPlugin()
qDebug() << Q_FUNC_INFO;
m_checkTimer.stop();
m_connectTimer.stop();
m_dmPollTimer.stop();
if( !m_friendsTimeline.isNull() )
delete m_friendsTimeline.data();
if( !m_mentions.isNull() )
@@ -235,6 +243,7 @@ TwitterPlugin::disconnectPlugin()
if( !m_twitterAuth.isNull() )
delete m_twitterAuth.data();
syncConfig();
m_cachedPeers.empty();
m_state = Disconnected;
emit stateChanged( m_state );
@@ -250,6 +259,7 @@ TwitterPlugin::connectAuthVerifyReply( const QTweetUser &user )
m_state = Disconnected;
m_connectTimer.stop();
m_checkTimer.stop();
m_dmPollTimer.stop();
emit stateChanged( m_state );
}
else
@@ -274,6 +284,7 @@ TwitterPlugin::connectAuthVerifyReply( const QTweetUser &user )
emit stateChanged( m_state );
m_connectTimer.start();
m_checkTimer.start();
m_dmPollTimer.start();
QMetaObject::invokeMethod( this, "checkTimerFired", Qt::AutoConnection );
QTimer::singleShot( 20000, this, SLOT( connectTimerFired() ) );
}
@@ -292,6 +303,7 @@ TwitterPlugin::connectAuthVerifyReply( const QTweetUser &user )
m_state = Disconnected;
m_connectTimer.stop();
m_checkTimer.stop();
m_dmPollTimer.stop();
emit stateChanged( m_state );
}
}
@@ -349,12 +361,13 @@ TwitterPlugin::connectTimerFired()
foreach( QString screenName, peerlist )
{
qDebug() << Q_FUNC_INFO << " checking peer " << screenName;
QHash< QString, QVariant > peerData = m_cachedPeers[screenName].toHash();
QVariantHash peerData = m_cachedPeers[screenName].toHash();
if ( Servent::instance()->connectedToSession( peerData["node"].toString() ) )
{
peerData["lastseen"] = QDateTime::currentMSecsSinceEpoch();
m_cachedPeers[screenName] = peerData;
syncConfig();
qDebug() << Q_FUNC_INFO << " already connected";
continue;
}
@@ -363,6 +376,7 @@ TwitterPlugin::connectTimerFired()
{
qDebug() << Q_FUNC_INFO << " aging peer " << screenName << " out of cache";
m_cachedPeers.remove( screenName );
syncConfig();
m_cachedAvatars.remove( screenName );
continue;
}
@@ -373,7 +387,7 @@ TwitterPlugin::connectTimerFired()
continue;
}
QMetaObject::invokeMethod( this, "registerOffer", Q_ARG( QString, screenName ), QGenericArgument( "QHash< QString, QVariant >", (const void*)&peerData ) );
QMetaObject::invokeMethod( this, "registerOffer", Q_ARG( QString, screenName ), Q_ARG( QVariantHash, peerData ) );
}
}
@@ -407,21 +421,23 @@ TwitterPlugin::parseGotTomahawk( const QRegExp &regex, const QString &screenName
else
qDebug() << "TwitterPlugin parsed node " << node << " out of the tweet";
if ( screenName == myScreenName && node == Database::instance()->dbid() )
if ( node == Database::instance()->dbid() )
{
qDebug() << "My screen name and my dbid found; ignoring";
qDebug() << "My dbid found; ignoring";
return;
}
QHash< QString, QVariant > peerData;
QVariantHash peerData;
if( m_cachedPeers.contains( screenName ) )
{
peerData = m_cachedPeers[screenName].toHash();
//force a re-send of info but no need to re-register
peerData["resend"] = QVariant::fromValue< bool >( true );
if ( peerData["node"].toString() != node )
peerData["rekey"] = QVariant::fromValue< bool >( true );
}
peerData["node"] = QVariant::fromValue< QString >( node );
QMetaObject::invokeMethod( this, "registerOffer", Q_ARG( QString, screenName ), QGenericArgument( "QHash< QString, QVariant >", (const void*)&peerData ) );
QMetaObject::invokeMethod( this, "registerOffer", Q_ARG( QString, screenName ), Q_ARG( QVariantHash, peerData ) );
}
void
@@ -429,7 +445,6 @@ TwitterPlugin::friendsTimelineStatuses( const QList< QTweetStatus > &statuses )
{
qDebug() << Q_FUNC_INFO;
QRegExp regex( s_gotTomahawkRegex, Qt::CaseSensitive, QRegExp::RegExp2 );
QString myScreenName = twitterScreenName();
QHash< QString, QTweetStatus > latestHash;
foreach ( QTweetStatus status, statuses )
@@ -456,9 +471,6 @@ TwitterPlugin::friendsTimelineStatuses( const QList< QTweetStatus > &statuses )
}
setTwitterCachedFriendsSinceId( m_cachedFriendsSinceId );
m_finishedFriends = true;
QMetaObject::invokeMethod( this, "pollDirectMessages", Qt::AutoConnection );
}
void
@@ -492,20 +504,11 @@ TwitterPlugin::mentionsStatuses( const QList< QTweetStatus > &statuses )
}
setTwitterCachedMentionsSinceId( m_cachedMentionsSinceId );
m_finishedMentions = true;
QMetaObject::invokeMethod( this, "pollDirectMessages", Qt::AutoConnection );
}
void
TwitterPlugin::pollDirectMessages()
{
if ( !m_finishedMentions || !m_finishedFriends )
return;
m_finishedFriends = false;
m_finishedMentions = false;
if ( !isValid() )
return;
@@ -580,9 +583,9 @@ TwitterPlugin::directMessages( const QList< QTweetDMStatus > &messages )
qDebug() << "TwitterPlugin found a peerstart message from " << status.senderScreenName() << " with host " << host << " and port " << port << " and pkey " << pkey << " and node " << splitNode[0] << " destined for node " << splitNode[1];
QHash< QString, QVariant > peerData = ( m_cachedPeers.contains( status.senderScreenName() ) ) ?
QVariantHash peerData = ( m_cachedPeers.contains( status.senderScreenName() ) ) ?
m_cachedPeers[status.senderScreenName()].toHash() :
QHash< QString, QVariant >();
QVariantHash();
peerData["host"] = QVariant::fromValue< QString >( host );
peerData["port"] = QVariant::fromValue< int >( port );
@@ -590,7 +593,7 @@ TwitterPlugin::directMessages( const QList< QTweetDMStatus > &messages )
peerData["node"] = QVariant::fromValue< QString >( splitNode[0] );
peerData["dirty"] = QVariant::fromValue< bool >( true );
QMetaObject::invokeMethod( this, "registerOffer", Q_ARG( QString, status.senderScreenName() ), QGenericArgument( "QHash< QString, QVariant >", (const void*)&peerData ) );
QMetaObject::invokeMethod( this, "registerOffer", Q_ARG( QString, status.senderScreenName() ), Q_ARG( QVariantHash, peerData ) );
if ( Database::instance()->dbid().startsWith( splitNode[1] ) )
{
@@ -605,7 +608,7 @@ TwitterPlugin::directMessages( const QList< QTweetDMStatus > &messages )
}
void
TwitterPlugin::registerOffer( const QString &screenName, const QHash< QString, QVariant > &peerData )
TwitterPlugin::registerOffer( const QString &screenName, const QVariantHash &peerData )
{
qDebug() << Q_FUNC_INFO;
@@ -618,7 +621,7 @@ TwitterPlugin::registerOffer( const QString &screenName, const QHash< QString, Q
if ( !m_cachedAvatars.contains( screenName ) )
QMetaObject::invokeMethod( this, "fetchAvatar", Q_ARG( QString, screenName ) );
QHash< QString, QVariant > _peerData( peerData );
QVariantHash _peerData( peerData );
if ( _peerData.contains( "dirty" ) )
{
@@ -646,8 +649,11 @@ TwitterPlugin::registerOffer( const QString &screenName, const QHash< QString, Q
needToSend = true;
}
if ( !m_keyCache.contains( _peerData["okey"].toString() ) )
if ( _peerData.contains( "rekey" ) || !m_keyCache.contains( _peerData["okey"].toString() ) )
{
_peerData.remove( "rekey" );
needToAddToCache = true;
}
if ( !_peerData.contains( "ohst" ) || !_peerData.contains( "oprt" ) ||
_peerData["ohst"].toString() != Servent::instance()->externalAddress() ||
@@ -668,7 +674,7 @@ TwitterPlugin::registerOffer( const QString &screenName, const QHash< QString, Q
_peerData["oprt"] = QVariant::fromValue< int >( Servent::instance()->externalPort() );
peersChanged = true;
if( !Servent::instance()->externalAddress().isEmpty() && !Servent::instance()->externalPort() == 0 )
QMetaObject::invokeMethod( this, "sendOffer", Q_ARG( QString, screenName ), QGenericArgument( "QHash< QString, QVariant >", (const void*)&_peerData ) );
QMetaObject::invokeMethod( this, "sendOffer", Q_ARG( QString, screenName ), Q_ARG( QVariantHash, _peerData ) );
else
qDebug() << "TwitterPlugin did not send offer because external address is " << Servent::instance()->externalAddress() << " and external port is " << Servent::instance()->externalPort();
}
@@ -676,17 +682,17 @@ TwitterPlugin::registerOffer( const QString &screenName, const QHash< QString, Q
if ( peersChanged )
{
_peerData["lastseen"] = QString::number( QDateTime::currentMSecsSinceEpoch() );
m_cachedPeers[screenName] = QVariant::fromValue< QHash< QString, QVariant > >( _peerData );
setTwitterCachedPeers( m_cachedPeers );
m_cachedPeers[screenName] = QVariant::fromValue< QVariantHash >( _peerData );
syncConfig();
}
if ( m_state == Connected && _peerData.contains( "host" ) && _peerData.contains( "port" ) && _peerData.contains( "pkey" ) )
QMetaObject::invokeMethod( this, "makeConnection", Q_ARG( QString, screenName ), QGenericArgument( "QHash< QString, QVariant >", (const void*)&_peerData ) );
QMetaObject::invokeMethod( this, "makeConnection", Q_ARG( QString, screenName ), Q_ARG( QVariantHash, _peerData ) );
}
void
TwitterPlugin::sendOffer( const QString &screenName, const QHash< QString, QVariant > &peerData )
TwitterPlugin::sendOffer( const QString &screenName, const QVariantHash &peerData )
{
qDebug() << Q_FUNC_INFO;
QString offerString = QString( "TOMAHAWKPEER:Host=%1:Port=%2:Node=%3*%4:PKey=%5" ).arg( peerData["ohst"].toString() )
@@ -700,7 +706,7 @@ TwitterPlugin::sendOffer( const QString &screenName, const QHash< QString, QVari
}
void
TwitterPlugin::makeConnection( const QString &screenName, const QHash< QString, QVariant > &peerData )
TwitterPlugin::makeConnection( const QString &screenName, const QVariantHash &peerData )
{
qDebug() << Q_FUNC_INFO;
if ( !peerData.contains( "host" ) || !peerData.contains( "port" ) || !peerData.contains( "pkey" ) || !peerData.contains( "node" ) ||
@@ -709,6 +715,14 @@ TwitterPlugin::makeConnection( const QString &screenName, const QHash< QString,
qDebug() << "TwitterPlugin could not find host and/or port and/or pkey and/or node for peer " << screenName;
return;
}
if ( peerData["host"].toString() == Servent::instance()->externalAddress() &&
peerData["port"].toInt() == Servent::instance()->externalPort() )
{
qDebug() << "TwitterPlugin asked to make connection to our own host and port, ignoring " << screenName;
return;
}
QString friendlyName = QString( '@' + screenName );
if ( !Servent::instance()->connectedToSession( peerData["node"].toString() ) )
Servent::instance()->connectToPeer( peerData["host"].toString(),
@@ -1022,7 +1036,7 @@ TwitterPlugin::setTwitterCachedDirectMessagesSinceId( qint64 cachedId )
TomahawkSettings::instance()->setValue( pluginId() + "/cacheddirectmessagessinceid", cachedId );
}
QHash<QString, QVariant>
QVariantHash
TwitterPlugin::twitterCachedPeers() const
{
TomahawkSettings* s = TomahawkSettings::instance();
@@ -1047,13 +1061,14 @@ TwitterPlugin::twitterCachedPeers() const
}
s->endGroup();
return s->value( pluginId() + "/cachedpeers", QHash<QString, QVariant>() ).toHash();
return s->value( pluginId() + "/cachedpeers", QVariantHash() ).toHash();
}
void
TwitterPlugin::setTwitterCachedPeers( const QHash<QString, QVariant> &cachedPeers )
TwitterPlugin::setTwitterCachedPeers( const QVariantHash &cachedPeers )
{
TomahawkSettings::instance()->setValue( pluginId() + "/cachedpeers", cachedPeers );
TomahawkSettings::instance()->sync();
}
Q_EXPORT_PLUGIN2( sipfactory, TwitterFactory )

View File

@@ -108,14 +108,15 @@ private slots:
void directMessagePosted( const QTweetDMStatus &message );
void directMessagePostError( QTweetNetBase::ErrorCode errorCode, const QString &message );
void directMessageDestroyed( const QTweetDMStatus &message );
void registerOffer( const QString &screenName, const QHash< QString, QVariant > &peerdata );
void sendOffer( const QString &screenName, const QHash< QString, QVariant > &peerdata );
void makeConnection( const QString &screenName, const QHash< QString, QVariant > &peerdata );
void registerOffer( const QString &screenName, const QVariantHash &peerdata );
void sendOffer( const QString &screenName, const QVariantHash &peerdata );
void makeConnection( const QString &screenName, const QVariantHash &peerdata );
void fetchAvatar( const QString &screenName );
void avatarUserDataSlot( const QTweetUser &user );
void profilePicReply();
private:
inline void syncConfig() { setTwitterCachedPeers( m_cachedPeers ); }
bool refreshTwitterAuth();
void parseGotTomahawk( const QRegExp &regex, const QString &screenName, const QString &text );
// handle per-plugin config
@@ -133,8 +134,8 @@ private:
void setTwitterCachedMentionsSinceId( qint64 sinceid );
qint64 twitterCachedDirectMessagesSinceId() const;
void setTwitterCachedDirectMessagesSinceId( qint64 sinceid );
QHash<QString, QVariant> twitterCachedPeers() const;
void setTwitterCachedPeers( const QHash<QString, QVariant> &cachedPeers );
QVariantHash twitterCachedPeers() const;
void setTwitterCachedPeers( const QVariantHash &cachedPeers );
QWeakPointer< TomahawkOAuthTwitter > m_twitterAuth;
QWeakPointer< QTweetFriendsTimeline > m_friendsTimeline;
@@ -146,14 +147,13 @@ private:
bool m_isAuthed;
QTimer m_checkTimer;
QTimer m_connectTimer;
QTimer m_dmPollTimer;
qint64 m_cachedFriendsSinceId;
qint64 m_cachedMentionsSinceId;
qint64 m_cachedDirectMessagesSinceId;
QHash< QString, QVariant > m_cachedPeers;
QVariantHash m_cachedPeers;
QHash< QString, QPixmap > m_cachedAvatars;
QSet<QString> m_keyCache;
bool m_finishedFriends;
bool m_finishedMentions;
ConnectionState m_state;
QWeakPointer<TwitterConfigWidget > m_configWidget;

View File

@@ -28,7 +28,7 @@
#include "widgets/playlisttypeselectordlg.h"
#include <playlist/dynamic/GeneratorInterface.h>
#include "utils/logger.h"
#include <globalactionmanager.h>
#include "dropjob.h"
using namespace Tomahawk;
@@ -122,7 +122,7 @@ CategoryAddItem::icon() const
bool
CategoryAddItem::willAcceptDrag( const QMimeData* data ) const
{
if ( ( m_categoryType == SourcesModel::PlaylistsCategory || m_categoryType == SourcesModel::StationsCategory ) && GlobalActionManager::instance()->acceptsMimeData( data ) )
if ( ( m_categoryType == SourcesModel::PlaylistsCategory || m_categoryType == SourcesModel::StationsCategory ) && DropJob::acceptsMimeData( data ) )
{
return true;
}
@@ -133,9 +133,96 @@ CategoryAddItem::willAcceptDrag( const QMimeData* data ) const
bool
CategoryAddItem::dropMimeData( const QMimeData* data, Qt::DropAction )
{
// As DropJob always converts dropped items to query_ptrs for all tracks we need to extract album/artist metadata ourselves for stations
if ( m_categoryType == SourcesModel::StationsCategory &&
( data->hasFormat( "application/tomahawk.metadata.artist" ) || data->hasFormat( "application/tomahawk.metadata.album" ) ) )
{
QByteArray mimeData;
if ( data->hasFormat( "application/tomahawk.metadata.artist" ) )
mimeData = data->data( "application/tomahawk.metadata.artist" );
else if ( data->hasFormat( "application/tomahawk.metadata.album" ) )
mimeData = data->data( "application/tomahawk.metadata.album" );
QDataStream stream( &mimeData, QIODevice::ReadOnly );
dynplaylist_ptr newpl = DynamicPlaylist::create( SourceList::instance()->getLocal(), uuid(), QString(), "", SourceList::instance()->getLocal()->friendlyName(), OnDemand, false );
newpl->setMode( OnDemand );
QString firstArtist;
// now we want to add each artist as a filter...
QList< dyncontrol_ptr > contrls;
while ( !stream.atEnd() )
{
QString artist;
stream >> artist;
if ( firstArtist.isEmpty() )
firstArtist = artist;
QString album;
if ( data->hasFormat( "application/tomahawk.metadata.album" ) )
stream >> album; // throw away album title... we only create artists filters for now
dyncontrol_ptr c = newpl->generator()->createControl( "Artist" );
c->setInput( QString( "%1" ).arg( artist ) );
contrls << c;
}
QString name = firstArtist.isEmpty() ? tr( "New Station" ) : tr( "%1 Station" ).arg( firstArtist );
newpl->rename( name );
newpl->createNewRevision( uuid(), newpl->currentrevision(), newpl->type(), contrls );
ViewManager::instance()->show( newpl );
return true;
}
// This could be needed once echonest supports filtering by album.
// If they never will, or if they do and this code still is not used, throw it away!
// If you enable this, make sure to remove the checks for album above.
/* if ( m_categoryType == SourcesModel::StationsCategory && data->hasFormat( "application/tomahawk.metadata.album" ) )
{
QByteArray mimeData = data->data( "application/tomahawk.metadata.album" );
QDataStream stream( &mimeData, QIODevice::ReadOnly );
dynplaylist_ptr newpl = DynamicPlaylist::create( SourceList::instance()->getLocal(), uuid(), QString(), "", SourceList::instance()->getLocal()->friendlyName(), OnDemand, false );
newpl->setMode( OnDemand );
QString firstAlbum;
// now we want to add each artist as a filter...
QList< dyncontrol_ptr > contrls;
while ( !stream.atEnd() )
{
QString artist;
stream >> artist;
QString album;
stream >> album;
if ( firstAlbum.isEmpty() )
{
firstAlbum = album;
}
dyncontrol_ptr c = newpl->generator()->createControl( "Album" );
c->setInput( QString( "%1" ).arg( artist ) );
contrls << c;
}
QString name = firstAlbum.isEmpty() ? tr( "New Station" ) : tr( "%1 Station" ).arg( firstAlbum );
newpl->rename( name );
newpl->createNewRevision( uuid(), newpl->currentrevision(), newpl->type(), contrls );
ViewManager::instance()->show( newpl );
// Give a shot to try to rename it. The playlist has to be created first. ugly.
QTimer::singleShot( 300, APP->mainWindow()->sourceTreeView(), SLOT( renamePlaylist() ) );
return true;
} */
// Create a new playlist seeded with these items
connect( GlobalActionManager::instance(), SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) );
GlobalActionManager::instance()->tracksFromMimeData( data );
DropJob *dj = new DropJob();
connect( dj, SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) );
dj->tracksFromMimeData( data );
return true;
}
@@ -143,7 +230,6 @@ CategoryAddItem::dropMimeData( const QMimeData* data, Qt::DropAction )
void
CategoryAddItem::parsedDroppedTracks( const QList< query_ptr >& tracks )
{
disconnect( GlobalActionManager::instance(), SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) );
if( m_categoryType == SourcesModel::PlaylistsCategory ) {
playlist_ptr newpl = Playlist::create( SourceList::instance()->getLocal(), uuid(), "New Playlist", "", SourceList::instance()->getLocal()->friendlyName(), false, tracks );

View File

@@ -139,7 +139,7 @@ CollectionItem::text() const
int
CollectionItem::peerSortValue() const
CollectionItem::IDValue() const
{
if( m_source.isNull() )
return -1;
@@ -150,6 +150,18 @@ CollectionItem::peerSortValue() const
}
int
CollectionItem::peerSortValue() const
{
if( m_source.isNull() )
return -1;
if( m_source->isLocal() )
return 0;
return 1;
}
void
CollectionItem::activate()
{
@@ -225,6 +237,8 @@ CollectionItem::playlistDeletedInternal( SourceTreeItem* parent, const T& p )
parent->beginRowsRemoved( i, i );
parent->removeChild( pl );
parent->endRowsRemoved();
delete pl;
break;
}
}

View File

@@ -37,6 +37,7 @@ public:
virtual void activate();
virtual QIcon icon() const;
virtual int peerSortValue() const;
virtual int IDValue() const;
Tomahawk::source_ptr source() const;

View File

@@ -28,7 +28,7 @@
#include "collectionitem.h"
#include "utils/tomahawkutils.h"
#include "utils/logger.h"
#include "globalactionmanager.h"
#include "dropjob.h"
using namespace Tomahawk;
@@ -81,6 +81,14 @@ PlaylistItem::onPlaylistChanged()
int
PlaylistItem::peerSortValue() const
{
// return m_playlist->createdOn();
return 0;
}
int
PlaylistItem::IDValue() const
{
return m_playlist->createdOn();
}
@@ -142,8 +150,9 @@ PlaylistItem::dropMimeData( const QMimeData* data, Qt::DropAction action )
data->data( "application/tomahawk.playlist.id" ) == m_playlist->guid() )
return false; // don't allow dropping on ourselves
connect( GlobalActionManager::instance(), SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) );
GlobalActionManager::instance()->tracksFromMimeData( data );
DropJob *dj = new DropJob();
connect( dj, SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) );
dj->tracksFromMimeData( data );
// TODO cant' know if it works or not yet...
return true;
@@ -152,7 +161,6 @@ PlaylistItem::dropMimeData( const QMimeData* data, Qt::DropAction action )
void
PlaylistItem::parsedDroppedTracks( const QList< query_ptr >& tracks)
{
disconnect( GlobalActionManager::instance(), SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ) );
if ( tracks.count() && !m_playlist.isNull() && m_playlist->author()->isLocal() )
{
qDebug() << "on playlist:" << m_playlist->title() << m_playlist->guid() << m_playlist->currentrevision();
@@ -237,6 +245,14 @@ DynamicPlaylistItem::onDynamicPlaylistLoaded( DynamicPlaylistRevision revision )
int
DynamicPlaylistItem::peerSortValue() const
{
// return m_dynplaylist->createdOn();
return 0;
}
int
DynamicPlaylistItem::IDValue() const
{
return m_dynplaylist->createdOn();
}

View File

@@ -38,6 +38,7 @@ public:
virtual QIcon icon() const;
virtual bool setData(const QVariant& v, bool role);
virtual int peerSortValue() const;
virtual int IDValue() const;
virtual bool activateCurrent();
@@ -67,6 +68,7 @@ public:
virtual bool willAcceptDrag( const QMimeData* data ) const;
virtual void activate();
virtual int peerSortValue() const;
virtual int IDValue() const;
virtual QIcon icon() const;
virtual bool activateCurrent();

View File

@@ -55,6 +55,7 @@ public:
virtual bool dropMimeData( const QMimeData*, Qt::DropAction ) { return false; }
virtual bool setData( const QVariant&, bool ) { return false; }
virtual int peerSortValue() const { return 0; } // How to sort relative to peers in the tree.
virtual int IDValue() const { return 0; }
/// don't call me unless you are a sourcetreeitem. i prefer this to making everyone a friend
void beginRowsAdded( int from, int to ) { emit beginChildRowsAdded( from, to ); }

View File

@@ -33,6 +33,7 @@
#include "utils/logger.h"
#include "globalactionmanager.h"
#include "dropjob.h"
#include "items/playlistitems.h"
using namespace Tomahawk;
@@ -105,6 +106,8 @@ SourcesModel::data( const QModelIndex& index, int role ) const
return itemFromIndex( index )->icon();
case SourcesModel::SortRole:
return itemFromIndex( index )->peerSortValue();
case SourcesModel::IDRole:
return itemFromIndex( index )->IDValue();
}
return QVariant();
}
@@ -175,7 +178,7 @@ SourcesModel::setData( const QModelIndex& index, const QVariant& value, int role
QStringList
SourcesModel::mimeTypes() const
{
return GlobalActionManager::instance()->mimeTypes();
return DropJob::mimeTypes();
}

View File

@@ -61,7 +61,8 @@ public:
enum Roles {
SourceTreeItemRole = Qt::UserRole + 10,
SourceTreeItemTypeRole = Qt::UserRole + 11,
SortRole = Qt::UserRole + 12
SortRole = Qt::UserRole + 12,
IDRole = Qt::UserRole + 13
};
SourcesModel( QObject* parent = 0 );

View File

@@ -20,6 +20,7 @@
#include <QTreeView>
#include "sourcelist.h"
#include "sourcesmodel.h"
#include "sourcetree/items/collectionitem.h"
@@ -36,7 +37,6 @@ SourcesProxyModel::SourcesProxyModel( SourcesModel* model, QObject* parent )
setSourceModel( model );
if ( model && model->metaObject()->indexOfSignal( "expandRequest(QModelIndex)" ) > -1 )
connect( model, SIGNAL( expandRequest( QModelIndex ) ), this, SLOT( expandRequested( QModelIndex ) ) );
if ( model && model->metaObject()->indexOfSignal( "selectRequest(QModelIndex)" ) > -1 )
@@ -51,13 +51,13 @@ SourcesProxyModel::showOfflineSources( bool offlineSourcesShown )
invalidateFilter();
}
bool
SourcesProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const
{
if ( !m_filtered )
return true;
CollectionItem* sti = qobject_cast< CollectionItem* >( m_model->data( sourceModel()->index( sourceRow, 0, sourceParent ), SourcesModel::SourceTreeItemRole ).value< SourceTreeItem* >() );
if ( sti )
{
@@ -70,6 +70,7 @@ SourcesProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourcePar
return true;
}
void
SourcesProxyModel::selectRequested( const QModelIndex& idx )
{
@@ -77,6 +78,7 @@ SourcesProxyModel::selectRequested( const QModelIndex& idx )
emit selectRequest( mapFromSource( idx ) );
}
void
SourcesProxyModel::expandRequested( const QModelIndex& idx )
{
@@ -84,3 +86,18 @@ SourcesProxyModel::expandRequested( const QModelIndex& idx )
emit expandRequest( mapFromSource( idx ) );
}
bool
SourcesProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) const
{
if ( m_model->data( left, SourcesModel::SortRole ) != m_model->data( right, SourcesModel::SortRole ) )
return ( m_model->data( left, SourcesModel::SortRole ).toInt() < m_model->data( right, SourcesModel::SortRole ).toInt() );
const QString& lefts = left.data().toString().toLower();
const QString& rights = right.data().toString().toLower();
if ( lefts == rights )
return ( m_model->data( left, SourcesModel::IDRole ).toInt() < m_model->data( right, SourcesModel::IDRole ).toInt() );
else
return QString::localeAwareCompare( lefts, rights ) < 0;
}

View File

@@ -42,6 +42,7 @@ signals:
protected:
bool filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const;
bool lessThan( const QModelIndex& left, const QModelIndex& right ) const;
private:
SourcesModel* m_model;

View File

@@ -37,7 +37,8 @@
#include "audio/audioengine.h"
#include "sourceplaylistinterface.h"
#include "tomahawksettings.h"
#include <globalactionmanager.h>
#include "globalactionmanager.h"
#include "dropjob.h"
#include "utils/logger.h"
@@ -162,7 +163,7 @@ SourceTreeView::setupMenus()
if ( pi && dynamic_cast< SourcePlaylistInterface* >( pi ) )
{
SourcePlaylistInterface* sourcepi = dynamic_cast< SourcePlaylistInterface* >( pi );
if ( !sourcepi->source().isNull() && sourcepi->source()->id() == source->id() )
if ( !sourcepi->source().isNull() && sourcepi->source()->id() == source->id() && !AudioEngine::instance()->state() == AudioEngine::Stopped )
{
m_latchOnAction->setText( tr( "&Catch Up" ) );
m_latchMenu.addSeparator();
@@ -439,7 +440,7 @@ SourceTreeView::dragEnterEvent( QDragEnterEvent* event )
qDebug() << Q_FUNC_INFO;
QTreeView::dragEnterEvent( event );
if ( GlobalActionManager::instance()->acceptsMimeData( event->mimeData() ) )
if ( DropJob::acceptsMimeData( event->mimeData() ) )
{
m_dragging = true;
m_dropRect = QRect();
@@ -470,7 +471,7 @@ SourceTreeView::dragMoveEvent( QDragMoveEvent* event )
bool accept = false;
QTreeView::dragMoveEvent( event );
if ( GlobalActionManager::instance()->acceptsMimeData( event->mimeData() ) )
if ( DropJob::acceptsMimeData( event->mimeData() ) )
{
setDirtyRegion( m_dropRect );
const QPoint pos = event->pos();

View File

@@ -141,6 +141,7 @@ TomahawkApp::init()
m_mainwindow = 0;
m_headless = arguments().contains( "--headless" );
setWindowIcon( QIcon( RESPATH "icons/tomahawk-icon-128x128.png" ) );
setQuitOnLastWindowClosed( false );
#endif
registerMetaTypes();
@@ -193,8 +194,6 @@ TomahawkApp::init()
Tomahawk::setApplicationHandler( this );
increaseMaxFileDescriptors();
setQuitOnLastWindowClosed( false );
#endif
// Connect up shortcuts
@@ -222,6 +221,7 @@ TomahawkApp::init()
tDebug() << "Init MainWindow.";
m_mainwindow = new TomahawkWindow();
m_mainwindow->setWindowTitle( "Tomahawk" );
m_mainwindow->setObjectName( "TH_Main_Window" );
m_mainwindow->show();
}
#endif

243
thirdparty/libqnetwm/libqnetwm/fixx11h.h vendored Normal file
View File

@@ -0,0 +1,243 @@
//#ifdef don't do this, this file is supposed to be included
//#define multiple times
/* Usage:
If you get compile errors caused by X11 includes (the line
where first error appears contains word like None, Unsorted,
Below, etc.), put #include <fixx11h.h> in the .cpp file
(not .h file!) between the place where X11 headers are
included and the place where the file with compile
error is included (or the place where the compile error
in the .cpp file occurs).
This file remaps X11 #defines to const variables or
inline functions. The side effect may be that these
symbols may now refer to different variables
(e.g. if X11 #defined NoButton, after this file
is included NoButton would no longer be X11's
NoButton, but Qt::NoButton instead). At this time,
there's no conflict known that could cause problems.
The original X11 symbols are still accessible
(e.g. for None) as X::None, XNone, and also still
None, unless name lookup finds different None
first (in the current class, etc.)
Use 'Unsorted', 'Bool' and 'index' as templates.
*/
namespace X
{
// template --->
// Affects: Should be without side effects.
#ifdef Unsorted
#ifndef FIXX11H_Unsorted
#define FIXX11H_Unsorted
const int XUnsorted = Unsorted;
#undef Unsorted
const int Unsorted = XUnsorted;
#endif
#undef Unsorted
#endif
// template <---
// Affects: Should be without side effects.
#ifdef None
#ifndef FIXX11H_None
#define FIXX11H_None
const XID XNone = None;
#undef None
const XID None = XNone;
#endif
#undef None
#endif
// template --->
// Affects: Should be without side effects.
#ifdef Bool
#ifndef FIXX11H_Bool
#define FIXX11H_Bool
typedef Bool XBool;
#undef Bool
typedef XBool Bool;
#endif
#undef Bool
#endif
#ifdef FontChange
#ifndef FIXX11H_FontChange
#define FIXX11H_FontChange
const int XFontChange = FontChange;
#undef FontChange
const int FontChange = XFontChange;
#endif
#undef FontChange
#endif
// template <---
// Affects: Should be without side effects.
#ifdef KeyPress
#ifndef FIXX11H_KeyPress
#define FIXX11H_KeyPress
const int XKeyPress = KeyPress;
#undef KeyPress
const int KeyPress = XKeyPress;
#endif
#undef KeyPress
#endif
// Affects: Should be without side effects.
#ifdef KeyRelease
#ifndef FIXX11H_KeyRelease
#define FIXX11H_KeyRelease
const int XKeyRelease = KeyRelease;
#undef KeyRelease
const int KeyRelease = XKeyRelease;
#endif
#undef KeyRelease
#endif
// Affects: Should be without side effects.
#ifdef Above
#ifndef FIXX11H_Above
#define FIXX11H_Above
const int XAbove = Above;
#undef Above
const int Above = XAbove;
#endif
#undef Above
#endif
// Affects: Should be without side effects.
#ifdef Below
#ifndef FIXX11H_Below
#define FIXX11H_Below
const int XBelow = Below;
#undef Below
const int Below = XBelow;
#endif
#undef Below
#endif
// Affects: Should be without side effects.
#ifdef FocusIn
#ifndef FIXX11H_FocusIn
#define FIXX11H_FocusIn
const int XFocusIn = FocusIn;
#undef FocusIn
const int FocusIn = XFocusIn;
#endif
#undef FocusIn
#endif
// Affects: Should be without side effects.
#ifdef FocusOut
#ifndef FIXX11H_FocusOut
#define FIXX11H_FocusOut
const int XFocusOut = FocusOut;
#undef FocusOut
const int FocusOut = XFocusOut;
#endif
#undef FocusOut
#endif
// Affects: Should be without side effects.
#ifdef Always
#ifndef FIXX11H_Always
#define FIXX11H_Always
const int XAlways = Always;
#undef Always
const int Always = XAlways;
#endif
#undef Always
#endif
// Affects: Should be without side effects.
#ifdef Success
#ifndef FIXX11H_Success
#define FIXX11H_Success
const int XSuccess = Success;
#undef Success
const int Success = XSuccess;
#endif
#undef Success
#endif
// Affects: Should be without side effects.
#ifdef GrayScale
#ifndef FIXX11H_GrayScale
#define FIXX11H_GrayScale
const int XGrayScale = GrayScale;
#undef GrayScale
const int GrayScale = XGrayScale;
#endif
#undef GrayScale
#endif
// Affects: Should be without side effects.
#ifdef Status
#ifndef FIXX11H_Status
#define FIXX11H_Status
typedef Status XStatus;
#undef Status
typedef XStatus Status;
#endif
#undef Status
#endif
// Affects: Should be without side effects.
#ifdef CursorShape
#ifndef FIXX11H_CursorShape
#define FIXX11H_CursorShape
const int XCursorShape = CursorShape;
#undef CursorShape
const int CursorShape = CursorShape;
#endif
#undef CursorShape
#endif
// template --->
// Affects: Should be without side effects.
#ifdef index
#ifndef FIXX11H_index
#define FIXX11H_index
inline
char* Xindex( const char* s, int c )
{
return index( s, c );
}
#undef index
inline
char* index( const char* s, int c )
{
return Xindex( s, c );
}
#endif
#undef index
#endif
// template <---
#ifdef rindex
// Affects: Should be without side effects.
#ifndef FIXX11H_rindex
#define FIXX11H_rindex
inline
char* Xrindex( const char* s, int c )
{
return rindex( s, c );
}
#undef rindex
inline
char* rindex( const char* s, int c )
{
return Xrindex( s, c );
}
#endif
#undef rindex
#endif
}
using namespace X;

735
thirdparty/libqnetwm/libqnetwm/netwm.cpp vendored Normal file
View File

@@ -0,0 +1,735 @@
/***************************************************************************
* Copyright (C) 2010 by Dmitry 'Krasu' Baryshev *
* ksquirrel.iv@gmail.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <QX11Info>
#include <climits>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <strings.h>
#include <unistd.h>
#include <X11/Xutil.h>
#if 0
#include <X11/extensions/Xcomposite.h>
#endif
#include "netwm.h"
#define DBG(...) //fprintf(stderr, ##__VA_ARGS__)
Atom NETWM::UTF8_STRING = 0;
Atom NETWM::XROOTPMAP_ID = 0;
Atom NETWM::WM_STATE = 0;
Atom NETWM::WM_CLASS = 0;
Atom NETWM::WM_NAME = 0;
Atom NETWM::WM_DELETE_WINDOW = 0;
Atom NETWM::WM_PROTOCOLS = 0;
Atom NETWM::WM_CHANGE_STATE = 0;
Atom NETWM::WM_WINDOW_ROLE = 0;
Atom NETWM::NET_WORKAREA = 0;
Atom NETWM::NET_CLIENT_LIST = 0;
Atom NETWM::NET_CLIENT_LIST_STACKING = 0;
Atom NETWM::NET_NUMBER_OF_DESKTOPS = 0;
Atom NETWM::NET_CURRENT_DESKTOP = 0;
Atom NETWM::NET_DESKTOP_NAMES = 0;
Atom NETWM::NET_ACTIVE_WINDOW = 0;
Atom NETWM::NET_CLOSE_WINDOW = 0;
Atom NETWM::NET_SUPPORTED = 0;
Atom NETWM::NET_WM_DESKTOP = 0;
Atom NETWM::NET_SHOWING_DESKTOP = 0;
Atom NETWM::NET_WM_STATE = 0;
Atom NETWM::NET_WM_STATE_MODAL = 0;
Atom NETWM::NET_WM_STATE_STICKY = 0;
Atom NETWM::NET_WM_STATE_MAXIMIZED_VERT = 0;
Atom NETWM::NET_WM_STATE_MAXIMIZED_HORZ = 0;
Atom NETWM::NET_WM_STATE_SHADED = 0;
Atom NETWM::NET_WM_STATE_SKIP_TASKBAR = 0;
Atom NETWM::NET_WM_STATE_SKIP_PAGER = 0;
Atom NETWM::NET_WM_STATE_HIDDEN = 0;
Atom NETWM::NET_WM_STATE_FULLSCREEN = 0;
Atom NETWM::NET_WM_STATE_ABOVE = 0;
Atom NETWM::NET_WM_STATE_BELOW = 0;
Atom NETWM::NET_WM_STATE_STAYS_ON_TOP = 0;
Atom NETWM::NET_WM_STATE_STAYS_ON_BOTTOM = 0;
Atom NETWM::NET_WM_STATE_DEMANDS_ATTENTION = 0;
Atom NETWM::NET_WM_WINDOW_TYPE = 0;
Atom NETWM::NET_WM_WINDOW_TYPE_DESKTOP = 0;
Atom NETWM::NET_WM_WINDOW_TYPE_DOCK = 0;
Atom NETWM::MODERRO_WINDOW_TYPE_DOCK = 0;
Atom NETWM::NET_WM_WINDOW_TYPE_TOOLBAR = 0;
Atom NETWM::NET_WM_WINDOW_TYPE_MENU = 0;
Atom NETWM::NET_WM_WINDOW_TYPE_UTILITY = 0;
Atom NETWM::NET_WM_WINDOW_TYPE_SPLASH = 0;
Atom NETWM::NET_WM_WINDOW_TYPE_DIALOG = 0;
Atom NETWM::NET_WM_WINDOW_TYPE_DROPDOWN_MENU = 0;
Atom NETWM::NET_WM_WINDOW_TYPE_POPUP_MENU = 0;
Atom NETWM::NET_WM_WINDOW_TYPE_TOOLTIP = 0;
Atom NETWM::NET_WM_WINDOW_TYPE_NOTIFICATION = 0;
Atom NETWM::NET_WM_WINDOW_TYPE_COMBO = 0;
Atom NETWM::NET_WM_WINDOW_TYPE_DND = 0;
Atom NETWM::NET_WM_WINDOW_TYPE_NORMAL = 0;
Atom NETWM::NET_WM_WINDOW_OPACITY = 0;
Atom NETWM::NET_WM_NAME = 0;
Atom NETWM::NET_WM_VISIBLE_NAME = 0;
Atom NETWM::NET_WM_STRUT = 0;
Atom NETWM::NET_WM_STRUT_PARTIAL = 0;
Atom NETWM::NET_WM_ICON = 0;
Atom NETWM::NET_WM_PID = 0;
NETWM::net_wm_state::net_wm_state()
: modal(0), sticky(0), maximized_vert(0),
maximized_horz(0), shaded(0), skip_taskbar(0),
skip_pager(0), hidden(0), fullscreen(0),
above(0), below(0), stays_on_top(0), stays_on_bottom(0),
demands_attention(0), valid(false)
{}
NETWM::net_wm_window_type::net_wm_window_type()
: desktop(0), dock(0), toolbar(0),
menu(0), utility(0), splash(0), dialog(0),
dropdown(0), popup(0), tooltip(0), notification(0),
combo(0), dnd(0), normal(0), valid(false)
{}
/**********************************************************/
void NETWM::init()
{
Display *dpy = QX11Info::display();
UTF8_STRING = XInternAtom(dpy, "UTF8_STRING", False);
XROOTPMAP_ID = XInternAtom(dpy, "_XROOTPMAP_ID", False);
WM_STATE = XInternAtom(dpy, "WM_STATE", False);
WM_CLASS = XInternAtom(dpy, "WM_CLASS", False);
WM_NAME = XInternAtom(dpy, "WM_NAME", False);
WM_DELETE_WINDOW = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
WM_CHANGE_STATE = XInternAtom(dpy, "WM_CHANGE_STATE", False);
WM_WINDOW_ROLE = XInternAtom(dpy, "WM_WINDOW_ROLE", False);
WM_PROTOCOLS = XInternAtom(dpy, "WM_PROTOCOLS", False);
NET_WORKAREA = XInternAtom(dpy, "_NET_WORKAREA", False);
NET_CLIENT_LIST = XInternAtom(dpy, "_NET_CLIENT_LIST", False);
NET_CLIENT_LIST_STACKING = XInternAtom(dpy, "_NET_CLIENT_LIST_STACKING", False);
NET_NUMBER_OF_DESKTOPS = XInternAtom(dpy, "_NET_NUMBER_OF_DESKTOPS", False);
NET_CURRENT_DESKTOP = XInternAtom(dpy, "_NET_CURRENT_DESKTOP", False);
NET_DESKTOP_NAMES = XInternAtom(dpy, "_NET_DESKTOP_NAMES", False);
NET_ACTIVE_WINDOW = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
NET_CLOSE_WINDOW = XInternAtom(dpy, "_NET_CLOSE_WINDOW", False);
NET_SUPPORTED = XInternAtom(dpy, "_NET_SUPPORTED", False);
NET_WM_DESKTOP = XInternAtom(dpy, "_NET_WM_DESKTOP", False);
NET_SHOWING_DESKTOP = XInternAtom(dpy, "_NET_SHOWING_DESKTOP", False);
NET_WM_STATE = XInternAtom(dpy, "_NET_WM_STATE", False);
NET_WM_STATE_MODAL = XInternAtom(dpy, "_NET_WM_STATE_MODAL", False);
NET_WM_STATE_STICKY = XInternAtom(dpy, "_NET_WM_STATE_STICKY", False);
NET_WM_STATE_MAXIMIZED_VERT = XInternAtom(dpy, "_NET_WM_STATE_MAXIMIZED_VERT", False);
NET_WM_STATE_MAXIMIZED_HORZ = XInternAtom(dpy, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
NET_WM_STATE_SHADED = XInternAtom(dpy, "_NET_WM_STATE_SHADED", False);
NET_WM_STATE_SKIP_TASKBAR = XInternAtom(dpy, "_NET_WM_STATE_SKIP_TASKBAR", False);
NET_WM_STATE_SKIP_PAGER = XInternAtom(dpy, "_NET_WM_STATE_SKIP_PAGER", False);
NET_WM_STATE_HIDDEN = XInternAtom(dpy, "_NET_WM_STATE_HIDDEN", False);
NET_WM_STATE_FULLSCREEN = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
NET_WM_STATE_ABOVE = XInternAtom(dpy, "_NET_WM_STATE_ABOVE", False);
NET_WM_STATE_BELOW = XInternAtom(dpy, "_NET_WM_STATE_BELOW", False);
NET_WM_STATE_STAYS_ON_TOP = XInternAtom(dpy, "_NET_WM_STATE_STAYS_ON_TOP", False);
NET_WM_STATE_STAYS_ON_BOTTOM = XInternAtom(dpy, "_NET_WM_STATE_STAYS_ON_BOTTOM", False);
NET_WM_STATE_DEMANDS_ATTENTION = XInternAtom(dpy, "_NET_WM_STATE_DEMANDS_ATTENTION", False);
NET_WM_WINDOW_TYPE = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
NET_WM_WINDOW_TYPE_DESKTOP = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DESKTOP", False);
NET_WM_WINDOW_TYPE_DOCK = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False);
MODERRO_WINDOW_TYPE_DOCK = XInternAtom(dpy, "_MODERRO_WINDOW_TYPE_DOCK", False);
NET_WM_WINDOW_TYPE_TOOLBAR = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_TOOLBAR", False);
NET_WM_WINDOW_TYPE_MENU = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_MENU", False);
NET_WM_WINDOW_TYPE_UTILITY = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_UTILITY", False);
NET_WM_WINDOW_TYPE_SPLASH = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_SPLASH", False);
NET_WM_WINDOW_TYPE_DIALOG = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
NET_WM_WINDOW_TYPE_DROPDOWN_MENU = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", False);
NET_WM_WINDOW_TYPE_POPUP_MENU = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_POPUP_MENU", False);
NET_WM_WINDOW_TYPE_TOOLTIP = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_TOOLTIP", False);
NET_WM_WINDOW_TYPE_NOTIFICATION = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_NOTIFICATION", False);
NET_WM_WINDOW_TYPE_COMBO = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_COMBO", False);
NET_WM_WINDOW_TYPE_DND = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DND", False);
NET_WM_WINDOW_TYPE_NORMAL = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_NORMAL", False);
NET_WM_WINDOW_OPACITY = XInternAtom(dpy, "_NET_WM_WINDOW_OPACITY", False);
NET_WM_NAME = XInternAtom(dpy, "_NET_WM_NAME", False);
NET_WM_VISIBLE_NAME = XInternAtom(dpy, "_NET_WM_VISIBLE_NAME", False);
NET_WM_STRUT = XInternAtom(dpy, "_NET_WM_STRUT", False);
NET_WM_STRUT_PARTIAL = XInternAtom(dpy, "_NET_WM_STRUT_PARTIAL", False);
NET_WM_ICON = XInternAtom(dpy, "_NET_WM_ICON", False);
NET_WM_PID = XInternAtom(dpy, "_NET_WM_PID", False);
}
int NETWM::setProperty(Window window, Atom atom, long offset, uchar *data, int nelem)
{
NETWM::checkInit();
return XChangeProperty(QX11Info::display(), window, atom, offset, 32, PropModeReplace, data, nelem);
}
int NETWM::setPropertySkipTaskbar(Window window)
{
NETWM::checkInit();
Atom state[3];
state[0] = NETWM::NET_WM_STATE_SKIP_PAGER;
state[1] = NETWM::NET_WM_STATE_SKIP_TASKBAR;
state[2] = NETWM::NET_WM_STATE_STICKY;
return NETWM::setProperty(window, NETWM::NET_WM_STATE, XA_ATOM, (uchar *)&state, 3);
}
int NETWM::setPropertyOnTop(Window window)
{
NETWM::checkInit();
Atom state[2];
state[0] = NETWM::NET_WM_STATE_ABOVE;
state[1] = NETWM::NET_WM_STATE_STAYS_ON_TOP;
return NETWM::setProperty(window, NETWM::NET_WM_STATE, XA_ATOM, (uchar *)&state, 2);
}
void* NETWM::property(Window win, Atom prop, Atom type, int *nitems, bool *ok)
{
NETWM::checkInit();
Atom type_ret;
int format_ret;
unsigned long items_ret;
unsigned long after_ret;
unsigned char *prop_data = 0;
if(XGetWindowProperty(QX11Info::display(),
win,
prop,
0,
0x7fffffff,
False,
type,
&type_ret,
&format_ret,
&items_ret,
&after_ret,
&prop_data) != Success)
{
if(ok)
*ok = false;
return 0;
}
if(nitems)
*nitems = items_ret;
if(ok)
*ok = true;
return prop_data;
}
bool NETWM::climsg(Window win, long type, long l0, long l1, long l2, long l3, long l4)
{
NETWM::checkInit();
XClientMessageEvent xev;
xev.type = ClientMessage;
xev.window = win;
xev.message_type = type;
xev.format = 32;
xev.data.l[0] = l0;
xev.data.l[1] = l1;
xev.data.l[2] = l2;
xev.data.l[3] = l3;
xev.data.l[4] = l4;
return (XSendEvent(QX11Info::display(), QX11Info::appRootWindow(), False,
(SubstructureNotifyMask | SubstructureRedirectMask),
(XEvent *)&xev) == Success);
}
bool NETWM::climsgwm(Window win, Atom type, Atom arg)
{
NETWM::checkInit();
XClientMessageEvent xev;
xev.type = ClientMessage;
xev.window = win;
xev.message_type = type;
xev.format = 32;
xev.data.l[0] = arg;
xev.data.l[1] = CurrentTime;
return (XSendEvent(QX11Info::display(), win, False, 0L, (XEvent *)&xev) == Success);
}
uint NETWM::netwmDesktopsNumber()
{
NETWM::checkInit();
uint desknum;
quint32 *data;
data = (quint32 *)NETWM::property(QX11Info::appRootWindow(), NETWM::NET_NUMBER_OF_DESKTOPS, XA_CARDINAL, 0);
if(!data)
return 0;
desknum = *data;
XFree(data);
return desknum;
}
uint NETWM::netwmCurrentDesktop()
{
NETWM::checkInit();
uint desk;
quint32 *data;
data = (quint32 *)NETWM::property(QX11Info::appRootWindow(), NETWM::NET_CURRENT_DESKTOP, XA_CARDINAL, 0);
if(!data)
return 0;
desk = *data;
XFree(data);
return desk;
}
qint64 NETWM::netwmPid(Window win)
{
NETWM::checkInit();
qint64 pid = -1;
ulong *data;
data = (ulong *)NETWM::property(win, NETWM::NET_WM_PID, XA_CARDINAL, 0);
if(data)
{
pid = *data;
XFree(data);
}
return pid;
}
bool NETWM::netwmActivateWindow(Window win)
{
NETWM::checkInit();
return NETWM::climsg(win, NETWM::NET_ACTIVE_WINDOW, 2, CurrentTime);
}
QList<Window> NETWM::netwmWindowList()
{
NETWM::checkInit();
QList<Window> list;
int num;
Window *win = reinterpret_cast<Window *>(NETWM::property(QX11Info::appRootWindow(), NETWM::NET_CLIENT_LIST, XA_WINDOW, &num));
if(!win)
{
qDebug("NETWM: Cannot get window list");
return list;
}
for(int i = 0;i < num;i++)
list.append(win[i]);
XFree(win);
return list;
}
int NETWM::netwmDesktop(Window win)
{
NETWM::checkInit();
int desk = 0;
ulong *data;
data = (ulong *)NETWM::property(win, NETWM::NET_WM_DESKTOP, XA_CARDINAL, 0);
if(data)
{
desk = *data;
XFree(data);
}
return desk;
}
NETWM::net_wm_state NETWM::netwmState(Window win)
{
NETWM::checkInit();
net_wm_state nws;
Atom *state;
int num3;
if(!(state = (Atom *)NETWM::property(win, NETWM::NET_WM_STATE, XA_ATOM, &num3)))
return nws;
while(--num3 >= 0)
{
if(state[num3] == NETWM::NET_WM_STATE_MODAL)
{
DBG("NET_WM_STATE_MODAL\n");
nws.modal = 1;
}
else if(state[num3] == NETWM::NET_WM_STATE_STICKY)
{
DBG("NET_WM_STATE_STICKY\n");
nws.sticky = 1;
}
else if(state[num3] == NETWM::NET_WM_STATE_MAXIMIZED_VERT)
{
DBG("NET_WM_STATE_MAXIMIZED_VERT\n");
nws.maximized_vert = 1;
}
else if(state[num3] == NETWM::NET_WM_STATE_MAXIMIZED_HORZ)
{
DBG("NET_WM_STATE_MAXIMIZED_HORZ\n");
nws.maximized_horz = 1;
}
else if(state[num3] == NETWM::NET_WM_STATE_SHADED)
{
DBG("NET_WM_STATE_SHADED\n");
nws.shaded = 1;
}
else if(state[num3] == NETWM::NET_WM_STATE_SKIP_TASKBAR)
{
DBG("NET_WM_STATE_SKIP_TASKBAR\n");
nws.skip_taskbar = 1;
}
else if(state[num3] == NETWM::NET_WM_STATE_SKIP_PAGER)
{
DBG("NET_WM_STATE_SKIP_PAGER\n");
nws.skip_pager = 1;
}
else if(state[num3] == NETWM::NET_WM_STATE_HIDDEN)
{
DBG("NET_WM_STATE_HIDDEN\n");
nws.hidden = 1;
}
else if(state[num3] == NETWM::NET_WM_STATE_FULLSCREEN)
{
DBG("NET_WM_STATE_FULLSCREEN\n");
nws.fullscreen = 1;
}
else if(state[num3] == NETWM::NET_WM_STATE_ABOVE)
{
DBG("NET_WM_STATE_ABOVE\n");
nws.above = 1;
}
else if(state[num3] == NETWM::NET_WM_STATE_BELOW)
{
DBG("NET_WM_STATE_BELOW\n");
nws.below = 1;
}
else if(state[num3] == NETWM::NET_WM_STATE_STAYS_ON_TOP)
{
DBG("NET_WM_STATE_STAYS_ON_TOP\n");
nws.stays_on_top = 1;
}
else if(state[num3] == NETWM::NET_WM_STATE_STAYS_ON_BOTTOM)
{
DBG("NET_WM_STATE_STAYS_ON_BOTTOM\n");
nws.stays_on_bottom = 1;
}
else if(state[num3] == NETWM::NET_WM_STATE_DEMANDS_ATTENTION)
{
DBG("NET_WM_STATE_DEMANDS_ATTENTION\n");
nws.demands_attention = 1;
}
}
nws.valid = true;
XFree(state);
return nws;
}
NETWM::net_wm_window_type NETWM::netwmWindowType(Window win)
{
NETWM::checkInit();
net_wm_window_type nwwt;
Atom *state;
int num3;
bool ok;
if(!(state = (Atom *)NETWM::property(win, NETWM::NET_WM_WINDOW_TYPE, XA_ATOM, &num3, &ok)))
{
if(ok)
{
nwwt.valid = true;
nwwt.normal = 1;
}
return nwwt;
}
nwwt.valid = true;
while(--num3 >= 0)
{
if(state[num3] == NETWM::NET_WM_WINDOW_TYPE_DESKTOP)
{
DBG("NET_WM_WINDOW_TYPE_DESKTOP\n");
nwwt.desktop = 1;
}
else if(state[num3] == NETWM::NET_WM_WINDOW_TYPE_DOCK)
{
DBG("NET_WM_WINDOW_TYPE_DOCK\n");
nwwt.dock = 1;
}
else if(state[num3] == NETWM::NET_WM_WINDOW_TYPE_TOOLBAR)
{
DBG("NET_WM_WINDOW_TYPE_TOOLBAR\n");
nwwt.toolbar = 1;
}
else if(state[num3] == NETWM::NET_WM_WINDOW_TYPE_MENU)
{
DBG("NET_WM_WINDOW_TYPE_MENU\n");
nwwt.menu = 1;
}
else if(state[num3] == NETWM::NET_WM_WINDOW_TYPE_UTILITY)
{
DBG("NET_WM_WINDOW_TYPE_UTILITY\n");
nwwt.utility = 1;
}
else if(state[num3] == NETWM::NET_WM_WINDOW_TYPE_SPLASH)
{
DBG("NET_WM_WINDOW_TYPE_SPLASH\n");
nwwt.splash = 1;
}
else if(state[num3] == NETWM::NET_WM_WINDOW_TYPE_DIALOG)
{
DBG("NET_WM_WINDOW_TYPE_DIALOG\n");
nwwt.dialog = 1;
}
else if(state[num3] == NETWM::NET_WM_WINDOW_TYPE_DROPDOWN_MENU)
{
DBG("NET_WM_WINDOW_TYPE_DROPDOWN_MENU\n");
nwwt.dropdown = 1;
}
else if(state[num3] == NETWM::NET_WM_WINDOW_TYPE_POPUP_MENU)
{
DBG("NET_WM_WINDOW_TYPE_POPUP_MENU\n");
nwwt.popup = 1;
}
else if(state[num3] == NETWM::NET_WM_WINDOW_TYPE_TOOLTIP)
{
DBG("NET_WM_WINDOW_TYPE_TOOLTIP\n");
nwwt.tooltip = 1;
}
else if(state[num3] == NETWM::NET_WM_WINDOW_TYPE_NOTIFICATION)
{
DBG("NET_WM_WINDOW_TYPE_NOTIFICATION\n");
nwwt.notification = 1;
}
else if(state[num3] == NETWM::NET_WM_WINDOW_TYPE_COMBO)
{
DBG("NET_WM_WINDOW_TYPE_COMBO\n");
nwwt.combo = 1;
}
else if(state[num3] == NETWM::NET_WM_WINDOW_TYPE_DND)
{
DBG("NET_WM_WINDOW_TYPE_DND\n");
nwwt.dnd = 1;
}
else if(state[num3] == NETWM::NET_WM_WINDOW_TYPE_NORMAL)
{
DBG("NET_WM_WINDOW_TYPE_NORMAL\n");
nwwt.normal = 1;
}
}
XFree(state);
return nwwt;
}
QString NETWM::icccmString(Window win, Atom atom)
{
NETWM::checkInit();
QString s;
char *data;
if(!(data = (char *)NETWM::property(win, atom, XA_STRING)))
return s;
s = QString::fromUtf8(data);
XFree(data);
return s;
}
QString NETWM::icccmUtf8String(Window win, Atom atom)
{
NETWM::checkInit();
Atom type;
int format;
ulong nitems;
ulong bytes_after;
int result;
uchar *tmp = 0;
QString val;
type = None;
result = XGetWindowProperty(QX11Info::display(), win, atom, 0, LONG_MAX, False,
NETWM::UTF8_STRING, &type, &format, &nitems,
&bytes_after, &tmp);
if(result != Success || type == None || !tmp)
return val;
if(type == NETWM::UTF8_STRING && format == 8 && nitems != 0)
val = QString::fromUtf8(reinterpret_cast<char *>(tmp));
XFree(tmp);
return val;
}
QString NETWM::icccmWindowRole(Window win)
{
NETWM::checkInit();
return NETWM::icccmString(win, NETWM::WM_WINDOW_ROLE);
}
QStringList NETWM::icccmClass(Window win)
{
NETWM::checkInit();
QStringList l;
char *data;
if(!(data = (char *)NETWM::property(win, NETWM::WM_CLASS, XA_STRING)))
return l;
l.append(QString::fromUtf8(data));
l.append(QString::fromUtf8(data+strlen(data)+1));
XFree(data);
return l;
}
QString NETWM::icccmName(Window win)
{
NETWM::checkInit();
return NETWM::icccmString(win, NETWM::WM_NAME);
}
QStringList NETWM::icccmCommand(Window win)
{
NETWM::checkInit();
QStringList list;
char **argv;
int argc;
if(!XGetCommand(QX11Info::display(), win, &argv, &argc))
return list;
for(int i = 0;i < argc;i++)
list.append(argv[i]);
XFreeStringList(argv);
return list;
}
#define MO_NETWM_OPAQUE 0xffffffff
void NETWM::transset(Window window, double d)
{
NETWM::checkInit();
Display *dpy = QX11Info::display();
uint opacity = (uint)(d * MO_NETWM_OPAQUE);
if(opacity == MO_NETWM_OPAQUE)
XDeleteProperty(dpy, window, NETWM::NET_WM_WINDOW_OPACITY);
else
XChangeProperty(dpy, window, NETWM::NET_WM_WINDOW_OPACITY,
XA_CARDINAL, 32, PropModeReplace, (uchar *)&opacity, 1L);
XSync(dpy, False);
}
#if 0
bool NETWM::isComposite()
{
int event_base, error_base;
Display *dpy = QX11Info::display();
// extension is not supported
if(!XCompositeQueryExtension(dpy, &event_base, &error_base))
{
qDebug("NETWM: Composite extension is not supported");
return false;
}
// NETWM-compliant composite manager MUST set selection owner
// of _NET_WM_CM_Sn
Window owner = XGetSelectionOwner(dpy, XInternAtom(dpy, "_NET_WM_CM_S0", False));
return (owner != None);
}
#endif
void NETWM::checkInit()
{
if(!NETWM::WM_STATE)
NETWM::init();
}

188
thirdparty/libqnetwm/libqnetwm/netwm.h vendored Normal file
View File

@@ -0,0 +1,188 @@
/***************************************************************************
* Copyright (C) 2010 by Dmitry 'Krasu' Baryshev *
* ksquirrel.iv@gmail.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef NETWM_H
#define NETWM_H
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include "fixx11h.h"
#include <QStringList>
#include <QString>
#include <QList>
class NETWM
{
public:
struct net_wm_state
{
net_wm_state();
unsigned int modal : 1;
unsigned int sticky : 1;
unsigned int maximized_vert : 1;
unsigned int maximized_horz : 1;
unsigned int shaded : 1;
unsigned int skip_taskbar : 1;
unsigned int skip_pager : 1;
unsigned int hidden : 1;
unsigned int fullscreen : 1;
unsigned int above : 1;
unsigned int below : 1;
unsigned int stays_on_top : 1;
unsigned int stays_on_bottom : 1;
unsigned int demands_attention : 1;
bool valid;
};
struct net_wm_window_type
{
net_wm_window_type();
unsigned int desktop : 1;
unsigned int dock : 1;
unsigned int toolbar : 1;
unsigned int menu : 1;
unsigned int utility : 1;
unsigned int splash : 1;
unsigned int dialog : 1;
unsigned int dropdown : 1;
unsigned int popup : 1;
unsigned int tooltip : 1;
unsigned int notification : 1;
unsigned int combo : 1;
unsigned int dnd : 1;
unsigned int normal : 1;
bool valid;
};
/*************************************************************************/
static void init();
static void transset(Window, double);
#if 0
static bool isComposite();
#endif
static int setProperty(Window, Atom, long, uchar *, int);
static int setPropertySkipTaskbar(Window);
static int setPropertyOnTop(Window);
static void* property(Window win, Atom prop, Atom type, int *nitems = 0, bool *ok = 0);
static bool climsg(Window win, long type, long l0, long l1 = 0, long l2 = 0, long l3 = 0, long l4 = 0);
static bool climsgwm(Window win, Atom type, Atom arg);
// NETWM helper functions
static qint64 netwmPid(Window win);
static QList<Window> netwmWindowList();
static uint netwmDesktopsNumber();
static uint netwmCurrentDesktop();
static int netwmDesktop(Window win);
static net_wm_state netwmState(Window win);
static net_wm_window_type netwmWindowType(Window win);
static bool netwmActivateWindow(Window win);
// ICCCM helper functions
static QString icccmString(Window win, Atom atom);
static QString icccmUtf8String(Window win, Atom atom);
static QString icccmWindowRole(Window win);
static QStringList icccmClass(Window win);
static QString icccmName(Window win);
static QStringList icccmCommand(Window win);
/*************************************************************************/
/********************************* Atoms *********************************/
/*************************************************************************/
static Atom UTF8_STRING;
static Atom XROOTPMAP_ID;
static Atom WM_STATE;
static Atom WM_CLASS;
static Atom WM_NAME;
static Atom WM_DELETE_WINDOW;
static Atom WM_PROTOCOLS;
static Atom WM_CHANGE_STATE;
static Atom WM_WINDOW_ROLE;
static Atom NET_WORKAREA;
static Atom NET_CLIENT_LIST;
static Atom NET_CLIENT_LIST_STACKING;
static Atom NET_NUMBER_OF_DESKTOPS;
static Atom NET_CURRENT_DESKTOP;
static Atom NET_DESKTOP_NAMES;
static Atom NET_ACTIVE_WINDOW;
static Atom NET_CLOSE_WINDOW;
static Atom NET_SUPPORTED;
static Atom NET_WM_DESKTOP;
static Atom NET_SHOWING_DESKTOP;
static Atom NET_WM_STATE;
static Atom NET_WM_STATE_MODAL;
static Atom NET_WM_STATE_STICKY;
static Atom NET_WM_STATE_MAXIMIZED_VERT;
static Atom NET_WM_STATE_MAXIMIZED_HORZ;
static Atom NET_WM_STATE_SHADED;
static Atom NET_WM_STATE_SKIP_TASKBAR;
static Atom NET_WM_STATE_SKIP_PAGER;
static Atom NET_WM_STATE_HIDDEN;
static Atom NET_WM_STATE_FULLSCREEN;
static Atom NET_WM_STATE_ABOVE;
static Atom NET_WM_STATE_BELOW;
static Atom NET_WM_STATE_STAYS_ON_TOP;
static Atom NET_WM_STATE_STAYS_ON_BOTTOM;
static Atom NET_WM_STATE_DEMANDS_ATTENTION;
static Atom NET_WM_WINDOW_TYPE;
static Atom NET_WM_WINDOW_TYPE_DESKTOP;
static Atom NET_WM_WINDOW_TYPE_DOCK;
static Atom MODERRO_WINDOW_TYPE_DOCK;
static Atom NET_WM_WINDOW_TYPE_TOOLBAR;
static Atom NET_WM_WINDOW_TYPE_MENU;
static Atom NET_WM_WINDOW_TYPE_UTILITY;
static Atom NET_WM_WINDOW_TYPE_SPLASH;
static Atom NET_WM_WINDOW_TYPE_DIALOG;
static Atom NET_WM_WINDOW_TYPE_DROPDOWN_MENU;
static Atom NET_WM_WINDOW_TYPE_POPUP_MENU;
static Atom NET_WM_WINDOW_TYPE_TOOLTIP;
static Atom NET_WM_WINDOW_TYPE_NOTIFICATION;
static Atom NET_WM_WINDOW_TYPE_COMBO;
static Atom NET_WM_WINDOW_TYPE_DND;
static Atom NET_WM_WINDOW_TYPE_NORMAL;
static Atom NET_WM_WINDOW_OPACITY;
static Atom NET_WM_NAME;
static Atom NET_WM_VISIBLE_NAME;
static Atom NET_WM_STRUT;
static Atom NET_WM_STRUT_PARTIAL;
static Atom NET_WM_ICON;
static Atom NET_WM_PID;
private:
static void checkInit();
};
#endif