From 3aba7dd788e05f2eeee9644bf810e2b637a62a9d Mon Sep 17 00:00:00 2001 From: Mark Liversedge Date: Thu, 30 Dec 2010 16:59:48 +0000 Subject: [PATCH] Inital V3 Branch --- qtsolutions/common.pri | 6 + qtsolutions/soap/QtSoapArray | 1 + qtsolutions/soap/QtSoapArrayIterator | 1 + qtsolutions/soap/QtSoapHttpTransport | 1 + qtsolutions/soap/QtSoapMessage | 1 + qtsolutions/soap/QtSoapNamespaces | 1 + qtsolutions/soap/QtSoapQName | 1 + qtsolutions/soap/QtSoapSimpleType | 1 + qtsolutions/soap/QtSoapStruct | 1 + qtsolutions/soap/QtSoapStructIterator | 1 + qtsolutions/soap/QtSoapType | 1 + qtsolutions/soap/QtSoapTypeConstructor | 1 + qtsolutions/soap/QtSoapTypeConstructorBase | 1 + qtsolutions/soap/QtSoapTypeFactory | 1 + qtsolutions/soap/qtsoap.cpp | 3305 ++++++++++++++++++ qtsolutions/soap/qtsoap.h | 611 ++++ qtsolutions/soap/qtsoap.pri | 16 + qxt/src/qxtglobal.h | 2 +- qxt/src/qxtnamespace.h | 3 +- qxt/src/qxtscheduleheaderwidget.cpp | 89 + qxt/src/qxtscheduleheaderwidget.h | 49 + qxt/src/qxtscheduleitemdelegate.cpp | 295 ++ qxt/src/qxtscheduleitemdelegate.h | 65 + qxt/src/qxtscheduleview.cpp | 952 +++++ qxt/src/qxtscheduleview.h | 138 + qxt/src/qxtscheduleview_p.cpp | 766 ++++ qxt/src/qxtscheduleview_p.h | 183 + qxt/src/qxtscheduleviewheadermodel_p.cpp | 211 ++ qxt/src/qxtscheduleviewheadermodel_p.h | 81 + qxt/src/qxtstyleoptionscheduleviewitem.cpp | 41 + qxt/src/qxtstyleoptionscheduleviewitem.h | 56 + src/.gitignore | 4 +- src/ANTplusController.h | 1 + src/AerobicDecoupling.cpp | 6 +- src/Aerolab.cpp | 21 +- src/Aerolab.h | 4 + src/AerolabWindow.cpp | 24 +- src/AerolabWindow.h | 5 +- src/AllPlot.cpp | 102 +- src/AllPlot.h | 12 +- src/AllPlotWindow.cpp | 162 +- src/AllPlotWindow.h | 48 +- src/AthleteTool.cpp | 66 + src/AthleteTool.h | 64 + src/BasicRideMetrics.cpp | 271 +- src/BestIntervalDialog.cpp | 3 + src/BestIntervalDialog.h | 3 + src/BikeScore.cpp | 34 +- src/BinRideFile.cpp | 13 +- src/BinRideFile.h | 1 + src/CalendarDownload.cpp | 115 + src/CalendarDownload.h | 49 + src/ChooseCyclistDialog.cpp | 36 +- src/ChooseCyclistDialog.h | 11 +- src/Coggan.cpp | 195 ++ src/ColorButton.h | 3 + src/Colors.cpp | 36 +- src/Colors.h | 100 +- src/CommPort.cpp | 6 +- src/CommPort.h | 7 +- src/Computrainer.h | 1 + src/Computrainer3dpFile.h | 1 + src/ComputrainerController.cpp | 1 + src/ComputrainerController.h | 1 + src/ConfigDialog.cpp | 89 +- src/ConfigDialog.h | 4 +- src/CpintPlot.cpp | 60 +- src/CpintPlot.h | 8 +- src/CriticalPowerWindow.cpp | 56 +- src/CriticalPowerWindow.h | 17 +- src/CsvRideFile.h | 7 +- src/D2XX.cpp | 16 +- src/D2XX.h | 7 +- src/DBAccess.cpp | 441 ++- src/DBAccess.h | 31 +- src/DanielsPoints.cpp | 12 +- src/DataProcessor.cpp | 4 +- src/DataProcessor.h | 6 +- src/DatePickerDialog.cpp | 36 +- src/DatePickerDialog.h | 9 +- src/DaysScaleDraw.h | 5 +- src/Device.cpp | 8 +- src/Device.h | 9 +- src/DeviceConfiguration.cpp | 32 +- src/DeviceConfiguration.h | 1 + src/DeviceTypes.cpp | 3 +- src/DeviceTypes.h | 2 + src/DiaryWindow.cpp | 302 ++ src/DiaryWindow.h | 88 + src/DownloadRideDialog.cpp | 34 +- src/DownloadRideDialog.h | 13 +- src/ErgFile.h | 1 + src/ErgFilePlot.cpp | 2 + src/ErgFilePlot.h | 3 + src/FitRideFile.h | 1 + src/FixGaps.cpp | 15 +- src/FixSpikes.cpp | 15 +- src/FixTorque.cpp | 9 +- src/GcCalendarModel.h | 435 +++ src/GcPane.cpp | 446 +++ src/GcPane.h | 82 + src/GcRideFile.cpp | 2 +- src/GcRideFile.h | 1 + src/GcWindowLayout.cpp | 167 + src/GcWindowLayout.h | 60 + src/GcWindowRegistry.cpp | 96 + src/GcWindowRegistry.h | 61 + src/GcWindowTool.cpp | 65 + src/GcWindowTool.h | 63 + src/GoldenCheetah.cpp | 163 + src/GoldenCheetah.h | 121 + src/GoldenClient.cpp | 430 +++ src/GoldenClient.h | 163 + src/GoogleMapControl.cpp | 30 +- src/GoogleMapControl.h | 5 +- src/GpxParser.cpp | 5 +- src/GpxParser.h | 1 + src/GpxRideFile.h | 1 + src/HelpWindow.cpp | 38 + src/HelpWindow.h | 46 + src/HistogramWindow.cpp | 188 +- src/HistogramWindow.h | 48 +- src/HomeWindow.cpp | 816 +++++ src/HomeWindow.h | 155 + src/HrTimeInZone.cpp | 2 +- src/HrZones.cpp | 2 - src/HrZones.h | 3 + src/ICalendar.cpp | 277 ++ src/ICalendar.h | 66 + src/IntervalItem.h | 1 + src/JsonRideFile.h | 39 + src/JsonRideFile.l | 70 + src/JsonRideFile.y | 407 +++ src/KmlRideFile.h | 1 + src/LTMCanvasPicker.h | 3 + src/LTMChartParser.cpp | 4 + src/LTMChartParser.h | 1 + src/LTMOutliers.h | 1 + src/LTMPlot.cpp | 483 ++- src/LTMPlot.h | 58 + src/LTMPopup.cpp | 296 ++ src/LTMPopup.h | 75 + src/LTMSettings.h | 28 +- src/LTMTool.cpp | 214 +- src/LTMTool.h | 19 +- src/LTMTrend.cpp | 38 +- src/LTMTrend.h | 6 +- src/LTMWindow.cpp | 174 +- src/LTMWindow.h | 44 +- src/LogTimeScaleDraw.cpp | 16 +- src/LogTimeScaleDraw.h | 11 +- src/LogTimeScaleEngine.cpp | 36 +- src/LogTimeScaleEngine.h | 13 +- src/MainWindow.cpp | 984 ++++-- src/MainWindow.h | 121 +- src/ManualRideDialog.cpp | 3 +- src/ManualRideDialog.h | 15 +- src/ManualRideFile.cpp | 18 +- src/ManualRideFile.h | 7 +- src/MetricAggregator.cpp | 80 +- src/MetricAggregator.h | 13 +- src/ModelPlot.cpp | 314 +- src/ModelPlot.h | 35 + src/ModelWindow.cpp | 200 +- src/ModelWindow.h | 40 +- src/MultiWindow.cpp | 34 + src/MultiWindow.h | 56 + src/NullController.cpp | 56 + src/NullController.h | 62 + src/Pages.cpp | 971 ++++- src/Pages.h | 311 +- src/PeakPower.cpp | 6 +- src/PerfPlot.cpp | 34 +- src/PerfPlot.h | 3 + src/PerformanceManagerWindow.cpp | 39 +- src/PerformanceManagerWindow.h | 9 +- src/PfPvPlot.cpp | 229 +- src/PfPvPlot.h | 15 +- src/PfPvWindow.cpp | 59 +- src/PfPvWindow.h | 29 +- src/PolarRideFile.cpp | 6 +- src/PolarRideFile.h | 7 +- src/PowerHist.cpp | 277 +- src/PowerHist.h | 12 +- src/PowerTapDevice.cpp | 16 +- src/PowerTapDevice.h | 9 +- src/PowerTapUtil.cpp | 18 +- src/PowerTapUtil.h | 11 +- src/ProtocolHandler.cpp | 631 ++++ src/ProtocolHandler.h | 256 ++ src/PwxRideFile.cpp | 7 + src/PwxRideFile.h | 1 + src/QtMacSegmentedButton.h | 75 + src/QtMacSegmentedButton.mm | 142 + src/QuarqParser.h | 1 + src/QuarqRideFile.h | 1 + src/QuarqdClient.h | 3 + src/QxtScheduleViewProxy.h | 201 ++ src/RaceCourse.cpp | 97 + src/RaceCourse.h | 58 + src/RaceDispatcher.cpp | 36 + src/RaceDispatcher.h | 48 + src/RaceLeaderboard.cpp | 36 + src/RaceLeaderboard.h | 50 + src/RaceRider.cpp | 134 + src/RaceRider.h | 61 + src/RaceRiders.cpp | 73 + src/RaceRiders.h | 52 + src/RaceWindow.cpp | 73 + src/RaceWindow.h | 65 + src/RawRideFile.cpp | 44 +- src/RawRideFile.h | 7 +- src/RealtimeController.h | 1 + src/RealtimeData.cpp | 10 + src/RealtimeData.h | 6 + src/RealtimePlot.cpp | 2 + src/RealtimePlot.h | 3 + src/RealtimeWindow.cpp | 326 +- src/RealtimeWindow.h | 20 +- src/RideCalendar.cpp | 9 +- src/RideCalendar.h | 3 + src/RideEditor.cpp | 136 +- src/RideEditor.h | 27 +- src/RideFile.cpp | 38 +- src/RideFile.h | 8 +- src/RideFileCommand.h | 3 + src/RideFileTableModel.h | 3 + src/RideImportWizard.cpp | 38 +- src/RideImportWizard.h | 5 + src/RideItem.cpp | 27 +- src/RideItem.h | 24 +- src/RideMetadata.cpp | 328 +- src/RideMetadata.h | 35 +- src/RideMetric.cpp | 11 +- src/RideMetric.h | 17 +- src/RideNavigator.cpp | 716 ++++ src/RideNavigator.h | 166 + src/RideNavigatorProxy.h | 385 ++ src/RideSummaryWindow.cpp | 161 +- src/RideSummaryWindow.h | 10 +- src/SaveDialogs.cpp | 36 +- src/SaveDialogs.h | 5 + src/ScatterPlot.cpp | 384 ++ src/ScatterPlot.h | 80 + src/ScatterWindow.cpp | 193 + src/ScatterWindow.h | 123 + src/Season.cpp | 2 +- src/Season.h | 7 +- src/SeasonParser.h | 1 + src/Serial.cpp | 12 +- src/Serial.h | 7 +- src/Settings.cpp | 34 + src/Settings.h | 86 +- src/SimpleNetworkClient.cpp | 18 +- src/SimpleNetworkClient.h | 1 + src/SimpleNetworkController.h | 1 + src/SpecialFields.cpp | 3 + src/SpecialFields.h | 1 + src/SplitRideDialog.h | 3 + src/SrdRideFile.h | 1 + src/SrmDevice.cpp | 6 +- src/SrmDevice.h | 9 +- src/SrmRideFile.h | 7 +- src/StressCalculator.cpp | 4 +- src/StressCalculator.h | 5 +- src/SummaryMetrics.cpp | 98 + src/SummaryMetrics.h | 23 +- src/SummaryWindow.cpp | 48 + src/SummaryWindow.h | 50 + src/TPDownload.cpp | 249 ++ src/TPDownload.h | 90 + src/TPDownloadDialog.cpp | 911 +++++ src/TPDownloadDialog.h | 122 + src/TPUpload.cpp | 125 + src/TPUpload.h | 48 + src/TPUploadDialog.cpp | 78 + src/TPUploadDialog.h | 56 + src/TRIMPPoints.cpp | 20 +- src/TcxParser.cpp | 13 +- src/TcxParser.h | 1 + src/TcxRideFile.cpp | 18 +- src/TcxRideFile.h | 7 +- src/TimeInZone.cpp | 6 +- src/TimeUtils.cpp | 10 +- src/TimeUtils.h | 7 +- src/ToolsDialog.h | 3 + src/TrainTabs.cpp | 9 +- src/TrainTabs.h | 18 +- src/TrainTool.cpp | 206 +- src/TrainTool.h | 65 +- src/TrainWindow.cpp | 41 +- src/TrainWindow.h | 8 +- src/TreeMapPlot.cpp | 205 ++ src/TreeMapPlot.h | 270 ++ src/TreeMapWindow.cpp | 262 ++ src/TreeMapWindow.h | 100 + src/TwitterDialog.cpp | 94 +- src/TwitterDialog.h | 5 +- src/TxtRideFile.h | 2 +- src/Units.h | 1 + src/VideoWindow.cpp | 80 + src/VideoWindow.h | 60 + src/ViewSelection.h | 3 + src/WattsPerKilogram.cpp | 255 ++ src/WeeklySummaryWindow.cpp | 237 +- src/WeeklySummaryWindow.h | 5 +- src/WeeklyViewItemDelegate.h | 49 + src/WithingsDownload.cpp | 72 + src/WithingsDownload.h | 49 + src/WithingsParser.h | 63 + src/WithingsParser.l | 76 + src/WithingsParser.y | 198 ++ src/WkoRideFile.cpp | 482 ++- src/WkoRideFile.h | 11 +- src/ZoneScaleDraw.h | 1 + src/Zones.h | 3 + src/application.qrc | 19 +- src/gcconfig.pri.in | 26 +- src/html/ltm-summary.html | 123 + src/images/cheetah.png | Bin 0 -> 2188 bytes src/images/config.png | Bin 7965 -> 162755 bytes src/images/splashscreen.png | Bin 0 -> 99860 bytes src/images/splashscreen.pptx | Bin 0 -> 251187 bytes src/images/toolbar/64 bit.png | Bin 0 -> 3307 bytes src/images/toolbar/Computer On.png | Bin 0 -> 2895 bytes src/images/toolbar/Computer.png | Bin 0 -> 2310 bytes src/images/toolbar/Credit card.png | Bin 0 -> 2574 bytes src/images/toolbar/Desktop 2.png | Bin 0 -> 2899 bytes src/images/toolbar/Ekisho Deep Ocean HD1.png | Bin 0 -> 3263 bytes src/images/toolbar/GeneralPreferences.png | Bin 0 -> 2491 bytes src/images/toolbar/Globe.png | Bin 0 -> 3470 bytes src/images/toolbar/Graphite Computer On.png | Bin 0 -> 2839 bytes src/images/toolbar/Graphite Desktop.png | Bin 0 -> 2851 bytes src/images/toolbar/Graphite Globe.png | Bin 0 -> 3495 bytes src/images/toolbar/Picture Cast.png | Bin 0 -> 3580 bytes src/images/toolbar/Podcast.png | Bin 0 -> 3801 bytes src/images/toolbar/RSS.png | Bin 0 -> 2775 bytes src/images/toolbar/RSS_alt.png | Bin 0 -> 2712 bytes src/images/toolbar/Universal.png | Bin 0 -> 2769 bytes src/images/toolbar/address books.png | Bin 0 -> 2904 bytes src/images/toolbar/advanced.png | Bin 0 -> 3495 bytes src/images/toolbar/applications.png | Bin 0 -> 3456 bytes src/images/toolbar/archive.png | Bin 0 -> 2960 bytes src/images/toolbar/back_alt.png | Bin 0 -> 2233 bytes src/images/toolbar/burn.png | Bin 0 -> 3127 bytes src/images/toolbar/calculator.png | Bin 0 -> 2060 bytes src/images/toolbar/cash.png | Bin 0 -> 3557 bytes src/images/toolbar/color.png | Bin 0 -> 3115 bytes src/images/toolbar/copy doc.png | Bin 0 -> 3078 bytes src/images/toolbar/delete archive.png | Bin 0 -> 2533 bytes src/images/toolbar/down_alt.png | Bin 0 -> 2256 bytes src/images/toolbar/edit.png | Bin 0 -> 2890 bytes src/images/toolbar/exclamation.png | Bin 0 -> 3008 bytes src/images/toolbar/exit.png | Bin 0 -> 2500 bytes src/images/toolbar/flipbutton.png | Bin 0 -> 1275 bytes src/images/toolbar/folder.png | Bin 0 -> 2451 bytes src/images/toolbar/folder_smart.png | Bin 0 -> 2784 bytes src/images/toolbar/forward all.png | Bin 0 -> 3151 bytes src/images/toolbar/forward button white.png | Bin 0 -> 2937 bytes src/images/toolbar/forward button.png | Bin 0 -> 2848 bytes src/images/toolbar/forward.png | Bin 0 -> 3113 bytes src/images/toolbar/forward_alt.png | Bin 0 -> 2217 bytes src/images/toolbar/heineken.png | Bin 0 -> 3531 bytes src/images/toolbar/help.png | Bin 0 -> 2952 bytes src/images/toolbar/iChat.png | Bin 0 -> 3249 bytes src/images/toolbar/iPhone.png | Bin 0 -> 2041 bytes src/images/toolbar/info.png | Bin 0 -> 2817 bytes src/images/toolbar/install.png | Bin 0 -> 3567 bytes src/images/toolbar/intel 2.png | Bin 0 -> 2448 bytes src/images/toolbar/intel.png | Bin 0 -> 2515 bytes src/images/toolbar/keyboard.png | Bin 0 -> 2055 bytes src/images/toolbar/library bookmarked.png | Bin 0 -> 2623 bytes src/images/toolbar/lock.png | Bin 0 -> 2931 bytes src/images/toolbar/main/analysis.png | Bin 0 -> 70396 bytes src/images/toolbar/main/athlete.png | Bin 0 -> 29209 bytes src/images/toolbar/main/config.png | Bin 0 -> 32943 bytes src/images/toolbar/main/diary.png | Bin 0 -> 14345 bytes src/images/toolbar/main/help.png | Bin 0 -> 71061 bytes src/images/toolbar/main/home.png | Bin 0 -> 72368 bytes src/images/toolbar/main/measures.png | Bin 0 -> 4234 bytes src/images/toolbar/main/tick.png | Bin 0 -> 202 bytes src/images/toolbar/main/train.png | Bin 0 -> 16712 bytes src/images/toolbar/minus white.png | Bin 0 -> 2627 bytes src/images/toolbar/minus.png | Bin 0 -> 2627 bytes src/images/toolbar/monitor.png | Bin 0 -> 2360 bytes src/images/toolbar/moon 2.png | Bin 0 -> 3776 bytes src/images/toolbar/moon 3.png | Bin 0 -> 3393 bytes src/images/toolbar/movies alt.png | Bin 0 -> 2464 bytes src/images/toolbar/movies.png | Bin 0 -> 3346 bytes src/images/toolbar/music 2.png | Bin 0 -> 2615 bytes src/images/toolbar/music.png | Bin 0 -> 3612 bytes src/images/toolbar/new archive.png | Bin 0 -> 2670 bytes src/images/toolbar/new doc.png | Bin 0 -> 2595 bytes src/images/toolbar/new mail.png | Bin 0 -> 2959 bytes src/images/toolbar/notebook.png | Bin 0 -> 2975 bytes src/images/toolbar/open alt.png | Bin 0 -> 3308 bytes src/images/toolbar/pause.png | Bin 0 -> 2726 bytes src/images/toolbar/picture.png | Bin 0 -> 2716 bytes src/images/toolbar/pinbutton.png | Bin 0 -> 1275 bytes src/images/toolbar/play.png | Bin 0 -> 2920 bytes src/images/toolbar/plus.png | Bin 0 -> 2764 bytes src/images/toolbar/popbutton.png | Bin 0 -> 1259 bytes src/images/toolbar/public.png | Bin 0 -> 3234 bytes src/images/toolbar/record.png | Bin 0 -> 2793 bytes src/images/toolbar/remove.png | Bin 0 -> 3577 bytes src/images/toolbar/rewind button white.png | Bin 0 -> 2932 bytes src/images/toolbar/rewind button.png | Bin 0 -> 2866 bytes src/images/toolbar/send.png | Bin 0 -> 2897 bytes src/images/toolbar/server.png | Bin 0 -> 2421 bytes src/images/toolbar/smiley sad.png | Bin 0 -> 2796 bytes src/images/toolbar/smiley.png | Bin 0 -> 2779 bytes src/images/toolbar/stop 2.png | Bin 0 -> 2672 bytes src/images/toolbar/stop.png | Bin 0 -> 3221 bytes src/images/toolbar/stop_alt.png | Bin 0 -> 2660 bytes src/images/toolbar/sun 2.png | Bin 0 -> 4256 bytes src/images/toolbar/sun.png | Bin 0 -> 3818 bytes src/images/toolbar/terminal.png | Bin 0 -> 2054 bytes src/images/toolbar/trash full.png | Bin 0 -> 3915 bytes src/images/toolbar/trash.png | Bin 0 -> 3657 bytes src/images/toolbar/unarchive.png | Bin 0 -> 2996 bytes src/images/toolbar/unread mail.png | Bin 0 -> 2763 bytes src/images/toolbar/up_alt.png | Bin 0 -> 2208 bytes src/images/toolbar/user.png | Bin 0 -> 2441 bytes src/images/toolbar/users.png | Bin 0 -> 2766 bytes src/images/toolbar/window.png | Bin 0 -> 2318 bytes src/images/toolbar/zoom in.png | Bin 0 -> 3037 bytes src/images/toolbar/zoom out.png | Bin 0 -> 2802 bytes src/main.cpp | 19 +- src/ppxml.rb | 0 src/simpleserver.py | 125 +- src/src.pro | 118 +- src/win32/GoldenCheetahInstall.nsi | 6 + src/xml/measures.xml | 36 + src/xml/metadata.xml | 5 + 434 files changed, 27702 insertions(+), 3196 deletions(-) create mode 100644 qtsolutions/common.pri create mode 100644 qtsolutions/soap/QtSoapArray create mode 100644 qtsolutions/soap/QtSoapArrayIterator create mode 100644 qtsolutions/soap/QtSoapHttpTransport create mode 100644 qtsolutions/soap/QtSoapMessage create mode 100644 qtsolutions/soap/QtSoapNamespaces create mode 100644 qtsolutions/soap/QtSoapQName create mode 100644 qtsolutions/soap/QtSoapSimpleType create mode 100644 qtsolutions/soap/QtSoapStruct create mode 100644 qtsolutions/soap/QtSoapStructIterator create mode 100644 qtsolutions/soap/QtSoapType create mode 100644 qtsolutions/soap/QtSoapTypeConstructor create mode 100644 qtsolutions/soap/QtSoapTypeConstructorBase create mode 100644 qtsolutions/soap/QtSoapTypeFactory create mode 100644 qtsolutions/soap/qtsoap.cpp create mode 100644 qtsolutions/soap/qtsoap.h create mode 100644 qtsolutions/soap/qtsoap.pri create mode 100644 qxt/src/qxtscheduleheaderwidget.cpp create mode 100644 qxt/src/qxtscheduleheaderwidget.h create mode 100644 qxt/src/qxtscheduleitemdelegate.cpp create mode 100644 qxt/src/qxtscheduleitemdelegate.h create mode 100644 qxt/src/qxtscheduleview.cpp create mode 100644 qxt/src/qxtscheduleview.h create mode 100644 qxt/src/qxtscheduleview_p.cpp create mode 100644 qxt/src/qxtscheduleview_p.h create mode 100644 qxt/src/qxtscheduleviewheadermodel_p.cpp create mode 100644 qxt/src/qxtscheduleviewheadermodel_p.h create mode 100644 qxt/src/qxtstyleoptionscheduleviewitem.cpp create mode 100644 qxt/src/qxtstyleoptionscheduleviewitem.h create mode 100644 src/AthleteTool.cpp create mode 100644 src/AthleteTool.h create mode 100644 src/CalendarDownload.cpp create mode 100644 src/CalendarDownload.h create mode 100644 src/Coggan.cpp create mode 100644 src/DiaryWindow.cpp create mode 100644 src/DiaryWindow.h create mode 100644 src/GcCalendarModel.h create mode 100644 src/GcPane.cpp create mode 100644 src/GcPane.h create mode 100644 src/GcWindowLayout.cpp create mode 100644 src/GcWindowLayout.h create mode 100644 src/GcWindowRegistry.cpp create mode 100644 src/GcWindowRegistry.h create mode 100644 src/GcWindowTool.cpp create mode 100644 src/GcWindowTool.h create mode 100644 src/GoldenCheetah.cpp create mode 100644 src/GoldenCheetah.h create mode 100644 src/GoldenClient.cpp create mode 100644 src/GoldenClient.h create mode 100644 src/HelpWindow.cpp create mode 100644 src/HelpWindow.h create mode 100644 src/HomeWindow.cpp create mode 100644 src/HomeWindow.h create mode 100644 src/ICalendar.cpp create mode 100644 src/ICalendar.h create mode 100644 src/JsonRideFile.h create mode 100644 src/JsonRideFile.l create mode 100644 src/JsonRideFile.y create mode 100644 src/LTMPopup.cpp create mode 100644 src/LTMPopup.h create mode 100644 src/MultiWindow.cpp create mode 100644 src/MultiWindow.h create mode 100644 src/NullController.cpp create mode 100644 src/NullController.h create mode 100644 src/ProtocolHandler.cpp create mode 100644 src/ProtocolHandler.h create mode 100644 src/QtMacSegmentedButton.h create mode 100644 src/QtMacSegmentedButton.mm create mode 100644 src/QxtScheduleViewProxy.h create mode 100644 src/RaceCourse.cpp create mode 100644 src/RaceCourse.h create mode 100644 src/RaceDispatcher.cpp create mode 100644 src/RaceDispatcher.h create mode 100644 src/RaceLeaderboard.cpp create mode 100644 src/RaceLeaderboard.h create mode 100644 src/RaceRider.cpp create mode 100644 src/RaceRider.h create mode 100644 src/RaceRiders.cpp create mode 100644 src/RaceRiders.h create mode 100644 src/RaceWindow.cpp create mode 100644 src/RaceWindow.h create mode 100644 src/RideNavigator.cpp create mode 100644 src/RideNavigator.h create mode 100644 src/RideNavigatorProxy.h create mode 100644 src/ScatterPlot.cpp create mode 100644 src/ScatterPlot.h create mode 100644 src/ScatterWindow.cpp create mode 100644 src/ScatterWindow.h create mode 100644 src/Settings.cpp create mode 100644 src/SummaryMetrics.cpp create mode 100644 src/SummaryWindow.cpp create mode 100644 src/SummaryWindow.h create mode 100644 src/TPDownload.cpp create mode 100644 src/TPDownload.h create mode 100644 src/TPDownloadDialog.cpp create mode 100644 src/TPDownloadDialog.h create mode 100644 src/TPUpload.cpp create mode 100644 src/TPUpload.h create mode 100644 src/TPUploadDialog.cpp create mode 100644 src/TPUploadDialog.h create mode 100644 src/TreeMapPlot.cpp create mode 100644 src/TreeMapPlot.h create mode 100644 src/TreeMapWindow.cpp create mode 100644 src/TreeMapWindow.h create mode 100644 src/VideoWindow.cpp create mode 100644 src/VideoWindow.h create mode 100644 src/WattsPerKilogram.cpp create mode 100644 src/WeeklyViewItemDelegate.h create mode 100644 src/WithingsDownload.cpp create mode 100644 src/WithingsDownload.h create mode 100644 src/WithingsParser.h create mode 100644 src/WithingsParser.l create mode 100644 src/WithingsParser.y create mode 100644 src/html/ltm-summary.html create mode 100644 src/images/cheetah.png create mode 100644 src/images/splashscreen.png create mode 100644 src/images/splashscreen.pptx create mode 100644 src/images/toolbar/64 bit.png create mode 100644 src/images/toolbar/Computer On.png create mode 100644 src/images/toolbar/Computer.png create mode 100644 src/images/toolbar/Credit card.png create mode 100644 src/images/toolbar/Desktop 2.png create mode 100644 src/images/toolbar/Ekisho Deep Ocean HD1.png create mode 100644 src/images/toolbar/GeneralPreferences.png create mode 100644 src/images/toolbar/Globe.png create mode 100644 src/images/toolbar/Graphite Computer On.png create mode 100644 src/images/toolbar/Graphite Desktop.png create mode 100644 src/images/toolbar/Graphite Globe.png create mode 100644 src/images/toolbar/Picture Cast.png create mode 100644 src/images/toolbar/Podcast.png create mode 100644 src/images/toolbar/RSS.png create mode 100644 src/images/toolbar/RSS_alt.png create mode 100644 src/images/toolbar/Universal.png create mode 100644 src/images/toolbar/address books.png create mode 100644 src/images/toolbar/advanced.png create mode 100644 src/images/toolbar/applications.png create mode 100644 src/images/toolbar/archive.png create mode 100644 src/images/toolbar/back_alt.png create mode 100644 src/images/toolbar/burn.png create mode 100644 src/images/toolbar/calculator.png create mode 100644 src/images/toolbar/cash.png create mode 100644 src/images/toolbar/color.png create mode 100644 src/images/toolbar/copy doc.png create mode 100644 src/images/toolbar/delete archive.png create mode 100644 src/images/toolbar/down_alt.png create mode 100644 src/images/toolbar/edit.png create mode 100644 src/images/toolbar/exclamation.png create mode 100644 src/images/toolbar/exit.png create mode 100644 src/images/toolbar/flipbutton.png create mode 100644 src/images/toolbar/folder.png create mode 100644 src/images/toolbar/folder_smart.png create mode 100644 src/images/toolbar/forward all.png create mode 100644 src/images/toolbar/forward button white.png create mode 100644 src/images/toolbar/forward button.png create mode 100644 src/images/toolbar/forward.png create mode 100644 src/images/toolbar/forward_alt.png create mode 100644 src/images/toolbar/heineken.png create mode 100644 src/images/toolbar/help.png create mode 100644 src/images/toolbar/iChat.png create mode 100644 src/images/toolbar/iPhone.png create mode 100644 src/images/toolbar/info.png create mode 100644 src/images/toolbar/install.png create mode 100644 src/images/toolbar/intel 2.png create mode 100644 src/images/toolbar/intel.png create mode 100644 src/images/toolbar/keyboard.png create mode 100644 src/images/toolbar/library bookmarked.png create mode 100644 src/images/toolbar/lock.png create mode 100644 src/images/toolbar/main/analysis.png create mode 100644 src/images/toolbar/main/athlete.png create mode 100644 src/images/toolbar/main/config.png create mode 100644 src/images/toolbar/main/diary.png create mode 100644 src/images/toolbar/main/help.png create mode 100644 src/images/toolbar/main/home.png create mode 100644 src/images/toolbar/main/measures.png create mode 100644 src/images/toolbar/main/tick.png create mode 100644 src/images/toolbar/main/train.png create mode 100644 src/images/toolbar/minus white.png create mode 100644 src/images/toolbar/minus.png create mode 100644 src/images/toolbar/monitor.png create mode 100644 src/images/toolbar/moon 2.png create mode 100644 src/images/toolbar/moon 3.png create mode 100644 src/images/toolbar/movies alt.png create mode 100644 src/images/toolbar/movies.png create mode 100644 src/images/toolbar/music 2.png create mode 100644 src/images/toolbar/music.png create mode 100644 src/images/toolbar/new archive.png create mode 100644 src/images/toolbar/new doc.png create mode 100644 src/images/toolbar/new mail.png create mode 100644 src/images/toolbar/notebook.png create mode 100644 src/images/toolbar/open alt.png create mode 100644 src/images/toolbar/pause.png create mode 100644 src/images/toolbar/picture.png create mode 100644 src/images/toolbar/pinbutton.png create mode 100644 src/images/toolbar/play.png create mode 100644 src/images/toolbar/plus.png create mode 100644 src/images/toolbar/popbutton.png create mode 100644 src/images/toolbar/public.png create mode 100644 src/images/toolbar/record.png create mode 100644 src/images/toolbar/remove.png create mode 100644 src/images/toolbar/rewind button white.png create mode 100644 src/images/toolbar/rewind button.png create mode 100644 src/images/toolbar/send.png create mode 100644 src/images/toolbar/server.png create mode 100644 src/images/toolbar/smiley sad.png create mode 100644 src/images/toolbar/smiley.png create mode 100644 src/images/toolbar/stop 2.png create mode 100644 src/images/toolbar/stop.png create mode 100644 src/images/toolbar/stop_alt.png create mode 100644 src/images/toolbar/sun 2.png create mode 100644 src/images/toolbar/sun.png create mode 100644 src/images/toolbar/terminal.png create mode 100644 src/images/toolbar/trash full.png create mode 100644 src/images/toolbar/trash.png create mode 100644 src/images/toolbar/unarchive.png create mode 100644 src/images/toolbar/unread mail.png create mode 100644 src/images/toolbar/up_alt.png create mode 100644 src/images/toolbar/user.png create mode 100644 src/images/toolbar/users.png create mode 100644 src/images/toolbar/window.png create mode 100644 src/images/toolbar/zoom in.png create mode 100644 src/images/toolbar/zoom out.png mode change 100755 => 100644 src/ppxml.rb create mode 100644 src/xml/measures.xml diff --git a/qtsolutions/common.pri b/qtsolutions/common.pri new file mode 100644 index 000000000..3a381e7f4 --- /dev/null +++ b/qtsolutions/common.pri @@ -0,0 +1,6 @@ +infile(config.pri, SOLUTIONS_LIBRARY, yes): CONFIG += qtsoap-uselib +TEMPLATE += fakelib +QTSOAP_LIBNAME = $$qtLibraryTarget(QtSolutions_SOAP-2.7) +TEMPLATE -= fakelib +QTSOAP_LIBDIR = $$PWD/lib +unix:qtsoap-uselib:!qtsoap-buildlib:QMAKE_RPATHDIR += $$QTSOAP_LIBDIR diff --git a/qtsolutions/soap/QtSoapArray b/qtsolutions/soap/QtSoapArray new file mode 100644 index 000000000..3a7ec02e6 --- /dev/null +++ b/qtsolutions/soap/QtSoapArray @@ -0,0 +1 @@ +#include "qtsoap.h" diff --git a/qtsolutions/soap/QtSoapArrayIterator b/qtsolutions/soap/QtSoapArrayIterator new file mode 100644 index 000000000..3a7ec02e6 --- /dev/null +++ b/qtsolutions/soap/QtSoapArrayIterator @@ -0,0 +1 @@ +#include "qtsoap.h" diff --git a/qtsolutions/soap/QtSoapHttpTransport b/qtsolutions/soap/QtSoapHttpTransport new file mode 100644 index 000000000..3a7ec02e6 --- /dev/null +++ b/qtsolutions/soap/QtSoapHttpTransport @@ -0,0 +1 @@ +#include "qtsoap.h" diff --git a/qtsolutions/soap/QtSoapMessage b/qtsolutions/soap/QtSoapMessage new file mode 100644 index 000000000..3a7ec02e6 --- /dev/null +++ b/qtsolutions/soap/QtSoapMessage @@ -0,0 +1 @@ +#include "qtsoap.h" diff --git a/qtsolutions/soap/QtSoapNamespaces b/qtsolutions/soap/QtSoapNamespaces new file mode 100644 index 000000000..3a7ec02e6 --- /dev/null +++ b/qtsolutions/soap/QtSoapNamespaces @@ -0,0 +1 @@ +#include "qtsoap.h" diff --git a/qtsolutions/soap/QtSoapQName b/qtsolutions/soap/QtSoapQName new file mode 100644 index 000000000..3a7ec02e6 --- /dev/null +++ b/qtsolutions/soap/QtSoapQName @@ -0,0 +1 @@ +#include "qtsoap.h" diff --git a/qtsolutions/soap/QtSoapSimpleType b/qtsolutions/soap/QtSoapSimpleType new file mode 100644 index 000000000..3a7ec02e6 --- /dev/null +++ b/qtsolutions/soap/QtSoapSimpleType @@ -0,0 +1 @@ +#include "qtsoap.h" diff --git a/qtsolutions/soap/QtSoapStruct b/qtsolutions/soap/QtSoapStruct new file mode 100644 index 000000000..3a7ec02e6 --- /dev/null +++ b/qtsolutions/soap/QtSoapStruct @@ -0,0 +1 @@ +#include "qtsoap.h" diff --git a/qtsolutions/soap/QtSoapStructIterator b/qtsolutions/soap/QtSoapStructIterator new file mode 100644 index 000000000..3a7ec02e6 --- /dev/null +++ b/qtsolutions/soap/QtSoapStructIterator @@ -0,0 +1 @@ +#include "qtsoap.h" diff --git a/qtsolutions/soap/QtSoapType b/qtsolutions/soap/QtSoapType new file mode 100644 index 000000000..3a7ec02e6 --- /dev/null +++ b/qtsolutions/soap/QtSoapType @@ -0,0 +1 @@ +#include "qtsoap.h" diff --git a/qtsolutions/soap/QtSoapTypeConstructor b/qtsolutions/soap/QtSoapTypeConstructor new file mode 100644 index 000000000..3a7ec02e6 --- /dev/null +++ b/qtsolutions/soap/QtSoapTypeConstructor @@ -0,0 +1 @@ +#include "qtsoap.h" diff --git a/qtsolutions/soap/QtSoapTypeConstructorBase b/qtsolutions/soap/QtSoapTypeConstructorBase new file mode 100644 index 000000000..3a7ec02e6 --- /dev/null +++ b/qtsolutions/soap/QtSoapTypeConstructorBase @@ -0,0 +1 @@ +#include "qtsoap.h" diff --git a/qtsolutions/soap/QtSoapTypeFactory b/qtsolutions/soap/QtSoapTypeFactory new file mode 100644 index 000000000..3a7ec02e6 --- /dev/null +++ b/qtsolutions/soap/QtSoapTypeFactory @@ -0,0 +1 @@ +#include "qtsoap.h" diff --git a/qtsolutions/soap/qtsoap.cpp b/qtsolutions/soap/qtsoap.cpp new file mode 100644 index 000000000..f3269d7a2 --- /dev/null +++ b/qtsolutions/soap/qtsoap.cpp @@ -0,0 +1,3305 @@ +/**************************************************************************** +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.1, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact Nokia at qt-info@nokia.com. +** +****************************************************************************/ + +#include "qtsoap.h" +#include +#include +#include + +/*! \page qtsoap-overview.html + + \title Qt SOAP component + + \tableofcontents + + \target overview + \section1 Overview of the SOAP support in the QtSoap classes + + QtSoapMessage provides an interface for creating, inspecting and + modifying SOAP messages. It has convenience functions for + generating method requests and inspecting method response + messages, and also functions that provide easy access to SOAP + Fault messages. + + The QtSoapType class allows you to inspect SOAP messages with no + knowledge of XML or DOM. Header and body items are all derived + from QtSoapType, and through easy accessors and iterators, this + class and its derivatives make it easy to build arrays + (QtSoapArray), structs (QtSoapStruct) and simple types like + String, Integer and Boolean (QtSoapSimpleType). + + Finally, QtSoapHttpTransport provides a convenient way to submit + SOAP messages to a host via the HTTP protocol. + + \target classes + \section1 The SOAP classes + + \table + \header \i Class \i Short description + \row \i \l QtSoapMessage + \i Represents a SOAP message + \row \i \l QtSoapQName + \i Represents qualified names (QNames) + \row \i \l QtSoapType + \i A superclass for all data constructs in a SOAP message. + \row \i \l QtSoapArray + \i Represents a SOAP array + \row \i \l QtSoapArrayIterator + \i Lets you iterate over all the values in a SOAP array + \row \i \l QtSoapStruct + \i Represents a SOAP struct + \row \i \l QtSoapStructIterator + \i Lets you iterate over all the values in a SOAP array + \row \i \l QtSoapSimpleType + \i Represents simple SOAP types such as String, Integer and Boolean. + \row \i \l QtSoapHttpTransport + \i Provides a method for transmitting SOAP messages to an + HTTP server and for getting the SOAP reply. + \endtable + + \target partial + \section1 Status of the SOAP component + + This is a partial implementation of the SOAP v1.1 protocol. + + \list + \i Server side SOAP is not supported. + \i References to values (id and href attributes) are not supported. + \i Only arrays with less than 5 dimensions are supported. + \i Namespaces for types are not checked. Only the type names are used. + \i The encodingStyle attribute is ignored. The serialization and + encoding rules from section 5 in the SOAP v1.1 specification are + assumed regardless of the value of the encodingStyle attribute. + \i QtSoapType does not have accessors for attributes, which means + for example that actor, mustUnderstand and so on are not + accessible in headers. + \i The SOAP root attribute is not supported. + \endlist +*/ + +namespace { + QString localName(const QString &tagName) + { + int pos; + if ((pos = tagName.indexOf(':'))) + return tagName.right(tagName.length() - pos - 1); + + return tagName; + } + + QString prefix(const QString &tagName) + { + int pos; + if ((pos = tagName.indexOf(':'))) + return tagName.left(pos); + + return tagName; + } + +} + +/*! \class QtSoapQName qtsoap.h + + \brief The QtSoapQName class provides a wrapper for QNames (names with + namespaces). + + This class is used extensively in QtSoap to define and identify + header and body elements, including method and argument names. + + The QtSoapQName consists of a name and a URI. The URI is used as + the name's namespace, i.e. the name is qualified (hence 'Q'-Name) + by the URI. The name() and uri() functions return the QNames's + name and URI. + + The QtSoapQName can be empty. It can also have just a name with no + URI. Special handling is often applied to a QtSoapQName that has + no URI. Typically, if a QName with no namespace is used in an + element in a SOAP document that already has a default namespace + defined, then that namespace will be applied to the QName. + + \code + QtSoapMessage message; + + message.setMethod(QtSoapQName("sendMessage", "http://messenging.example.com/")); + message.addMethodArgument(QtSoapSimpleType(QtSoapQName("a"), 15)); + \endcode +*/ + + +/*! + Constructs a QName. Sets the QName name to \a name and the URI to + \a uri. +*/ +QtSoapQName::QtSoapQName(const QString &name, const QString &uri) + : n(name), nuri(uri) +{ +} + +/*! + Destructs the QtSoapQName. +*/ +QtSoapQName::~QtSoapQName() +{ +} + +/*! + Returns QName's name. +*/ +QString QtSoapQName::name() const +{ + return n; +} + +/*! + Returns the QName's URI. +*/ +QString QtSoapQName::uri() const +{ + return nuri; +} + +/*! + Sets the QName's name to \a s, and sets the URI to an empty string. +*/ +QtSoapQName &QtSoapQName::operator =(const QString &s) +{ + n = s; + nuri = ""; + + return *this; +} + +/*! + \fn bool operator==(const QtSoapQName &s1, const QtSoapQName &s2) + + \relates QtSoapQName + + If \a s2 has a non-empty URI, this function returns true if the + merge of the URI and the name of \a s1 is equal to that of \a s2; + otherwise it returns false. + + If \a s2 has an empty URI, this function returns true if the name + of \a s1 is equal to the name of \a s2; otherwise it returns false. + + The comparison is case-insensitive. +*/ +bool operator ==(const QtSoapQName &s1, const QtSoapQName &s2) +{ + if (s2.uri() == "") + return s1.name().toLower() == s2.name().toLower(); + + return s1.name().toLower() == s2.name().toLower() + && s1.uri().toLower() == s2.uri().toLower(); +} + +/*! + \fn bool operator<(const QtSoapQName &s1, const QtSoapQName &s2) + + \relates QtSoapQName + + If \a s2 has a non-empty URI, this function returns true if the + merge of the URI and the name of \a s1 is lexically less than that + of \a s2; otherwise it returns false. + + If \a s2 has an empty URI, this function returns true if the name + of \a s1 is lexically less than the name of \a s2; otherwise + it returns false. + + The comparison is case-insensitive. +*/ +bool operator <(const QtSoapQName &s1, const QtSoapQName &s2) +{ + if (s2.uri() == "") + return s1.name().toLower() < s2.name().toLower(); + + return (s1.uri().toLower()+s1.name().toLower()) < (s2.uri().toLower()+s2.name().toLower()); +} + +/*! \class QtSoapType qtsoap.h + \brief The QtSoapType class is the base class for all SOAP types + in QtSoap. + + Although it is not used to create data types, a QtSoapType + reference can be conveniently used to inspect a tree of QtSoapType + subclasses. Accessors from this base class, such as operator[](), + allow safe navigation. + + \code + const QtSoapType &root = message.returnValue(); + if (root["fault"].isValid()) { + qWarning("Warning: %s", root["fault"]["faultstring"].toString().toLatin1().constData()); + } + \endcode + + This class holds the name(), type(), id() and href() of all its + derived types. + + If a QtSoapType reference or pointer points to a QtSoapStruct, a + QtSoapArray or a QtSoapSimpleType, isValid() returns true. + + \sa QtSoapStruct, QtSoapArray, QtSoapSimpleType +*/ + +/*! + \class QtSoapStruct qtsoap.h + \brief The QtSoapStruct class is an implementation of the SOAP + struct type. + + A SOAP struct is a dictionary of QtSoapTypes where entries are + looked up by name. Entries in a struct can be of any QtSoapType + type, such as QtSoapArray, QtSoapStruct or QtSoapSimpleType. + + The struct can be created in several ways. parse() generates a + struct by analyzing a QDomNode. insert() is used to add items + manually. + + \code + QtSoapStruct myStruct(QtSoapQName("myStruct")); + + myStruct.insert(new QtSoapSimpleType(QtSoapQName("item1"), 5)); + myStruct.insert(new QtSoapSimpleType(QtSoapQName("item2"), "hello")); + myStruct.insert(new QtSoapSimpleType(QtSoapQName("item3"), true)); + \endcode + + Use the operator[]() or at() when looking up entries in a struct + by name. If the names are unknown, QtSoapStructIterator lets you + iterate through all the items. + + \code + QtSoapType &helloItem = myStruct["Hello"]; + \endcode + + toDomElement() converts the QtSoapStruct to a DomElement. + + \sa QtSoapStructIterator, QtSoapType, QtSoapArray, QtSoapSimpleType +*/ + +/*! \class QtSoapStructIterator + \brief The QtSoapStructIterator class provides an iterator for + traversing the items in a QtSoapStruct. + + The iterator is created by passing a QtSoapStruct to the + constructor. It it not defined which item the iterator initially + will point to. Neither is the order in which the items are + processed. + + key() returns the name of the current item. data() and current() + return a pointer to the current item, or 0 if there is none. + operator++() navigates to the next item in the struct. + + \code + for (QtSoapStructIterator it(myStruct); it.current(); ++it) { + QtSoapType *item = it.data(); + // process item + } + \endcode + + \sa QtSoapArrayIterator +*/ + +/*! \class QtSoapArray qtsoap.h + \brief The QtSoapArray class is an implementation of the SOAP + array type. + + A SOAP array is a sequence of QtSoapType objects such as + QtSoapArrays, QtSoapStructs or QtSoapSimpleTypes, and they are + accessible through their ordinal position. An array can be + consecutive (0, 1, 2, 3, ...) or sparse (1, 5, 6, 10, ...) and + they can be multidimensional ((0, 0), (0, 1), ...). QtSoapArray + supports arrays of up to 5 dimensions. The size and dimensions of + the array are set in the constructor. The default constructor + produces a one dimensional array, with an unset size, meaning that + the array can grow as required. + + All elements in a SOAP array must be of the same type. Inserting + different types will result in a run time error. + + The parse() function builds the array by analyzing a QDomNode from + a SOAP document. count() returns the number of items in the array. + + Retrieve items in the array with at() or operator[](). Note that + operator[]() only works with one dimensional arrays, but there are + at() functions for arrays of up to 5 dimensions. + QtSoapArrayIterator lets you iterate through all items in an + array. + + Use insert() or append() to insert items into an array manually. + append() only works with one dimensional arrays. + + toDomElement() returns a QDomElement representation of the SOAP + array. + + \code + QtSoapArray array; + + array.insert(0, new QtSoapSimpleType(QtSoapQName("Peter"), "peter")); + array.insert(1, new QtSoapSimpleType(QtSoapQName("Lisa"), "lisa")); + array.insert(2, new QtSoapSimpleType(QtSoapQName("Paul"), "paul")); + array.insert(3, new QtSoapSimpleType(QtSoapQName("Heather"), "heather")); + \endcode + + \sa QtSoapType, QtSoapStruct, QtSoapSimpleType +*/ + +/*! \class QtSoapArrayIterator qtsoap.h + \brief The QtSoapArrayIterator class provides an iterator for + traversing the items in a QtSoapArray. + + The items are traversed in ascending order of index position, + depth first. + + \code + // Construct a 2x2 array of Strings. + QtSoapArray array("Array of strings", String, 2, 2); + array.insert(0, 0, new QtSoapSimpleType(Q4SoapQName("top left"), "top left"); + array.insert(0, 1, new QtSoapSimpleType(Q4SoapQName("top right"), "top right"); + array.insert(1, 0, new QtSoapSimpleType(Q4SoapQName("bottom left"), "bottom left"); + array.insert(1, 1, new QtSoapSimpleType(Q4SoapQName("bottom right"), "bottom right"); + + // Traverse all items. + QtSoapArrayIterator it(array); + while (!it.current()) { + + // Find the position of the current element. + int pos1, pos2; + it.pos(&pos1, &pos2); + + qDebug() << "Position (" << pos1 << ", " << pos2 << ") is " + << "the " << it.current()->toString() << " coordinate of the grid." << endl; + + ++it; + } + \endcode +*/ + +/*! \class QtSoapSimpleType qtsoap.h + \brief The QtSoapSimpleType class provides a container for all + simple SOAP types, such as strings, integers and booleans. + + + + \sa QtSoapType, QtSoapStruct, QtSoapArray +*/ + +/*! \enum QtSoapType::Type + + SOAP supports the types described in XML Schema Part 2: Datatypes + listed at \l http://www.w3.org/TR/xmlschema-2/. The following + values are represented using QtSoapSimpleType, except where noted. + + \value AnyURI + \value Array Represented by QtSoapArray + \value Base64Binary + \value Boolean + \value Byte + \value Date + \value DateTime + \value Decimal + \value Double + \value Duration + \value ENTITY + \value Float + \value GDay + \value GMonth + \value GMonthDay + \value GYear + \value GYearMonth + \value HexBinary + \value ID + \value IDREF + \value Int + \value Integer + \value Language + \value Long + \value NCName + \value NMTOKEN + \value NOTATION + \value Name + \value NegativeInteger + \value NonNegativeInteger + \value NonPositiveInteger + \value NormalizedString + \value Other + \value PositiveInteger + \value QName + \value Short + \value String + \value Struct Represented by QtSoapStruct + \value Time + \value Token + \value UnsignedByte + \value UnsignedInt + \value UnsignedLong + \value UnsignedShort +*/ + +/*! + Constructs a QtSoapType. +*/ +QtSoapType::QtSoapType() +{ + t = Other; + errorStr = "Unknown error"; +} + +/*! + Constructs a QtSoapType whose name is \a name and type is \a type. + This contructor is usually only called by constructors in + subclasses. +*/ +QtSoapType::QtSoapType(const QtSoapQName &name, Type type) + : t(type), n(name) +{ + errorStr = "Unknown error"; +} + +/*! + Creates a QtSoapType copy of \a copy. +*/ +QtSoapType::QtSoapType(const QtSoapType ©) + : t(copy.t), errorStr(copy.errorStr), i(copy.i), + n(copy.n), u(copy.u), h(copy.h) +{ +} + +/*! + Destructs a QtSoapType. +*/ +QtSoapType::~QtSoapType() +{ +} + +/*! + Clears any contents. In this base implementation, clear() does + nothing. +*/ +void QtSoapType::clear() +{ +} + +/*! + Makes this QtSoapType equal to \a copy. +*/ +QtSoapType &QtSoapType::operator =(const QtSoapType ©) +{ + t = copy.t; + errorStr = copy.errorStr; + i = copy.i; + n = copy.n; + u = copy.u; + h = copy.h; + return *this; +} + +/*! + Returns true if this object is of type QtSoapStruct, QtSoapArray + or QtSoapSimpletype; otherwise returns false. +*/ +bool QtSoapType::isValid() const +{ + return false; +} + +/*! + Returns the QString equivalent of type \a t. +*/ +QString QtSoapType::typeToName(QtSoapType::Type t) +{ + switch (t) { + case Duration: return "duration"; + case DateTime: return "dateTime"; + case Time: return "time"; + case Date: return "date"; + case GYearMonth: return "gYearMonth"; + case GYear: return "gYear"; + case GMonthDay: return "gMonthDay"; + case GDay: return "gDay"; + case GMonth: return "gMonth"; + case Boolean: return "boolean"; + case Base64Binary: return "base64Binary"; + case HexBinary: return "hexBinary"; + case Float: return "float"; + case Double: return "double"; + case AnyURI: return "anyURI"; + case QName: return "QName"; + case NOTATION: return "NOTATION"; + case String: return "string"; + case NormalizedString: return "normalizedString"; + case Token: return "token"; + case Language: return "language"; + case Name: return "name"; + case NMTOKEN: return "NMToken"; + case NCName: return "NCName"; + case ID: return "ID"; + case IDREF: return "IDREF"; + case ENTITY: return "ENTITY"; + case Decimal: return "decimal"; + case Integer: return "integer"; + case NonPositiveInteger: return "nonPositiveInteger"; + case NegativeInteger: return "negativeInteger"; + case Long: return "long"; + case Int: return "int"; + case Short: return "short"; + case Byte: return "byte"; + case NonNegativeInteger: return "nonNegativeInteger"; + case UnsignedLong: return "unsignedLong"; + case PositiveInteger: return "positiveInteger"; + case UnsignedInt: return "unsignedInt"; + case UnsignedShort: return "unsignedShort"; + case UnsignedByte: return "unsignedByte"; + case Array: return "array"; + case Struct: return "struct"; + default: return "other"; + } +} + +/*! + Returns the QtSoapType::Type called \a name. +*/ +QtSoapType::Type QtSoapType::nameToType(const QString &name) +{ + const QString type = name.trimmed().toLower(); + + if (type == "string") + return String; + else if (type == "normalizedstring") + return NormalizedString; + else if (type == "token") + return Token; + else if (type == "language") + return Language; + else if (type == "name") + return Name; + else if (type == "ncname") + return NCName; + else if (type == "nmtoken") + return NMTOKEN; + else if (type == "id") + return ID; + else if (type == "idref") + return IDREF; + else if (type == "entity") + return ENTITY; + else if (type == "base64binary") + return Base64Binary; + else if (type == "hexBinary") + return HexBinary; + else if (type == "anyuri") + return AnyURI; + else if (type == "qname") + return QName; + else if (type == "notation") + return NOTATION; + else if (type == "duration") + return Duration; + else if (type == "datetime") + return DateTime; + else if (type == "time") + return Time; + else if (type == "date") + return Date; + else if (type == "gyearmonth") + return GYearMonth; + else if (type == "gyear") + return GYear; + else if (type == "gmonthday") + return GMonthDay; + else if (type == "gday") + return GDay; + else if (type == "gmonth") + return GMonth; + else if (type == "decimal") + return Decimal; + else if (type == "integer") + return Integer; + else if (type == "nonPositiveinteger") + return NonPositiveInteger; + else if (type == "negativeinteger") + return NegativeInteger; + else if (type == "long") + return Long; + else if (type == "int") + return Int; + else if (type == "short") + return Short; + else if (type == "byte") + return Byte; + else if (type == "nonnegativeinteger") + return NonNegativeInteger; + else if (type == "unsignedlong") + return UnsignedLong; + else if (type == "unsignedint") + return UnsignedInt; + else if (type == "unsignedshort") + return UnsignedShort; + else if (type == "unsignedbyte") + return UnsignedByte; + else if (type == "positiveinteger") + return PositiveInteger; + else if (type == "float") + return Float; + else if (type == "double") + return Double; + else if (type == "boolean") + return Boolean; + else + return Other; +} + +/*! + Returns QString::null. +*/ +QString QtSoapType::toString() const +{ + return QString::null; +} + +/*! + Returns 0. +*/ +int QtSoapType::toInt() const +{ + return 0; +} + +/*! + Returns false. +*/ +bool QtSoapType::toBool() const +{ + return false; +} + +/*! + Returns the QDomElement representation of this QtSoapType + as a child of \a document. +*/ +QDomElement QtSoapType::toDomElement(QDomDocument document) const +{ + Q_UNUSED(document); + return QDomElement(); +} + +/*! + Returns the QString representation of this QtSoapType's type. +*/ +QString QtSoapType::typeName() const +{ + return QtSoapType::typeToName(type()); +} + +/*! + Returns the type as a QtSoapType::Type. +*/ +QtSoapType::Type QtSoapType::type() const +{ + return t; +} + +/*! + Returns the QName (qualified name) of this QtSoapType. +*/ +QtSoapQName QtSoapType::name() const +{ + return n; +} + +/*! + Returns the ID of this QtSoapType. +*/ +QString QtSoapType::id() const +{ + return i; +} + +/*! + Returns the href attribute of this QtSoapType. +*/ +QString QtSoapType::href() const +{ + return h; +} + +/*! + Sets the QName (qualified name) of this QtSoapType to \a name. +*/ +void QtSoapType::setName(const QtSoapQName &name) +{ + this->n = name; +} + +/*! + Sets the ID of this QtSoapType to \a i. +*/ +void QtSoapType::setId(const QString &i) +{ + this->i = i; +} + +/*! + Sets the href attribute of this QtSoapType to \a h. +*/ +void QtSoapType::setHref(const QString &h) +{ + this->h = h; +} + +/*! + Returns the value of this QtSoapType. In the base implementation, + an invalid QVariant() is returned. +*/ +QVariant QtSoapType::value() const +{ + return QVariant(); +} + +/*! + Returns a human readable explanation of the most recent error. +*/ +QString QtSoapType::errorString() const +{ + return errorStr; +} + +/*! + Returns the number of child items in this QtSoapType. In the base + implementation, this returns 0. +*/ +int QtSoapType::count() const +{ + return 0; +} + +/*! + Returns a reference to the child item at ordinal position \a pos. + If no item exists at this position, returns an empty QtSoapType. +*/ +QtSoapType &QtSoapType::operator [](int /* pos */ ) +{ + static QtSoapType NIL; + return NIL; +} + +/*! + \overload + + Returns a reference to the child item whose QName (qualified name) + is \a key. If no item exists with this key an empty QtSoapType is + returned. +*/ +QtSoapType &QtSoapType::operator [](const QtSoapQName & /* key */) +{ + static QtSoapType NIL; + return NIL; +} + +/*! + \overload + + Returns a reference to the child item whose QName (qualified name) + is \a key, regardless of the qualified name's URI. If no item + exists with this key, an empty QtSoapType is returned. +*/ +QtSoapType &QtSoapType::operator [](const QString & /* key */) +{ + static QtSoapType NIL; + return NIL; +} + +/*! + \overload + + Returns a reference to the child item at ordinal position \a pos. + If no item exists at this position, returns an empty QtSoapType. +*/ +const QtSoapType &QtSoapType::operator [](int /* pos */) const +{ + static QtSoapType NIL; + return NIL; +} + +/*! + \overload + + Returns a reference to the child item whose QName (qualified name) + is \a key. If no item exists with this key, returns an empty + QtSoapType. +*/ +const QtSoapType &QtSoapType::operator [](const QtSoapQName & /* key */) const +{ + static QtSoapType NIL; + return NIL; +} + +/*! + \overload + + Returns a reference to the child item whose QName (qualified name) + is \a key, regardless of the qualified name's URI. If no item + exists with this key, returns an empty QtSoapType. +*/ +const QtSoapType &QtSoapType::operator [](const QString & /* key */) const +{ + static QtSoapType NIL; + return NIL; +} + +/*! + Attempts to interpret \a node as a QtSoapType, and returns true if + successful. This base implementation always returns false. +*/ +bool QtSoapType::parse(QDomNode /* node */) +{ + return false; +} + +/*! + \overload + + Constructs an empty one dimensional QtSoapArray whose element type + is undefined. The first insert will decide what type of elements + the array can contain. +*/ +QtSoapArray::QtSoapArray() + : QtSoapType(QtSoapQName(), Array), arrayType(Other), order(1) +{ + lastIndex = 0; + siz0 = 0; + siz1 = 0; + siz2 = 0; + siz3 = 0; + siz4 = 0; +} + +/*! + Constructs an empty QtSoapArray whose QName (qualified name) is \a + name, that contains elements of type \a type, and whose dimensions + are given by \a size0, \a size1, \a size2, \a size3 and \a size4. + + To construct a one dimensional array of size 5, set \a size0 = 5. To + create a two dimensional array of size 5x10, set \a size0 = 5 and \a + size1 = 10. The maximum dimension of a QtSoapArray is 5. +*/ +QtSoapArray::QtSoapArray(const QtSoapQName &name, QtSoapType::Type type, int size0, + int size1, int size2, int size3, int size4) + : QtSoapType(name, Array), lastIndex(0), arrayType(type), + siz0(size0), siz1(size1), siz2(size2), siz3(size3), + siz4(size4) +{ + if (size4 != -1) order = 5; + else if (size3 != -1) order = 4; + else if (size2 != -1) order = 3; + else if (size1 != -1) order = 2; + else order = 1; +} + +/*! + Create a QtSoapArray that is a copy of \a copy. +*/ +QtSoapArray::QtSoapArray(const QtSoapArray ©) + : QtSoapType(copy) +{ + *this = copy; +} + +/*! + Destructs the QtSoapArray. +*/ +QtSoapArray::~QtSoapArray() +{ +} + +/*! + Clears the contents, and the dimensions of the QtSoapArray. +*/ +void QtSoapArray::clear() +{ + array.clear(); + lastIndex = 0; + arrayType = Other; + siz0 = siz1 = siz2 = siz3 = siz4 = 0; + order = -1; +} + +/*! + Makes this QtSoapArray a copy of \a copy. +*/ +QtSoapArray &QtSoapArray::operator = (const QtSoapArray ©) +{ + if (this == ©) + return *this; + t = copy.t; + errorStr = copy.errorStr; + i = copy.i; + n = copy.n; + u = copy.u; + h = copy.h; + lastIndex = copy.lastIndex; + order = copy.order; + siz0 = copy.siz0; + siz1 = copy.siz1; + siz2 = copy.siz2; + siz3 = copy.siz3; + siz4 = copy.siz4; + array = copy.array; + + return *this; +} + +/*! + Appends the QtSoapType \a item to the end of this array, which must + be one dimensional. + + \sa insert() +*/ +void QtSoapArray::append(QtSoapType *item) +{ + if (order != 1) { + qWarning("Attempted to insert item at position (%i) in %i-dimensional QtSoapArray.", + lastIndex, order); + return; + } + + if (array.count() == 0) { + array.insert(0, item); + } else { + array.insert(lastIndex + 1, item); + ++lastIndex; + } +} + +/*! + Inserts the QtSoapType \a item at the absolute position \a pos in + the array. Note that this function can be used to insert items + into arrays with any number of dimensions. + + If the array is one dimensional, then \a pos is simply the index + position in the array. But if the array is multi-dimensional then + \a pos is the absolute position. For example, if we have a two + dimensional array [['a', 'b'], ['c', 'd'], ['e', 'f']], the + element at position 0 is 'a', at position 1 is 'b', at position 2 + is 'c', and so on. (There are other insert() overloads that allow + for each dimension to be specified individually.) +*/ +void QtSoapArray::insert(int pos, QtSoapType *item) +{ + if (arrayType == Other) + arrayType = item->type(); + + if (item->type() != arrayType) { + qWarning("Attempted to insert item of type \"%s\" in QtSoapArray of type \"%s\".", + item->typeName().toLatin1().constData(), QtSoapType::typeToName(arrayType).toLatin1().constData()); + return; + } + + if (order == -1) + order = 1; + else if (order == 1 && pos > lastIndex) + lastIndex = pos; + + array.insert(pos, item); +} + +/*! + \overload + + Insert the QtoSoapType \a item at position \a pos0 x \a pos1 in a + two dimensional array. +*/ +void QtSoapArray::insert(int pos0, int pos1, QtSoapType *item) +{ + if (order != 2) { + qWarning("Attempted to insert item at position (%i, %i)" + " in %i-dimensional QtSoapArray.", + pos0, pos1, order); + return; + } + + if (pos0 < 0 || pos0 >= siz0 || pos1 < 0 || pos1 >= siz1) { + qWarning("Attempted to insert item at position (%i, %i)" + " when range of QtSoapArray is (0..%i, 0..%i)", + pos0, pos1, siz0 - 1, siz1 - 1); + return; + } + + insert((pos0 * siz1) + pos1, item); +} + +/*! + \overload + + Insert the QtoSoapType \a item at position \a pos0 x \a pos1 x \a + pos2 in a three dimensional array. +*/ +void QtSoapArray::insert(int pos0, int pos1, int pos2, QtSoapType *item) +{ + if (order != 3) { + qWarning("Attempted to insert item at position (%i, %i, %i)" + " in %i-dimensional QtSoapArray.", + pos0, pos1, pos2, order); + return; + } + + if (pos0 < 0 || pos0 >= siz0 || pos1 < 0 || pos1 >= siz1 || pos2 < 0 || pos2 >= siz2) { + qWarning("Attempted to insert item at position (%i, %i, %i)" + " when range of QtSoapArray is (0..%i, 0..%i, 0..%i)", + pos0, pos1, pos2, siz0 - 1, siz1 - 1, siz2 - 1); + return; + } + + insert((pos0 * siz2 * siz1) + (pos1 * siz2) + pos2, item); +} + +/*! + \overload + + Insert the QtoSoapType \a item at position \a pos0 x \a pos1 x \a + pos2 x \a pos3 in a four dimensional array. +*/ +void QtSoapArray::insert(int pos0, int pos1, int pos2, int pos3, QtSoapType *item) +{ + if (order != 4) { + qWarning("Attempted to insert item at position (%i, %i, %i, %i)" + " in %i-dimensional QtSoapArray.", + pos0, pos1, pos2, pos3, order); + return; + } + + insert((pos0 * siz3 * siz2 * siz1) + + (pos1 * siz3 * siz2) + + (pos2 * siz3) + + pos3, + item); +} + +/*! + \overload + + Insert the QtoSoapType \a item at position \a pos0 x \a pos1 x \a + pos2 x \a pos3 x \a pos4 in a five dimensional array. +*/ +void QtSoapArray::insert(int pos0, int pos1, int pos2, int pos3, int pos4, + QtSoapType *item) +{ + if (order != 5) { + qWarning("Attempted to insert item at position (%i, %i, %i, %i, %i)" + " in %i-dimensional QtSoapArray.", + pos0, pos1, pos2, pos3, pos4, order); + return; + } + + insert((pos0 * siz4 * siz3 * siz2 * siz1) + + (pos1 * siz4 * siz3 * siz2) + + (pos2 * siz4 * siz3) + + (pos3 * siz4) + + pos4, + item); +} + +/*! \internal + + Given the size and dimensions of the array, generates a string + used to represent the array in XML. For example, a 1-dimensional + array of size 5 would get [5], a 2-dimensional array of size 5x10 + would get [5,10]. +*/ +QString QtSoapArray::arraySizeString() const +{ + QString arraySize = "["; + if (siz0 != -1) { + arraySize += QString::number(siz0); + if (order > 1) arraySize += "," + QString::number(siz1); + if (order > 2) arraySize += "," + QString::number(siz2); + if (order > 3) arraySize += "," + QString::number(siz3); + if (order > 4) arraySize += "," + QString::number(siz4); + } + + arraySize += "]"; + return arraySize; +} + +/*! \internal + + Recursively inspects the items and any child arrays' items to + generate the aggregate type of items in this array. It the array + contains ints, returns "int[5]", but if the array is of arrays of + arrays of ints, the function returns "int[][][5]". +*/ +QString QtSoapArray::arrayTypeString() const +{ + if (arrayType != Array) + return QtSoapType::typeToName(arrayType); + + QString atString; + QtSoapArray *ar = const_cast(this); + do { + if (ar->count() == 0) + break; + + atString += ar->arraySizeString(); + + QtSoapArrayIterator it(*const_cast(this)); + if (it.data()->type() != Array) + break; + + ar = (QtSoapArray *)it.data(); + } while (ar); + + + QtSoapArrayIterator it(*const_cast(this)); + if (ar->count() == 0) + atString = QtSoapSimpleType::typeToName(Int) + atString; + else + atString = it.data()->typeName() + atString; + + return atString; +} + +/*! + Returns the QDomElement representation of this QtSoapArray. The + returned QDomElement is created using \a doc. +*/ +QDomElement QtSoapArray::toDomElement(QDomDocument doc) const +{ + QString prefix = QtSoapNamespaces::instance().prefixFor(n.uri()); + QDomElement a = n.uri() == "" + ? doc.createElement( n.name()) + : doc.createElementNS(n.uri(), prefix + ":" + n.name()); + + QString schemaprefix = QtSoapNamespaces::instance().prefixFor(XML_SCHEMA_INSTANCE); + a.setAttributeNS(XML_SCHEMA_INSTANCE, schemaprefix + ":type", "xsd:Array"); + + QString encprefix = QtSoapNamespaces::instance().prefixFor(SOAPv11_ENCODING); + a.setAttributeNS(SOAPv11_ENCODING, encprefix + ":arrayType", "xsd:" + arrayTypeString()); + + for (QtSoapArrayIterator i(*const_cast(this)); !i.atEnd(); ++i) { + QDomElement item = i.data()->toDomElement(doc); + item.setTagName("item"); + + int pos0, pos1, pos2, pos3, pos4; + i.pos(&pos0, &pos1, &pos2, &pos3, &pos4); + + QString position = "[" + QString::number(pos0); + if (order > 1) position += "," + QString::number(pos1); + if (order > 2) position += "," + QString::number(pos2); + if (order > 3) position += "," + QString::number(pos3); + if (order > 4) position += "," + QString::number(pos4); + position += "]"; + + QString envprefix = QtSoapNamespaces::instance().prefixFor(SOAPv11_ENVELOPE); + item.setAttributeNS(SOAPv11_ENVELOPE, envprefix + ":position", position); + a.appendChild(item); + } + + return a; +} + +/*! \reimp + + For this class, always returns true. +*/ +bool QtSoapArray::isValid() const +{ + return true; +} + +/*! + Inspects \a node and builds the content of the QtSoapArray if \a + node qualifies as a SOAP array. Returns true if it does; otherwise + returns false. +*/ +bool QtSoapArray::parse(QDomNode node) +{ + if (node.isNull() || !node.isElement()) + return false; + + QDomElement e = node.toElement(); + QDomAttr typeattr = e.attributeNode("type"); + if (!typeattr.isNull() && (localName(typeattr.value()).toLower() != "array")) + return false; + + + QDomNodeList children = e.childNodes(); + int c = children.count(); + array.clear(); + // ### array.resize(c); + + int pos = 0; + for (int i = 0; i < c; ++i) { + QDomNode n = children.item(i); + if (n.isComment()) + continue; + if (!n.isElement()){ + // ### An error in the soap document. + return false; + } + + QDomElement elem = n.toElement(); + + QtSmartPtr type = QtSoapTypeFactory::instance().soapType(elem); + if (!type.ptr()) { + // ### An error in the soap document. + return false; + } + + // ### Check namespace + QDomAttr posattr = elem.attributeNode("position"); + if (!posattr.isNull()) + pos = posattr.value().toInt(); + + array.insert(pos, type); + ++pos; + } + + setName(QtSoapQName(localName(e.tagName()), e.namespaceURI())); + return true; +} + +/*! + Returns the number of items in the array. Note that this is not + the same as the size of the array, because the array may be sparse. +*/ +int QtSoapArray::count() const +{ + return array.count(); +} + +/*! + Returns a reference to the item at ordinal position \a pos. If + there is no item at position \a pos, returns an empty QtSoapType. +*/ +QtSoapType &QtSoapArray::operator [](int pos) +{ + return at(pos); +} + +/*! + \overload + + Returns a reference to the child item whose local name is \a s. If + there is no item with this local name, returns an empty + QtSoapType. +*/ +QtSoapType &QtSoapArray::operator [](const QString &s) +{ + return QtSoapType::operator[](s); +} + +/*! + \overload + + Returns a reference to the child item whose QName (qualified name) + is \a s. If there is no item with this name, returns an empty + QtSoapType. +*/ +QtSoapType &QtSoapArray::operator [](const QtSoapQName &s) +{ + return QtSoapType::operator[](s); +} + +/*! + \overload + + Returns a reference to the item at ordinal position \a pos. If + there is no item at position \a pos, returns an empty QtSoapType. +*/ +const QtSoapType &QtSoapArray::operator [] (int pos) const +{ + return at(pos); +} + +/*! + \overload + + Returns a reference to the child item whose local name is \a s. If + there is no item with this local name, returns an empty + QtSoapType. +*/ +const QtSoapType &QtSoapArray::operator [](const QString &s) const +{ + return QtSoapType::operator[](s); +} + +/*! + \overload + + Returns a reference to the child item whose QName (qualified name) + is \a s. If there is no item with this name, returns an empty + QtSoapType. +*/ +const QtSoapType &QtSoapArray::operator [](const QtSoapQName &s) const +{ + return QtSoapType::operator[](s); +} + +/*! + Returns a reference to the item at ordinal position \a pos. If + there is no item at position \a pos, returns an empty QtSoapType. +*/ +QtSoapType &QtSoapArray::at(int pos) +{ + static QtSoapType NIL; + + if (array.find(pos) != array.end()) + return *array[pos]; + else + return NIL; +} + +/*! + \overload + + Returns a reference to the item at ordinal position \a pos0 x \a + pos1 in a two dimensional array. If there is no such item, returns + an empty QtSoapType. +*/ +QtSoapType &QtSoapArray::at(int pos0, int pos1) +{ + return at(pos0 * siz1 + pos1); +} + +/*! + \overload + + Returns a reference to the item at ordinal position \a pos0 x \a + pos1 x \a pos2 in a three dimensional array. If there is no such + item, returns an empty QtSoapType. +*/ +QtSoapType &QtSoapArray::at(int pos0, int pos1, int pos2) +{ + return at((pos0 * siz2 * siz1) + (pos1 * siz2) + pos2); +} + +/*! + \overload + + Returns a reference to the item at ordinal position \a pos0 x \a + pos1 x \a pos2 x \a pos3 in a four dimensional array. If there is no + such item, returns an empty QtSoapType. +*/ +QtSoapType &QtSoapArray::at(int pos0, int pos1, int pos2, int pos3) +{ + return at((pos0 * siz3 * siz2 * siz1) + + (pos1 * siz3 * siz2) + + (pos2 * siz3) + + pos3); +} + +/*! + \overload + + Returns a reference to the item at ordinal position \a pos0 x \a + pos1 x \a pos2 x \a pos3 x \a pos4 in a five dimensional array. If + there is no such item, returns an empty QtSoapType. +*/ +QtSoapType &QtSoapArray::at(int pos0, int pos1, int pos2, int pos3, int pos4) +{ + return at((pos0 * siz4 * siz3 * siz2 * siz1) + + (pos1 * siz4 * siz3 * siz2) + + (pos2 * siz4 * siz3) + + (pos3 * siz4) + + pos4); +} + +/*! + \overload + + Returns a reference to the item at ordinal position \a pos. If + there is no item at position \a pos, returns an empty QtSoapType. +*/ +const QtSoapType &QtSoapArray::at(int pos) const +{ + static QtSoapType NIL; + + if (array.find(pos) != array.end()) + return *array[pos]; + else + return NIL; +} + +/*! + \overload + + Returns a reference to the item at ordinal position \a pos0 x \a + pos1 in a two dimensional array. If there is no such item, returns + an empty QtSoapType. +*/ +const QtSoapType &QtSoapArray::at(int pos0, int pos1) const +{ + return at(pos0 * siz1 + pos1); +} + +/*! + \overload + + Returns a reference to the item at ordinal position \a pos0 x \a + pos1 x \a pos2 in a three dimensional array. If there is no such + item, returns an empty QtSoapType. +*/ +const QtSoapType &QtSoapArray::at(int pos0, int pos1, int pos2) const +{ + return at((pos0 * siz2 * siz1) + (pos1 * siz2) + pos2); +} + +/*! + \overload + + Returns a reference to the item at ordinal position \a pos0 x \a + pos1 x \a pos2 x \a pos3 in a four dimensional array. If there is no + such item, returns an empty QtSoapType. +*/ +const QtSoapType &QtSoapArray::at(int pos0, int pos1, int pos2, int pos3) const +{ + return at((pos0 * siz3 * siz2 * siz1) + + (pos1 * siz3 * siz2) + + (pos2 * siz3) + + pos3); +} + +/*! + \overload + + Returns a reference to the item at ordinal position \a pos0 x \a + pos1 x \a pos2 x \a pos3 x \a pos4 in a five dimensional array. If + there is no such item, returns an empty QtSoapType. +*/ +const QtSoapType &QtSoapArray::at(int pos0, int pos1, int pos2, int pos3, int pos4) const +{ + return at((pos0 * siz4 * siz3 * siz2 * siz1) + + (pos1 * siz4 * siz3 * siz2) + + (pos2 * siz4 * siz3) + + (pos3 * siz4) + + pos4); +} + +/*! + Constructs a QtSoapArrayIterator on \a array, initializing + the iterator to point to the first element. +*/ +QtSoapArrayIterator::QtSoapArrayIterator(QtSoapArray &array) + : it(array.array.begin()), arr(&array) +{ +} + +/*! + Constructs a QtSoapArrayIterator that is a copy of \a copy. +*/ +QtSoapArrayIterator::QtSoapArrayIterator(const QtSoapArrayIterator ©) + : it(copy.it), arr(copy.arr) +{ +} + +/*! + Returns false if this iterator points to an item in the array, otherwise true. +*/ +bool QtSoapArrayIterator::atEnd() const +{ + return (it == arr->array.end()); +} + +/*! + Assignment operator of QtSoapArrayIterator. Makes this iterator a + copy of \a copy. +*/ +QtSoapArrayIterator &QtSoapArrayIterator::operator =(const QtSoapArrayIterator ©) +{ + it = copy.it; + arr = copy.arr; + + return *this; +} + +/*! + Destructs the QtSoapArrayIterator. +*/ +QtSoapArrayIterator::~QtSoapArrayIterator() +{ +} + +/*! + \overload + + Returns the ordinal position of the iterator. Works for arrays of + any dimension, but is only useful for one dimensional arrays. +*/ +int QtSoapArrayIterator::pos() const +{ + return it.key(); +} + +/*! + Populates the arguments \a pos0, \a pos1, \a pos2, \a pos3 and \a + pos4 with the coordinate of the current position of the iterator. + For a one dimensional array, only \a pos0 is populated. For a two + dimensional array, \a pos0 and \a pos1 are populated, and so on. + + Any of the arguments that are 0-pointers are ignored. +*/ +void QtSoapArrayIterator::pos(int *pos0, int *pos1, int *pos2, + int *pos3, int *pos4) const +{ + const int key = it.key(); + + switch (arr->order) { + case 1: + if (pos0) *pos0 = key; + break; + case 2: { + const int tmp = key / arr->siz1; + if (pos0) *pos0 = tmp; + if (pos1) *pos1 = key - (tmp * arr->siz1); + } + break; + case 3: { + const int tmp0 = key / (arr->siz2 * arr->siz1); + const int tmp1 = key - (tmp0 * (arr->siz2 * arr->siz1)); + const int tmp2 = tmp1 / arr->siz2; + if (pos0) *pos0 = tmp0; + if (pos1) *pos1 = tmp2; + if (pos2) *pos2 = tmp1 - (tmp2 * arr->siz2); + } + break; + case 4: { + const int tmp0 = key / (arr->siz3 * arr->siz2 * arr->siz1); + const int tmp1 = key - (tmp0 * (arr->siz3 * arr->siz2 * arr->siz1)); + const int tmp2 = tmp1 / (arr->siz3 * arr->siz2); + const int tmp3 = tmp1 - (tmp2 * (arr->siz3 * arr->siz2)); + const int tmp4 = tmp3 / arr->siz3; + const int tmp5 = tmp3 - (tmp4 * arr->siz3); + if (pos0) *pos0 = tmp0; + if (pos1) *pos1 = tmp2; + if (pos2) *pos2 = tmp4; + if (pos3) *pos3 = tmp5; + } + break; + case 5: { + const int tmp0 = key / (arr->siz4 * arr->siz3 * arr->siz2 * arr->siz1); + const int tmp1 = key - (tmp0 * (arr->siz4 * arr->siz3 * arr->siz2 * arr->siz1)); + const int tmp2 = tmp1 / (arr->siz4 * arr->siz3 * arr->siz2); + const int tmp3 = tmp1 - (tmp2 * (arr->siz4 * arr->siz3 * arr->siz2)); + const int tmp4 = tmp3 / (arr->siz4 * arr->siz3); + const int tmp5 = tmp3 - (tmp4 * arr->siz4 * arr->siz3); + const int tmp6 = tmp5 / arr->siz3; + const int tmp7 = tmp5 - (tmp6 * arr->siz3); + if (pos0) *pos0 = tmp0; + if (pos1) *pos1 = tmp2; + if (pos2) *pos2 = tmp4; + if (pos3) *pos3 = tmp6; + if (pos4) *pos4 = tmp7; + } + break; + default: + break; + } +} + +/*! + Returns a reference to the item that the iterator is currently + pointing to. +*/ +QtSoapType *QtSoapArrayIterator::data() +{ + if (it == arr->array.end()) + return 0; + return it.value().ptr(); +} + +/*! + Returns a reference to the item that the iterator is currently + pointing to. +*/ +const QtSoapType *QtSoapArrayIterator::current() const +{ + if (it == arr->array.end()) + return 0; + return it.value().ptr(); +} + +/*! + Moves the iterator position to the next item in the array. +*/ +void QtSoapArrayIterator::operator ++() +{ + ++it; +} + +/*! + Returns true if this Iterator's position is not equal to + the position of \a j; otherwise returns false. +*/ +bool QtSoapArrayIterator::operator != (const QtSoapArrayIterator &j) const +{ + return it != j.it; +} + +/*! + Returns true if this Iterator's position is equal to the position + of \a j; otherwise returns false. +*/ +bool QtSoapArrayIterator::operator == (const QtSoapArrayIterator &j) const +{ + return it == j.it; +} + +/*! + Constructs an empty QtSoapStruct. +*/ +QtSoapStruct::QtSoapStruct() + : QtSoapType(QtSoapQName(), Struct) +{ +} + +/*! + Constructs an empty QtSoapStruct and sets its QName (qualified + name) to \a name. +*/ +QtSoapStruct::QtSoapStruct(const QtSoapQName &name) + : QtSoapType(name, Struct) +{ +} + +/*! + Constructs a QtSoapStruct that is a copy of \a copy. +*/ +QtSoapStruct::QtSoapStruct(const QtSoapStruct ©) + : QtSoapType(copy) +{ + *this = copy; +} + +/*! + Destructs the QtSoapStruct. +*/ +QtSoapStruct::~QtSoapStruct() +{ +} + +/*! + Removes all items from the struct. +*/ +void QtSoapStruct::clear() +{ + dict.clear(); +} + +/*! + Makes this struct a copy of \a copy. +*/ +QtSoapStruct &QtSoapStruct::operator =(const QtSoapStruct ©) +{ + if (this == ©) + return *this; + t = copy.t; + errorStr = copy.errorStr; + i = copy.i; + n = copy.n; + u = copy.u; + h = copy.h; + i = copy.i; + dict = copy.dict; + + return *this; +} + +/*! + Inserts the QtSoapType \a item into this struct. Any existing + item with the same QName (qualified name) will be erased. +*/ +void QtSoapStruct::insert(QtSoapType *item) +{ + dict.append(item); +} + +/*! + Generates the QDomElement representation of this struct. The + returned QDomElement is created using \a doc. +*/ +QDomElement QtSoapStruct::toDomElement(QDomDocument doc) const +{ + QString prefix = QtSoapNamespaces::instance().prefixFor(n.uri()); + QDomElement a = n.uri() == "" + ? doc.createElement(n.name()) + : doc.createElementNS(n.uri(), prefix + ":" + n.name()); + + for (QtSoapStructIterator i(*const_cast(this)); i.data(); ++i) + a.appendChild(i.data()->toDomElement(doc)); + + return a; +} + +/*! \reimp + */ +bool QtSoapStruct::isValid() const +{ + return true; +} + +/*! + Inspects \a node and constructs the equivalent QtSoapStruct if \a + node qualifies as a SOAP struct. Returns true if it does; + otherwise returns false. +*/ +bool QtSoapStruct::parse(QDomNode node) +{ + if (node.isNull() || !node.isElement()) + return false; + + QDomElement e = node.toElement(); + QDomNodeList children = e.childNodes(); + int c = children.count(); + dict.clear(); + + for (int i = 0; i < c; ++i) { + QDomNode n = children.item(i); + if (n.isComment()) + continue; + if (!n.isElement()){ + errorStr = "In the struct element " + e.tagName(); + errorStr += ", the " + QString::number(i) + "th child "; + errorStr += "is not an element."; + return false; + } + + QtSmartPtr type = QtSoapTypeFactory::instance().soapType(n.toElement()); + if (!type.ptr()) { + errorStr = "In the struct element " + e.tagName(); + errorStr += ", child #" + QString::number(i) + ", "; + errorStr += n.toElement().tagName() + ", was not recognized as a SOAP type."; + return false; + } + + dict.append(type); + } + + setName(QtSoapQName(localName(e.tagName()), e.namespaceURI())); + return true; +} + +/*! + Returns the number of items in this struct. +*/ +int QtSoapStruct::count() const +{ + return dict.count(); +} + +/*! + Returns a reference to the item in this struct whose QName + (qualified name) is \a key. If no such item exists, an empty + QtSoapType is returned. +*/ +QtSoapType &QtSoapStruct::operator [](const QtSoapQName &key) +{ + return at(key); +} + +/*! + \overload + + Returns a reference to the item in this struct whose QName + (qualified name) is \a key. If no such item exists, an empty + QtSoapType is returned. +*/ +const QtSoapType &QtSoapStruct::operator [](const QtSoapQName &key) const +{ + return at(key); +} + +/*! + \overload + + Returns a reference to the item in this struct whose QName + (qualified name) is \a key. If no such item exists, an empty + QtSoapType is returned. +*/ +QtSoapType &QtSoapStruct::operator [](const QString &key) +{ + return at(QtSoapQName(key, "")); +} + +/*! + \overload + + Returns a reference to the item in this struct whose QName + (qualified name) is \a key. If no such item exists, an empty + QtSoapType is returned. +*/ +const QtSoapType &QtSoapStruct::operator [](const QString &key) const +{ + return at(QtSoapQName(key, "")); +} + +/*! + \overload + + Returns a reference to item number \a i in this struct. If no such + item exists, an empty QtSoapType is returned. + + The items are ordered in the sequence in which they were inserted, + starting from 0. +*/ +QtSoapType &QtSoapStruct::operator [](int i) +{ + static QtSoapType NIL; + if (i < 0 || i >= dict.count()) + return NIL; + + return *dict[i].ptr(); +} + +/*! + \overload + + Returns a reference to item number \a i in this struct. If no such + item exists, an empty QtSoapType is returned. + + The items are ordered in the sequence in which they were inserted, + starting from 0. +*/ +const QtSoapType &QtSoapStruct::operator [](int i) const +{ + static QtSoapType NIL; + if (i < 0 || i >= dict.count()) + return NIL; + + return *dict[i].ptr(); +} + +/*! + Returns a reference to the item in this struct whose QName + (qualified name) is \a key. If no such item exists, an empty + QtSoapType is returned. +*/ +QtSoapType &QtSoapStruct::at(const QtSoapQName &key) +{ + static QtSoapType NIL; + + QListIterator > it(dict); + while (it.hasNext()) { + QtSoapType *ret = it.next().ptr(); + if (ret->name() == key) + return *ret; + } + + return NIL; +} + +/*! + \overload + + Returns a reference to the item in this struct whose QName + (qualified name) is \a key. If no such item exists, an empty + QtSoapType is returned. +*/ +const QtSoapType &QtSoapStruct::at(const QtSoapQName &key) const +{ + static QtSoapType NIL; + + for (QtSoapStructIterator i(*const_cast(this)); i.current(); ++i) + if (i.key() == key) + return *i.current(); + + return NIL; +} + +/*! + Constructs a QtSoapStructIterator and initializes it to point to + the first element in the struct \a s. +*/ +QtSoapStructIterator::QtSoapStructIterator(QtSoapStruct &s) : it(s.dict.begin()), itEnd(s.dict.end()) +{ +} + +/*! + Destructs the QtSoapStructIterator. +*/ +QtSoapStructIterator::~QtSoapStructIterator() +{ +} + + +/*! + Returns the QName (qualified name) of the current item. +*/ +QtSoapQName QtSoapStructIterator::key() const +{ + if (it == itEnd) + return QtSoapQName(); + return (*it)->name(); +} + +/*! + Returns a pointer to the current item, or 0 if there is none. +*/ +QtSoapType *QtSoapStructIterator::data() +{ + if (it == itEnd) + return 0; + return it->ptr(); +} + +/*! + Returns a pointer to the current item, or 0 if there is none. +*/ +const QtSoapType *QtSoapStructIterator::current() const +{ + if (it == itEnd) + return 0; + return it->ptr(); +} + +/*! + Moves the iterator to the next item in the struct. +*/ +void QtSoapStructIterator::operator ++() +{ + if (it == itEnd) + return; + ++it; +} + +/*! + Returns true if this iterator's position is not equal to that of + \a j; otherwise returns false. +*/ +bool QtSoapStructIterator::operator !=(const QtSoapStructIterator &j) const +{ + return it != j.it; +} + +/*! + Returns true if this iterator's position is equal to that of \a + j; otherwise returns false. +*/ +bool QtSoapStructIterator::operator ==(const QtSoapStructIterator &j) const +{ + return it == j.it; +} + +/*! + Constructs an empty QtSoapSimpleType. +*/ +QtSoapSimpleType::QtSoapSimpleType() +{ +} + +/*! + \overload + + Constructs an empty QtSoapSimpleType, and sets its QName + (qualified name) to \a name. +*/ +QtSoapSimpleType::QtSoapSimpleType(const QtSoapQName &name) + : QtSoapType(name) +{ +} + +/*! + \overload + + Constructs a QtSoapSimpleType of type Int. Sets its QName + (qualified name) to \a name and its value to \a n. +*/ +QtSoapSimpleType::QtSoapSimpleType(const QtSoapQName &name, int n) + : QtSoapType(name, Int), v(QVariant(n)) +{ +} + +/*! + \overload + + Constructs a QtSoapSimpleType of type Boolean. Sets its QName + (qualified name) to \a name and its value to \a n. + + \a dummy is an unused variable that should be set to 0; it is + needed for older compilers that cannot distinguish between bool + and int. +*/ +QtSoapSimpleType::QtSoapSimpleType(const QtSoapQName &name, bool n, int) + : QtSoapType(name, Boolean), v(QVariant(n)) +{ +} + +/*! + \overload + + Constructs a QtSoapSimpleType of type String. Sets its QName + (qualified name) to \a name and its value to \a n. +*/ +QtSoapSimpleType::QtSoapSimpleType(const QtSoapQName &name, const QString &n) + : QtSoapType(name, String), v(QVariant(n)) +{ +} + +/*! + Constructs a QtSoapSimpleType that is a copy of \a copy. +*/ +QtSoapSimpleType::QtSoapSimpleType(const QtSoapSimpleType ©) + : QtSoapType(copy), v(copy.v) +{ +} + +/*! + Destructs the QtSoapSimpleType. +*/ +QtSoapSimpleType::~QtSoapSimpleType() +{ +} + +/*! + Erases the value of this QtSoapSimpleType. +*/ +void QtSoapSimpleType::clear() +{ + v.clear(); +} + +/*! + Returns the QDomElement representation of this QtSoapSimpleType. + The returned QDomElement is created using \a doc. +*/ +QDomElement QtSoapSimpleType::toDomElement(QDomDocument doc) const +{ + QString prefix = QtSoapNamespaces::instance().prefixFor(n.uri()); + QDomElement a = n.uri() == "" + ? doc.createElement(n.name()) + : doc.createElementNS(n.uri(), prefix + ":" + n.name()); + + QString schemaprefix = QtSoapNamespaces::instance().prefixFor(XML_SCHEMA_INSTANCE); + a.setAttributeNS(XML_SCHEMA_INSTANCE, schemaprefix + ":type", "xsd:" + typeName()); + a.appendChild(doc.createTextNode(v.toString())); + + return a; +} + +/*! \reimp +*/ +bool QtSoapSimpleType::isValid() const +{ + return true; +} + +/*! + Makes this QtSoapSimpleType a copy of \a copy. +*/ +QtSoapSimpleType &QtSoapSimpleType::operator =(const QtSoapSimpleType ©) +{ + t = copy.t; + errorStr = copy.errorStr; + i = copy.i; + n = copy.n; + u = copy.u; + h = copy.h; + v = copy.v; + + return *this; +} + +/*! + Inspects \a node and constructs the QtSoapSimpleType content if \a + node qualifies as a SOAP simple type. Returns true if it does; + otherwise returns false. +*/ +bool QtSoapSimpleType::parse(QDomNode node) +{ + if (node.isNull() || !node.isElement()) + return false; + + QDomElement e = node.toElement(); + + QDomAttr typeattr = e.attributeNode("type"); + QString type = typeattr.isNull() ? QString("string") : localName(typeattr.value()).toLower(); + + t = QtSoapType::nameToType(type); + switch (t) { + case Duration: + case DateTime: + case Time: + case Date: + case GYearMonth: + case GYear: + case GMonthDay: + case GDay: + case GMonth: + case Base64Binary: + case HexBinary: + case AnyURI: + case QName: + case NOTATION: + case String: + case NormalizedString: + case Token: + case Language: + case Name: + case NMTOKEN: + case NCName: + case ID: + case IDREF: + case ENTITY: + v = QVariant(e.text()); + break; + case Float: + v = QVariant(e.text().toFloat()); + break; + case Double: + v = QVariant(e.text().toDouble()); + break; + case Decimal: + case Integer: + case NonPositiveInteger: + case NegativeInteger: + case Long: + case Int: + case Short: + case Byte: + case NonNegativeInteger: + case UnsignedLong: + case PositiveInteger: + case UnsignedInt: + case UnsignedShort: + case UnsignedByte: + if (e.text() == "" || (e.text() != "" && (e.text()[0].isNumber() || e.text()[0] == '-'))) + v = QVariant(e.text().toInt()); + else { + errorStr = "Type error at element \"" + e.tagName() + "\""; + return false; + } + + break; + case Boolean: { + QString val = e.text().trimmed().toLower(); + if (val == "false") + v = QVariant(false); + else if (val == "true") + v = QVariant(true); + } + break; + default: + v = e.text(); + break; + } + + setName(QtSoapQName(localName(e.tagName()), e.namespaceURI())); + return true; +} + +/*! + Returns the value of the simple type as a QString. +*/ +QString QtSoapSimpleType::toString() const +{ + return v.toString(); +} + +/*! + Returns the value of the simple type as an int. +*/ +int QtSoapSimpleType::toInt() const +{ + return v.toInt(); +} + +/*! + Returns the value of the simple type as a bool. +*/ +bool QtSoapSimpleType::toBool() const +{ + return v.toBool(); +} + + +/*! + Returns the QVariant value of this QtSoapSimpleType. +*/ +QVariant QtSoapSimpleType::value() const +{ + return v; +} + +/*! \class QtSoapMessage qtsoap.h + \brief The QtSoapMessage class provides easy access to SOAP + messages. + + With this class, you can create and inspect any SOAP message. + There are convenience functions available for generating the most + common types of SOAP messages, and any other messages can be + constructed manually using addBodyItem(). + + Use setMethod() and addMethodArgument() to construct a method + request. The return value of a method response is available + from returnValue(). + + Use setFaultCode(), setFaultString() and addFaultDetail() to + construct a Fault message. To inspect a Fault message, use + faultCode(), faultString() and faultDetail(). + + To add items to the body part of the SOAP message, use + addBodyItem(). To add items to the header, use addHeaderItem(). + + toXmlString() returns a QString XML representation of the SOAP + message. clear() resets all content in the message, creating an + empty SOAP message. + + \code + QtSoapMessage message; + + message.setMethod("getTemperature", "http://weather.example.com/temperature"); + message.addMethodArgument("city", "Oslo"); + + // Get the SOAP message as an XML string. + QString xml = message.toXmlString(); + \endcode + + QtSoap provides a partial implementation of version 1.1 of the + SOAP protocol as defined in \l http://www.w3.org/TR/SOAP/. + + \list + \i Server side SOAP is not supported. + \i References to values (id and href attributes) are not supported. + \i Arrays support a maximum of five dimensions. + \i Namespaces for types are not checked. Only the type names are used. + \i The encodingStyle attribute is ignored. The serialization and + encoding rules from section 5 in the SOAP v1.1 specification + are assumed regardless of the value of the encodingStyle + attribute. + \i QtSoapType does not have accessors for attributes, which means + for example that actor, mustUnderstand and so on are not + accessible in headers. + \i The SOAP root attribute is not supported. + \endlist + + \sa QtSoapType, QtSoapQName, QtSoapHttpTransport + +*/ + +/*! \enum QtSoapMessage::FaultCode + + This enum describes all the supported SOAP Fault codes: + + \value VersionMismatch + The namespace for the Envelope element was unrecognized by the + remote SOAP server. This usually means that the remote server does + not support version 1.1 of the SOAP protocol. + + \value MustUnderstand + One of the header items in the SOAP message with a + "MustUnderstand" attribute was not recognized by the remote + server. + + \value Client + An error in the SOAP message or transport prevents further + processing by the remote SOAP server. + + \value Server + An error in the remote SOAP server prevents it from processing the + SOAP message. + + \omitvalue Other +*/ + +/*! \enum QtSoapMessage::MessageType + + \value Fault + \value MethodRequest + \value MethodResponse + \value OtherType + +*/ + +/*! + Constructs an empty QtSoapMessage. The message only contains the + Envelope element, with no header and no body. +*/ +QtSoapMessage::QtSoapMessage() + : type(OtherType), envelope(QtSoapQName("Envelope", SOAPv11_ENVELOPE)) +{ + init(); +} + +/*! + Constructs a copy of \a copy. +*/ +QtSoapMessage::QtSoapMessage(const QtSoapMessage ©) + : type(copy.type), envelope(copy.envelope), m(copy.m), margs(copy.margs), + errorStr(copy.errorStr) +{ + init(); +} + +/*! + Destructs a QtSoapMessage. +*/ +QtSoapMessage::~QtSoapMessage() +{ +} + +/*! \internal + + Registers the standard SOAP namespaces with prefixes. +*/ +void QtSoapMessage::init() +{ + QtSoapNamespaces::instance().registerNamespace("SOAP-ENV", SOAPv11_ENVELOPE); + QtSoapNamespaces::instance().registerNamespace("SOAP-ENC", SOAPv11_ENCODING); + QtSoapNamespaces::instance().registerNamespace("xsi", XML_SCHEMA_INSTANCE); + QtSoapNamespaces::instance().registerNamespace("xsd", XML_SCHEMA); +} + +/*! + Clears the content of the SOAP message. +*/ +void QtSoapMessage::clear() +{ + type = OtherType; + envelope.clear(); + m = QtSoapQName(); + margs.clear(); + errorStr = "Unknown error"; +} + +/*! + Makes this message a copy of \a copy. +*/ +QtSoapMessage &QtSoapMessage::operator =(const QtSoapMessage ©) +{ + envelope = copy.envelope; + m = copy.m; + margs = copy.margs; + errorStr = copy.errorStr; + return *this; +} + +/*! + Imports the QDomDocument \a d if it validates as a SOAP + message. Any existing message content is replaced. + + If the import fails, this message becomes a Fault message. + + Returns true if the import succeeds, otherwise false. +*/ +bool QtSoapMessage::setContent(QDomDocument &d) +{ + if (isValidSoapMessage(d)) { + clear(); + QDomNode node = d.firstChild(); + if (!node.isElement()) + node = node.nextSibling(); + + if (envelope.parse(node)) + return true; + } + + return false; +} + +/*! + \overload + + Parses the XML document in \a buffer. Imports the document if it + validates as a SOAP message. Any existing message content is + replaced. + + If the import fails, this message becomes a Fault message. + + Returns true if the import succeeds, otherwise false. +*/ +bool QtSoapMessage::setContent(const QByteArray &buffer) +{ + int errorLine, errorColumn; + QString errorMsg; + + QDomDocument doc; + if (!doc.setContent(buffer, true, &errorMsg, + &errorLine, &errorColumn)) { + QString s; + s.sprintf("%s at line %i, column %i", errorMsg.toLatin1().constData(), + errorLine, errorColumn); + setFaultCode(VersionMismatch); + setFaultString("XML parse error"); + addFaultDetail(new QtSoapSimpleType(QtSoapQName("ParseError"), s)); + return false; + } + + if (!isValidSoapMessage(doc)) + return false; + + QDomNode node = doc.firstChild(); + if (!node.isElement()) + node = node.nextSibling(); + bool res = envelope.parse(node); + if (!res) + qDebug("QtSoapMessage::setContent(), parsing failed: %s", envelope.errorString().toLatin1().constData()); + return res; +} + +/*! + Validates the QDomDocument \a candidate using some simple + heuristics. Returns true if the document is a valid SOAP message; + otherwise returns false. +*/ +bool QtSoapMessage::isValidSoapMessage(const QDomDocument &candidate) +{ + QDomNode tmp = candidate.firstChild(); + if (tmp.isNull()) + return false; + + // Skip the initial processing instruction if there is one. Most + // likely this isn't actually a processing instruction, but rather + // the initial xml declaration (node); + fault.insert(new QtSoapSimpleType(QtSoapQName("Faultcode"), codeStr)); +} + +/*! + Sets the Fault faultstring of the SOAP Fault message to \a s. +*/ +void QtSoapMessage::setFaultString(const QString &s) +{ + if (type != Fault && type != OtherType) { + clear(); + type = Fault; + } + + if (!body()[QtSoapQName("Fault", SOAPv11_ENVELOPE)].isValid()) + addBodyItem(new QtSoapStruct(QtSoapQName("Fault", SOAPv11_ENVELOPE))); + + QtSoapType &node = body()[QtSoapQName("Fault", SOAPv11_ENVELOPE)]; + QtSoapStruct &fault = reinterpret_cast(node); + fault.insert(new QtSoapSimpleType(QtSoapQName("Faultstring"), s)); +} + +/*! + Adds the QtSoapType \a detail to the end of the list of faultdetail + items in a SOAP Fault message. +*/ +void QtSoapMessage::addFaultDetail(QtSoapType *detail) +{ + if (type != Fault && type != OtherType) { + clear(); + type = Fault; + } + + if (!body()[QtSoapQName("Fault", SOAPv11_ENVELOPE)].isValid()) + addBodyItem(new QtSoapStruct(QtSoapQName("Fault", SOAPv11_ENVELOPE))); + + QtSoapType &node = body()[QtSoapQName("Fault", SOAPv11_ENVELOPE)]; + QtSoapStruct &fault = reinterpret_cast(node); + if (!fault[QtSoapQName("Faultdetail", SOAPv11_ENVELOPE)].isValid()) + fault.insert(new QtSoapStruct(QtSoapQName("Faultdetail", SOAPv11_ENVELOPE))); + + QtSoapType &node2 = fault[QtSoapQName("Faultdetail", SOAPv11_ENVELOPE)]; + QtSoapStruct &fdetail = reinterpret_cast(node2); + + fdetail.insert(detail); +} + +/*! + Returns the method of a SOAP method request or response + as a QtSoapType. +*/ +const QtSoapType &QtSoapMessage::method() const +{ + static QtSoapType NIL; + + if (body().count() == 0) + return NIL; + + QtSoapStructIterator it(body()); + + return *it.data(); +} + +/*! + Sets the QName (qualified name) of the method to call in a SOAP + method request to \a meth. + + This function \e must be called before calling + addMethodArgument(). +*/ +void QtSoapMessage::setMethod(const QtSoapQName &meth) +{ + if (type != MethodRequest && type != OtherType) { + clear(); + type = MethodRequest; + } + + addBodyItem(new QtSoapStruct(meth)); +} + +/*! \overload + + Sets the method name to \a name and uri to \a uri. +*/ +void QtSoapMessage::setMethod(const QString &name, const QString &uri) +{ + setMethod(QtSoapQName(name, uri)); +} + +/*! + Adds argument \a arg to the list of arguments that are passed in a + SOAP method request. + + \warning setMethod() must be called before calling this function. +*/ +void QtSoapMessage::addMethodArgument(QtSoapType *arg) +{ + if (body().count() == 0) { + qWarning("Attempted to add argument (%s:%s) without first setting method", + arg->name().uri().toLatin1().constData(), arg->name().name().toLatin1().constData()); + return; + } + + QtSoapStructIterator it(body()); + QtSoapType &node = *it.data(); + QtSoapStruct &meth = static_cast(node); + meth.insert(arg); +} + +/*! \overload + + Adds an argument called \a name with a uri of \a uri. The type + of the argument is QtSoapType::String and its value is \a value. +*/ +void QtSoapMessage::addMethodArgument(const QString &name, const QString &uri, const QString &value) +{ + addMethodArgument(new QtSoapSimpleType(QtSoapQName(name, uri), value)); +} + +/*! \overload + + Adds an argument called \a name with a uri of \a uri. The type + of the argument is QtSoapType::Boolean and its value is \a value. + + The \a dummy argument is used to distinguish this function from + the overload which takes an int. +*/ +void QtSoapMessage::addMethodArgument(const QString &name, const QString &uri, bool value, int dummy) +{ + addMethodArgument(new QtSoapSimpleType(QtSoapQName(name, uri), value, dummy)); +} + +/*! \overload + + Adds an argument called \a name with a uri of \a uri. The type + of the argument is QtSoapType::Integer and its value is \a value. +*/ +void QtSoapMessage::addMethodArgument(const QString &name, const QString &uri, int value) +{ + addMethodArgument(new QtSoapSimpleType(QtSoapQName(name, uri), value)); +} + +/*! + Constructs a QtSoapTypeFactory and initializes it with all the + known SOAP types. +*/ +QtSoapTypeFactory::QtSoapTypeFactory() +{ + QtSoapTypeConstructor *structConstructor = new QtSoapTypeConstructor(); + deleteList.append(structConstructor); + QtSoapTypeConstructor *arrayConstructor = new QtSoapTypeConstructor(); + deleteList.append(arrayConstructor); + QtSoapTypeConstructor *basicTypeConstructor = new QtSoapTypeConstructor(); + deleteList.append(basicTypeConstructor); + + registerHandler("struct", structConstructor); + registerHandler("array", arrayConstructor); + registerHandler("string", basicTypeConstructor); + registerHandler("normalizedstring", basicTypeConstructor); + registerHandler("token", basicTypeConstructor); + registerHandler("language", basicTypeConstructor); + registerHandler("name", basicTypeConstructor); + registerHandler("ncname", basicTypeConstructor); + registerHandler("id", basicTypeConstructor); + registerHandler("idref", basicTypeConstructor); + registerHandler("entity", basicTypeConstructor); + registerHandler("nmtoken", basicTypeConstructor); + registerHandler("nmtokens", basicTypeConstructor); + registerHandler("boolean", basicTypeConstructor); + registerHandler("decimal", basicTypeConstructor); + registerHandler("integer", basicTypeConstructor); + registerHandler("nonpositiveinteger", basicTypeConstructor); + registerHandler("negativeinteger", basicTypeConstructor); + registerHandler("int", basicTypeConstructor); + registerHandler("long", basicTypeConstructor); + registerHandler("short", basicTypeConstructor); + registerHandler("byte", basicTypeConstructor); + registerHandler("nonnegativeinteger", basicTypeConstructor); + registerHandler("unsignedlong", basicTypeConstructor); + registerHandler("unsignedint", basicTypeConstructor); + registerHandler("unsignedshort", basicTypeConstructor); + registerHandler("unsignedbyte", basicTypeConstructor); + registerHandler("positiveinteger", basicTypeConstructor); + registerHandler("float", basicTypeConstructor); + registerHandler("double", basicTypeConstructor); + registerHandler("other", structConstructor); +} + +/*! + Destructs the QtSoapTypeFactory. This destructor is called when + the application exits. +*/ +QtSoapTypeFactory::~QtSoapTypeFactory() +{ + QLinkedList::ConstIterator it = deleteList.begin(); + while (it != deleteList.end()) { + delete *it; + ++it; + } +} + +/*! + Returns a reference to the QtSoapTypeFactory singleton. +*/ +QtSoapTypeFactory &QtSoapTypeFactory::instance() +{ + static QtSoapTypeFactory factory; + return factory; +} + +/*! + Registers a handler \a handler for a QtSoapType called \a name. +*/ +bool QtSoapTypeFactory::registerHandler(const QString &name, QtSoapTypeConstructorBase *handler) +{ + if (typeHandlers.find(name) != typeHandlers.end()) { + errorStr = "A handler for " + name + " is already registered."; + return false; + } + + typeHandlers.insert(name, handler); + return true; +} + +/*! \internal +*/ +QtSmartPtr QtSoapTypeFactory::soapType(QDomNode node) const +{ + if (node.isNull() || !node.isElement()) + return QtSmartPtr(); + + QDomElement elem = node.toElement(); + + QDomAttr attr = elem.attributeNode("type"); + QtSoapTypeConstructorBase *constructor = 0; + if (!attr.isNull()) { + QHash::ConstIterator it; + it = typeHandlers.find(localName(attr.value().toLower())); + if (it != typeHandlers.end()) + constructor = *it; + } + + if (attr.isNull() || !constructor) { + QHash::ConstIterator it; + if (node.firstChild().isElement()) { + if (localName(node.nodeName().toLower()) == "array") { + it = typeHandlers.find("array"); + } else + it = typeHandlers.find("struct"); + } else + it = typeHandlers.find("string"); + if (it != typeHandlers.end()) + constructor = *it; + } + + if (!constructor) { + return QtSmartPtr(); + } + + QtSoapType *type = constructor->createObject(node); + + if (!type) + errorStr = constructor->errorString(); + + return QtSmartPtr(type); +} + +/*! + Returns a human readable interpretation of the last error + that occurred. +*/ +QString QtSoapTypeFactory::errorString() const +{ + return errorStr; +} + +/*! \class QtSoapHttpTransport + + \brief The QtSoapHttpTransport class provides a mechanism for + transporting SOAP messages to and from other hosts using the + HTTP protocol. + + Use this class to submit SOAP messages to a web service. + Set the hostname of the SOAP server with setHost(). Some servers + also require the SOAPAction header to be set, and you can do this + with setAction(). Next, submit the request with submitRequest(), + passing the message to submit together with the path that you want + to submit the message to. The responseReady() signal is emitted + when a response has been received. Call getResponse() to get the + reponse from the service. + + QtSoapHttpTransport usage example: If a SOAP weather service was + running on the host weather.example.com, the following code might + be used to find the temperature in any given city: + + \code + void WeatherFetcher::findTemperature(const QString &city) + { + QtSoapMessage message; + message.setMethod("getTemperature", "http://weather.example.com/temperature"); + message.setMethodArgument("city", "", city); + + // transport is a private member of WeatherFetcher, of type QtSoapHttpTransport + transport.setHost("www.example.com"); + connect(&transport, SIGNAL(responseReady()), SLOT(readResponse())); + + transport.submitRequest(message, "/weatherfetcher/fetch.asp"); + } + \endcode + + This is an example implementation of the readResponse() slot in + the WeatherFetcher class: + + \code + void WeatherFetcher::readResponse() + { + const QtSoapMessage &response = transport.getResponse(); + if (response.isFault()) { + cout << response.faultString().toString().toLatin1().constData() << endl; + return; + } + + const QtSoapType &returnValue = response.returnValue(); + if (returnValue["temperature"].isValid()) { + cout << "The current temperature is " + << returnValue["temperature"].toString().toLatin1().constData() + << " degrees Celcius." << endl; + } + \endcode + + \sa QtSoapMessage, QtSoapType +*/ + +/*! \fn void QtSoapHttpTransport::responseReady() + + This signal is emitted when a SOAP response is received from a + remote peer. + + \sa getResponse() +*/ + +/*! \fn void QtSoapHttpTransport::responseReady(const QtSoapMessage &response) + + This signal is emitted when a SOAP response is received from a + remote peer. The received response is available in \a + response. This signal is emitted in tandem with the argument-less + responseReady() signal. + + \sa responseReady() +*/ + +/*! + Constructs a QtSoapHttpTransport object. Passes \a parent to + QObject's constructor. +*/ + +QtSoapHttpTransport::QtSoapHttpTransport(QObject *parent) + : QObject(parent), networkMgr(this) +{ + connect(&networkMgr, SIGNAL(finished(QNetworkReply *)), + SLOT(readResponse(QNetworkReply *))); +} + +/*! + Destructs a QtSoapHttpTransport. +*/ +QtSoapHttpTransport::~QtSoapHttpTransport() +{ +} + +/*! + \obsolete +*/ + +void QtSoapHttpTransport::setHost(const QString &host, int port) +{ + setHost(host, false, port); +} + +/*! + Sets the \a host this transport should connect to. The transport + mode will be HTTP, unless \a useSecureHTTP is set, in which case it + will be HTTPS. This transport will connect to the well-known ports + by default (80 for HTTP, 443 for HTTPS), unless a different, + non-zero port is specified in \a port. +*/ +void QtSoapHttpTransport::setHost(const QString &host, bool useSecureHTTP, int port) +{ + url.setHost(host); + url.setScheme(useSecureHTTP ? QLatin1String("https") : QLatin1String("http")); + if (port) + url.setPort(port); + else + url.setPort(useSecureHTTP ? 443 : 80); +} + +/*! + Sets the HTTP header SOAPAction to \a action. +*/ +void QtSoapHttpTransport::setAction(const QString &action) +{ + soapAction = action; +} + +/*! + Submits the SOAP message \a request to the path \a path on the + HTTP server set using setHost(). +*/ +void QtSoapHttpTransport::submitRequest(QtSoapMessage &request, const QString &path) +{ + QNetworkRequest networkReq; + networkReq.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("text/xml;charset=utf-8")); + networkReq.setRawHeader("SOAPAction", soapAction.toAscii()); + url.setPath(path); + networkReq.setUrl(url); + + soapResponse.clear(); + networkRep = networkMgr.post(networkReq, request.toXmlString().toUtf8().constData()); +} + + +/*! + Returns the most recently received response SOAP message. This + message could be a Fault message, so it is wise to check using + QtSoapMessage::isFault() before processing the response. +*/ +const QtSoapMessage &QtSoapHttpTransport::getResponse() const +{ + return soapResponse; +} + + +/*! + Returns a pointer to the QNetworkAccessManager object used by this + transport. This is useful if the application needs to connect to its + signals, or set or read its cookie jar, etc. +*/ + +QNetworkAccessManager *QtSoapHttpTransport::networkAccessManager() +{ + return &networkMgr; +} + + +/*! + Returns a pointer to the QNetworkReply object of the current (or last) + request, or 0 if no such object is currently available. + + This is useful if the application needs to access the raw header + data etc. +*/ + +QNetworkReply *QtSoapHttpTransport::networkReply() +{ + return networkRep; +} + +/*! + +*/ + +void QtSoapHttpTransport::readResponse(QNetworkReply *reply) +{ + networkRep = reply; + switch (reply->error()) { + case QNetworkReply::NoError: + case QNetworkReply::ContentAccessDenied: + case QNetworkReply::ContentOperationNotPermittedError: + case QNetworkReply::ContentNotFoundError: + case QNetworkReply::UnknownContentError: + { + soapResponse.setContent(reply->readAll()); + + int httpStatus = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + if (httpStatus != 200 && httpStatus != 100) { + if (soapResponse.faultCode() == QtSoapMessage::Other) + soapResponse.setFaultCode(QtSoapMessage::Client); + /* + QString httpReason = reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString(); + soapResponse.setFaultString(QString("HTTP status %2 (%3).\n%1").arg(soapResponse.faultString().toString()).arg(httpStatus).arg(httpReason)); + */ + } + } + break; + default: + { + soapResponse.setFaultCode(QtSoapMessage::Client); + soapResponse.setFaultString(QString("Network transport error (%1): %2").arg(reply->error()).arg(reply->errorString())); + } + break; + } + + emit responseReady(); + emit responseReady(soapResponse); + + reply->deleteLater(); +} + +/*! \class QtSoapNamespaces qtsoap.h + + \brief The QtSoapNamespaces class provides a registry for XML + namespaces and prefixes for use in QtSoap. + + When a QtSoapMessage is converted to XML via + QtSoapMessage::toXmlString(), this class is used to find + appropriate XML namespace prefixes for the QNames (qualified + names) in the message. + + To register a namespace with a prefix, call register(). + prefixFor() will then return the prefix that is registered for the + given namespace, if any. + + To access the QtSoapNamespaces registry, call + QtSoapNamespaces::instance(). + + \code + QtSoapNamespaces ®istry = QtSoapNamespaces::instance(); + + registry.register("pre", "http://www.example.com/"); + QString prefix = registry.prefixFor("http://www.example.com/"); // returns "pre" + \endcode + + \sa QtSoapMessage +*/ + +/*! + Returns a reference to the QtSoapNamespaces singleton. +*/ +QtSoapNamespaces &QtSoapNamespaces::instance() +{ + static QtSoapNamespaces ns; + return ns; +} + +/*! \internal + + Constructs a QtSoapNamespaces object. +*/ +QtSoapNamespaces::QtSoapNamespaces() +{ +} + +/*! + Registers the namespace \a uri with the prefix \a prefix. +*/ +void QtSoapNamespaces::registerNamespace(const QString &prefix, const QString &uri) +{ + namespaces.insert(uri, prefix); +} + +/*! + Returns the prefix for the namespace \a uri, or an empty string if + no prefix has been registered for \a uri. +*/ +QString QtSoapNamespaces::prefixFor(const QString &uri) +{ + return namespaces.value(uri); +} diff --git a/qtsolutions/soap/qtsoap.h b/qtsolutions/soap/qtsoap.h new file mode 100644 index 000000000..5e151f266 --- /dev/null +++ b/qtsolutions/soap/qtsoap.h @@ -0,0 +1,611 @@ +/**************************************************************************** +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of a Qt Solutions component. +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.1, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact Nokia at qt-info@nokia.com. +** +****************************************************************************/ + +#ifndef QTSOAP_H +#define QTSOAP_H +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(Q_WS_WIN) +# if !defined(QT_QTSOAP_EXPORT) && !defined(QT_QTSOAP_IMPORT) +# define QT_QTSOAP_EXPORT +# elif defined(QT_QTSOAP_IMPORT) +# if defined(QT_QTSOAP_EXPORT) +# undef QT_QTSOAP_EXPORT +# endif +# define QT_QTSOAP_EXPORT __declspec(dllimport) +# elif defined(QT_QTSOAP_EXPORT) +# undef QT_QTSOAP_EXPORT +# define QT_QTSOAP_EXPORT __declspec(dllexport) +# endif +#else +# define QT_QTSOAP_EXPORT +#endif + +#define SOAPv11_ENVELOPE "http://schemas.xmlsoap.org/soap/envelope/" +#define SOAPv11_ENCODING "http://schemas.xmlsoap.org/soap/encoding/" +#define SOAPv11_ACTORNEXT "http://schemas.xmlsoap.org/soap/actor/next" + +#define XML_SCHEMA "http://www.w3.org/1999/XMLSchema" +#define XML_SCHEMA_INSTANCE "http://www.w3.org/1999/XMLSchema-instance" +#define XML_NAMESPACE "http://www.w3.org/XML/1998/namespace" + +template +class QtSmartPtr +{ +public: + inline QtSmartPtr(T *data = 0) + { + d = data; + r = new int; + *r = 1; + } + + inline QtSmartPtr(const QtSmartPtr ©) + { + if (*copy.r != 0) + ++(*copy.r); + + r = copy.r; + d = copy.d; + } + + inline ~QtSmartPtr() + { + if ((*r) == 0) + delete r; + else if ((*r) != 0 && --(*r) == 0) { + delete r; + if (d) delete d; + } + } + + inline QtSmartPtr &operator =(const QtSmartPtr ©) + { + if (*copy.r != 0) + ++(*copy.r); + + if ((*r) == 0) + delete r; + else if ((*r) != 0 && --(*r) == 0) { + delete r; + if (d) delete d; + } + + r = copy.r; + d = copy.d; + return *this; + } + + inline T &operator *() const + { + return *d; + } + + inline T *operator ->() const + { + return d; + } + + inline T *ptr() const + { + return d; + } + + inline T &ref() const + { + return *d; + } + + inline T *releasedPtr() const + { + (*r) = 0; + return d; + } + + inline bool isNull() const + { + return d == 0; + } + +private: + int *r; + T *d; +}; + +class QT_QTSOAP_EXPORT QtSoapQName +{ +public: + QtSoapQName(const QString &name = QString::null, const QString &uri = QString::null); + ~QtSoapQName(); + + QtSoapQName &operator =(const QString &s); + + QString name() const; + QString uri() const; + +private: + QString n; + QString nuri; +}; + +bool operator ==(const QtSoapQName &n1, const QtSoapQName &n2); +bool operator <(const QtSoapQName &n1, const QtSoapQName &n2); + +class QT_QTSOAP_EXPORT QtSoapType +{ +public: + enum Type { + Duration, DateTime, Time, Date, GYearMonth, GYear, GMonthDay, + GDay, GMonth, Boolean, Base64Binary, HexBinary, Float, Double, + AnyURI, QName, NOTATION, String, NormalizedString, Token, Language, + Name, NMTOKEN, NCName, ID, IDREF, ENTITY, Decimal, Integer, + NonPositiveInteger, NegativeInteger, Long, Int, Short, + Byte, NonNegativeInteger, UnsignedLong, PositiveInteger, + UnsignedInt, UnsignedShort, UnsignedByte, + Array, Struct, Other + }; + + QtSoapType(); + QtSoapType(const QtSoapQName &name, Type t = Other); + QtSoapType(const QtSoapType ©); + QtSoapType &operator =(const QtSoapType ©); + virtual ~QtSoapType(); + + virtual void clear(); + + virtual bool parse(QDomNode); + virtual bool isValid() const; + + virtual int count() const; + virtual QVariant value() const; + + virtual QtSoapType &operator [](int); + virtual QtSoapType &operator [](const QtSoapQName &s); + virtual QtSoapType &operator [](const QString &name); + + virtual const QtSoapType &operator [](int) const; + virtual const QtSoapType &operator [](const QtSoapQName &s) const; + virtual const QtSoapType &operator [](const QString &name) const; + + virtual QDomElement toDomElement(QDomDocument) const; + + virtual Type type() const; + virtual QString id() const; + virtual QString href() const; + virtual QString typeName() const; + virtual QtSoapQName name() const; + + virtual QString toString() const; + virtual int toInt() const; + virtual bool toBool() const; + + void setName(const QtSoapQName &); + void setId(const QString &); + void setHref(const QString &); + + QString errorString() const; + + static QString typeToName(QtSoapType::Type t); + static Type nameToType(const QString &); + +protected: + Type t; + QString errorStr; + QString i; + QtSoapQName n; + QString u; + QString h; +}; + +class QtSoapArrayIterator; + +class QT_QTSOAP_EXPORT QtSoapArray : public QtSoapType +{ +public: + QtSoapArray(); + QtSoapArray(const QtSoapQName &name, QtSoapType::Type type = Other, + int size0 = -1, int size1 = -1, int size2 = -1, int size3 = -1, int size4 = -1); + QtSoapArray(const QtSoapArray ©); + QtSoapArray &operator = (const QtSoapArray ©); + ~QtSoapArray(); + + void clear(); + + bool parse(QDomNode node); + bool isValid() const; + + int count() const; + + QtSoapType &at(int pos0); + QtSoapType &at(int pos0, int pos1); + QtSoapType &at(int pos0, int pos1, int pos2); + QtSoapType &at(int pos0, int pos1, int pos2, int pos3); + QtSoapType &at(int pos0, int pos1, int pos2, int pos3, int pos4); + QtSoapType &operator [](int i); + QtSoapType &operator [](const QString &); + QtSoapType &operator [](const QtSoapQName &); + + const QtSoapType &at(int pos) const; + const QtSoapType &at(int pos0, int pos1) const; + const QtSoapType &at(int pos0, int pos1, int pos2) const; + const QtSoapType &at(int pos0, int pos1, int pos2, int pos3) const; + const QtSoapType &at(int pos0, int pos1, int pos2, int pos3, int pos4) const; + const QtSoapType &operator [](int i) const; + const QtSoapType &operator [](const QString &) const; + const QtSoapType &operator [](const QtSoapQName &) const; + + void append(QtSoapType *item); + void insert(int pos0, QtSoapType *item); + void insert(int pos0,int pos1, QtSoapType *item); + void insert(int pos0,int pos1,int pos2, QtSoapType *item); + void insert(int pos0,int pos1,int pos2,int pos3, QtSoapType *item); + void insert(int pos0,int pos1,int pos2,int pos3,int pos4, QtSoapType *item); + + QDomElement toDomElement(QDomDocument doc) const; + + friend class QtSoapArrayIterator; + +protected: + QString arraySizeString() const; + QString arrayTypeString() const; + + QHash > array; + int lastIndex; + +private: + Type arrayType; + int order; + int siz0, siz1, siz2, siz3, siz4; +}; + +class QT_QTSOAP_EXPORT QtSoapArrayIterator +{ +public: + QtSoapArrayIterator(QtSoapArray &); + QtSoapArrayIterator(const QtSoapArrayIterator ©); + QtSoapArrayIterator &operator =(const QtSoapArrayIterator &j); + ~QtSoapArrayIterator(); + + int pos() const; + void pos(int *pos0, int *pos1 = 0, int *pos2 = 0, int *pos3 = 0, int *pos4 = 0) const; + + QtSoapType *data(); + const QtSoapType *current() const; + + void operator ++(); + bool operator !=(const QtSoapArrayIterator &j) const; + bool operator ==(const QtSoapArrayIterator &j) const; + + bool atEnd() const; + +private: + QHash >::Iterator it; + QtSoapArray *arr; +}; + +class QtSoapStructIterator; + +class QT_QTSOAP_EXPORT QtSoapStruct : public QtSoapType +{ +public: + QtSoapStruct(); + QtSoapStruct(const QtSoapQName &name); + QtSoapStruct(const QtSoapStruct ©); + QtSoapStruct &operator =(const QtSoapStruct ©); + ~QtSoapStruct(); + + void clear(); + + bool parse(QDomNode node); + bool isValid() const; + + int count() const; + + QtSoapType &at(const QtSoapQName &key); + const QtSoapType &at(const QtSoapQName &key) const; + + QtSoapType &operator [](int); + QtSoapType &operator [](const QtSoapQName &key); + QtSoapType &operator [](const QString &key); + + const QtSoapType &operator [](int) const; + const QtSoapType &operator [](const QtSoapQName &key) const; + const QtSoapType &operator [](const QString &key) const; + + void insert(QtSoapType *item); + + QDomElement toDomElement(QDomDocument doc) const; + + friend class QtSoapStructIterator; + +protected: + QList > dict; +}; + +class QT_QTSOAP_EXPORT QtSoapStructIterator +{ +public: + QtSoapStructIterator(QtSoapStruct &); + ~QtSoapStructIterator(); + + QtSoapQName key() const; + QtSoapType *data(); + const QtSoapType *current() const; + + void operator ++(); + bool operator !=(const QtSoapStructIterator &j) const; + bool operator ==(const QtSoapStructIterator &j) const; + +private: + QList >::Iterator it; + QList >::Iterator itEnd; +}; + +class QT_QTSOAP_EXPORT QtSoapSimpleType : public QtSoapType +{ +public: + QtSoapSimpleType(); + QtSoapSimpleType(const QtSoapQName &name); + QtSoapSimpleType(const QtSoapQName &name, int n); + QtSoapSimpleType(const QtSoapQName &name, bool n, int dummy); + QtSoapSimpleType(const QtSoapQName &name, const QString &n); + QtSoapSimpleType(const QtSoapSimpleType ©); + QtSoapSimpleType &operator =(const QtSoapSimpleType ©); + ~QtSoapSimpleType(); + + void clear(); + + bool parse(QDomNode node); + bool isValid() const; + + QString toString() const; + int toInt() const; + bool toBool() const; + QVariant value() const; + + QDomElement toDomElement(QDomDocument doc) const; + +protected: + QVariant v; +}; + +class QT_QTSOAP_EXPORT QtSoapMessage +{ + friend class QtSoapHttpServer; + +public: + QtSoapMessage(); + QtSoapMessage(const QtSoapMessage ©); + ~QtSoapMessage(); + + QtSoapMessage &operator =(const QtSoapMessage ©); + + bool setContent(const QByteArray &buffer); + bool setContent(QDomDocument &d); + + void addBodyItem(QtSoapType *); + void addHeaderItem(QtSoapType *); + + // Method and response + const QtSoapType &method() const; + const QtSoapType &returnValue() const; + void setMethod(const QtSoapQName &); + void setMethod(const QString &name, const QString &url = QString::null); + void addMethodArgument(QtSoapType *); + void addMethodArgument(const QString &uri, const QString &name, const QString &value); + void addMethodArgument(const QString &uri, const QString &name, bool value, int dummy); + void addMethodArgument(const QString &uri, const QString &name, int value); + + // Fault + enum FaultCode { + VersionMismatch, + MustUnderstand, + Client, + Server, + Other + }; + + bool isFault() const; + FaultCode faultCode() const; + const QtSoapType &faultString() const; + const QtSoapType &faultDetail() const; + void setFaultCode(FaultCode code); + void setFaultString(const QString &fstring); + void addFaultDetail(QtSoapType *detail); + + // Generating + void clear(); + QString toXmlString(int indent = 0) const; + + // Errors + QString errorString() const; + +protected: + enum MessageType { + Fault, + MethodRequest, + MethodResponse, + OtherType + }; + + bool isValidSoapMessage(const QDomDocument &candidate); + + QtSoapStruct &body() const; + QtSoapStruct &header() const; + + void init(); + +private: + MessageType type; + + mutable QtSoapStruct envelope; + + QtSoapQName m; + QtSoapStruct margs; + + QString errorStr; +}; + +class QT_QTSOAP_EXPORT QtSoapTypeConstructorBase +{ +public: + inline QtSoapTypeConstructorBase() + { + } + + virtual inline ~QtSoapTypeConstructorBase() + { + } + + virtual QtSoapType *createObject(QDomNode) = 0; + + virtual QString errorString() const = 0; +}; + +template +class QT_QTSOAP_EXPORT QtSoapTypeConstructor : public QtSoapTypeConstructorBase +{ +public: + QtSoapTypeConstructor() + { + } + + QtSoapType *createObject(QDomNode node) + { + T *t = new T(); + if (t->parse(node)) { + return t; + } else { + errorStr = t->errorString(); + delete t; + return 0; + } + } + + QString errorString() const + { + return errorStr; + } + +private: + mutable QString errorStr; +}; + +class QT_QTSOAP_EXPORT QtSoapTypeFactory +{ +private: + QtSoapTypeFactory(); + +public: + ~QtSoapTypeFactory(); + + static QtSoapTypeFactory &instance(); + + bool registerHandler(const QString &name, QtSoapTypeConstructorBase *handler); + + QtSmartPtr soapType(QDomNode node) const; + + QString errorString() const; + +private: + mutable QString errorStr; + QHash typeHandlers; + QLinkedList deleteList; +}; + +class QT_QTSOAP_EXPORT QtSoapNamespaces +{ +public: + void registerNamespace(const QString &prefix, const QString &uri); + QString prefixFor(const QString &ns); + + static QtSoapNamespaces &instance(); + +private: + QMap namespaces; + QtSoapNamespaces(); +}; + +class QT_QTSOAP_EXPORT QtSoapHttpTransport : public QObject +{ + Q_OBJECT + +public: + QtSoapHttpTransport(QObject *parent = 0); + ~QtSoapHttpTransport(); + + void setHost(const QString &host, bool useSecureHTTP = false, int port = 0); + void setHost(const QString &host, int port); //obsolete + void setAction(const QString &action); + void submitRequest(QtSoapMessage &request, const QString &path); + const QtSoapMessage &getResponse() const; + + QNetworkAccessManager *networkAccessManager(); + QNetworkReply *networkReply(); + +Q_SIGNALS: + void responseReady(); + void responseReady(const QtSoapMessage &response); + +private Q_SLOTS: + void readResponse(QNetworkReply *reply); + +private: + QNetworkAccessManager networkMgr; + QPointer networkRep; + QUrl url; + QString soapAction; + QtSoapMessage soapResponse; +}; + +#endif diff --git a/qtsolutions/soap/qtsoap.pri b/qtsolutions/soap/qtsoap.pri new file mode 100644 index 000000000..c7d2d68ed --- /dev/null +++ b/qtsolutions/soap/qtsoap.pri @@ -0,0 +1,16 @@ +include(../common.pri) +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD +QT += xml network + +qtsoap-uselib:!qtsoap-buildlib { + LIBS += -L$$QTSOAP_LIBDIR -l$$QTSOAP_LIBNAME +} else { + SOURCES += $$PWD/qtsoap.cpp + HEADERS += $$PWD/qtsoap.h +} + +win32 { + contains(TEMPLATE, lib):contains(CONFIG, shared):DEFINES += QT_QTSOAP_EXPORT + else:qtsoap-uselib:DEFINES += QT_QTSOAP_IMPORT +} diff --git a/qxt/src/qxtglobal.h b/qxt/src/qxtglobal.h index 9bcd095d8..02a6973a4 100644 --- a/qxt/src/qxtglobal.h +++ b/qxt/src/qxtglobal.h @@ -60,7 +60,7 @@ #else # define QXT_GUI_EXPORT #endif // BUILD_QXT_GUI - + #if !defined(QXT_STATIC) # if defined(BUILD_QXT_NETWORK) # define QXT_NETWORK_EXPORT Q_DECL_EXPORT diff --git a/qxt/src/qxtnamespace.h b/qxt/src/qxtnamespace.h index d922beeeb..41213f74d 100644 --- a/qxt/src/qxtnamespace.h +++ b/qxt/src/qxtnamespace.h @@ -88,7 +88,8 @@ namespace Qxt { ItemStartTimeRole = Qt::UserRole + 1, ItemDurationRole = ItemStartTimeRole + 1, - UserRole = ItemDurationRole + 23 + OutlineRole = ItemDurationRole + 1, + UserRole = OutlineRole + 23 }; enum Timeunit diff --git a/qxt/src/qxtscheduleheaderwidget.cpp b/qxt/src/qxtscheduleheaderwidget.cpp new file mode 100644 index 000000000..a7c885025 --- /dev/null +++ b/qxt/src/qxtscheduleheaderwidget.cpp @@ -0,0 +1,89 @@ +/**************************************************************************** + ** + ** Copyright (C) Qxt Foundation. Some rights reserved. + ** + ** This file is part of the QxtGui module of the Qxt library. + ** + ** This library is free software; you can redistribute it and/or modify it + ** under the terms of the Common Public License, version 1.0, as published + ** by IBM, and/or under the terms of the GNU Lesser General Public License, + ** version 2.1, as published by the Free Software Foundation. + ** + ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY + ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY + ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR + ** FITNESS FOR A PARTICULAR PURPOSE. + ** + ** You should have received a copy of the CPL and the LGPL along with this + ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files + ** included with the source distribution for more information. + ** If you did not receive a copy of the licenses, contact the Qxt Foundation. + ** + ** + ** + ****************************************************************************/ +#include "qxtscheduleheaderwidget.h" +#include "qxtscheduleview.h" +#include "qxtscheduleviewheadermodel_p.h" + +#include +#include +#include +#include + + +/*! + * @internal the QxtAgendaHeaderWidget operates on a internal model , that uses the QxtScheduleView as DataSource + * + */ + +QxtScheduleHeaderWidget::QxtScheduleHeaderWidget(Qt::Orientation orientation , QxtScheduleView *parent) : QHeaderView(orientation, parent) +{ + QxtScheduleViewHeaderModel *model = new QxtScheduleViewHeaderModel(this); + setModel(model); + + if (parent) + { + model->setDataSource(parent); + } +} + +void QxtScheduleHeaderWidget::paintSection(QPainter * painter, const QRect & rect, int logicalIndex) const +{ + if (model()) + { + switch (orientation()) + { + case Qt::Horizontal: + { + QHeaderView::paintSection(painter, rect, logicalIndex); + } + break; + case Qt::Vertical: + { + QTime time = model()->headerData(logicalIndex, Qt::Vertical, Qt::DisplayRole).toTime(); + if (time.isValid()) + { + QRect temp = rect; + temp.adjust(1, 1, -1, -1); + + painter->fillRect(rect, this->palette().background()); + + if (time.minute() == 0) + { + painter->drawLine(temp.topLeft() + QPoint(temp.width() / 3, 0), temp.topRight()); + painter->drawText(temp, Qt::AlignTop | Qt::AlignRight, time.toString("hh:mm")); + } + } + } + break; + default: + Q_ASSERT(false); //this will never happen... normally + } + } +} + +void QxtScheduleHeaderWidget::setModel(QxtScheduleViewHeaderModel *model) +{ + QHeaderView::setModel(model); +} diff --git a/qxt/src/qxtscheduleheaderwidget.h b/qxt/src/qxtscheduleheaderwidget.h new file mode 100644 index 000000000..6eaa2a338 --- /dev/null +++ b/qxt/src/qxtscheduleheaderwidget.h @@ -0,0 +1,49 @@ +/**************************************************************************** + ** + ** Copyright (C) Qxt Foundation. Some rights reserved. + ** + ** This file is part of the QxtGui module of the Qxt library. + ** + ** This library is free software; you can redistribute it and/or modify it + ** under the terms of the Common Public License, version 1.0, as published + ** by IBM, and/or under the terms of the GNU Lesser General Public License, + ** version 2.1, as published by the Free Software Foundation. + ** + ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY + ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY + ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR + ** FITNESS FOR A PARTICULAR PURPOSE. + ** + ** You should have received a copy of the CPL and the LGPL along with this + ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files + ** included with the source distribution for more information. + ** If you did not receive a copy of the licenses, contact the Qxt Foundation. + ** + ** + ** + ****************************************************************************/ +#ifndef QXTSCHEDULEHEADERWIDGET_H_INCLUDED +#define QXTSCHEDULEHEADERWIDGET_H_INCLUDED + +#include +#include "qxtglobal.h" + +class QxtScheduleView; +class QxtScheduleViewHeaderModel; + +class QXT_GUI_EXPORT QxtScheduleHeaderWidget : public QHeaderView +{ + Q_OBJECT + +public: + explicit QxtScheduleHeaderWidget(Qt::Orientation orientation, QxtScheduleView *parent = 0); + +protected: + virtual void paintSection(QPainter * painter, const QRect & rect, int logicalIndex) const; + +private: + virtual void setModel(QxtScheduleViewHeaderModel *model); +}; + +#endif // QXTSCHEDULEHEADERWIDGET_H_INCLUDED + diff --git a/qxt/src/qxtscheduleitemdelegate.cpp b/qxt/src/qxtscheduleitemdelegate.cpp new file mode 100644 index 000000000..8d1401e52 --- /dev/null +++ b/qxt/src/qxtscheduleitemdelegate.cpp @@ -0,0 +1,295 @@ +/**************************************************************************** + ** + ** Copyright (C) Qxt Foundation. Some rights reserved. + ** + ** This file is part of the QxtGui module of the Qxt library. + ** + ** This library is free software; you can redistribute it and/or modify it + ** under the terms of the Common Public License, version 1.0, as published + ** by IBM, and/or under the terms of the GNU Lesser General Public License, + ** version 2.1, as published by the Free Software Foundation. + ** + ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY + ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY + ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR + ** FITNESS FOR A PARTICULAR PURPOSE. + ** + ** You should have received a copy of the CPL and the LGPL along with this + ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files + ** included with the source distribution for more information. + ** If you did not receive a copy of the licenses, contact the Qxt Foundation. + ** + ** + ** + ****************************************************************************/ +#include "qxtscheduleitemdelegate.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qxtnamespace.h" + +QxtScheduleItemDelegate::QxtScheduleItemDelegate(QObject *parent) + : QAbstractItemDelegate(parent) +{ +} + + +QxtScheduleItemDelegate::~QxtScheduleItemDelegate() +{ +} + + +/*! + * reimplemented for item painting + * You should not reimplement this to change the item painting, use paintItemBody, paintItemHeader and paintSubItem instead + * because this function uses caches to speed up painting. If you want to change the item shape only you could also reimplement + * the createPainterPath function. + * \note the parameter option hast to be of type QxtStyleOptionScheduleViewItem or the delegate will not paint something + * \sa paintItemBody(), paintItemHeader(), paintSubItem() + */ +void QxtScheduleItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + + const QxtStyleOptionScheduleViewItem *agendaOption = qstyleoption_cast(&option); + if (!agendaOption) { + return; + } + + //QStringList rowsData = index.data(Qt::EditRole).toStringList(); + + QRect currRect; + + painter->save(); + + if (agendaOption->itemPaintCache->size() != agendaOption->itemGeometries.size()) + (*agendaOption->itemPaintCache) = QVector(agendaOption->itemGeometries.size(), QPixmap()); + + int lastPart = agendaOption->itemGeometries.size() - 1; + int paintedSubItems = 0; + + for (int iLoop = 0; iLoop < agendaOption->itemGeometries.size();iLoop++) + { + if ((*agendaOption->itemPaintCache)[iLoop].width() != agendaOption->itemGeometries[iLoop].width() + || (*agendaOption->itemPaintCache)[iLoop].height() != agendaOption->itemGeometries[iLoop].height()) + { + //If we enter this codepath we have to rebuild the pixmap cache + //so first we create a empty pixmap + (*agendaOption->itemPaintCache)[iLoop] = QPixmap(agendaOption->itemGeometries[iLoop].size()); + (*agendaOption->itemPaintCache)[iLoop].fill(Qt::transparent); + + QPainter cachePainter(&(*agendaOption->itemPaintCache)[iLoop]); + QRect rect = QRect(QPoint(0, 0), agendaOption->itemGeometries[iLoop].size()); + + //what kind of itempart do we need to paint? + ItemPart part = iLoop == 0 ? Top : (iLoop == lastPart ? Bottom : Middle); + + //if the item has only one part + if (lastPart == 0) + part = Single; + + //paint the item body + cachePainter.save(); + paintItemBody(&cachePainter, rect, *agendaOption, part, index); + cachePainter.restore(); + + int remainingHeight = rect.height(); + + //paint item header + if (iLoop == 0 && agendaOption->itemHeaderHeight > 0 && agendaOption->itemHeaderHeight < remainingHeight) + { + QRect headerRect(0, 0, rect.width(), agendaOption->itemHeaderHeight); + paintItemHeader(&cachePainter, headerRect, *agendaOption, index); + remainingHeight -= agendaOption->itemHeaderHeight; + } + + //paint subitems if there are any + int subItems = index.model()->rowCount(index); + for (int items = paintedSubItems; items < subItems; items++) + { + QModelIndex currSubItem = index.model()->index(items, 0, index); + QSize size = sizeHint(option, currSubItem); + + if (currSubItem.isValid()){ + paintSubItem(&cachePainter, QRect(), *agendaOption, currSubItem); + } + + paintedSubItems++; + } + + cachePainter.end(); + + } + currRect = agendaOption->itemGeometries[iLoop]; + currRect.translate(agendaOption->translate); + painter->drawPixmap(currRect, (*agendaOption->itemPaintCache)[iLoop]); + } + painter->restore(); +} + +/*! + * \brief paints the items body reimplement this to paint a custom body + * \a QPainter *painter the initialized painter + * \a const QRect rect the ItemPart rect + * \a const QxtStyleOptionScheduleViewItem & option + * \a const ItemPart part this indicates what part of the item gets painted, remember items can be splitted in parts + * \a const QModelIndex &index the items model index + */ +void QxtScheduleItemDelegate::paintItemBody(QPainter *painter, const QRect rect , const QxtStyleOptionScheduleViewItem & option , const ItemPart part, const QModelIndex & index) const +{ + int iCurrRoundTop, iCurrRoundBottom; + iCurrRoundTop = iCurrRoundBottom = 0; + + QColor fillColor = index.data(Qt::BackgroundRole).value(); + fillColor.setAlpha(120); + QColor outLineColor = index.data(Qt::ForegroundRole).value(); + int outLineWidth = index.data(Qxt::OutlineRole).toInt(); + if (!outLineWidth) outLineWidth = 2; + QVariant vfont = index.data(Qt::FontRole); + painter->setFont(vfont.value()); + QPen pen; + pen.setColor(outLineColor); + pen.setWidth(outLineWidth); + painter->setBrush(fillColor); + painter->setPen(pen); + painter->setRenderHint(QPainter::Antialiasing); + + if (part == Top || part == Single) + iCurrRoundTop = option.roundCornersRadius; + if (part == Bottom || part == Single) + iCurrRoundBottom = option.roundCornersRadius; + + QPainterPath cachePath; + QRect cacheRect = QRect(QPoint(1, 1), rect.size() - QSize(1, 1)); + + createPainterPath(cachePath, cacheRect, iCurrRoundTop, iCurrRoundBottom); + painter->drawPath(cachePath); + + //QString text = startTime.toString("hh:mm") + ' ' + endTime.toString("hh:mm"); + QString text = index.data(Qt::DisplayRole).toString(); + //QFontMetrics metr(font); + //text = metr.elidedText(text, Qt::ElideRight, rect.width()); + QRect textRect = QRect(QPoint(3, 3), rect.size() - QSize(3, 3)); + painter->drawText(textRect, Qt::AlignLeft | Qt::TextWordWrap, text); +} + +/*! + * \brief paints the items header reimplement this to paint a custom header + * \a QPainter *painter the initialized painter + * \a const QRect rect the header rect + * \a const QxtStyleOptionScheduleViewItem & option + * \a const QModelIndex &index the items model index + */ +void QxtScheduleItemDelegate::paintItemHeader(QPainter *painter, const QRect rect , const QxtStyleOptionScheduleViewItem & option, const QModelIndex &index) const +{ + bool converted = false; + int startUnixTime = index.data(Qxt::ItemStartTimeRole).toInt(&converted); + if (!converted) + return; + + int duration = index.data(Qxt::ItemDurationRole).toInt(&converted); + if (!converted) + return; + + QDateTime startTime = QDateTime::fromTime_t(startUnixTime); + QDateTime endTime = QDateTime::fromTime_t(startUnixTime + duration); + + if (!startTime.isValid() || !endTime.isValid()) + return; + + QFont font; + QVariant vfont = index.data(Qt::FontRole); + + if (vfont.isValid()) + font = vfont.value(); + else + font = option.font; + + //QString text = startTime.toString("hh:mm") + ' ' + endTime.toString("hh:mm"); + //QString text = index.data(Qt::DisplayRole).toString(); + //QFontMetrics metr(font); + //text = metr.elidedText(text, Qt::ElideRight, rect.width()); + //painter->drawText(rect, Qt::AlignLeft, text); +} + +/*! + * \brief paints a subitem, if you want custom subitem painting reimplement this member function + * \a QPainter *painter the initialized painter + * \a const QRect rect the subitem rect + * \a const QxtStyleOptionScheduleViewItem & option + * \a const QModelIndex &index the items model index + */ +void QxtScheduleItemDelegate::paintSubItem(QPainter * /*painter*/, const QRect /*rect*/, const QxtStyleOptionScheduleViewItem & /*option*/, const QModelIndex & index) const +{ +} + +/*! + * \brief returns the sizeHint for subitems. + */ +QSize QxtScheduleItemDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const +{ + //we return the size only for subitems and only the height + + if (index.parent().isValid()) + { + QSize size = index.data(Qt::SizeHintRole).toSize(); + + if (!size.isValid()) + { + QFont font; + QVariant vfont = index.data(Qt::FontRole); + + if (vfont.isValid()) + font = vfont.value(); + else + font = option.font; + + int height = 0; + QFontMetrics metr(font); + height = metr.height() + 2; + + return QSize(0, height); + } + } + return QSize(); +} + +void QxtScheduleItemDelegate::createPainterPath(QPainterPath & emptyPath, const QRect & fullItemRect, const int iRoundTop, const int iRoundBottom) const +{ + emptyPath = QPainterPath(); + bool bRoundTop = iRoundTop > 0; + bool bRountBottom = iRoundBottom > 0; + + if (bRoundTop) + { + emptyPath.moveTo(fullItemRect.topLeft() + QPoint(0, iRoundTop)); + emptyPath.quadTo(fullItemRect.topLeft(), fullItemRect.topLeft() + QPoint(iRoundTop, 0)); + } + else + emptyPath.moveTo(fullItemRect.topLeft()); + + emptyPath.lineTo(fullItemRect.topRight() - QPoint(iRoundTop, 0)); + + if (bRoundTop) + emptyPath.quadTo(fullItemRect.topRight(), fullItemRect.topRight() + QPoint(0, iRoundTop)); + + emptyPath.lineTo(fullItemRect.bottomRight() - QPoint(0, iRoundBottom)); + + if (bRountBottom) + emptyPath.quadTo(fullItemRect.bottomRight(), fullItemRect.bottomRight() - QPoint(iRoundBottom, 0)); + + emptyPath.lineTo(fullItemRect.bottomLeft() + QPoint(iRoundBottom, 0)); + + if (bRountBottom) + emptyPath.quadTo(fullItemRect.bottomLeft(), fullItemRect.bottomLeft() - QPoint(0, iRoundBottom)); + + emptyPath.closeSubpath(); +} + + diff --git a/qxt/src/qxtscheduleitemdelegate.h b/qxt/src/qxtscheduleitemdelegate.h new file mode 100644 index 000000000..298459933 --- /dev/null +++ b/qxt/src/qxtscheduleitemdelegate.h @@ -0,0 +1,65 @@ +/**************************************************************************** + ** + ** Copyright (C) Qxt Foundation. Some rights reserved. + ** + ** This file is part of the QxtGui module of the Qxt library. + ** + ** This library is free software; you can redistribute it and/or modify it + ** under the terms of the Common Public License, version 1.0, as published + ** by IBM, and/or under the terms of the GNU Lesser General Public License, + ** version 2.1, as published by the Free Software Foundation. + ** + ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY + ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY + ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR + ** FITNESS FOR A PARTICULAR PURPOSE. + ** + ** You should have received a copy of the CPL and the LGPL along with this + ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files + ** included with the source distribution for more information. + ** If you did not receive a copy of the licenses, contact the Qxt Foundation. + ** + ** + ** + ****************************************************************************/ +#ifndef QXTSCHEDULEITEMDELEGATE_H_INCLUDED +#define QXTSCHEDULEITEMDELEGATE_H_INCLUDED + +#include +#include +#include "qxtstyleoptionscheduleviewitem.h" +#include "qxtglobal.h" + +QT_FORWARD_DECLARE_CLASS(QPainter) + +/*! + @author Benjamin Zeller +*/ +class QXT_GUI_EXPORT QxtScheduleItemDelegate : public QAbstractItemDelegate +{ + Q_OBJECT +public: + + enum ItemPart + { + Top, + Middle, + Bottom, + Single + + }; + + QxtScheduleItemDelegate(QObject *parent = 0); + ~QxtScheduleItemDelegate(); + + virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; + virtual void paintItemBody(QPainter *painter, const QRect rect , const QxtStyleOptionScheduleViewItem & option , const ItemPart part, const QModelIndex & index) const; + virtual void paintItemHeader(QPainter *painter, const QRect rect , const QxtStyleOptionScheduleViewItem & option , const QModelIndex & index) const; + virtual void paintSubItem(QPainter *painter, const QRect rect , const QxtStyleOptionScheduleViewItem & option , const QModelIndex & index) const; + virtual QSize sizeHint(const QStyleOptionViewItem & option , const QModelIndex & index) const; + virtual void createPainterPath(QPainterPath &emptyPath, const QRect &fullItemRect , const int iRoundTop, const int iRoundBottom) const; + + +}; + +#endif // QXTSCHEDULEITEMDELEGATE_H_INCLUDED diff --git a/qxt/src/qxtscheduleview.cpp b/qxt/src/qxtscheduleview.cpp new file mode 100644 index 000000000..8f5a9c338 --- /dev/null +++ b/qxt/src/qxtscheduleview.cpp @@ -0,0 +1,952 @@ +/**************************************************************************** + ** + ** Copyright (C) Qxt Foundation. Some rights reserved. + ** + ** This file is part of the QxtGui module of the Qxt library. + ** + ** This library is free software; you can redistribute it and/or modify it + ** under the terms of the Common Public License, version 1.0, as published + ** by IBM, and/or under the terms of the GNU Lesser General Public License, + ** version 2.1, as published by the Free Software Foundation. + ** + ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY + ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY + ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR + ** FITNESS FOR A PARTICULAR PURPOSE. + ** + ** You should have received a copy of the CPL and the LGPL along with this + ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files + ** included with the source distribution for more information. + ** If you did not receive a copy of the licenses, contact the Qxt Foundation. + ** + ** + ** + ****************************************************************************/ + +#include "qxtscheduleview.h" +#include "qxtscheduleview_p.h" +#include "qxtscheduleheaderwidget.h" +#include "qxtscheduleviewheadermodel_p.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/*! + \class QxtScheduleView QxtScheduleView + \inmodule QxtGui + \brief The QxtScheduleView class provides an iCal like view to plan events + + QxtScheduleView is a item based View,inspired by iCal, that makes it possible to visualize event planning. + + \raw HTML + It's time based and can show the events in different modes: +
    +
  • DayMode : Every column in the view shows one day
  • +
  • HourMode : Every column in the view shows one hour
  • +
  • MinuteMode : Every column in the view shows one minute
  • +
+ In addition you can adjust how much time every cell represents in the view. The default value is 900 seconds + or 15 minutes and DayMode. + \endraw + + + \image qxtscheduleview.png QxtScheduleView + +*/ + +QxtScheduleView::QxtScheduleView(QWidget *parent) + : QAbstractScrollArea(parent) +{ + QXT_INIT_PRIVATE(QxtScheduleView); + + /*standart values are 15 minutes per cell and 69 rows == 1 Day*/ + qxt_d().m_currentZoomDepth = 15 * 60; + qxt_d().m_currentViewMode = DayView; + qxt_d().m_startUnixTime = QDateTime(QDate::currentDate(),QTime(0, 0, 0)).toTime_t(); + qxt_d().m_endUnixTime = QDateTime(QDate::currentDate().addDays(6),QTime(23, 59, 59)).toTime_t(); + qxt_d().delegate = qxt_d().defaultDelegate = new QxtScheduleItemDelegate(this); + +#if 0 + qxt_d().m_vHeader = new QxtScheduleHeaderWidget(Qt::Vertical, this); + connect(qxt_d().m_vHeader, SIGNAL(geometriesChanged()), this, SLOT(updateGeometries())); + qxt_d().m_vHeader->hide(); + + qxt_d().m_hHeader = new QxtScheduleHeaderWidget(Qt::Horizontal, this); + connect(qxt_d().m_hHeader, SIGNAL(geometriesChanged()), this, SLOT(updateGeometries())); + qxt_d().m_hHeader->hide(); +#else + //init will take care of initializing headers + qxt_d().m_vHeader = 0; + qxt_d().m_hHeader = 0; +#endif + +} + +/*! + * returns the vertial header + *\note can be NULL if the view has not called init() already (FIXME) + */ +QHeaderView* QxtScheduleView::verticalHeader ( ) const +{ + return qxt_d().m_vHeader; +} + +/** + * returns the horizontal header + *\note can be NULL if the view has not called init() already (FIXME) + */ +QHeaderView* QxtScheduleView::horizontalHeader ( ) const +{ + return qxt_d().m_hHeader; +} + +/** + * sets the model for QxtScheduleView + * + * \a model + */ +void QxtScheduleView::setModel(QAbstractItemModel *model) +{ + if (qxt_d().m_Model) + { + /*delete all cached items*/ + qDeleteAll(qxt_d().m_Items.begin(), qxt_d().m_Items.end()); + qxt_d().m_Items.clear(); + + /*disconnect all signals*/ + disconnect(qxt_d().m_Model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(dataChanged(const QModelIndex &, const QModelIndex &))); + disconnect(qxt_d().m_Model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)), this, SLOT(rowsAboutToBeInserted(const QModelIndex &, int , int))); + disconnect(qxt_d().m_Model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SLOT(rowsInserted(const QModelIndex &, int , int))); + disconnect(qxt_d().m_Model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)), this, SLOT(rowsAboutToBeRemoved(const QModelIndex &, int , int))); + disconnect(qxt_d().m_Model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this, SLOT(rowsRemoved(const QModelIndex &, int , int))); + + /*don't delete the model maybe someone else will use it*/ + qxt_d().m_Model = 0; + } + + if (model != 0) + { + /*initialize the new model*/ + qxt_d().m_Model = model; + connect(model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(dataChanged(const QModelIndex &, const QModelIndex &))); + connect(model, SIGNAL(rowsAboutToBeInserted(const QModelIndex &, int, int)), this, SLOT(rowsAboutToBeInserted(const QModelIndex &, int , int))); + connect(model, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SLOT(rowsInserted(const QModelIndex &, int , int))); + connect(model, SIGNAL(rowsAboutToBeRemoved(const QModelIndex &, int, int)), this, SLOT(rowsAboutToBeRemoved(const QModelIndex &, int , int))); + connect(model, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this, SLOT(rowsRemoved(const QModelIndex &, int , int))); + } + qxt_d().init(); +} + +QAbstractItemModel * QxtScheduleView::model() const +{ + return qxt_d().m_Model; +} + +/*! + * changes the current ViewMode + * The QxtScheduleView supports some different viewmodes. A viewmode defines how much time a column holds. + * It is also possible to define custom viewmodes. To do that you have to set the currentView mode to Custom and + * reimplement timePerColumn + * + * \a QxtScheduleView::ViewMode mode the new ViewMode + * + * \sa timePerColumn() + * \sa viewMode() + */ +void QxtScheduleView::setViewMode(const QxtScheduleView::ViewMode mode) +{ + qxt_d().m_currentViewMode = mode; + + //this will calculate the correct alignment + //\BUG this may not work because the currentZoomDepth may not fit into the new viewMode + setCurrentZoomDepth(qxt_d().m_currentZoomDepth); +} + +/*! + *\\esc returns the current used delegate + */ +QxtScheduleItemDelegate* QxtScheduleView::delegate () const +{ + return qxt_d().delegate; +} + +/*! + *Sets the item delegate for this view and its model to delegate. This is useful if you want complete control over the editing and display of items. +*Any existing delegate will be removed, but not deleted. QxtScheduleView does not take ownership of delegate. +*Passing a 0 pointer will restore the view to use the default delegate. +*\Warning You should not share the same instance of a delegate between views. Doing so can cause incorrect or unintuitive behavior. + */ +void QxtScheduleView::setItemDelegate (QxtScheduleItemDelegate * delegate) +{ + if(!delegate) + qxt_d().delegate = qxt_d().defaultDelegate; + else + qxt_d().delegate = delegate; + + //the delegate changed repaint everything + viewport()->update(); +} + +/*! + * returns the current ViewMode + * + * Returns QxtScheduleView::ViewMode + * \sa setViewMode() + */ +QxtScheduleView::ViewMode QxtScheduleView::viewMode() const +{ + return (ViewMode)qxt_d().m_currentViewMode; +} + +/*! + * changes the current Zoom step width + * Changes the current Zoom step width. Zooming in QxtScheduleView means to change the amount + * of time one cell holds. For example 5 Minutes. The zoom step width defines how many time + * is added / removed from the cell when zooming the view. + * + * \a int zoomWidth the new zoom step width + * \a Qxt::Timeunit unit the unit of the new step width (Minutes , Seconds , Hours) + * + * \sa zoomIn() zoomOut() setCurrentZoomDepth() + */ +void QxtScheduleView::setZoomStepWidth(const int zoomWidth , const Qxt::Timeunit unit) +{ + switch (unit) + { + case Qxt::Second: + { + qxt_d().m_zoomStepWidth = zoomWidth; + } + break; + case Qxt::Minute: + { + qxt_d().m_zoomStepWidth = zoomWidth * 60; + } + break; + case Qxt::Hour: + { + qxt_d().m_zoomStepWidth = zoomWidth * 60 * 60; + } + break; + default: + qWarning() << "This Timeunit is not implemented yet you can use Second,Minute,Hour using standart 15 minutes"; + qxt_d().m_zoomStepWidth = 900; + break; + } +} + +/*! + * changes the current zoom depth + * The current zoom depth in QxtScheduleView defines how many time one cell holds in the view. + * If the new depth does not fit in the view the next possible value is used. If no possible value can be found + * nothing changes. + * Normally this is used only to initialize the view, later you want to use zoomIn and zoomOut + * + * \a int depth + * \a Qxt::Timeunit unit + * + * \sa zoomIn() zoomOut() setCurrentZoomDepth() + */ +void QxtScheduleView::setCurrentZoomDepth(const int depth , const Qxt::Timeunit unit) +{ + int newZoomDepth = 900; + + //a zoom depth of 0 is invalid + if (depth == 0) + return; + + switch (unit) + { + case Qxt::Second: + { + newZoomDepth = depth; + } + break; + case Qxt::Minute: + { + newZoomDepth = depth * 60; + } + break; + case Qxt::Hour: + { + newZoomDepth = depth * 60 * 60; + } + break; + default: + qWarning() << "This Timeunit is not implemented yet you can use Second,Minute,Hour using standart 15 minutes"; + break; + } + + //now we have to align the currentZoomDepth to the viewMode + int timePerCol = timePerColumn(); + + newZoomDepth = newZoomDepth > timePerCol ? timePerCol : newZoomDepth; + newZoomDepth = newZoomDepth <= 0 ? 1 : newZoomDepth; + + while (timePerCol % newZoomDepth) + { + if (depth > qxt_d().m_currentZoomDepth) + { + newZoomDepth++; + if (newZoomDepth >= timePerCol) + return; + } + + else + { + newZoomDepth--; + if (newZoomDepth <= 1) + return; + } + } + + //qDebug() << "Zoomed, old zoom depth: " << qxt_d().m_currentZoomDepth << " new zoom depth: " << newZoomDepth; + + qxt_d().m_currentZoomDepth = newZoomDepth; + emit this->newZoomDepth(newZoomDepth); + + /*reinit the view*/ + if (model()) + { + updateGeometries(); + qxt_d().reloadItemsFromModel(); + } +} + +/*! + * returns the current zoom depth + */ +int QxtScheduleView::currentZoomDepth(const Qxt::Timeunit unit) +{ + switch (unit) + { + case Qxt::Second: + { + return qxt_d().m_currentZoomDepth; + } + break; + case Qxt::Minute: + { + return qxt_d().m_currentZoomDepth / 60; + } + break; + case Qxt::Hour: + { + return qxt_d().m_currentZoomDepth / 60 / 60; + } + break; + default: + qWarning() << "This Timeunit is not implemented yet you can use Second,Minute,Hour returning seconds"; + return qxt_d().m_currentZoomDepth; + break; + } +} + +/*! + * zooms one step in + * + * \sa zoomOut() setCurrentZoomDepth() setZoomStepWidth() + */ +void QxtScheduleView::zoomIn() +{ + setCurrentZoomDepth(qxt_d().m_currentZoomDepth - qxt_d().m_zoomStepWidth); +} + +/*! + * zooms one step out + * + * \sa zoomIn() setCurrentZoomDepth() setZoomStepWidth() + */ +void QxtScheduleView::zoomOut() +{ + setCurrentZoomDepth(qxt_d().m_currentZoomDepth + qxt_d().m_zoomStepWidth); +} + +void QxtScheduleView::paintEvent(QPaintEvent * /*event*/) +{ + if (model()) + { + /*paint the grid*/ + + int iNumRows = qxt_d().m_vHeader->count(); + //qDebug() << "Painting rows " << iNumRows; + + int xRowEnd = qxt_d().m_hHeader->sectionViewportPosition(qxt_d().m_hHeader->count() - 1) + qxt_d().m_hHeader->sectionSize(qxt_d().m_hHeader->count() - 1); + QPainter painter(viewport()); + + + painter.save(); + painter.setPen(QColor(220, 220, 220)); + + bool thinLine; + thinLine = false; + for (int iLoop = 0; iLoop < iNumRows; iLoop += 2) + { + painter.drawLine(0 , qxt_d().m_vHeader->sectionViewportPosition(iLoop), xRowEnd, qxt_d().m_vHeader->sectionViewportPosition(iLoop)); + } + + int iNumCols = qxt_d().m_hHeader->count(); + int iYColEnd = qxt_d().m_vHeader->sectionViewportPosition(qxt_d().m_vHeader->count() - 1) + qxt_d().m_vHeader->sectionSize(qxt_d().m_vHeader->count() - 1); + + for (int iLoop = 0; iLoop < iNumCols ; iLoop++) + { + painter.drawLine(qxt_d().m_hHeader->sectionViewportPosition(iLoop), 0, qxt_d().m_hHeader->sectionViewportPosition(iLoop), iYColEnd); + } + + painter.restore(); + + QListIterator itemIterator(qxt_d().m_Items); + while (itemIterator.hasNext()) + { + QxtScheduleInternalItem * currItem = itemIterator.next(); + QxtStyleOptionScheduleViewItem style; + + //\BUG use the correct section here or find a way to forbit section resizing + style.roundCornersRadius = qxt_d().m_vHeader->sectionSize(1) / 2; + style.itemHeaderHeight = qxt_d().m_vHeader->sectionSize(1); + style.maxSubItemHeight = qxt_d().m_vHeader->sectionSize(1); + + if (currItem->isDirty) + currItem->m_cachedParts.clear(); + + style.itemGeometries = currItem->m_geometries; + style.itemPaintCache = &currItem->m_cachedParts; + style.translate = QPoint(-qxt_d().m_hHeader->offset(), -qxt_d().m_vHeader->offset()); + painter.save(); + qxt_d().delegate->paint(&painter, style, currItem->modelIndex()); + painter.restore(); + currItem->setDirty(false); + } + + painter.end(); + } +} + +void QxtScheduleView::updateGeometries() +{ + //check if we are already initialized + if(!qxt_d().m_Model || !qxt_d().m_vHeader || !qxt_d().m_hHeader) + return; + + this->setViewportMargins(qxt_d().m_vHeader->sizeHint().width() + 1, qxt_d().m_hHeader->sizeHint().height() + 1, 0, 0); + + + verticalScrollBar()->setRange(0, qxt_d().m_vHeader->count()*qxt_d().m_vHeader->defaultSectionSize() - viewport()->height()); + verticalScrollBar()->setSingleStep(qxt_d().m_vHeader->defaultSectionSize()); + verticalScrollBar()->setPageStep(qxt_d().m_vHeader->defaultSectionSize()); + + int left = 2; + int top = qxt_d().m_hHeader->sizeHint().height() + 2; + int width = qxt_d().m_vHeader->sizeHint().width(); + int height = viewport()->height(); + qxt_d().m_vHeader->setGeometry(left, top, width, height); + + left = left + width; + top = 1; + width = viewport()->width(); + height = qxt_d().m_hHeader->sizeHint().height(); + + qxt_d().m_hHeader->setGeometry(left, top, width, height); + qxt_d().m_hHeader->setDefaultSectionSize(viewport()->width() / 7); + + for (int iLoop = 0; iLoop < qxt_d().m_hHeader->count(); iLoop++) + qxt_d().m_hHeader->resizeSection(iLoop, viewport()->width() / 7); + qxt_d().m_hHeader->setResizeMode(QHeaderView::Fixed); + + + horizontalScrollBar()->setRange(0, (qxt_d().m_hHeader->count() * qxt_d().m_hHeader->defaultSectionSize() - viewport()->width())); + horizontalScrollBar()->setSingleStep(qxt_d().m_hHeader->defaultSectionSize()); + horizontalScrollBar()->setPageStep(qxt_d().m_hHeader->defaultSectionSize()); + + + qxt_d().m_vHeader->show(); + qxt_d().m_hHeader->show(); + qxt_d().handleItemConcurrency(0, this->rows() * this->cols() - 1); + viewport()->update(); +} + +void QxtScheduleView::scrollContentsBy(int dx, int dy) +{ + qxt_d().m_vHeader->setOffset(qxt_d().m_vHeader->offset() - dy); + qxt_d().m_hHeader->setOffset(qxt_d().m_hHeader->offset() - dx); + QAbstractScrollArea::scrollContentsBy(dx, dy); +} + +void QxtScheduleView::mouseMoveEvent(QMouseEvent * e) +{ + if (qxt_d().m_selectedItem) + { + int currentMousePosTableOffset = qxt_d().pointToOffset((e->pos())); + + if (currentMousePosTableOffset != qxt_d().m_lastMousePosOffset) + { + if (currentMousePosTableOffset >= 0) + { + /*i cannot use the model data here because all changes are committed to the model only when the move ends*/ + int startTableOffset = qxt_d().m_selectedItem->visualStartTableOffset(); + int endTableOffset = -1; + + /*i simply use the shape to check if we have a move or a resize. Because we enter this codepath the shape gets not changed*/ + if (this->viewport()->cursor().shape() == Qt::SizeVerCursor) + { + QVector geo = qxt_d().m_selectedItem->geometry(); + QRect rect = geo[geo.size()-1]; + endTableOffset = currentMousePosTableOffset; + } + else + { + /*well the duration is the same for a move*/ + //qint32 difference = qxt_d().rowsTo(qxt_d().m_lastMousePosIndex,currentMousePos); // tableCellToUnixTime(currentMousePos) - tableCellToUnixTime(this->m_lastMousePosIndex); + int difference = currentMousePosTableOffset - qxt_d().m_lastMousePosOffset; + //qDebug()<<"Difference Rows: "<rows() - 1; + } + if (startTableOffset >= 0 && endTableOffset >= startTableOffset && endTableOffset < (rows()*cols())) + { + QVector< QRect > newGeometry = qxt_d().calculateRangeGeometries(startTableOffset, endTableOffset); + + int oldStartOffset = qxt_d().m_selectedItem->visualStartTableOffset(); + int newStartOffset = qxt_d().m_selectedItem->visualEndTableOffset(); + + qxt_d().m_selectedItem->setGeometry(newGeometry); + qxt_d().m_selectedItem->setDirty(); + qxt_d().m_lastMousePosOffset = currentMousePosTableOffset; +#if 1 + if (newGeometry.size() > 0) + { + int start = qxt_d().m_selectedItem->visualStartTableOffset(); + int end = qxt_d().m_selectedItem->visualEndTableOffset(); + qxt_d().handleItemConcurrency(oldStartOffset, newStartOffset); + qxt_d().handleItemConcurrency(start, end); + } +#endif + + } + } + } + return; + } + else + { + /*change the cursor to show the resize arrow*/ + QPoint translatedPos = mapFromViewport(e->pos()); + QxtScheduleInternalItem * it = qxt_d().internalItemAt(translatedPos); + if (it) + { + QVector geo = it->geometry(); + QRect rect = geo[geo.size()-1]; + if (rect.contains(translatedPos) && (translatedPos.y() >= rect.bottom() - 5 && translatedPos.y() <= rect.bottom())) + { + this->viewport()->setCursor(Qt::SizeVerCursor); + return; + } + } + + if (this->viewport()->cursor().shape() != Qt::ArrowCursor) + this->viewport()->setCursor(Qt::ArrowCursor); + } +} + +void QxtScheduleView::mouseDoubleClickEvent ( QMouseEvent * e ) +{ + qxt_d().m_currentItem = qxt_d().internalItemAt(mapFromViewport(e->pos())); + if (qxt_d().m_currentItem) + { + emit indexDoubleClicked(qxt_d().m_currentItem->modelIndex()); + } +} + +void QxtScheduleView::mousePressEvent(QMouseEvent * e) +{ + qxt_d().m_currentItem = qxt_d().internalItemAt(mapFromViewport(e->pos())); + + if (qxt_d().m_currentItem) + { + emit indexSelected(qxt_d().m_currentItem->modelIndex()); + } + else + emit indexSelected(QModelIndex()); + + if (e->button() == Qt::RightButton) + { + if (qxt_d().m_currentItem) + emit contextMenuRequested(qxt_d().m_currentItem->modelIndex()); + } + else + { + qxt_d().m_lastMousePosOffset = qxt_d().pointToOffset(e->pos()); + if (qxt_d().m_lastMousePosOffset >= 0) + { + qxt_d().m_selectedItem = qxt_d().m_currentItem; + + + if (qxt_d().m_selectedItem) + { + //qDebug() << "Selected Item:" << qxt_d().m_selectedItem->m_iModelRow; + raiseItem(qxt_d().m_selectedItem->modelIndex()); + qxt_d().m_selectedItem->startMove(); + qxt_d().scrollTimer.start(100); + } + else + qxt_d().m_lastMousePosOffset = -1; + } + } + + +} + +void QxtScheduleView::mouseReleaseEvent(QMouseEvent * /*e*/) +{ + qxt_d().scrollTimer.stop(); + if (qxt_d().m_selectedItem) + { + int oldStartTableOffset = qxt_d().m_selectedItem->startTableOffset(); + int oldEndTableOffset = oldStartTableOffset + qxt_d().m_selectedItem->rows() - 1 ; + + + QVector geo = qxt_d().m_selectedItem->geometry(); + if (geo.size() == 0) return; //XXX crash! + QRect rect = geo[geo.size()-1]; + + int newStartTableOffset = qxt_d().m_selectedItem->visualStartTableOffset(); + int newEndTableOffset = qxt_d().m_selectedItem->visualEndTableOffset(); + + qxt_d().m_selectedItem->stopMove(); + + QVariant newStartUnixTime; + QVariant newDuration; + + newStartUnixTime = qxt_d().offsetToUnixTime(newStartTableOffset); + model()->setData(qxt_d().m_selectedItem->modelIndex(), newStartUnixTime, Qxt::ItemStartTimeRole); + newDuration = qxt_d().offsetToUnixTime(newEndTableOffset, true) - newStartUnixTime.toInt(); + model()->setData(qxt_d().m_selectedItem->modelIndex(), newDuration, Qxt::ItemDurationRole); + + qxt_d().m_selectedItem = NULL; + qxt_d().m_lastMousePosOffset = -1; + + /*only call for the old geometry the dataChanged slot will call it for the new position*/ + qxt_d().handleItemConcurrency(oldStartTableOffset, oldEndTableOffset); + //qxt_d().handleItemConcurrency(newStartIndex,newEndIndex); + + } + // this forces a repaint! + setCurrentZoomDepth(qxt_d().m_currentZoomDepth); +} + +void QxtScheduleView::wheelEvent(QWheelEvent * e) +{ + /*time scrolling when pressing ctrl while using the mouse wheel*/ + if (e->modifiers() & Qt::ControlModifier) + { + if (e->delta() < 0) + zoomOut(); + else + zoomIn(); + + } + else + QAbstractScrollArea::wheelEvent(e); +} + +/*! + * returns the current row count of the view + */ +int QxtScheduleView::rows() const +{ + if (!model()) + return 0; + + int timePerCol = timePerColumn(); + + Q_ASSERT(timePerCol % qxt_d().m_currentZoomDepth == 0); + int iNeededRows = timePerCol / qxt_d().m_currentZoomDepth; + + return iNeededRows; + +} + +/*! + * returns the current column count of the view + */ +int QxtScheduleView::cols() const +{ + if (!model()) + return 0; + + int cols = 0; + int timeToShow = qxt_d().m_endUnixTime - qxt_d().m_startUnixTime + 1 ; + int timePerCol = timePerColumn(); + + //Q_ASSERT(timeToShow % timePerCol == 0); + //cols = (timeToShow / timePerCol); + + //return cols; + return 7; +} + +/*! + * reimplement this to support custom view modes + *Returns the time per column in seconds + */ +int QxtScheduleView::timePerColumn() const +{ + int timePerColumn = 0; + + switch (qxt_d().m_currentViewMode) + { + case DayView: + timePerColumn = 24 * 60 * 60; + break; + case HourView: + timePerColumn = 60 * 60; + break; + case MinuteView: + timePerColumn = 60; + break; + default: + Q_ASSERT(false); + } + + return timePerColumn; +} + +/*! + * reimplement this to support custom view modes + * This function has to adjust the given start and end time to the current view mode: + * For example, the DayMode always adjust to time 0:00:00am for startTime and 11:59:59pm for endTime + */ +void QxtScheduleView::adjustRangeToViewMode(QDateTime *startTime, QDateTime *endTime) const +{ + switch (qxt_d().m_currentViewMode) + { + case DayView: + startTime->setTime(QTime(0, 0)); + endTime ->setTime(QTime(23, 59, 59)); + break; + case HourView: + startTime->setTime(QTime(startTime->time().hour(), 0)); + endTime ->setTime(QTime(endTime->time().hour(), 59, 59)); + break; + case MinuteView: + startTime->setTime(QTime(startTime->time().hour(), startTime->time().minute(), 0)); + endTime ->setTime(QTime(endTime->time().hour(), endTime->time().minute(), 59)); + break; + default: + Q_ASSERT(false); + } +} + +QPoint QxtScheduleView::mapFromViewport(const QPoint & point) const +{ + return point + QPoint(qxt_d().m_hHeader->offset(), qxt_d().m_vHeader->offset()); +} + +QPoint QxtScheduleView::mapToViewport(const QPoint & point) const +{ + return point - QPoint(qxt_d().m_hHeader->offset(), qxt_d().m_vHeader->offset()); +} + +/*! + * raises the item belonging to index + */ +void QxtScheduleView::raiseItem(const QModelIndex &index) +{ + QxtScheduleInternalItem *item = qxt_d().itemForModelIndex(index); + if (item) + { + int iItemIndex = -1; + if ((iItemIndex = qxt_d().m_Items.indexOf(item)) >= 0) + { + qxt_d().m_Items.takeAt(iItemIndex); + qxt_d().m_Items.append(item); + viewport()->update(); + } + } +} + +void QxtScheduleView::dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight) +{ + for (int iLoop = topLeft.row(); iLoop <= bottomRight.row();iLoop++) + { + QModelIndex index = model()->index(iLoop, 0); + QxtScheduleInternalItem * item = qxt_d().itemForModelIndex(index); + if (item) + { + int startOffset = item->startTableOffset(); + int endIndex = item->startTableOffset() + item->rows() - 1; + + if (item->m_geometries.count() > 0) + { + int oldStartOffset = qxt_d().pointToOffset(mapToViewport(item->m_geometries[0].topLeft())); + int oldEndOffset = qxt_d().pointToOffset(mapToViewport(item->m_geometries[item->m_geometries.size()-1].bottomRight())); + qxt_d().handleItemConcurrency(oldStartOffset, oldEndOffset); + } + + /*that maybe will set a empty geometry thats okay because the item maybe out of bounds of the view */ + item->setGeometry(qxt_d().calculateRangeGeometries(startOffset, endIndex)); + /*force item cache update even if the geometry is the same*/ + item->setDirty(); + + qxt_d().handleItemConcurrency(startOffset, endIndex); + + + viewport()->update(); + } + } +} + +/*! + * triggers the view to relayout the items that are concurrent to index + */ +void QxtScheduleView::handleItemConcurrency(const QModelIndex &index) +{ + QxtScheduleInternalItem *item = qxt_d().itemForModelIndex(index); + if (item) + qxt_d().handleItemConcurrency(item); +} + + +void QxtScheduleView::resizeEvent(QResizeEvent * /* e*/) +{ + updateGeometries(); +} + + +void QxtScheduleView::rowsRemoved(const QModelIndex & parent, int start, int end) +{ + /*! + *\FIXME write correct code here + */ + return qxt_d().reloadItemsFromModel(); + /*for now we care only about toplevel items*/ + if (!parent.isValid()) + { + for (int iLoop = 0; iLoop < qxt_d().m_Items.count();iLoop++) + { + QxtScheduleInternalItem *item = qxt_d().m_Items.at(iLoop); + if (item) + { + if (item->m_iModelRow >= start && item->m_iModelRow <= end) + { + qxt_d().m_Items.takeAt(iLoop); + if (item == qxt_d().m_currentItem) + { + qxt_d().m_currentItem = 0; + emit indexSelected(QModelIndex()); + } + delete item; + continue; + } + if (item->m_iModelRow > end) + { + int iDifference = end - start + 1; + item->m_iModelRow -= iDifference; + } + } + } + } +} + +void QxtScheduleView::rowsInserted(const QModelIndex & parent, int start, int end) +{ + /*for now we care only about toplevel items*/ + if (!parent.isValid()) + { + for (int iLoop = start; iLoop <= end;iLoop++) + { + /*now create the items*/ + QxtScheduleInternalItem *currentItem = new QxtScheduleInternalItem(this, model()->index(iLoop, 0)); + qxt_d().m_Items.append(currentItem); + connect(currentItem, SIGNAL(geometryChanged(QxtScheduleInternalItem*, QVector)), &qxt_d(), SLOT(itemGeometryChanged(QxtScheduleInternalItem * , QVector< QRect >))); + qxt_d().handleItemConcurrency(currentItem); + } + } + + viewport()->update(); +} + +void QxtScheduleView::rowsAboutToBeRemoved(const QModelIndex & parent, int start, int end) +{ + Q_UNUSED(parent); + Q_UNUSED(start); + Q_UNUSED(end); + /*for now we care only about toplevel items*/ + return; +} + +void QxtScheduleView::rowsAboutToBeInserted(const QModelIndex & parent, int start, int end) +{ + /*for now we care only about toplevel items*/ + if (!parent.isValid()) + { + int iDifference = end - start; + for (int iLoop = 0; iLoop < qxt_d().m_Items.count();iLoop++) + { + QxtScheduleInternalItem * item = qxt_d().m_Items[iLoop]; + if (item) + if (item->m_iModelRow >= start && item->m_iModelRow < model()->rowCount()) + item->m_iModelRow += iDifference + 1; + } + } +} + +/*! + * returns the current selected index + */ +QModelIndex QxtScheduleView::currentIndex() +{ + QModelIndex currIndex; + if (qxt_d().m_currentItem) + currIndex = qxt_d().m_currentItem->modelIndex(); + return currIndex; + +} + +/*! + * sets the timerange + * This function will set a Timerange from fromDate 00:00am to toDate 23:59pm + */ +void QxtScheduleView::setDateRange(const QDate & fromDate, const QDate & toDate) +{ + Q_UNUSED(fromDate); + Q_UNUSED(toDate); + + QDateTime startTime = QDateTime(fromDate, QTime(0, 0, 0)); + QDateTime endTime = QDateTime(toDate, QTime(23, 59, 59)); + setTimeRange(startTime, endTime); +} + +/*! + * sets the timerange + * This function will set the passed timerange, but may adjust it to the current viewmode. + * e.g You cannot start at 1:30am in a DayMode, this gets adjusted to 00:00am + */ +void QxtScheduleView::setTimeRange(const QDateTime & fromDateTime, const QDateTime & toDateTime) +{ + QDateTime startTime = fromDateTime; + QDateTime endTime = toDateTime; + + //adjust the timeranges to fit in the view + adjustRangeToViewMode(&startTime, &endTime); + qxt_d().m_startUnixTime = startTime.toTime_t(); + qxt_d().m_endUnixTime = endTime.toTime_t(); +} + +QDateTime QxtScheduleView::getStartTime() const +{ + return QDateTime().fromTime_t(qxt_d().m_startUnixTime); +} + + diff --git a/qxt/src/qxtscheduleview.h b/qxt/src/qxtscheduleview.h new file mode 100644 index 000000000..a3096138c --- /dev/null +++ b/qxt/src/qxtscheduleview.h @@ -0,0 +1,138 @@ +/**************************************************************************** + ** + ** Copyright (C) Qxt Foundation. Some rights reserved. + ** + ** This file is part of the QxtGui module of the Qxt library. + ** + ** This library is free software; you can redistribute it and/or modify it + ** under the terms of the Common Public License, version 1.0, as published + ** by IBM, and/or under the terms of the GNU Lesser General Public License, + ** version 2.1, as published by the Free Software Foundation. + ** + ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY + ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY + ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR + ** FITNESS FOR A PARTICULAR PURPOSE. + ** + ** You should have received a copy of the CPL and the LGPL along with this + ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files + ** included with the source distribution for more information. + ** If you did not receive a copy of the licenses, contact the Qxt Foundation. + ** + ** + ** + ****************************************************************************/ +#ifndef QXTSCHEDULEVIEW_H_INCLUDED +#define QXTSCHEDULEVIEW_H_INCLUDED + +#include +#include +#include +#include +#include +#include + +#include "qxtglobal.h" +#include "qxtnamespace.h" + +QT_FORWARD_DECLARE_CLASS(QAbstractItemModel) +class QxtScheduleItemDelegate; +class QxtScheduleViewPrivate; +class QxtScheduleInternalItem; + +#if 0 +enum +{ + TIMERANGEPERCOL = 86400, + TIMESTEP = 900 +}; +#endif + + +class QXT_GUI_EXPORT QxtScheduleView : public QAbstractScrollArea +{ + Q_OBJECT + + QXT_DECLARE_PRIVATE(QxtScheduleView) + friend class QxtScheduleInternalItem; + friend class QxtScheduleViewHeaderModel; + +public: + + enum ViewMode + { + MinuteView, + HourView, + DayView, + CustomView + }; + + QxtScheduleView(QWidget *parent = 0); + + void setModel(QAbstractItemModel *model); + QAbstractItemModel* model() const; + + void setViewMode(const QxtScheduleView::ViewMode mode); + QxtScheduleView::ViewMode viewMode() const; + + QHeaderView* verticalHeader ( ) const; + QHeaderView* horizontalHeader ( ) const; + + void setDateRange(const QDate & fromDate , const QDate & toDate); + void setTimeRange(const QDateTime &fromDateTime , const QDateTime &toTime); + + QxtScheduleItemDelegate* delegate () const; + void setItemDelegate (QxtScheduleItemDelegate * delegate); + + void setZoomStepWidth(const int zoomWidth , const Qxt::Timeunit unit = Qxt::Second); + void setCurrentZoomDepth(const int depth , const Qxt::Timeunit unit = Qxt::Second); + int currentZoomDepth(const Qxt::Timeunit unit = Qxt::Second); + + QPoint mapFromViewport(const QPoint& point) const; + QPoint mapToViewport(const QPoint& point) const; + + QModelIndex indexAt(const QPoint &pt); + void raiseItem(const QModelIndex &index); + void handleItemConcurrency(const QModelIndex &index); + QModelIndex currentIndex(); + int rows() const; + int cols() const; + + QDateTime getStartTime() const; + +Q_SIGNALS: + void itemMoved(int rows, int cols, QModelIndex index); + void indexSelected(QModelIndex index); + void indexDoubleClicked(QModelIndex index); + void contextMenuRequested(QModelIndex index); + void newZoomDepth(const int newDepthInSeconds); + void viewModeChanged(const int newViewMode); + + +protected: + virtual int timePerColumn() const; + virtual void adjustRangeToViewMode(QDateTime *startTime, QDateTime *endTime) const; + + virtual void scrollContentsBy(int dx, int dy); + virtual void paintEvent(QPaintEvent *e); + virtual void mouseMoveEvent(QMouseEvent * e); + virtual void mousePressEvent(QMouseEvent * e); + virtual void mouseReleaseEvent(QMouseEvent * e); + virtual void mouseDoubleClickEvent ( QMouseEvent * e ); + virtual void resizeEvent(QResizeEvent * e); + virtual void wheelEvent(QWheelEvent * e); + +public Q_SLOTS: + void dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight); + void updateGeometries(); + void zoomIn(); + void zoomOut(); + +protected Q_SLOTS: + virtual void rowsAboutToBeRemoved(const QModelIndex & parent, int start, int end); + virtual void rowsAboutToBeInserted(const QModelIndex & parent, int start, int end); + virtual void rowsRemoved(const QModelIndex & parent, int start, int end); + virtual void rowsInserted(const QModelIndex & parent, int start, int end); +}; + +#endif //QXTSCHEDULEVIEW_H_INCLUDED diff --git a/qxt/src/qxtscheduleview_p.cpp b/qxt/src/qxtscheduleview_p.cpp new file mode 100644 index 000000000..6bfb93c67 --- /dev/null +++ b/qxt/src/qxtscheduleview_p.cpp @@ -0,0 +1,766 @@ +/**************************************************************************** + ** + ** Copyright (C) Qxt Foundation. Some rights reserved. + ** + ** This file is part of the QxtGui module of the Qxt library. + ** + ** This library is free software; you can redistribute it and/or modify it + ** under the terms of the Common Public License, version 1.0, as published + ** by IBM, and/or under the terms of the GNU Lesser General Public License, + ** version 2.1, as published by the Free Software Foundation. + ** + ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY + ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY + ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR + ** FITNESS FOR A PARTICULAR PURPOSE. + ** + ** You should have received a copy of the CPL and the LGPL along with this + ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files + ** included with the source distribution for more information. + ** If you did not receive a copy of the licenses, contact the Qxt Foundation. + ** + ** + ** + ****************************************************************************/ + +#include "qxtscheduleview_p.h" +#include "qxtscheduleview.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qxtscheduleheaderwidget.h" + +/*-------------------------------------start private functions-------------------------------------------------------*/ + +int QxtScheduleViewPrivate::offsetToVisualColumn(const int iOffset) const +{ + if (iOffset >= 0) + return iOffset / qxt_p().rows(); + return -1; +} + +int QxtScheduleViewPrivate::visualIndexToOffset(const int iRow, const int iCol) const +{ + return (iCol* qxt_p().rows()) + iRow; +} + +int QxtScheduleViewPrivate::offsetToVisualRow(const int iOffset) const +{ + if (iOffset >= 0 && qxt_p().model()) + return iOffset % qxt_p().rows(); + return -1; +} + +QVector< QRect > QxtScheduleViewPrivate::calculateRangeGeometries(const int iStartOffset, const int iEndOffset) const +{ + QVector rects; +//qDebug()<<"calc geometries..."<count() - 1, offsetToVisualColumn(iCurrentStartOffset)); + + qint32 iLeft = m_hHeader->sectionPosition(offsetToVisualColumn(iCurrentStartOffset)); + qint32 iTop = m_vHeader->sectionPosition(offsetToVisualRow(iCurrentStartOffset)); + qint32 iBottom = m_vHeader->sectionPosition(offsetToVisualRow(iCurrentEndOffset)) + m_vHeader->sectionSize(offsetToVisualRow(iCurrentEndOffset)); + qint32 iRight = m_hHeader->sectionPosition(offsetToVisualColumn(iCurrentEndOffset)) + m_hHeader->sectionSize(offsetToVisualColumn(iCurrentEndOffset)); + rects.append(QRect(iLeft + 1, iTop + 1, iRight - iLeft - 1, iBottom - iTop - 1)); + + iCurrentStartOffset = visualIndexToOffset(0, offsetToVisualColumn(iCurrentEndOffset) + 1); + + } + while (iCurrentEndOffset < iEndOffset); + +//qDebug()<<"calc geometries... returing:"<visualIndexAt(point.y()); + int iCol = m_hHeader->visualIndexAt(point.x()); + return visualIndexToOffset(iRow, iCol); +} + + + +QxtScheduleInternalItem * QxtScheduleViewPrivate::internalItemAt(const QPoint & pt) +{ + QListIteratoriterator(m_Items); + QxtScheduleInternalItem *currentItem; + iterator.toBack(); + while (iterator.hasPrevious()) + { + currentItem = iterator.previous(); + if (currentItem->contains(pt)) + return currentItem; + } + return 0; +} + + +void QxtScheduleViewPrivate::reloadItemsFromModel() +{ + qDeleteAll(m_Items.begin(), m_Items.end()); + m_Items.clear(); + m_selectedItem = NULL; + + int iNumItems = qxt_p().model()->rowCount(); + //delete all old stuff here + QxtScheduleInternalItem *currentItem; + for (int iLoop = 0; iLoop < iNumItems; iLoop++) + { + currentItem = new QxtScheduleInternalItem(&qxt_p(), qxt_p().model()->index(iLoop, 0)); + m_Items.append(currentItem); + connect(currentItem, SIGNAL(geometryChanged(QxtScheduleInternalItem*, QVector)), this, SLOT(itemGeometryChanged(QxtScheduleInternalItem * , QVector< QRect >))); + } + + handleItemConcurrency(0, (qxt_p().rows()*qxt_p().cols()) - 1); +} + +void QxtScheduleViewPrivate::init() +{ + if (qxt_p().model()) + { + qxt_p().viewport()->setMouseTracking(true); + + if (!m_vHeader) + { + m_vHeader = new QxtScheduleHeaderWidget(Qt::Vertical, &qxt_p()); + connect(m_vHeader, SIGNAL(geometriesChanged()), &qxt_p(), SLOT(updateGeometries())); + } + m_vHeader->show(); + + if (!m_hHeader) + { + m_hHeader = new QxtScheduleHeaderWidget(Qt::Horizontal, &qxt_p()); + connect(m_hHeader, SIGNAL(geometriesChanged()), &qxt_p(), SLOT(updateGeometries())); + } + m_hHeader->show(); + + /*here we also initialize the items*/ + m_vHeader->setDefaultSectionSize(20); + m_vHeader->setResizeMode(QHeaderView::Fixed); + reloadItemsFromModel(); + } + qxt_p().updateGeometries(); +} + +/*! + * @desc collects groups of concurrent items in the offset range + */ +QList< QLinkedList > QxtScheduleViewPrivate::findConcurrentItems(const int from, const int to) const +{ + QList< QLinkedList > allConcurrentItems; + + QList allItemsSorted = m_Items; + + if(m_Items.size() == 0) + return allConcurrentItems; + + qSort(allItemsSorted.begin(), allItemsSorted.end(), qxtScheduleItemLessThan); + + int startItem = 0; + int endItem = allItemsSorted.size() - 1; + + //find the startitem that interferes with our range + for (int i = 0; i < allItemsSorted.size(); i++) + { + if (i > 0) + { + if (!(allItemsSorted.at(i - 1)->visualEndTableOffset() >= allItemsSorted.at(i)->visualStartTableOffset() + && allItemsSorted.at(i - 1)->visualStartTableOffset() <= allItemsSorted.at(i)->visualEndTableOffset())) + startItem = i; + } + + if (allItemsSorted.at(i)->visualEndTableOffset() >= from && allItemsSorted.at(i)->visualStartTableOffset() <= to) + break; + } + + //find the last item that interferes with our range + for (int i = allItemsSorted.size() - 1; i >= 0 ; i--) + { + if (i < allItemsSorted.size() - 1) + { + if (!(allItemsSorted.at(i + 1)->visualEndTableOffset() >= allItemsSorted.at(i)->visualStartTableOffset() + && allItemsSorted.at(i + 1)->visualStartTableOffset() <= allItemsSorted.at(i)->visualEndTableOffset())) + endItem = i; + } + + if (allItemsSorted.at(i)->visualEndTableOffset() >= from && allItemsSorted.at(i)->visualStartTableOffset() <= to) + break; + } + + int startOffset = allItemsSorted.at(startItem)->visualStartTableOffset(); + int endOffset = allItemsSorted.at(endItem)->visualEndTableOffset(); + + /*now we have to populate a list with all items that interfere with our range */ + QLinkedList concurrentItems; + for (int iAllItemLoop = startItem; iAllItemLoop <= endItem; iAllItemLoop++) + { + int tempStartOffset = allItemsSorted.at(iAllItemLoop)->visualStartTableOffset(); + int tempEndOffset = allItemsSorted.at(iAllItemLoop)->visualEndTableOffset(); + + if (tempEndOffset >= startOffset && tempStartOffset <= endOffset) + { + + if (concurrentItems.size() >= 1) + { + bool bAppend = false; + /*check all items in the list if the current items interfers although the items are ordered by startIndex + *we can loose some of them if the endTime of the last Item is before the endTime of the pre last item + */ + + for (QLinkedList::iterator it = concurrentItems.begin(); it != concurrentItems.end(); ++it) + { + int lastStartOffset = (*it)->visualStartTableOffset(); + int lastEndOffset = (*it)->visualEndTableOffset(); + + if (tempEndOffset >= lastStartOffset && tempStartOffset <= lastEndOffset) + { + bAppend = true; + break; + } + } + + if (bAppend) + { + concurrentItems.append(allItemsSorted.at(iAllItemLoop)); + } + else + { + allConcurrentItems.append(concurrentItems); + concurrentItems.clear(); + concurrentItems.append(allItemsSorted.at(iAllItemLoop)); + } + } + else + concurrentItems.append(allItemsSorted.at(iAllItemLoop)); + + if (tempStartOffset < startOffset) + startOffset = tempStartOffset; + + if (tempEndOffset > endOffset) + endOffset = tempEndOffset; + } + } + if (concurrentItems.size() > 0) + allConcurrentItems.append(concurrentItems); + + return allConcurrentItems; +} + +void QxtScheduleViewPrivate::handleItemConcurrency(const int from, const int to) +{ + /*collect all items that interfere only in that range*/ + if (from < 0 || to < 0 || m_Items.size() == 0){ + //do a update or we may have artifacts + qxt_p().viewport()->update(); + return; + } + + //qDebug() << "handleItemConcurrency"; + + if (handlesConcurrency) + return; + + handlesConcurrency = true; + + QList< QLinkedList > allConcurrentItems = findConcurrentItems(from, to); + + /*thx to ahigerd for suggesting that algorithm*/ + //[16:24] Start with the first event. Put it in a list. + //[16:25] Iterate until you find an event that doesn't overlap with the first event. Put it in the list. Repeat until you've reached the last event. + //[16:25] This fills the left column optimally. + //[16:25] Repeat the algorithm for the second column, etc., until there aren't any events left that don't have a column. + //[16:27] This algorithm is O(n*m), where n is the number of events and m is the maximum number of overlapping events. + + QList< QList< QxtScheduleInternalItem *> >virtualTable; + + for (int iListLoop = 0; iListLoop < allConcurrentItems.size(); iListLoop++) + { + QLinkedList & currentItems = allConcurrentItems[iListLoop]; + QList< QxtScheduleInternalItem * > currentColumn; + + //qDebug() << "handle overlapping for " << currentItems.size() << " Items"; + + virtualTable.clear(); + + //we iterate over the currect collection and remove every item that can be placed in the current column + //when the collection is empty we are done + while (currentItems.size()) + { + QMutableLinkedListIterator< QxtScheduleInternalItem * > iter(currentItems); + + while (iter.hasNext()) + { + iter.next(); + //initialize the current column + if (currentColumn.isEmpty() || currentColumn[currentColumn.size()-1]->visualEndTableOffset() < iter.value()->visualStartTableOffset()) + { + currentColumn.append(iter.value()); + iter.remove(); + continue; + } + } + + if (!currentColumn.isEmpty()) + { + virtualTable.append(currentColumn); + currentColumn.clear(); + } + } + + //qDebug() << "Found columns" << virtualTable.size(); + + //this code part resizes the item geometries + for (int col = 0; col < virtualTable.size(); col++) + { + for (int item = 0; item < virtualTable.at(col).size() ; item++) + { + int startVisualCol = offsetToVisualColumn(virtualTable[col][item]->visualStartTableOffset()); + QVector geo = virtualTable[col][item]->geometry(); + + for (int rect = 0; rect < geo.size(); rect++) + { + int sectionStart = m_hHeader->sectionPosition(startVisualCol); + int fullWidth = m_hHeader->sectionSize(startVisualCol); + int oneItemWidth = fullWidth / virtualTable.size(); + int itemWidth = oneItemWidth; + int itemXStart = (col * oneItemWidth) + sectionStart; + int overlap = oneItemWidth / 10; + int adjustX1 = 0; + int adjustX2 = 0; + + //this is very expensive.I try to check if my item can span over more than one col + int possibleCols = 1; + bool foundCollision; + + for (int tmpCol = col + 1; tmpCol < virtualTable.size(); tmpCol++) + { + foundCollision = false; + for (int tmpItem = 0; tmpItem < virtualTable.at(tmpCol).size() ; tmpItem++) + { + if ((virtualTable[tmpCol][tmpItem]->visualEndTableOffset() >= virtualTable[col][item]->visualStartTableOffset() + && virtualTable[tmpCol][tmpItem]->visualStartTableOffset() <= virtualTable[col][item]->visualEndTableOffset())) + { + foundCollision = true; + break; + } + } + + if (!foundCollision) + possibleCols++; + else + break; + } + //now lets adjust the size to get a nice overlapping of items + if (virtualTable.size() > 1) + { + if (col == 0) + adjustX2 = overlap; + else if (col == virtualTable.size() - 1) + { + adjustX1 = -overlap; + adjustX2 = overlap; + } + else + { + if (col + possibleCols == virtualTable.size()) + adjustX2 = overlap; + else + adjustX2 = overlap * 2; + + adjustX1 = -overlap; + } + } + + // possibleCols = 1; + itemWidth = oneItemWidth * possibleCols; + + //qDebug() << "orginial rect" << geo[rect]; + geo[rect].setLeft(itemXStart + adjustX1); + geo[rect].setWidth(itemWidth + adjustX2); + //qDebug() << "new rect" << geo[rect]; + + + startVisualCol++; + } + virtualTable[col][item]->setGeometry(geo); + } + } + } + handlesConcurrency = false; + qxt_p().viewport()->update(); +} + +QxtScheduleViewPrivate::QxtScheduleViewPrivate() +{ + m_Cols = 0; + m_vHeader = 0; + m_hHeader = 0; + m_Model = 0; + m_selectedItem = 0; + handlesConcurrency = false; + delegate = 0; + m_zoomStepWidth = 0; + + connect(&scrollTimer, SIGNAL(timeout()), this, SLOT(scrollTimerTimeout())); +} + +void QxtScheduleViewPrivate::itemGeometryChanged(QxtScheduleInternalItem * item, QVector< QRect > oldGeometry) +{ + QRegion oldRegion; + + if (item->geometry() == oldGeometry) + return; + + QVectorIterator iter(oldGeometry); + QRect currRect; + while (iter.hasNext()) + { + currRect = iter.next(); + currRect.adjust(-1, -1, 2, 2); + oldRegion += currRect; + } + //viewport()->update(oldRegion); + + + QRegion newRegion; + QVectorIterator newIter(item->geometry()); + while (newIter.hasNext()) + { + currRect = newIter.next(); + currRect.adjust(-1, -1, 2, 2); + newRegion += currRect; + } + //viewport()->update(newRegion); + qxt_p().viewport()->update(); +} + +int QxtScheduleViewPrivate::unixTimeToOffset(const uint constUnixTime, bool indexEndTime) const +{ + uint unixTime = constUnixTime; + if (unixTime >= m_startUnixTime && unixTime <= m_endUnixTime) + { + if (indexEndTime) + { + unixTime -= m_currentZoomDepth; + } + qint32 rows = qxt_p().rows(); + qint32 iOffset = unixTime - m_startUnixTime; + + //round to the closest boundaries + iOffset = qRound((qreal)iOffset / (qreal)m_currentZoomDepth); + + qint32 iCol = iOffset / rows; + qint32 iRow = iOffset % rows; + return visualIndexToOffset(iRow, iCol); + } + //virtual void handleItemOverlapping(QxtScheduleInternalItem *item); + return -1; +} + +void QxtScheduleViewPrivate::scrollTimerTimeout() +{ + QPoint globalPos = QCursor::pos(); + QPoint viewportPos = qxt_p().viewport()->mapFromGlobal(globalPos); + + int iScrollVertical = this->m_vHeader->defaultSectionSize(); + int iScrollHorizontal = this->m_hHeader->defaultSectionSize(); + + if (viewportPos.y() <= iScrollVertical) + { + int iCurrPos = qxt_p().verticalScrollBar()->value(); + if (iCurrPos > qxt_p().verticalScrollBar()->minimum() + iScrollVertical) + { + qxt_p().verticalScrollBar()->setValue(iCurrPos - iScrollVertical); + } + else + qxt_p().verticalScrollBar()->setValue(qxt_p().verticalScrollBar()->minimum()); + + } + else if (viewportPos.y() >= qxt_p().viewport()->height() - iScrollVertical) + { + int iCurrPos = qxt_p().verticalScrollBar()->value(); + if (iCurrPos < qxt_p().verticalScrollBar()->maximum() - iScrollVertical) + { + qxt_p().verticalScrollBar()->setValue(iCurrPos + iScrollVertical); + } + else + qxt_p().verticalScrollBar()->setValue(qxt_p().verticalScrollBar()->maximum()); + } + + if (viewportPos.x() <= iScrollHorizontal / 2) + { + int iCurrPos = qxt_p().horizontalScrollBar()->value(); + if (iCurrPos > qxt_p().horizontalScrollBar()->minimum() + iScrollHorizontal) + { + qxt_p().horizontalScrollBar()->setValue(iCurrPos - iScrollHorizontal); + } + else + qxt_p().horizontalScrollBar()->setValue(qxt_p().horizontalScrollBar()->minimum()); + + } + else if (viewportPos.x() >= qxt_p().viewport()->width() - (iScrollHorizontal / 2)) + { + int iCurrPos = qxt_p().horizontalScrollBar()->value(); + if (iCurrPos < qxt_p().horizontalScrollBar()->maximum() - iScrollHorizontal) + { + qxt_p().horizontalScrollBar()->setValue(iCurrPos + iScrollHorizontal); + } + else + qxt_p().horizontalScrollBar()->setValue(qxt_p().horizontalScrollBar()->maximum()); + } + +} + +int QxtScheduleViewPrivate::offsetToUnixTime(const int offset, bool indexEndTime) const +{ + qint32 rows = qxt_p().rows(); + uint unixTime = (offsetToVisualRow(offset) + (offsetToVisualColumn(offset) * rows)) * m_currentZoomDepth; + unixTime += m_startUnixTime; + + if (indexEndTime) + { + unixTime += m_currentZoomDepth; + } + + if (unixTime >= m_startUnixTime && unixTime <= m_endUnixTime + 1) + return unixTime; + return -1; +} + + +QxtScheduleInternalItem::QxtScheduleInternalItem(QxtScheduleView *parent, QModelIndex index, QVector geometries) + : QObject(parent), m_iModelRow(index.row()), m_geometries(geometries) +{ + m_moving = false; + if (parent) + { + if (index.isValid()) + { + if (m_geometries.empty()) + { +//qDebug()<<"add item: geometries empty so adding"; + int startOffset = this->startTableOffset(); + int endOffset = startOffset + this->rows() - 1; + m_geometries = parent->qxt_d().calculateRangeGeometries(startOffset, endOffset); + } + } + } +} + +QxtScheduleView * QxtScheduleInternalItem::parentView() const +{ + return qobject_cast(parent()); +} + +/*! + * @desc returns the currently VISUAL offset (changes when a item moves) + */ +int QxtScheduleInternalItem::visualStartTableOffset() const +{ + if (m_geometries.size() == 0 || !parentView()) + return -1; + + //are we in a move? + if (!m_moving) + return startTableOffset(); + + int offset = parentView()->qxt_d().pointToOffset(parentView()->mapToViewport((this->geometry()[0].topLeft()))); + return offset; +} + +/*! + * @desc returns the currently VISUAL offset (changes when a item moves) + */ +int QxtScheduleInternalItem::visualEndTableOffset() const +{ + if (m_geometries.size() == 0 || !parentView()) + return -1; + + //are we in a move? + if (!m_moving) + return endTableOffset(); + + QRect rect = m_geometries[m_geometries.size()-1]; + int endTableOffset = parentView()->qxt_d().pointToOffset(parentView()->mapToViewport(rect.bottomRight())); + return endTableOffset; +} + +bool QxtScheduleInternalItem::setData(QVariant data, int role) +{ + if (parentView() && parentView()->model()) + { + return parentView()->model()->setData(modelIndex(), data, role); + } + return false; +} + +QVariant QxtScheduleInternalItem::data(int role) const +{ + if (modelIndex().isValid()) + return modelIndex().data(role); + return QVariant(); +} + +int QxtScheduleInternalItem::startTableOffset() const +{ + if (parentView() && parentView()->model()) + { + int startTime = data(Qxt::ItemStartTimeRole).toInt(); + int zoomDepth = parentView()->currentZoomDepth(Qxt::Second); + + qint32 offset = startTime - parentView()->qxt_d().m_startUnixTime; + + //the start of the current item does not fit in the view + //so we have to align it to the nearest boundaries + if (offset % zoomDepth) + { + int lower = offset / zoomDepth * zoomDepth; + int upper = lower + zoomDepth; + + offset = (offset - lower >= upper - offset ? upper : lower); + + return parentView()->qxt_d().unixTimeToOffset(offset + parentView()->qxt_d().m_startUnixTime); + } + +//qDebug()<<"start Table offset kicked in... offset="<qxt_d().unixTimeToOffset(startTime); + return parentView()->qxt_d().unixTimeToOffset(startTime); + } + return -1; +} + +int QxtScheduleInternalItem::endTableOffset() const +{ + return startTableOffset() + rows() - 1; +} + +void QxtScheduleInternalItem::setStartTableOffset(int iOffset) +{ + if (parentView() && parentView()->model()) + { + setData(parentView()->qxt_d().offsetToUnixTime(iOffset), Qxt::ItemStartTimeRole); + } +} + +void QxtScheduleInternalItem::setRowsUsed(int rows) +{ + if (parentView() && parentView()->model()) + { + int seconds = rows * parentView()->currentZoomDepth(Qxt::Second); + setData(seconds, Qxt::ItemDurationRole); + } +} + +int QxtScheduleInternalItem::rows() const +{ + if (parentView() && parentView()->model()) + { + int iNumSecs = data(Qxt::ItemDurationRole).toInt(); + int zoomDepth = parentView()->currentZoomDepth(Qxt::Second); + + //the length of the current item does not fit in the view + //so we have to align it to the nearest boundaries + if (iNumSecs % zoomDepth) + { + int lower = iNumSecs / zoomDepth * zoomDepth; + int upper = lower + zoomDepth; + + return (iNumSecs - lower >= upper - iNumSecs ? (upper / zoomDepth) : (lower / zoomDepth)); + + } + return (iNumSecs / zoomDepth); + } + return -1; +} + +bool qxtScheduleItemLessThan(const QxtScheduleInternalItem * item1, const QxtScheduleInternalItem * item2) +{ + if (item1->visualStartTableOffset() < item2->visualStartTableOffset()) + return true; + if (item1->visualStartTableOffset() == item2->visualStartTableOffset() && item1->modelIndex().row() < item2->modelIndex().row()) + return true; + return false; +} + +bool QxtScheduleInternalItem::contains(const QPoint & pt) +{ + QVectorIterator iterator(this->m_geometries); + while (iterator.hasNext()) + { + if (iterator.next().contains(pt)) + return true; + } + return false; +} + +void QxtScheduleInternalItem::setGeometry(const QVector< QRect > geo) +{ + if (!this->parent()) + return; + + QVector oldGeo = this->m_geometries; + this->m_geometries.clear(); + this->m_geometries = geo; + emit geometryChanged(this, oldGeo); +} + +QVector< QRect > QxtScheduleInternalItem::geometry() const +{ + return this->m_geometries; +} + +void QxtScheduleInternalItem::startMove() +{ + //save old geometry before we start to move + this->m_SavedGeometries = this->m_geometries; + m_moving = true; +} + +void QxtScheduleInternalItem::resetMove() +{ + this->setGeometry(this->m_SavedGeometries); + this->m_SavedGeometries.clear(); + m_moving = false; +} + +void QxtScheduleInternalItem::stopMove() +{ + this->m_SavedGeometries.clear(); + m_moving = false; +} + +QModelIndex QxtScheduleInternalItem::modelIndex() const +{ + QModelIndex indx; + if (parentView() && parentView()->model()) + { + indx = parentView()->model()->index(this->m_iModelRow, 0); + } + return indx; +} diff --git a/qxt/src/qxtscheduleview_p.h b/qxt/src/qxtscheduleview_p.h new file mode 100644 index 000000000..423c0f6ff --- /dev/null +++ b/qxt/src/qxtscheduleview_p.h @@ -0,0 +1,183 @@ +/**************************************************************************** + ** + ** Copyright (C) Qxt Foundation. Some rights reserved. + ** + ** This file is part of the QxtGui module of the Qxt library. + ** + ** This library is free software; you can redistribute it and/or modify it + ** under the terms of the Common Public License, version 1.0, as published + ** by IBM, and/or under the terms of the GNU Lesser General Public License, + ** version 2.1, as published by the Free Software Foundation. + ** + ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY + ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY + ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR + ** FITNESS FOR A PARTICULAR PURPOSE. + ** + ** You should have received a copy of the CPL and the LGPL along with this + ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files + ** included with the source distribution for more information. + ** If you did not receive a copy of the licenses, contact the Qxt Foundation. + ** + ** + ** + ****************************************************************************/ + +#ifndef QXTSCHEDULEVIEW_P_INCLUDED +#define QXTSCHEDULEVIEW_P_INCLUDED + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qxt API. It exists for the convenience +// of other Qxt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#include +#include +#include +#include +#include +#include +#include +#include "qxtnamespace.h" +#include "qxtscheduleitemdelegate.h" + +class QxtScheduleView; +class QxtScheduleHeaderWidget; + +class QxtScheduleInternalItem : public QObject +{ + Q_OBJECT + friend class QxtScheduleView; + +public: + + QxtScheduleInternalItem(QxtScheduleView *parent , QModelIndex index , QVector geometries = QVector()); + + bool setData(QVariant data , int role); + QVariant data(int role) const; + + int visualStartTableOffset() const; + int visualEndTableOffset() const; + + int startTableOffset() const; + int endTableOffset() const; + void setStartTableOffset(int iOffset); + + int rows() const; + void setRowsUsed(int rows); + QxtScheduleView* parentView() const; + + void startMove(); + void resetMove(); + void stopMove(); + + bool contains(const QPoint &pt); + void setGeometry(const QVector geo); + QModelIndex modelIndex() const; + void setDirty(bool state = true) + { + isDirty = state; + } + + QVector geometry() const; + +Q_SIGNALS: + void geometryChanged(QxtScheduleInternalItem * item , QVector oldGeometry); + +public: + bool m_moving; + bool isDirty; + int m_iModelRow; + QVector m_geometries; + QVector m_SavedGeometries; + QVector m_cachedParts; +}; + +class QxtScheduleViewPrivate : public QObject, public QxtPrivate +{ + Q_OBJECT +public: + + QXT_DECLARE_PUBLIC(QxtScheduleView) + QxtScheduleViewPrivate(); + + + int offsetToVisualColumn(const int iOffset) const; + int offsetToVisualRow(const int iOffset) const; + int visualIndexToOffset(const int iRow, const int iCol) const; + int unixTimeToOffset(const uint constUnixTime, bool indexEndTime = false) const; + int offsetToUnixTime(const int offset, bool indexEndTime = false) const; + QVector< QRect > calculateRangeGeometries(const int iStartOffset, const int iEndOffset) const; + int pointToOffset(const QPoint & point); + void handleItemConcurrency(const int from, const int to); + QList< QLinkedList >findConcurrentItems(const int from, const int to) const; + + + QxtScheduleInternalItem *internalItemAt(const QPoint &pt); + QxtScheduleInternalItem *internalItemAtModelIndex(const QModelIndex &index); + + + void init(); + void reloadItemsFromModel(); + + QxtScheduleInternalItem * itemForModelIndex(const QModelIndex &index)const + { + for (int iLoop = 0; iLoop < m_Items.size(); iLoop++) + { + if (m_Items.at(iLoop)->modelIndex() == index) + return m_Items.at(iLoop); + } + return 0; + } + + void handleItemConcurrency(QxtScheduleInternalItem *item) + { + if (item) + { + int startOffset = item->startTableOffset(); + int endOffset = startOffset + item->rows() - 1 ; + handleItemConcurrency(startOffset, endOffset); + } + } + + QxtScheduleInternalItem *m_currentItem; + QxtScheduleInternalItem *m_selectedItem; + + int m_lastMousePosOffset; + int m_currentZoomDepth; + int m_zoomStepWidth; + int m_currentViewMode; + uint m_startUnixTime; + uint m_endUnixTime; + + QList m_Items ; + QList m_InactiveItems; /*used for items that are not in the range of our view but we need to look if they get updates*/ + + QTimer scrollTimer; + + QxtScheduleHeaderWidget *m_vHeader; + QxtScheduleHeaderWidget *m_hHeader; + + int m_Cols; + + QAbstractItemModel *m_Model; + bool handlesConcurrency; + QxtScheduleItemDelegate *delegate; + QxtScheduleItemDelegate *defaultDelegate; + +public Q_SLOTS: + void itemGeometryChanged(QxtScheduleInternalItem * item, QVector oldGeometry); + void scrollTimerTimeout(); + +}; + +bool qxtScheduleItemLessThan(const QxtScheduleInternalItem * item1, const QxtScheduleInternalItem * item2); + +#endif diff --git a/qxt/src/qxtscheduleviewheadermodel_p.cpp b/qxt/src/qxtscheduleviewheadermodel_p.cpp new file mode 100644 index 000000000..ba2966248 --- /dev/null +++ b/qxt/src/qxtscheduleviewheadermodel_p.cpp @@ -0,0 +1,211 @@ +/**************************************************************************** + ** + ** Copyright (C) Qxt Foundation. Some rights reserved. + ** + ** This file is part of the QxtGui module of the Qxt library. + ** + ** This library is free software; you can redistribute it and/or modify it + ** under the terms of the Common Public License, version 1.0, as published + ** by IBM, and/or under the terms of the GNU Lesser General Public License, + ** version 2.1, as published by the Free Software Foundation. + ** + ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY + ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY + ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR + ** FITNESS FOR A PARTICULAR PURPOSE. + ** + ** You should have received a copy of the CPL and the LGPL along with this + ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files + ** included with the source distribution for more information. + ** If you did not receive a copy of the licenses, contact the Qxt Foundation. + ** + ** + ** + ****************************************************************************/ + +#include "qxtscheduleviewheadermodel_p.h" +#include "qxtscheduleview.h" +#include "qxtscheduleview_p.h" +#include +#include + +QxtScheduleViewHeaderModel::QxtScheduleViewHeaderModel(QObject *parent) : QAbstractTableModel(parent) + , m_rowCountBuffer(0) + , m_colCountBuffer(0) +{ + +} + +void QxtScheduleViewHeaderModel::newZoomDepth(const int zoomDepth) +{ + Q_UNUSED(zoomDepth); + + if (this->m_dataSource) + { + /* + qDebug()<<"old rows "<rows(); + beginRemoveRows(QModelIndex(),0,m_rowCountBuffer); + m_rowCountBuffer = 0; + endRemoveRows(); + + beginInsertRows(QModelIndex(),0,m_dataSource->rows()); + m_rowCountBuffer = m_dataSource->rows(); + endInsertRows(); + */ + m_rowCountBuffer = m_dataSource->rows(); + reset(); + + } +} + +void QxtScheduleViewHeaderModel::viewModeChanged(const int viewMode) +{ + Q_UNUSED(viewMode); + + if (this->m_dataSource) + { + beginRemoveRows(QModelIndex(), 0, m_rowCountBuffer); + m_rowCountBuffer = 0; + endRemoveRows(); + + beginInsertRows(QModelIndex(), 0, m_dataSource->rows()); + m_rowCountBuffer = m_dataSource->rows(); + endInsertRows(); + + beginRemoveColumns(QModelIndex(), 0, m_colCountBuffer); + m_colCountBuffer = 0; + endRemoveColumns(); + + beginInsertColumns(QModelIndex(), 0, m_dataSource->cols()); + m_colCountBuffer = m_dataSource->cols(); + endInsertColumns(); + } +} + +void QxtScheduleViewHeaderModel::setDataSource(QxtScheduleView *dataSource) +{ + if (this->m_dataSource) + { + disconnect(m_dataSource, SIGNAL(newZoomDepth(const int)), this, SLOT(newZoomDepth(const int))); + disconnect(m_dataSource, SIGNAL(viewModeChanged(const int)), this, SLOT(viewModeChanged(const int))); + + emit beginRemoveRows(QModelIndex(), 0, m_rowCountBuffer); + m_rowCountBuffer = 0; + emit endRemoveRows(); + + emit beginRemoveColumns(QModelIndex(), 0, m_colCountBuffer); + m_colCountBuffer = 0; + emit endRemoveColumns(); + } + + if (dataSource) + { + connect(dataSource, SIGNAL(newZoomDepth(const int)), this, SLOT(newZoomDepth(const int))); + connect(dataSource, SIGNAL(viewModeChanged(const int)), this, SLOT(viewModeChanged(const int))); + + emit beginInsertRows(QModelIndex(), 0, dataSource->rows()); + m_rowCountBuffer = dataSource->rows(); + emit endInsertRows(); + + emit beginInsertColumns(QModelIndex(), 0, dataSource->cols()); + m_colCountBuffer = dataSource->cols(); + emit endInsertColumns(); + } + + m_dataSource = dataSource; +} + +QModelIndex QxtScheduleViewHeaderModel::parent(const QModelIndex & index) const +{ + Q_UNUSED(index); + return QModelIndex(); +} + +int QxtScheduleViewHeaderModel::rowCount(const QModelIndex & parent) const +{ + if (!parent.isValid()) + { + if (this->m_dataSource) + return m_dataSource->rows(); + } + return 0; +} + +int QxtScheduleViewHeaderModel::columnCount(const QModelIndex & parent) const +{ + if (!parent.isValid()) + { + if (this->m_dataSource) + return m_dataSource->cols(); + } + return 0; +} + +QVariant QxtScheduleViewHeaderModel::data(const QModelIndex & index, int role) const +{ + Q_UNUSED(index); + Q_UNUSED(role); + return QVariant(); +} + +bool QxtScheduleViewHeaderModel::setData(const QModelIndex & index, const QVariant & value, int role) +{ + Q_UNUSED(index); + Q_UNUSED(value); + Q_UNUSED(role); + return false; +} + +bool QxtScheduleViewHeaderModel::insertRow(int row, const QModelIndex & parent) +{ + Q_UNUSED(row); + Q_UNUSED(parent); + return false; +} + +QVariant QxtScheduleViewHeaderModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (!m_dataSource) + return QVariant(); +#if 0 + if (role == Qt::SizeHintRole) + { + if (this->viewModel) + { + return viewModel->headerData(section, orientation, role); + } + } +#endif + + if (role == Qt::DisplayRole || role == Qt::EditRole) + { + if (Qt::Horizontal == orientation) + { + int iTableOffset = m_dataSource->qxt_d().visualIndexToOffset(0, section); + QDateTime startTime = QDateTime::fromTime_t(m_dataSource->qxt_d().offsetToUnixTime(iTableOffset)); + return QVariant(startTime.date().toString()); + } + else + { + int iTableOffset = m_dataSource->qxt_d().visualIndexToOffset(section, 0); + QTime time = QDateTime::fromTime_t(m_dataSource->qxt_d().offsetToUnixTime(iTableOffset)).time(); + return QVariant(time.toString()); + } + } + + return QVariant(); +} + +Qt::ItemFlags QxtScheduleViewHeaderModel::flags(const QModelIndex & index) const +{ + Q_UNUSED(index); + return Qt::ItemIsEnabled; +} + +bool QxtScheduleViewHeaderModel::hasChildren(const QModelIndex & parent) const +{ + Q_UNUSED(parent); + return false; +} + + diff --git a/qxt/src/qxtscheduleviewheadermodel_p.h b/qxt/src/qxtscheduleviewheadermodel_p.h new file mode 100644 index 000000000..69a97dd26 --- /dev/null +++ b/qxt/src/qxtscheduleviewheadermodel_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** + ** + ** Copyright (C) Qxt Foundation. Some rights reserved. + ** + ** This file is part of the QxtGui module of the Qxt library. + ** + ** This library is free software; you can redistribute it and/or modify it + ** under the terms of the Common Public License, version 1.0, as published + ** by IBM, and/or under the terms of the GNU Lesser General Public License, + ** version 2.1, as published by the Free Software Foundation. + ** + ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY + ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY + ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR + ** FITNESS FOR A PARTICULAR PURPOSE. + ** + ** You should have received a copy of the CPL and the LGPL along with this + ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files + ** included with the source distribution for more information. + ** If you did not receive a copy of the licenses, contact the Qxt Foundation. + ** + ** + ** + ****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qxt API. It exists for the convenience +// of other Qxt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef QXTSCHEDULEVIEWHEADERMODEL_P_H_INCLUDED +#define QXTSCHEDULEVIEWHEADERMODEL_P_H_INCLUDED + +#include +#include +#include + +/*! + *@author Benjamin Zeller + *@desc This Model is used to tell the Header how many rows and columns the view has + */ + +class QxtScheduleView; + +class QxtScheduleViewHeaderModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + QxtScheduleViewHeaderModel(QObject *parent = 0); + + void setDataSource(QxtScheduleView *dataSource); + + virtual QModelIndex parent(const QModelIndex & index) const; + virtual int rowCount(const QModelIndex & parent = QModelIndex()) const; + virtual int columnCount(const QModelIndex & parent = QModelIndex()) const; + virtual QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; + virtual bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole); + virtual bool insertRow(int row, const QModelIndex & parent = QModelIndex()); + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + virtual Qt::ItemFlags flags(const QModelIndex &index) const; + virtual bool hasChildren(const QModelIndex & parent = QModelIndex()) const; + +public Q_SLOTS: + void newZoomDepth(const int zoomDepth); + void viewModeChanged(const int viewMode); + +private: + QPointer m_dataSource; + int m_rowCountBuffer; + int m_colCountBuffer; +}; + +#endif // SPLITVIEWHEADERMODEL_H diff --git a/qxt/src/qxtstyleoptionscheduleviewitem.cpp b/qxt/src/qxtstyleoptionscheduleviewitem.cpp new file mode 100644 index 000000000..9c7d47e8c --- /dev/null +++ b/qxt/src/qxtstyleoptionscheduleviewitem.cpp @@ -0,0 +1,41 @@ +/**************************************************************************** + ** + ** Copyright (C) Qxt Foundation. Some rights reserved. + ** + ** This file is part of the QxtGui module of the Qxt library. + ** + ** This library is free software; you can redistribute it and/or modify it + ** under the terms of the Common Public License, version 1.0, as published + ** by IBM, and/or under the terms of the GNU Lesser General Public License, + ** version 2.1, as published by the Free Software Foundation. + ** + ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY + ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY + ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR + ** FITNESS FOR A PARTICULAR PURPOSE. + ** + ** You should have received a copy of the CPL and the LGPL along with this + ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files + ** included with the source distribution for more information. + ** If you did not receive a copy of the licenses, contact the Qxt Foundation. + ** + ** + ** + ****************************************************************************/ + +#include "qxtstyleoptionscheduleviewitem.h" + +QxtStyleOptionScheduleViewItem::QxtStyleOptionScheduleViewItem() + : QStyleOptionViewItem() +{ + //make qstyleoption_cast work + version = Version; + type = Type; +} + + +QxtStyleOptionScheduleViewItem::~QxtStyleOptionScheduleViewItem() +{ +} + + diff --git a/qxt/src/qxtstyleoptionscheduleviewitem.h b/qxt/src/qxtstyleoptionscheduleviewitem.h new file mode 100644 index 000000000..fc5b1b91a --- /dev/null +++ b/qxt/src/qxtstyleoptionscheduleviewitem.h @@ -0,0 +1,56 @@ +/**************************************************************************** + ** + ** Copyright (C) Qxt Foundation. Some rights reserved. + ** + ** This file is part of the QxtGui module of the Qxt library. + ** + ** This library is free software; you can redistribute it and/or modify it + ** under the terms of the Common Public License, version 1.0, as published + ** by IBM, and/or under the terms of the GNU Lesser General Public License, + ** version 2.1, as published by the Free Software Foundation. + ** + ** This file is provided "AS IS", without WARRANTIES OR CONDITIONS OF ANY + ** KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY + ** WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR + ** FITNESS FOR A PARTICULAR PURPOSE. + ** + ** You should have received a copy of the CPL and the LGPL along with this + ** file. See the LICENSE file and the cpl1.0.txt/lgpl-2.1.txt files + ** included with the source distribution for more information. + ** If you did not receive a copy of the licenses, contact the Qxt Foundation. + ** + ** + ** + ****************************************************************************/ +#ifndef QXTSTYLEOPTIONSCHEDULEVIEWITEM_H +#define QXTSTYLEOPTIONSCHEDULEVIEWITEM_H + +#include +#include +#include +#include + + +/*! + @author Benjamin Zeller +*/ +class QxtStyleOptionScheduleViewItem : public QStyleOptionViewItem +{ +public: + QxtStyleOptionScheduleViewItem(); + ~QxtStyleOptionScheduleViewItem(); + + enum StyleOptionType { Type = QStyleOption::SO_CustomBase }; + enum StyleOptionVersion { Version = 1 }; + + + QVector itemGeometries; + mutable QVector *itemPaintCache; + QPoint translate; + int roundCornersRadius; + int itemHeaderHeight; + int maxSubItemHeight; + +}; + +#endif diff --git a/src/.gitignore b/src/.gitignore index 3186f561c..2fb3b1f6d 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -24,7 +24,9 @@ qrc_application.cpp Makefile # ignore executables -GoldenCheetah* +GoldenCheetah +GoldenCheetah.exe +GoldenCheetah.app #ignore ctags for vim tags diff --git a/src/ANTplusController.h b/src/ANTplusController.h index 20fa67292..811063b37 100644 --- a/src/ANTplusController.h +++ b/src/ANTplusController.h @@ -15,6 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "GoldenCheetah.h" #include "RealtimeController.h" #include "DeviceConfiguration.h" diff --git a/src/AerobicDecoupling.cpp b/src/AerobicDecoupling.cpp index ec0b366f1..1d8381818 100644 --- a/src/AerobicDecoupling.cpp +++ b/src/AerobicDecoupling.cpp @@ -53,8 +53,10 @@ class AerobicDecoupling : public RideMetric { setImperialUnits(tr("%")); setPrecision(2); } - void compute(const RideFile *ride, const Zones *, int, const HrZones *, int, - const QHash &) { + void compute(const RideFile *ride, const Zones *, int, + const HrZones *, int, + const QHash &, + const MainWindow *) { double firstHalfPower = 0.0, secondHalfPower = 0.0; double firstHalfHR = 0.0, secondHalfHR = 0.0; int halfway = ride->dataPoints().size() / 2; diff --git a/src/Aerolab.cpp b/src/Aerolab.cpp index d1e02deed..2d5a03701 100644 --- a/src/Aerolab.cpp +++ b/src/Aerolab.cpp @@ -55,8 +55,7 @@ Aerolab::Aerolab(QWidget *parent): eta = 1.0; eoffset = 0.0; - boost::shared_ptr settings = GetApplicationSettings(); - unit = settings->value(GC_UNIT); + unit = appsettings->value(this, GC_UNIT); useMetricUnits = true; insertLegend(new QwtLegend(), QwtPlot::BottomLegend); @@ -85,10 +84,10 @@ Aerolab::configChanged() // set colors setCanvasBackground(GColor(CPLOTBACKGROUND)); QPen vePen = QPen(GColor(CAEROVE)); - vePen.setWidth(1); + vePen.setWidth(appsettings->value(this, GC_LINEWIDTH, 2.0).toDouble()); veCurve->setPen(vePen); QPen altPen = QPen(GColor(CAEROEL)); - altPen.setWidth(1); + altPen.setWidth(appsettings->value(this, GC_LINEWIDTH, 2.0).toDouble()); altCurve->setPen(altPen); QPen gridPen(GColor(CPLOTGRID)); gridPen.setStyle(Qt::DotLine); @@ -209,6 +208,20 @@ Aerolab::setData(RideItem *_rideItem, bool new_zoom) { } } +void +Aerolab::setAxisTitle(int axis, QString label) +{ + // setup the default fonts + QFont stGiles; // hoho - Chart Font St. Giles ... ok you have to be British to get this joke + stGiles.fromString(appsettings->value(this, GC_FONT_CHARTLABELS, QFont().toString()).toString()); + stGiles.setPointSize(appsettings->value(NULL, GC_FONT_CHARTLABELS_SIZE, 8).toInt()); + + QwtText title(label); + title.setFont(stGiles); + QwtPlot::setAxisFont(axis, stGiles); + QwtPlot::setAxisTitle(axis, title); +} + struct DataPoint { double time, hr, watts, speed, cad, alt; DataPoint(double t, double h, double w, double s, double c, double a) : diff --git a/src/Aerolab.h b/src/Aerolab.h index ebf293ee3..ef3573748 100644 --- a/src/Aerolab.h +++ b/src/Aerolab.h @@ -18,6 +18,7 @@ #ifndef _GC_Aerolab_h #define _GC_Aerolab_h 1 +#include "GoldenCheetah.h" #include #include @@ -35,12 +36,15 @@ class MainWindow; class Aerolab : public QwtPlot { Q_OBJECT + G_OBJECT + public: Aerolab(QWidget *parent); bool byDistance() const { return bydist; } bool useMetricUnits; // whether metric units are used (or imperial) void setData(RideItem *_rideItem, bool new_zoom); + void setAxisTitle(int axis, QString label); public slots: void setByDistance(); diff --git a/src/AerolabWindow.cpp b/src/AerolabWindow.cpp index eab916275..c1b3bcf2e 100644 --- a/src/AerolabWindow.cpp +++ b/src/AerolabWindow.cpp @@ -26,7 +26,9 @@ #include AerolabWindow::AerolabWindow(MainWindow *mainWindow) : - QWidget(mainWindow), mainWindow(mainWindow) { + GcWindow(mainWindow), mainWindow(mainWindow) { + setInstanceName("Aerolab Window"); + setControls(NULL); // Aerolab tab layout: QVBoxLayout *vLayout = new QVBoxLayout; @@ -182,7 +184,8 @@ AerolabWindow::AerolabWindow(MainWindow *mainWindow) : allZoomer->setEnabled(true); // SIGNALs to SLOTs: - connect(mainWindow, SIGNAL(rideSelected()), this, SLOT(rideSelected())); + //connect(mainWindow, SIGNAL(rideSelected()), this, SLOT(rideSelected())); + connect(this, SIGNAL(rideItemChanged(RideItem*)), this, SLOT(rideSelected())); connect(crrSlider, SIGNAL(valueChanged(int)),this, SLOT(setCrrFromSlider())); connect(cdaSlider, SIGNAL(valueChanged(int)), this, SLOT(setCdaFromSlider())); connect(mSlider, SIGNAL(valueChanged(int)),this, SLOT(setTotalMassFromSlider())); @@ -209,10 +212,9 @@ AerolabWindow::configChanged() void AerolabWindow::rideSelected() { - if (mainWindow->activeTab() != this) - return; + if (!amVisible()) return; - RideItem *ride = mainWindow->rideItem(); + RideItem *ride = myRideItem; if (!ride) return; @@ -227,7 +229,7 @@ AerolabWindow::setCrrFromSlider() { if (aerolab->intCrr() != crrSlider->value()) { aerolab->setIntCrr(crrSlider->value()); crrQLCDNumber->display(QString("%1").arg(aerolab->getCrr())); - RideItem *ride = mainWindow->rideItem(); + RideItem *ride = myRideItem; aerolab->setData(ride, false); } } @@ -238,7 +240,7 @@ AerolabWindow::setCdaFromSlider() { if (aerolab->intCda() != cdaSlider->value()) { aerolab->setIntCda(cdaSlider->value()); cdaQLCDNumber->display(QString("%1").arg(aerolab->getCda())); - RideItem *ride = mainWindow->rideItem(); + RideItem *ride = myRideItem; aerolab->setData(ride, false); } } @@ -249,7 +251,7 @@ AerolabWindow::setTotalMassFromSlider() { if (aerolab->intTotalMass() != mSlider->value()) { aerolab->setIntTotalMass(mSlider->value()); mQLCDNumber->display(QString("%1").arg(aerolab->getTotalMass())); - RideItem *ride = mainWindow->rideItem(); + RideItem *ride = myRideItem; aerolab->setData(ride, false); } } @@ -260,7 +262,7 @@ AerolabWindow::setRhoFromSlider() { if (aerolab->intRho() != rhoSlider->value()) { aerolab->setIntRho(rhoSlider->value()); rhoQLCDNumber->display(QString("%1").arg(aerolab->getRho())); - RideItem *ride = mainWindow->rideItem(); + RideItem *ride = myRideItem; aerolab->setData(ride, false); } } @@ -271,7 +273,7 @@ AerolabWindow::setEtaFromSlider() { if (aerolab->intEta() != etaSlider->value()) { aerolab->setIntEta(etaSlider->value()); etaQLCDNumber->display(QString("%1").arg(aerolab->getEta())); - RideItem *ride = mainWindow->rideItem(); + RideItem *ride = myRideItem; aerolab->setData(ride, false); } } @@ -282,7 +284,7 @@ AerolabWindow::setEoffsetFromSlider() { if (aerolab->intEoffset() != eoffsetSlider->value()) { aerolab->setIntEoffset(eoffsetSlider->value()); eoffsetQLCDNumber->display(QString("%1").arg(aerolab->getEoffset())); - RideItem *ride = mainWindow->rideItem(); + RideItem *ride = myRideItem; aerolab->setData(ride, false); } } diff --git a/src/AerolabWindow.h b/src/AerolabWindow.h index 94cf28392..08e032a69 100644 --- a/src/AerolabWindow.h +++ b/src/AerolabWindow.h @@ -18,6 +18,7 @@ #ifndef _GC_AerolabWindow_h #define _GC_AerolabWindow_h 1 +#include "GoldenCheetah.h" #include @@ -31,8 +32,10 @@ class QLCDNumber; class RideItem; class IntervalItem; -class AerolabWindow : public QWidget { +class AerolabWindow : public GcWindow { Q_OBJECT + G_OBJECT + public: AerolabWindow(MainWindow *mainWindow); diff --git a/src/AllPlot.cpp b/src/AllPlot.cpp index ab2e4d9a0..4dccb796f 100644 --- a/src/AllPlot.cpp +++ b/src/AllPlot.cpp @@ -32,6 +32,8 @@ #include #include #include +#include +#include #include #include #include @@ -206,30 +208,24 @@ AllPlot::AllPlot(AllPlotWindow *parent, MainWindow *mainWindow): bydist(false), parent(parent) { - boost::shared_ptr settings = GetApplicationSettings(); - unit = settings->value(GC_UNIT); + setInstanceName("AllPlot"); + unit = appsettings->value(this, GC_UNIT); referencePlot = NULL; useMetricUnits = (unit.toString() == "Metric"); + if (appsettings->value(this, GC_SHADEZONES, true).toBool()==false) + shade_zones = false; - // options for turning off/on shading on all plot - // will come in with a future patch, for now we - // enable zone shading by default, since this is - // the current default behaviour - if (false) shade_zones = false; - else shade_zones = true; - - smooth = settings->value(GC_RIDE_PLOT_SMOOTHING).toInt(); - if (smooth < 2) - smooth = 30; + smooth = appsettings->value(this, GC_RIDE_PLOT_SMOOTHING).toInt(); + if (smooth < 1) smooth = 1; // create a background object for shading bg = new AllPlotBackground(this); bg->attach(this); insertLegend(new QwtLegend(), QwtPlot::BottomLegend); - setCanvasBackground(GColor(CPLOTBACKGROUND)); + setCanvasBackground(GColor(CRIDEPLOTBACKGROUND)); setXTitle(); @@ -254,8 +250,11 @@ AllPlot::AllPlot(AllPlotWindow *parent, MainWindow *mainWindow): intervalHighlighterCurve->attach(this); this->legend()->remove(intervalHighlighterCurve); // don't show in legend + + // setup that grid grid = new QwtPlotGrid(); - grid->enableX(false); + grid->enableX(true); + grid->enableY(true); grid->attach(this); // get rid of nasty blank space on right of the plot @@ -268,16 +267,9 @@ void AllPlot::configChanged() { - double width = 1.0; + double width = appsettings->value(this, GC_LINEWIDTH, 2.0).toDouble(); - boost::shared_ptr settings = GetApplicationSettings(); - useMetricUnits = (settings->value(GC_UNIT).toString() == "Metric"); - - // placeholder for setting antialiasing, will come - // in with a future patch. For now antialiasing is - // not enabled since it can slow down plotting on - // windows and linux platforms. - if (false) { + if (appsettings->value(this, GC_ANTIALIAS, false).toBool() == true) { wattsCurve->setRenderHint(QwtPlotItem::RenderAntialiased); hrCurve->setRenderHint(QwtPlotItem::RenderAntialiased); speedCurve->setRenderHint(QwtPlotItem::RenderAntialiased); @@ -286,7 +278,7 @@ AllPlot::configChanged() intervalHighlighterCurve->setRenderHint(QwtPlotItem::RenderAntialiased); } - setCanvasBackground(GColor(CPLOTBACKGROUND)); + setCanvasBackground(GColor(CRIDEPLOTBACKGROUND)); QPen wattsPen = QPen(GColor(CPOWER)); wattsPen.setWidth(width); wattsCurve->setPen(wattsPen); @@ -328,6 +320,20 @@ bool AllPlot::shadeZones() const return shade_zones; } +void +AllPlot::setAxisTitle(int axis, QString label) +{ + // setup the default fonts + QFont stGiles; // hoho - Chart Font St. Giles ... ok you have to be British to get this joke + stGiles.fromString(appsettings->value(this, GC_FONT_CHARTLABELS, QFont().toString()).toString()); + stGiles.setPointSize(appsettings->value(NULL, GC_FONT_CHARTLABELS_SIZE, 8).toInt()); + + QwtText title(label); + title.setFont(stGiles); + QwtPlot::setAxisFont(axis, stGiles); + QwtPlot::setAxisTitle(axis, title); +} + void AllPlot::refreshZoneLabels() { foreach(AllPlotZoneLabel *label, zoneLabels) { @@ -517,11 +523,18 @@ void AllPlot::setYMax() { if (wattsCurve->isVisible()) { + double maxY = (referencePlot == NULL) ? (1.05 * wattsCurve->maxYValue()) : + (1.05 * referencePlot->wattsCurve->maxYValue()); + // grid lines for 100 or 25 + QwtValueList xytick[QwtScaleDiv::NTickTypes]; + for (int i=0;imaxYValue()); - else - setAxisScale(yLeft, 0.0, 1.05 * referencePlot->wattsCurve->maxYValue()); + //setAxisScale(yLeft, 0.0, maxY); + setAxisScaleDiv(QwtPlot::yLeft,QwtScaleDiv(0.0,maxY,xytick)); setAxisLabelRotation(yLeft,270); setAxisLabelAlignment(yLeft,Qt::AlignVCenter); } @@ -595,7 +608,7 @@ AllPlot::setDataFromPlot(AllPlot *plot, int startidx, int stopidx) referencePlot = plot; - setTitle(plot->rideItem->ride()->startTime().toString(GC_DATETIME_FORMAT)); + //setTitle(plot->rideItem->ride()->startTime().toString(GC_DATETIME_FORMAT)); // reference the plot for data and state @@ -640,6 +653,21 @@ AllPlot::setDataFromPlot(AllPlot *plot, int startidx, int stopidx) cadCurve->setData(xaxis, smoothC, stopidx-startidx); altCurve->setData(xaxis, smoothA, stopidx-startidx); + QwtSymbol sym; + sym.setPen(QPen(GColor(CPLOTMARKER))); + if (stopidx-startidx < 150) { + sym.setStyle(QwtSymbol::Ellipse); + sym.setSize(3); + } else { + sym.setStyle(QwtSymbol::NoSymbol); + sym.setSize(0); + } + wattsCurve->setSymbol(sym); + hrCurve->setSymbol(sym); + speedCurve->setSymbol(sym); + cadCurve->setSymbol(sym); + altCurve->setSymbol(sym); + setYMax(); setAxisMaxMajor(yLeft, 5); setAxisMaxMajor(yLeft2, 5); @@ -727,7 +755,7 @@ AllPlot::setDataFromRide(RideItem *_rideItem) recalc(); } else { - setTitle("no data"); + //setTitle("no data"); wattsCurve->detach(); hrCurve->detach(); @@ -808,8 +836,7 @@ void AllPlot::setSmoothing(int value) { smooth = value; - boost::shared_ptr settings = GetApplicationSettings(); - settings->setValue(GC_RIDE_PLOT_SMOOTHING, value); + appsettings->setValue(GC_RIDE_PLOT_SMOOTHING, value); recalc(); } @@ -964,12 +991,15 @@ AllPlot::pointHover(QwtPlotCurve *curve, int index) { if (index >= 0 && curve != intervalHighlighterCurve) { - double value = curve->y(index); + double yvalue = curve->y(index); + double xvalue = curve->x(index); // output the tooltip - QString text = QString("%1 %2") - .arg(value, 0, 'f', 0) - .arg(this->axisTitle(curve->yAxis()).text()); + QString text = QString("%1 %2\n%3 %4") + .arg(yvalue, 0, 'f', 0) + .arg(this->axisTitle(curve->yAxis()).text()) + .arg(xvalue, 0, 'f', 2) + .arg(this->axisTitle(curve->xAxis()).text()); // set that text up tooltip->setText(text); diff --git a/src/AllPlot.h b/src/AllPlot.h index ff7cffae4..e338ab28a 100644 --- a/src/AllPlot.h +++ b/src/AllPlot.h @@ -1,16 +1,16 @@ -/* +/* * Copyright (c) 2006 Sean C. Rhea (srhea@srhea.net) * * 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 2 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., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA @@ -18,6 +18,7 @@ #ifndef _GC_AllPlot_h #define _GC_AllPlot_h 1 +#include "GoldenCheetah.h" #include #include @@ -40,6 +41,8 @@ class LTMCanvasPicker; class AllPlot : public QwtPlot { Q_OBJECT + G_OBJECT + public: @@ -57,6 +60,7 @@ class AllPlot : public QwtPlot bool shadeZones() const; void refreshZoneLabels(); void refreshIntervalMarkers(); + void setAxisTitle(int axis, QString label); // refresh data / plot parameters void recalc(); @@ -75,6 +79,8 @@ class AllPlot : public QwtPlot void setSmoothing(int value); void setByDistance(int value); void configChanged(); + + // for tooltip void pointHover(QwtPlotCurve*, int); protected: diff --git a/src/AllPlotWindow.cpp b/src/AllPlotWindow.cpp index 942fea804..03318f122 100644 --- a/src/AllPlotWindow.cpp +++ b/src/AllPlotWindow.cpp @@ -48,36 +48,35 @@ #include "LTMWindow.h" AllPlotWindow::AllPlotWindow(MainWindow *mainWindow) : - QWidget(mainWindow), current(NULL), mainWindow(mainWindow), active(false), stale(true) + GcWindow(mainWindow), current(NULL), mainWindow(mainWindow), active(false), stale(true) { - boost::shared_ptr settings = GetApplicationSettings(); - QVBoxLayout *vlayout = new QVBoxLayout; + setInstanceName("Ride Plot Window"); - QHBoxLayout *showLayout = new QHBoxLayout; - QLabel *showLabel = new QLabel(tr("Show:"), this); - showLayout->addWidget(showLabel); + QWidget *c = new QWidget; + QVBoxLayout *cl = new QVBoxLayout(c); + setControls(c); + + setContentsMargins(0,0,0,0); + + // setup the controls + QLabel *showLabel = new QLabel(tr("Show"), c); + cl->addWidget(showLabel); showStack = new QCheckBox(tr("Stacked view"), this); - - if (settings->value(GC_RIDE_PLOT_STACK).toInt()) + if (appsettings->value(this, GC_RIDE_PLOT_STACK).toInt()) showStack->setCheckState(Qt::Checked); else showStack->setCheckState(Qt::Unchecked); - showLayout->addWidget(showStack); + cl->addWidget(showStack); stackWidth = 15; - - QLabel *labelspacer = new QLabel(this); - labelspacer->setFixedWidth(5); - showLayout->addWidget(labelspacer); - stackZoomUp = new QwtArrowButton(1, Qt::UpArrow,this); stackZoomUp->setFixedHeight(15); stackZoomUp->setFixedWidth(15); stackZoomUp->setEnabled(false); stackZoomUp->setContentsMargins(0,0,0,0); stackZoomUp->setFlat(true); - showLayout->addWidget(stackZoomUp); + cl->addWidget(stackZoomUp); stackZoomDown = new QwtArrowButton(1, Qt::DownArrow,this); stackZoomDown->setFixedHeight(15); @@ -85,52 +84,49 @@ AllPlotWindow::AllPlotWindow(MainWindow *mainWindow) : stackZoomDown->setEnabled(false); stackZoomDown->setContentsMargins(0,0,0,0); stackZoomDown->setFlat(true); - showLayout->addWidget(stackZoomDown); + cl->addWidget(stackZoomDown); - QCheckBox *showGrid = new QCheckBox(tr("Grid"), this); + showGrid = new QCheckBox(tr("Grid"), this); showGrid->setCheckState(Qt::Checked); - showLayout->addWidget(showGrid); + cl->addWidget(showGrid); showHr = new QCheckBox(tr("Heart Rate"), this); showHr->setCheckState(Qt::Checked); - showLayout->addWidget(showHr); + cl->addWidget(showHr); showSpeed = new QCheckBox(tr("Speed"), this); showSpeed->setCheckState(Qt::Checked); - showLayout->addWidget(showSpeed); + cl->addWidget(showSpeed); showCad = new QCheckBox(tr("Cadence"), this); showCad->setCheckState(Qt::Checked); - showLayout->addWidget(showCad); + cl->addWidget(showCad); showAlt = new QCheckBox(tr("Altitude"), this); showAlt->setCheckState(Qt::Checked); - showLayout->addWidget(showAlt); + cl->addWidget(showAlt); showPower = new QComboBox(); showPower->addItem(tr("Power + shade")); showPower->addItem(tr("Power - shade")); showPower->addItem(tr("No Power")); - showLayout->addWidget(showPower); + cl->addWidget(showPower); + if (appsettings->value(this, GC_SHADEZONES, true).toBool() == true) + showPower->setCurrentIndex(0); + else + showPower->setCurrentIndex(1); - // shade zones defaults will come in with a - // future patch. For now we have place holder - // to update when new config arrives - if (true) showPower->setCurrentIndex(0); - else showPower->setCurrentIndex(1); - - QHBoxLayout *smoothLayout = new QHBoxLayout; - QComboBox *comboDistance = new QComboBox(); + comboDistance = new QComboBox(); comboDistance->addItem(tr("X Axis Shows Time")); comboDistance->addItem(tr("X Axis Shows Distance")); - smoothLayout->addWidget(comboDistance); + cl->addWidget(comboDistance); QLabel *smoothLabel = new QLabel(tr("Smoothing (secs)"), this); smoothLineEdit = new QLineEdit(this); smoothLineEdit->setFixedWidth(40); - smoothLayout->addWidget(smoothLabel); - smoothLayout->addWidget(smoothLineEdit); + cl->addWidget(smoothLabel); + cl->addWidget(smoothLineEdit); smoothSlider = new QSlider(Qt::Horizontal); smoothSlider->setTickPosition(QSlider::TicksBelow); smoothSlider->setTickInterval(10); @@ -139,9 +135,13 @@ AllPlotWindow::AllPlotWindow(MainWindow *mainWindow) : smoothLineEdit->setValidator(new QIntValidator(smoothSlider->minimum(), smoothSlider->maximum(), smoothLineEdit)); - smoothLayout->addWidget(smoothSlider); + cl->addWidget(smoothSlider); + cl->addStretch(); allPlot = new AllPlot(this, mainWindow); + allPlot->setInstanceName("allPlot"); + allPlot->setContentsMargins(0,0,0,0); + smoothSlider->setValue(allPlot->smooth); smoothLineEdit->setText(QString("%1").arg(allPlot->smooth)); @@ -201,22 +201,44 @@ AllPlotWindow::AllPlotWindow(MainWindow *mainWindow) : allMarker2->setLabelAlignment(Qt::AlignTop|Qt::AlignRight); allPlot->allMarker2=allMarker2; + // Container widgets should not paint + // since they tend to use naff defaults and + // 'complicate' or 'make busy' the general + // look and feel + QPalette palette; + palette.setBrush(QPalette::Background, Qt::NoBrush); + // // stack view // + stackPlotLayout = new QVBoxLayout(); + stackPlotLayout->setSpacing(0); + stackPlotLayout->setContentsMargins(0,0,0,0); + stackWidget = new QWidget(); + stackWidget->setAutoFillBackground(false); + stackWidget->setPalette(palette); + stackWidget->setLayout(stackPlotLayout); + stackFrame = new QScrollArea(); stackFrame->hide(); - stackPlotLayout = new QVBoxLayout(); - stackWidget = new QWidget(); - stackWidget->setLayout(stackPlotLayout); + stackFrame->setPalette(palette); + stackFrame->setAutoFillBackground(false); stackFrame->setWidgetResizable(true); stackFrame->setWidget(stackWidget); + stackFrame->setFrameStyle(QFrame::NoFrame); + stackFrame->setContentsMargins(0,0,0,0); // // allPlot view // QVBoxLayout *allPlotLayout = new QVBoxLayout; + allPlotLayout->setSpacing(0); + allPlotLayout->setContentsMargins(0,0,0,0); allPlotFrame = new QScrollArea(); + allPlotFrame->setFrameStyle(QFrame::NoFrame); + allPlotFrame->setAutoFillBackground(false); + allPlotFrame->setPalette(palette); + allPlotFrame->setContentsMargins(0,0,0,0); spanSlider = new QxtSpanSlider(Qt::Horizontal); spanSlider->setHandleMovementMode(QxtSpanSlider::NoOverlapping); @@ -251,6 +273,7 @@ AllPlotWindow::AllPlotWindow(MainWindow *mainWindow) : #endif fullPlot = new AllPlot(this, mainWindow); + fullPlot->setInstanceName("fullPlot"); fullPlot->grid->enableY(false); fullPlot->setCanvasBackground(GColor(CPLOTTHUMBNAIL)); fullPlot->setCanvasLineWidth(0); @@ -268,6 +291,8 @@ AllPlotWindow::AllPlotWindow(MainWindow *mainWindow) : // controls... controlsLayout = new QGridLayout; + controlsLayout->setSpacing(0); + controlsLayout->setContentsMargins(5,5,5,5); controlsLayout->addWidget(fullPlot, 0,1); controlsLayout->addWidget(spanSlider, 1,1); controlsLayout->addWidget(scrollLeft,1,0); @@ -281,20 +306,20 @@ AllPlotWindow::AllPlotWindow(MainWindow *mainWindow) : #else controlsLayout->setSpacing(0); #endif + allPlotLayout->addLayout(controlsLayout); + allPlotLayout->setStretch(0,100); + allPlotLayout->setStretch(1,20); + QVBoxLayout *vlayout = new QVBoxLayout(this); + vlayout->setContentsMargins(10,10,10,10); + vlayout->setSpacing(0); vlayout->addWidget(allPlotFrame); vlayout->addWidget(stackFrame); - vlayout->addLayout(controlsLayout); - vlayout->addLayout(showLayout); - vlayout->addLayout(smoothLayout); - vlayout->setStretch(0,100); - vlayout->setStretch(1,100); - vlayout->setStretch(2,15); - vlayout->setStretch(3,1); - vlayout->setStretch(4,1); vlayout->setSpacing(1); setLayout(vlayout); + setContentsMargins(0,0,0,0); + // common controls connect(showPower, SIGNAL(currentIndexChanged(int)), this, SLOT(setShowPower(int))); connect(showHr, SIGNAL(stateChanged(int)), this, SLOT(setShowHr(int))); @@ -318,7 +343,8 @@ AllPlotWindow::AllPlotWindow(MainWindow *mainWindow) : connect(scrollRight, SIGNAL(clicked()), this, SLOT(moveRight())); // GC signals - connect(mainWindow, SIGNAL(rideSelected()), this, SLOT(rideSelected())); + //connect(mainWindow, SIGNAL(rideSelected()), this, SLOT(rideSelected())); + connect(this, SIGNAL(rideItemChanged(RideItem*)), this, SLOT(rideSelected())); connect(mainWindow, SIGNAL(rideDirty()), this, SLOT(rideSelected())); connect(mainWindow, SIGNAL(zonesChanged()), this, SLOT(zonesChanged())); connect(mainWindow, SIGNAL(intervalsChanged()), this, SLOT(intervalsChanged())); @@ -334,8 +360,8 @@ AllPlotWindow::configChanged() // we're going to replot, but only if we're active // and all the other guff - RideItem *ride = mainWindow->rideItem(); - if (mainWindow->activeTab() != this) { + RideItem *ride = myRideItem; + if (!amVisible()) { stale = true; return; } @@ -399,7 +425,7 @@ AllPlotWindow::redrawFullPlot() //fullPlot->setTitle(""); if (fullPlot->bydist) - fullPlot->setAxisScale(QwtPlot::xBottom, + fullPlot->setAxisScale(QwtPlot::xBottom, ride->ride()->dataPoints().first()->km * (fullPlot->useMetricUnits ? 1 : MILES_PER_KM), ride->ride()->dataPoints().last()->km * (fullPlot->useMetricUnits ? 1 : MILES_PER_KM)); else @@ -466,10 +492,10 @@ AllPlotWindow::moveRight() void AllPlotWindow::rideSelected() { - RideItem *ride = mainWindow->rideItem(); + RideItem *ride = myRideItem; // ignore if not active - if (mainWindow->activeTab() != this) { + if (!amVisible()) { stale = true; return; } @@ -509,7 +535,7 @@ AllPlotWindow::rideSelected() void AllPlotWindow::zonesChanged() { - if (mainWindow->activeTab() != this) { + if (!amVisible()) { stale = true; return; } @@ -521,7 +547,7 @@ AllPlotWindow::zonesChanged() void AllPlotWindow::intervalsChanged() { - if (mainWindow->activeTab() != this) { + if (!amVisible()) { stale = true; return; } @@ -544,7 +570,7 @@ AllPlotWindow::intervalsChanged() void AllPlotWindow::intervalSelected() { - if (mainWindow->activeTab() != this) { + if (!amVisible()) { stale = true; return; } @@ -565,6 +591,12 @@ AllPlotWindow::intervalSelected() } } +void +AllPlotWindow::setStacked(int value) +{ + showStack->setChecked(value); +} + void AllPlotWindow::setSmoothingFromSlider() { @@ -606,6 +638,7 @@ AllPlotWindow::setAllPlotWidgets(RideItem *ride) // ride. It also hides/shows widgets depending // upon wether we are in 'normal' mode or // stacked plot mode + if (!ride) return; // checkboxes to show/hide specific data series... const RideFileDataPresent *dataPresent = ride->ride()->areDataPresent(); @@ -682,7 +715,6 @@ AllPlotWindow::setAllPlotWidgets(RideItem *ride) spanSlider->show(); scrollLeft->show(); scrollRight->show(); - stackZoomUp->setEnabled(false); stackZoomDown->setEnabled(false); } @@ -1102,6 +1134,7 @@ AllPlotWindow::stackZoomDownShouldEnable(int sw) else { return true; } + } void @@ -1143,8 +1176,7 @@ AllPlotWindow::showStackChanged(int value) // out of date. Then call setAllPlotWidgets // to make sure all the controls are setup // and the right widgets are hidden/shown. - boost::shared_ptr settings = GetApplicationSettings(); - settings->setValue(GC_RIDE_PLOT_STACK, value); + appsettings->setValue(GC_RIDE_PLOT_STACK, value); if (value) { // refresh plots @@ -1156,7 +1188,6 @@ AllPlotWindow::showStackChanged(int value) } else { // refresh plots redrawAllPlot(); - } // reset the view @@ -1202,6 +1233,9 @@ AllPlotWindow::setupStackPlots() else nbplot = (int)floor(duration/_stackWidth/60)+1; + QPalette palette; + palette.setBrush(QPalette::Background, Qt::NoBrush); + for(int i = 0 ; i < nbplot ; i++) { // calculate the segment of ride this stack plot contains @@ -1221,6 +1255,9 @@ AllPlotWindow::setupStackPlots() // create that plot AllPlot *_allPlot = new AllPlot(this, mainWindow); + _allPlot->setInstanceName("stackPlot"); + _allPlot->setAutoFillBackground(false); + _allPlot->setPalette(palette); // add to the list allPlots.append(_allPlot); @@ -1233,7 +1270,7 @@ AllPlotWindow::setupStackPlots() if (i==0){ // First plot view title and legend - _allPlot->setTitle(allPlot->title()); + //_allPlot->setTitle(allPlot->title()); _allPlot->plotLayout()->setLegendPosition(QwtPlot::TopLegend); _allPlot->setFixedHeight(120+stackWidth*2+50); } @@ -1245,6 +1282,7 @@ AllPlotWindow::setupStackPlots() // No x axis titles _allPlot->setAxisTitle(QwtPlot::xBottom,NULL); +#if 0 // Smaller y axis Titles QFont axisFont = QFont("Helvetica",10, QFont::Normal); QwtText text = _allPlot->axisTitle(QwtPlot::yLeft); @@ -1259,7 +1297,7 @@ AllPlotWindow::setupStackPlots() text = _allPlot->axisTitle(QwtPlot::yRight2); text.setFont(axisFont); _allPlot->setAxisTitle(QwtPlot::yRight2,text); - +#endif _allPlot->setShadeZones(showPower->currentIndex() == 0); // XXX todo - set the showHR, showCad stuff too... @@ -1275,6 +1313,8 @@ AllPlotWindow::setupStackPlots() // set new widgets QWidget *stackWidget = new QWidget; + stackWidget->setPalette(palette); + stackWidget->setAutoFillBackground(false); stackWidget->setLayout(newLayout); stackFrame->setWidget(stackWidget); diff --git a/src/AllPlotWindow.h b/src/AllPlotWindow.h index fb5dd6cb7..93a1357b4 100644 --- a/src/AllPlotWindow.h +++ b/src/AllPlotWindow.h @@ -18,8 +18,10 @@ #ifndef _GC_AllPlotWindow_h #define _GC_AllPlotWindow_h 1 +#include "GoldenCheetah.h" #include +#include // for Q_PROPERTY class AllPlot; class MainWindow; @@ -35,9 +37,26 @@ class QxtGroupBox; #include "LTMWindow.h" // for tooltip/canvaspicker -class AllPlotWindow : public QWidget +class AllPlotWindow : public GcWindow { Q_OBJECT + G_OBJECT + + // the plot properties are used by the layout manager + // to save and restore the plot parameters, this is so + // we can have multiple windows open at once with different + // settings managed by the user. + // in this example we might show a stacked plot and a ride + // plot at the same time. + Q_PROPERTY(bool stacked READ isStacked WRITE setStacked USER true) + Q_PROPERTY(int showGrid READ isShowGrid WRITE setShowGrid USER true) + Q_PROPERTY(int showHr READ isShowHr WRITE setShowHr USER true) + Q_PROPERTY(int showSpeed READ isShowSpeed WRITE setShowSpeed USER true) + Q_PROPERTY(int showCad READ isShowCad WRITE setShowCad USER true) + Q_PROPERTY(int showAlt READ isShowAlt WRITE setShowAlt USER true) + Q_PROPERTY(int showPower READ isShowPower WRITE setShowPower USER true) + Q_PROPERTY(int byDistance READ isByDistance WRITE setByDistance USER true) + Q_PROPERTY(int smoothing READ smoothing WRITE setSmoothing USER true) public: @@ -53,6 +72,17 @@ class AllPlotWindow : public QWidget // zoom to interval range (via span-slider) void zoomInterval(IntervalItem *); + // get properties - the setters are below + bool isStacked() const { return showStack->isChecked(); } + int isShowGrid() const { return showGrid->checkState(); } + int isShowHr() const { return showHr->checkState(); } + int isShowSpeed() const { return showSpeed->checkState(); } + int isShowCad() const { return showCad->checkState(); } + int isShowAlt() const { return showAlt->checkState(); } + int isShowPower() const { return showPower->currentIndex(); } + int isByDistance() const { return comboDistance->currentIndex(); } + int smoothing() const { return smoothSlider->value(); } + public slots: // trap GC signals @@ -62,15 +92,11 @@ class AllPlotWindow : public QWidget void intervalsChanged(); void configChanged(); - // trap child widget signals + // set properties void setSmoothingFromSlider(); void setSmoothingFromLineEdit(); void setStackZoomUp(); void setStackZoomDown(); - void zoomChanged(); - void moveLeft(); - void moveRight(); - void showStackChanged(int state); void setShowPower(int state); void setShowHr(int state); void setShowSpeed(int state); @@ -79,6 +105,13 @@ class AllPlotWindow : public QWidget void setShowGrid(int state); void setSmoothing(int value); void setByDistance(int value); + void setStacked(int value); + + // trap widget signals + void zoomChanged(); + void moveLeft(); + void moveRight(); + void showStackChanged(int state); protected: @@ -114,11 +147,13 @@ class AllPlotWindow : public QWidget // Common controls QGridLayout *controlsLayout; QCheckBox *showStack; + QCheckBox *showGrid; QCheckBox *showHr; QCheckBox *showSpeed; QCheckBox *showCad; QCheckBox *showAlt; QComboBox *showPower; + QComboBox *comboDistance; QSlider *smoothSlider; QLineEdit *smoothLineEdit; QxtSpanSlider *spanSlider; @@ -148,4 +183,3 @@ class AllPlotWindow : public QWidget }; #endif // _GC_AllPlotWindow_h - diff --git a/src/AthleteTool.cpp b/src/AthleteTool.cpp new file mode 100644 index 000000000..05023ea06 --- /dev/null +++ b/src/AthleteTool.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2010 Mark Liversedge (liversedge@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 2 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., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "GoldenCheetah.h" +#include "GcWindowRegistry.h" +#include "AthleteTool.h" +#include + +AthleteTool::AthleteTool(QString path, QWidget *parent) : QWidget(parent) +{ + QVBoxLayout *mainLayout = new QVBoxLayout(this); + mainLayout->setContentsMargins(0,0,0,0); + setLayout(mainLayout); + + athleteTree = new QTreeWidget; + mainLayout->addWidget(athleteTree); + + // setup the athlete list + athleteTree->setColumnCount(1); + athleteTree->setSelectionMode(QAbstractItemView::SingleSelection); + athleteTree->header()->hide(); + athleteTree->setAlternatingRowColors (true); + athleteTree->setIndentation(5); + athleteTree->setDragDropMode(QAbstractItemView::DragOnly); + allAthletes = new QTreeWidgetItem(athleteTree, ROOT_TYPE); + allAthletes->setText(0, tr("Athletes")); + + // get a dir list of athletes + // initialise the metrics catalogue and user selector + foreach (QString name, QDir(path).entryList(QDir::Dirs | QDir::NoDotAndDotDot)) { + + // add to the tree + QTreeWidgetItem *add; + add = new QTreeWidgetItem(allAthletes, 0); + add->setText(0, name); + add->setFlags(add->flags() | Qt::ItemIsDragEnabled); + } + athleteTree->expandItem(allAthletes); + + connect(athleteTree,SIGNAL(itemSelectionChanged()), this, SLOT(athleteTreeWidgetSelectionChanged())); +} + +/*---------------------------------------------------------------------- + * Selections Made + *----------------------------------------------------------------------*/ + +void +AthleteTool::athleteTreeWidgetSelectionChanged() +{ + athleteSelected(); +} diff --git a/src/AthleteTool.h b/src/AthleteTool.h new file mode 100644 index 000000000..d0522a5ed --- /dev/null +++ b/src/AthleteTool.h @@ -0,0 +1,64 @@ + +/* + * Copyright (c) 2010 Mark Liversedge (liversedge@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 2 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., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _GC_AthleteTool_h +#define _GC_AthleteTool_h 1 +#include "GoldenCheetah.h" + +#include "MainWindow.h" +#include "Season.h" +#include "RideMetric.h" +#include "LTMSettings.h" + +#include +#include + +// tree widget types +#define ROOT_TYPE 1 +#define CHART_TYPE 3 + +// list of athletes to add to a window +class AthleteTool : public QWidget +{ + Q_OBJECT + G_OBJECT + + + public: + + AthleteTool(QString path, QWidget *parent = 0); + + QTreeWidgetItem *selectedAthlete() { return athleteTree->selectedItems().first(); } + + signals: + + void athleteSelected(); + + private slots: + void athleteTreeWidgetSelectionChanged(); + + private: + + QTreeWidget *athleteTree; + QTreeWidgetItem *allAthletes; + +}; + +#endif // _GC_AthleteTool_h + diff --git a/src/BasicRideMetrics.cpp b/src/BasicRideMetrics.cpp index dc0eab576..28095ac75 100644 --- a/src/BasicRideMetrics.cpp +++ b/src/BasicRideMetrics.cpp @@ -1,27 +1,54 @@ -/* +/* * Copyright (c) 2008 Sean C. Rhea (srhea@srhea.net) * * 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 2 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., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "RideMetric.h" +#include "LTMOutliers.h" #include "Units.h" +#include "math.h" #include +#include #define tr(s) QObject::tr(s) +class RideCount : public RideMetric { + public: + + RideCount() + { + setSymbol("ride_count"); + setName(tr("Rides")); + setMetricUnits(tr("")); + setImperialUnits(tr("")); + } + + void compute(const RideFile *, const Zones *, int, + const HrZones *, int, + const QHash &, + const MainWindow *) { + setValue(1); + } + RideMetric *clone() const { return new RideCount(*this); } +}; + +static bool countAdded = + RideMetricFactory::instance().addMetric(RideCount()); + +////////////////////////////////////////////////////////////////////////////// class WorkoutTime : public RideMetric { double seconds; @@ -35,8 +62,10 @@ class WorkoutTime : public RideMetric { setImperialUnits(tr("seconds")); } - void compute(const RideFile *ride, const Zones *, int, const HrZones *, int, - const QHash &) { + void compute(const RideFile *ride, const Zones *, int, + const HrZones *, int, + const QHash &, + const MainWindow *) { seconds = ride->dataPoints().back()->secs - ride->dataPoints().front()->secs + ride->recIntSecs(); setValue(seconds); @@ -61,8 +90,10 @@ class TimeRiding : public RideMetric { setMetricUnits(tr("seconds")); setImperialUnits(tr("seconds")); } - void compute(const RideFile *ride, const Zones *, int, const HrZones *, int, - const QHash &) { + void compute(const RideFile *ride, const Zones *, int, + const HrZones *, int, + const QHash &, + const MainWindow *) { secsMovingOrPedaling = 0; foreach (const RideFilePoint *point, ride->dataPoints()) { if ((point->kph > 0.0) || (point->cad > 0.0)) @@ -97,8 +128,10 @@ class TotalDistance : public RideMetric { setPrecision(1); setConversion(MILES_PER_KM); } - void compute(const RideFile *ride, const Zones *, int, const HrZones *, int, - const QHash &) { + void compute(const RideFile *ride, const Zones *, int, + const HrZones *, int, + const QHash &, + const MainWindow *) { // Note: The 'km' in each sample is the distance travelled by the // *end* of the sampling period. The last term in this equation // accounts for the distance traveled *during* the first sample. @@ -131,8 +164,10 @@ class ElevationGain : public RideMetric { setImperialUnits(tr("feet")); setConversion(FEET_PER_METER); } - void compute(const RideFile *ride, const Zones *, int, const HrZones *, int, - const QHash &) { + void compute(const RideFile *ride, const Zones *, int, + const HrZones *, int, + const QHash &, + const MainWindow *) { const double hysteresis = 3.0; bool first = true; foreach (const RideFilePoint *point, ride->dataPoints()) { @@ -170,8 +205,10 @@ class TotalWork : public RideMetric { setMetricUnits(tr("kJ")); setImperialUnits(tr("kJ")); } - void compute(const RideFile *ride, const Zones *, int, const HrZones *, int, - const QHash &) { + void compute(const RideFile *ride, const Zones *, int, + const HrZones *, int, + const QHash &, + const MainWindow *) { foreach (const RideFilePoint *point, ride->dataPoints()) { if (point->watts >= 0.0) joules += point->watts * ride->recIntSecs(); @@ -203,8 +240,10 @@ class AvgSpeed : public RideMetric { setConversion(MILES_PER_KM); } - void compute(const RideFile *ride, const Zones *, int, const HrZones *, int, - const QHash &deps) { + void compute(const RideFile *ride, const Zones *, int, + const HrZones *, int, + const QHash &deps, + const MainWindow *) { assert(deps.contains("total_distance")); km = deps.value("total_distance")->value(true); foreach (const RideFilePoint *point, ride->dataPoints()) @@ -213,7 +252,7 @@ class AvgSpeed : public RideMetric { setValue(secsMoving ? km / secsMoving * 3600.0 : 0.0); } - void aggregateWith(const RideMetric &other) { + void aggregateWith(const RideMetric &other) { assert(symbol() == other.symbol()); const AvgSpeed &as = dynamic_cast(other); secsMoving += as.secsMoving; @@ -242,8 +281,10 @@ struct AvgPower : public RideMetric { setImperialUnits(tr("watts")); setType(RideMetric::Average); } - void compute(const RideFile *ride, const Zones *, int, const HrZones *, int, - const QHash &) { + void compute(const RideFile *ride, const Zones *, int, + const HrZones *, int, + const QHash &, + const MainWindow *) { total = count = 0; foreach (const RideFilePoint *point, ride->dataPoints()) { if (point->watts >= 0.0) { @@ -274,8 +315,10 @@ struct AvgHeartRate : public RideMetric { setImperialUnits(tr("bpm")); setType(RideMetric::Average); } - void compute(const RideFile *ride, const Zones *, int, const HrZones *, int, - const QHash &) { + void compute(const RideFile *ride, const Zones *, int, + const HrZones *, int, + const QHash &, + const MainWindow *) { total = count = 0; foreach (const RideFilePoint *point, ride->dataPoints()) { if (point->hr > 0) { @@ -306,8 +349,10 @@ struct AvgCadence : public RideMetric { setImperialUnits(tr("rpm")); setType(RideMetric::Average); } - void compute(const RideFile *ride, const Zones *, int, const HrZones *, int, - const QHash &) { + void compute(const RideFile *ride, const Zones *, int, + const HrZones *, int, + const QHash &, + const MainWindow *) { total = count = 0; foreach (const RideFilePoint *point, ride->dataPoints()) { if (point->cad > 0) { @@ -337,8 +382,10 @@ class MaxPower : public RideMetric { setImperialUnits(tr("watts")); setType(RideMetric::Peak); } - void compute(const RideFile *ride, const Zones *, int, const HrZones *, int, - const QHash &) { + void compute(const RideFile *ride, const Zones *, int, + const HrZones *, int, + const QHash &, + const MainWindow *) { foreach (const RideFilePoint *point, ride->dataPoints()) { if (point->watts >= max) max = point->watts; @@ -364,8 +411,10 @@ class MaxHr : public RideMetric { setImperialUnits(tr("bpm")); setType(RideMetric::Peak); } - void compute(const RideFile *ride, const Zones *, int, const HrZones *, int, - const QHash &) { + void compute(const RideFile *ride, const Zones *, int, + const HrZones *, int, + const QHash &, + const MainWindow *) { foreach (const RideFilePoint *point, ride->dataPoints()) { if (point->hr >= max) max = point->hr; @@ -391,8 +440,10 @@ class NinetyFivePercentHeartRate : public RideMetric { setImperialUnits(tr("bpm")); setType(RideMetric::Average); } - void compute(const RideFile *ride, const Zones *, int, const HrZones *, int, - const QHash &) { + void compute(const RideFile *ride, const Zones *, int, + const HrZones *, int, + const QHash &, + const MainWindow *) { QVector hrs; foreach (const RideFilePoint *point, ride->dataPoints()) { if (point->hr >= 0.0) @@ -410,3 +461,167 @@ class NinetyFivePercentHeartRate : public RideMetric { static bool ninetyFivePercentHeartRateAdded = RideMetricFactory::instance().addMetric(NinetyFivePercentHeartRate()); +/////////////////////////////////////////////////////////////////////////////// + +class VAM : public RideMetric { + + public: + VAM() + { + setSymbol("vam"); + setName(tr("VAM")); + setImperialUnits(""); + setMetricUnits(""); + setType(RideMetric::Average); + } + void compute(const RideFile *, const Zones *, int, + const HrZones *, int, + const QHash &deps, + const MainWindow *) { + + ElevationGain *el = dynamic_cast(deps.value("elevation_gain")); + WorkoutTime *wt = dynamic_cast(deps.value("workout_time")); + setValue((el->value(true)*3600)/wt->value(true)); + } + RideMetric *clone() const { return new VAM(*this); } +}; + +static bool addVam() +{ + QVector deps; + deps.append("elevation_gain"); + deps.append("workout_time"); + RideMetricFactory::instance().addMetric(VAM(), &deps); + return true; +} + +static bool vamAdded = addVam(); + +/////////////////////////////////////////////////////////////////////////////// + +class Gradient : public RideMetric { + + public: + Gradient() + { + setSymbol("gradient"); + setName(tr("Gradient")); + setImperialUnits("%"); + setMetricUnits("%"); + setPrecision(1); + setType(RideMetric::Average); + } + void compute(const RideFile *, const Zones *, int, + const HrZones *, int, + const QHash &deps, + const MainWindow *) { + + ElevationGain *el = dynamic_cast(deps.value("elevation_gain")); + TotalDistance *td = dynamic_cast(deps.value("total_distance")); + if (td->value(true) && el->value(true)) { + setValue(100.00 *(el->value(true) / (1000 *td->value(true)))); + } else + setValue(0.0); + } + RideMetric *clone() const { return new Gradient(*this); } +}; + +static bool addGradient() +{ + QVector deps; + deps.append("elevation_gain"); + deps.append("total_distance"); + RideMetricFactory::instance().addMetric(Gradient(), &deps); + return true; +} + +static bool gradientAdded = addGradient(); + +/////////////////////////////////////////////////////////////////////////////// + +class MeanPowerVariance : public RideMetric { + + public: + LTMOutliers *outliers; + + MeanPowerVariance() + { + outliers = NULL; + setSymbol("meanpowervariance"); + setName(tr("Average Power Variance")); + setImperialUnits("watts change"); + setMetricUnits("watts change"); + setPrecision(2); + setType(RideMetric::Average); + } + void compute(const RideFile *ride, const Zones *, int, + const HrZones *, int, + const QHash &, + const MainWindow *) { + + // Less than 30s don't bother + if (ride->dataPoints().count() < 30) + setValue(0); + else { + + QVector power; + QVector secs; + foreach (RideFilePoint *point, ride->dataPoints()) { + power.append(point->watts); + secs.append(point->secs); + } + + if (outliers) delete outliers; + outliers = new LTMOutliers(secs.data(), power.data(), power.count(), 30, false); + setValue(outliers->getStdDeviation()); + } + } + RideMetric *clone() const { return new MeanPowerVariance(*this); } +}; + +static bool addMeanPowerVariance() +{ + RideMetricFactory::instance().addMetric(MeanPowerVariance()); + return true; +} + +static bool meanPowerVarianceAdded = addMeanPowerVariance(); + +/////////////////////////////////////////////////////////////////////////////// + +class MaxPowerVariance : public RideMetric { + + public: + MaxPowerVariance() + { + setSymbol("maxpowervariance"); + setName(tr("Max Power Variance")); + setImperialUnits("watts change"); + setMetricUnits("watts change"); + setPrecision(2); + setType(RideMetric::Average); + } + void compute(const RideFile *ride, const Zones *, int, + const HrZones *, int, + const QHash &deps, + const MainWindow *) { + + MeanPowerVariance *mean = dynamic_cast(deps.value("meanpowervariance")); + if (ride->dataPoints().count() < 30) + setValue(0); + else + setValue(mean->outliers->getYForRank(0)); + } + RideMetric *clone() const { return new MaxPowerVariance(*this); } +}; + +static bool addMaxPowerVariance() +{ + QVector deps; + deps.append("meanpowervariance"); + RideMetricFactory::instance().addMetric(MaxPowerVariance(), &deps); + return true; +} + +static bool maxPowerVarianceAdded = addMaxPowerVariance(); + diff --git a/src/BestIntervalDialog.cpp b/src/BestIntervalDialog.cpp index 4baf28e54..202b53325 100644 --- a/src/BestIntervalDialog.cpp +++ b/src/BestIntervalDialog.cpp @@ -256,6 +256,9 @@ BestIntervalDialog::findBests(const RideFile *ride, double windowSizeSecs, double totalWatts = 0.0; QList window; + // ride is shorter than the window size! + if (windowSizeSecs > ride->dataPoints().last()->secs + secsDelta) return; + // We're looking for intervals with durations in [windowSizeSecs, windowSizeSecs + secsDelta). foreach (const RideFilePoint *point, ride->dataPoints()) { // Discard points until interval duration is < windowSizeSecs + secsDelta. diff --git a/src/BestIntervalDialog.h b/src/BestIntervalDialog.h index aee052341..df45509ba 100644 --- a/src/BestIntervalDialog.h +++ b/src/BestIntervalDialog.h @@ -18,6 +18,7 @@ #ifndef _GC_BestIntervalDialog_h #define _GC_BestIntervalDialog_h 1 +#include "GoldenCheetah.h" #include @@ -27,6 +28,8 @@ class RideFile; class BestIntervalDialog : public QDialog { Q_OBJECT + G_OBJECT + public: diff --git a/src/BikeScore.cpp b/src/BikeScore.cpp index 78a5b60f7..181a12629 100644 --- a/src/BikeScore.cpp +++ b/src/BikeScore.cpp @@ -1,16 +1,16 @@ -/* +/* * Copyright (c) 2008 Sean C. Rhea (srhea@srhea.net) * * 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 2 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., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA @@ -48,8 +48,10 @@ class XPower : public RideMetric { setImperialUnits(tr("watts")); } - void compute(const RideFile *ride, const Zones *, int, const HrZones *, int, - const QHash &) { + void compute(const RideFile *ride, const Zones *, int, + const HrZones *, int, + const QHash &, + const MainWindow *) { static const double EPSILON = 0.1; static const double NEGLIGIBLE = 0.1; @@ -58,7 +60,7 @@ class XPower : public RideMetric { double sampsPerWindow = 25.0 / secsDelta; double attenuation = sampsPerWindow / (sampsPerWindow + secsDelta); double sampleWeight = secsDelta / (sampsPerWindow + secsDelta); - + double lastSecs = 0.0; double weighted = 0.0; @@ -104,8 +106,10 @@ class VariabilityIndex : public RideMetric { setPrecision(3); } - void compute(const RideFile *, const Zones *, int, const HrZones *, int, - const QHash &deps) { + void compute(const RideFile *, const Zones *, int, + const HrZones *, int, + const QHash &deps, + const MainWindow *) { assert(deps.contains("skiba_xpower")); assert(deps.contains("average_power")); XPower *xp = dynamic_cast(deps.value("skiba_xpower")); @@ -135,8 +139,10 @@ class RelativeIntensity : public RideMetric { setImperialUnits(tr("")); setPrecision(3); } - void compute(const RideFile *, const Zones *zones, int zoneRange, const HrZones *, int, - const QHash &deps) { + void compute(const RideFile *, const Zones *zones, int zoneRange, + const HrZones *, int, + const QHash &deps, + const MainWindow *) { if (zones && zoneRange >= 0) { assert(deps.contains("skiba_xpower")); XPower *xp = dynamic_cast(deps.value("skiba_xpower")); @@ -150,7 +156,7 @@ class RelativeIntensity : public RideMetric { // added djconnel: allow RI to be combined across rides bool canAggregate() const { return true; } - void aggregateWith(const RideMetric &other) { + void aggregateWith(const RideMetric &other) { assert(symbol() == other.symbol()); const RelativeIntensity &ap = dynamic_cast(other); reli = secs * pow(reli, bikeScoreN) + ap.count() * pow(ap.value(true), bikeScoreN); @@ -176,8 +182,10 @@ class BikeScore : public RideMetric { setImperialUnits(""); } - void compute(const RideFile *, const Zones *zones, int zoneRange,const HrZones *, int, - const QHash &deps) { + void compute(const RideFile *, const Zones *zones, int zoneRange, + const HrZones *, int, + const QHash &deps, + const MainWindow *) { if (!zones || zoneRange < 0) return; diff --git a/src/BinRideFile.cpp b/src/BinRideFile.cpp index 902a0ee82..629530a68 100644 --- a/src/BinRideFile.cpp +++ b/src/BinRideFile.cpp @@ -199,7 +199,7 @@ struct BinFileReaderState *sum += (0xff & c1) + (0xff & c2); if (count) *count += 2; - + return 256*(0xff & c1) + (0xff & c2); } @@ -254,7 +254,7 @@ struct BinFileReaderState int i = 0; QString deviceInfo = ""; QDateTime t; - + foreach(const BinField &field, def.fields) { if (!global_format_identifiers.contains(field.id)) { unknown_format_identifiers.insert(field.id); @@ -424,7 +424,7 @@ struct BinFileReaderState int i = 0; int temperature_count = 0; double temperature = 0.0; - + foreach(const BinField &field, def.fields) { if (!global_format_identifiers.contains(field.id)) { unknown_format_identifiers.insert(field.id); @@ -484,7 +484,6 @@ struct BinFileReaderState unknown_format_identifiers.insert(field.id); } else { int value = values[i++]; - bool b0 = (value % 2); bool b1 = (value / 2>1); bool b2 = (value / 4>1); @@ -493,12 +492,12 @@ struct BinFileReaderState bool b5 = (value / 32>1); bool b6 = (value / 64>1); bool b7 = (value / 128>1); - + QString b = QString("DataError : %1 %2 %3 %4 %5 %6 %7 %8").arg(b0?"0":"").arg(b1?"1":"").arg(b2?"2":"").arg(b3?"3":"").arg(b4?"4":"").arg(b5?"5":"").arg(b6?"6":"").arg(b7?"7":""); - + switch (field.id) { case FORMAT_ID__DROPOUT_FLAGS : - + //errors << QString("DataError field.id %1 value %2").arg(field.id).arg(b); unused_format_identifiers_for_record_types[def.format_identifier].insert(field.id); break; diff --git a/src/BinRideFile.h b/src/BinRideFile.h index ac7bf1456..5583d8d55 100644 --- a/src/BinRideFile.h +++ b/src/BinRideFile.h @@ -18,6 +18,7 @@ #ifndef _BinRideFile_h #define _BinRideFile_h +#include "GoldenCheetah.h" #include "RideFile.h" diff --git a/src/CalendarDownload.cpp b/src/CalendarDownload.cpp new file mode 100644 index 000000000..84220fd91 --- /dev/null +++ b/src/CalendarDownload.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2010 Mark Liversedge (liversedge@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 2 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., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "CalendarDownload.h" +#ifdef GC_HAVE_ICAL +#include "ICalendar.h" +#include +#endif + +CalendarDownload::CalendarDownload(MainWindow *main) : main(main) +{ + nam = new QNetworkAccessManager(this); + connect(nam, SIGNAL(finished(QNetworkReply*)), this, SLOT(downloadFinished(QNetworkReply*))); +} + +bool +CalendarDownload::download() +{ + QString request = appsettings->cvalue(main->cyclist, GC_WEBCAL_URL, "").toString(); + if (request == "") return false; + else { + // change webcal to http, since it is basically the same port + QRegExp webcal("^webcal:"); + request.replace(webcal, QString("http:")); + } + + QNetworkReply *reply = nam->get(QNetworkRequest(QUrl(request))); + + if (reply->error() != QNetworkReply::NoError) { + QMessageBox::warning(main, tr("Calendar Data Download"), reply->errorString()); + return false; + } + return true; +} + +#ifdef GC_HAVE_ICAL +static QString propertyToString(icalproperty *p) +{ + if (p) { + icalvalue *v = icalproperty_get_value(p); + QString converted(icalvalue_as_ical_string(v)); + + // some special characters are escaped in the text + converted.replace("\\n", "\n"); + converted.replace("\\;", ";"); + + return converted; + } else { + return QString(""); + } +} + +static QDate propertyToDate(icalproperty *p) +{ + if (p) { + icalvalue *v = icalproperty_get_value(p); + struct icaltimetype date = icalvalue_get_datetime(v); + QDate when(date.year, date.month, date.day); + return when; + } else { + return QDate(); + } +} +#endif + +void +CalendarDownload::downloadFinished(QNetworkReply *reply) +{ + QString fulltext = reply->readAll(); + QStringList errors; + +#ifdef GC_HAVE_ICAL + QString remoteCache = main->home.absolutePath()+"/remote.ics"; + QFile remoteCacheFile(remoteCache); + + if (fulltext != "") { + + // update remote cache - write to it! + remoteCacheFile.open(QFile::ReadWrite | QFile::Text); + QTextStream out(&remoteCacheFile); + out << fulltext; + remoteCacheFile.close(); + + } else { + QMessageBox msgBox; + msgBox.setText("Remote Calendar not available, reverting to cached workouts."); + msgBox.setIcon(QMessageBox::Information); + msgBox.exec(); + + // read cache + // read in the whole thing + remoteCacheFile.open(QFile::ReadOnly | QFile::Text); + QTextStream in(&remoteCacheFile); + fulltext = in.readAll(); + remoteCacheFile.close(); + } + + main->rideCalendar->refreshRemote(fulltext); +#endif +} diff --git a/src/CalendarDownload.h b/src/CalendarDownload.h new file mode 100644 index 000000000..65555b867 --- /dev/null +++ b/src/CalendarDownload.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2010 Mark Liversedge (liversedge@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 2 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., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _Gc_CalendarDownload_h +#define _Gc_CalendarDownload_h +#include "GoldenCheetah.h" + +#include +#include +#include +#include "MainWindow.h" +#include "Settings.h" +/* #include "CalendarParser.h" */ +#include "MetricAggregator.h" + +class CalendarDownload : public QObject +{ + Q_OBJECT + G_OBJECT + + +public: + CalendarDownload(MainWindow *main); + + bool download(); +public slots: + void downloadFinished(QNetworkReply *reply); + +private: + MainWindow *main; + QNetworkAccessManager *nam; +/* CalendarParser *parser; */ +}; +#endif diff --git a/src/ChooseCyclistDialog.cpp b/src/ChooseCyclistDialog.cpp index edb2b1e04..febfd2539 100644 --- a/src/ChooseCyclistDialog.cpp +++ b/src/ChooseCyclistDialog.cpp @@ -1,16 +1,16 @@ -/* +/* * Copyright (c) 2006 Sean C. Rhea (srhea@srhea.net) * * 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 2 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., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA @@ -19,7 +19,7 @@ #include "ChooseCyclistDialog.h" #include -ChooseCyclistDialog::ChooseCyclistDialog(const QDir &home, bool allowNew) : +ChooseCyclistDialog::ChooseCyclistDialog(const QDir &home, bool allowNew) : home(home) { setWindowTitle(tr("Choose a Cyclist")); @@ -44,62 +44,62 @@ ChooseCyclistDialog::ChooseCyclistDialog(const QDir &home, bool allowNew) : if (allowNew) connect(newButton, SIGNAL(clicked()), this, SLOT(newClicked())); connect(cancelButton, SIGNAL(clicked()), this, SLOT(cancelClicked())); - connect(listWidget, + connect(listWidget, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), this, SLOT(enableOk(QListWidgetItem*))); connect(listWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), - this, SLOT(accept())); + this, SLOT(accept())); - QHBoxLayout *buttonLayout = new QHBoxLayout; - buttonLayout->addWidget(okButton); + QHBoxLayout *buttonLayout = new QHBoxLayout; + buttonLayout->addWidget(okButton); if (allowNew) - buttonLayout->addWidget(newButton); - buttonLayout->addWidget(cancelButton); + buttonLayout->addWidget(newButton); + buttonLayout->addWidget(cancelButton); QVBoxLayout *mainLayout = new QVBoxLayout(this); mainLayout->addWidget(listWidget); mainLayout->addLayout(buttonLayout); } -QString +QString ChooseCyclistDialog::choice() { return listWidget->currentItem()->text(); } -void +void ChooseCyclistDialog::enableOk(QListWidgetItem *item) { okButton->setEnabled(item != NULL); } -void +void ChooseCyclistDialog::cancelClicked() { reject(); } -QString +QString ChooseCyclistDialog::newCyclistDialog(QDir &homeDir, QWidget *parent) { QDir home(homeDir); bool ok; - QString name = QInputDialog::getText(parent, tr("Create New Cyclist"), + QString name = QInputDialog::getText(parent, tr("Create New Cyclist"), tr("Enter New Cyclist's Name"), QLineEdit::Normal, "", &ok); if (ok && !name.isEmpty()) { if (!home.exists(name)) { if (home.mkdir(name)) return name; - QMessageBox::critical(0, tr("Fatal Error"), - tr("Can't create new directory ") + QMessageBox::critical(0, tr("Fatal Error"), + tr("Can't create new directory ") + home.path() + "/" + name, "OK"); } } return QString(); } -void +void ChooseCyclistDialog::newClicked() { QString name = newCyclistDialog(home, this); diff --git a/src/ChooseCyclistDialog.h b/src/ChooseCyclistDialog.h index e6ef631aa..3184185a5 100644 --- a/src/ChooseCyclistDialog.h +++ b/src/ChooseCyclistDialog.h @@ -1,16 +1,16 @@ -/* +/* * Copyright (c) 2006 Sean C. Rhea (srhea@srhea.net) * * 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 2 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., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA @@ -18,6 +18,7 @@ #ifndef _GC_ChooseCyclistDialog_h #define _GC_ChooseCyclistDialog_h 1 +#include "GoldenCheetah.h" #include #include @@ -26,9 +27,11 @@ class QListWidget; class QListWidgetItem; class QPushButton; -class ChooseCyclistDialog : public QDialog +class ChooseCyclistDialog : public QDialog { Q_OBJECT + G_OBJECT + public: ChooseCyclistDialog(const QDir &home, bool allowNew); diff --git a/src/Coggan.cpp b/src/Coggan.cpp new file mode 100644 index 000000000..f2e35f3a3 --- /dev/null +++ b/src/Coggan.cpp @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2010 Mark Liversedge (liversedge@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 2 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., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "RideMetric.h" +#include "Zones.h" +#include + +#define tr(s) QObject::tr(s) + +class NP : public RideMetric { + double np; + double secs; + + public: + + NP() : np(0.0), secs(0.0) + { + setSymbol("coggan_np"); + setName("NP"); + setType(RideMetric::Average); + setMetricUnits("watts"); + setImperialUnits("watts"); + setPrecision(0); + } + void compute(const RideFile *ride, const Zones *, int, + const HrZones *, int, + const QHash &, + const MainWindow *) { + QVector last30secs; + + double secsDelta = ride->recIntSecs(); + double sampsPerWindow = ceil(30.0 / secsDelta); + last30secs.resize(sampsPerWindow + 1); // add 1 just in case + + double total = 0.0; + int count = 0; + + foreach(const RideFilePoint *point, ride->dataPoints()) { + if (count < sampsPerWindow) { + last30secs[count] = point->watts; + count++; + } else { + + double rolling = 0.0; + for (int i=0; iwatts; + count++; + } + } + if (count > sampsPerWindow) + np = pow(total / (count-sampsPerWindow), 0.25); + secs = count * secsDelta; + + setValue(np); + setCount(secs); + } + RideMetric *clone() const { return new NP(*this); } +}; + +class VI : public RideMetric { + double vi; + double secs; + + public: + + VI() : vi(0.0), secs(0.0) + { + setSymbol("coggam_variability_index"); + setName("VI"); + setType(RideMetric::Average); + setPrecision(3); + } + void compute(const RideFile *, const Zones *, int, + const HrZones *, int, + const QHash &deps, + const MainWindow *) { + assert(deps.contains("coggan_np")); + assert(deps.contains("average_power")); + NP *np = dynamic_cast(deps.value("coggan_np")); + assert(np); + RideMetric *ap = dynamic_cast(deps.value("average_power")); + assert(ap); + vi = np->value(true) / ap->value(true); + secs = np->count(); + + setValue(vi); + setCount(secs); + } + + RideMetric *clone() const { return new VI(*this); } +}; + +class IntensityFactor : public RideMetric { + double rif; + double secs; + + public: + + IntensityFactor() : rif(0.0), secs(0.0) + { + setSymbol("coggan_if"); + setName("IF"); + setType(RideMetric::Average); + setPrecision(3); + } + void compute(const RideFile *, const Zones *zones, int zoneRange, + const HrZones *, int, + const QHash &deps, + const MainWindow *) { + if (zones && zoneRange >= 0) { + assert(deps.contains("coggan_np")); + NP *np = dynamic_cast(deps.value("coggan_np")); + assert(np); + rif = np->value(true) / zones->getCP(zoneRange); + secs = np->count(); + + setValue(rif); + setCount(secs); + } + } + + RideMetric *clone() const { return new IntensityFactor(*this); } +}; + +class TSS : public RideMetric { + double score; + + public: + + TSS() : score(0.0) + { + setSymbol("coggan_tss"); + setName("TSS"); + setType(RideMetric::Total); + } + void compute(const RideFile *, const Zones *zones, int zoneRange, + const HrZones *, int, + const QHash &deps, + const MainWindow *) { + if (!zones || zoneRange < 0) + return; + assert(deps.contains("coggan_np")); + assert(deps.contains("coggan_if")); + NP *np = dynamic_cast(deps.value("coggan_np")); + RideMetric *rif = deps.value("coggan_if"); + assert(rif); + double normWork = np->value(true) * np->count(); + double rawTSS = normWork * rif->value(true); + double workInAnHourAtCP = zones->getCP(zoneRange) * 3600; + score = rawTSS / workInAnHourAtCP * 100.0; + + setValue(score); + } + + RideMetric *clone() const { return new TSS(*this); } +}; + +static bool addAllCoggan() { + RideMetricFactory::instance().addMetric(NP()); + QVector deps; + deps.append("coggan_np"); + RideMetricFactory::instance().addMetric(IntensityFactor(), &deps); + deps.append("coggan_if"); + RideMetricFactory::instance().addMetric(TSS(), &deps); + deps.clear(); + deps.append("coggan_np"); + deps.append("average_power"); + RideMetricFactory::instance().addMetric(VI(), &deps); + return true; +} + +static bool CogganAdded = addAllCoggan(); + diff --git a/src/ColorButton.h b/src/ColorButton.h index ee3f5b4b2..0ae482326 100644 --- a/src/ColorButton.h +++ b/src/ColorButton.h @@ -18,6 +18,7 @@ #ifndef _GC_ColorButton_h #define _GC_ColorButton_h 1 +#include "GoldenCheetah.h" #include #include @@ -25,6 +26,8 @@ class ColorButton : public QPushButton { Q_OBJECT + G_OBJECT + public: ColorButton(QWidget *parent, QString, QColor); diff --git a/src/Colors.cpp b/src/Colors.cpp index 2870454fa..9d09896af 100644 --- a/src/Colors.cpp +++ b/src/Colors.cpp @@ -22,8 +22,9 @@ #include #include "Settings.h" -static Colors ColorList[45] = { +static Colors ColorList[56] = { { "Plot Background", "COLORPLOTBACKGROUND", Qt::white }, + { "Ride Plot Background", "COLORRIDEPLOTBACKGROUND", Qt::black }, { "Plot Thumbnail Background", "COLORPLOTTHUMBNAIL", Qt::gray }, { "Plot Title", "COLORPLOTTITLE", Qt::black }, { "Plot Selection Pen", "COLORPLOTSELECT", Qt::blue }, @@ -55,18 +56,28 @@ static Colors ColorList[45] = { { "Power Zone 8 Shading", "COLORZONE8", Qt::gray }, { "Power Zone 9 Shading", "COLORZONE9", Qt::gray }, { "Power Zone 10 Shading", "COLORZONE10", Qt::gray }, - { "Heartrate Zone 1 Shading", "COLORHRZONE1", QColor(255,0,255) }, - { "Heartrate Zone 2 Shading", "COLORHRZONE2", QColor(42,0,255) }, - { "Heartrate Zone 3 Shading", "COLORHRZONE3", QColor(0,170,255) }, - { "Heartrate Zone 4 Shading", "COLORHRZONE4", QColor(0,255,128) }, - { "Heartrate Zone 5 Shading", "COLORHRZONE5", QColor(85,255,0) }, - { "Heartrate Zone 6 Shading", "COLORHRZONE6", QColor(255,213,0) }, - { "Heartrate Zone 7 Shading", "COLORHRZONE7", QColor(255,0,0) }, - { "Heartrate Zone 8 Shading", "COLORHRZONE8", Qt::gray }, - { "Heartrate Zone 9 Shading", "COLORHRZONE9", Qt::gray }, - { "Heartrate Zone 10 Shading", "COLORHRZONE10", Qt::gray }, + { "HR Zone 1 Shading", "HRCOLORZONE1", QColor(255,0,255) }, + { "HR Zone 2 Shading", "HRCOLORZONE2", QColor(42,0,255) }, + { "HR Zone 3 Shading", "HRCOLORZONE3", QColor(0,170,255) }, + { "HR Zone 4 Shading", "HRCOLORZONE4", QColor(0,255,128) }, + { "HR Zone 5 Shading", "HRCOLORZONE5", QColor(85,255,0) }, + { "HR Zone 6 Shading", "HRCOLORZONE6", QColor(255,213,0) }, + { "HR Zone 7 Shading", "HRCOLORZONE7", QColor(255,0,0) }, + { "HR Zone 8 Shading", "HRCOLORZONE8", Qt::gray }, + { "HR Zone 9 Shading", "HRCOLORZONE9", Qt::gray }, + { "HR Zone 10 Shading", "HRCOLORZONE10", Qt::gray }, { "Aerolab VE", "COLORAEROVE", Qt::blue }, { "Aerolab Elevation", "COLORAEROEL", Qt::green }, + { "Calendar background", "CCALCELL", Qt::white }, + { "Calendar heading", "CCALHEAD", QColor(230,230,230) }, + { "Calendar Current Selection", "CCALCURRENT", Qt::darkBlue }, + { "Calendar Actual Workout", "CCALACTUAL", Qt::green }, + { "Calendar Planned Workout", "CCALPLANNED", Qt::yellow }, + { "Calendar Today", "CCALTODAY", Qt::cyan }, + { "Pop Up Windows Background", "CPOPUP", Qt::lightGray }, + { "Pop Up Windows Foreground", "CPOPUPTEXT", Qt::white }, + { "Chart Bar Unselected", "CTILEBAR", Qt::gray }, + { "Chart Bar Selected", "CTILEBARSELECT", Qt::yellow }, { "", "", QColor(0,0,0) }, }; @@ -91,10 +102,9 @@ GCColor::invert(QColor color) void GCColor::readConfig() { - boost::shared_ptr settings = GetApplicationSettings(); // read in config settings and populate the color table for (unsigned int i=0; ColorList[i].name != ""; i++) { - QString colortext = settings->value(ColorList[i].setting, "").toString(); + QString colortext = appsettings->value(this, ColorList[i].setting, "").toString(); if (colortext != "") { // color definitions are stored as "r:g:b" QStringList rgb = colortext.split(":"); diff --git a/src/Colors.h b/src/Colors.h index c1d94f641..2b2c01280 100644 --- a/src/Colors.h +++ b/src/Colors.h @@ -18,6 +18,7 @@ #ifndef _GC_Colors_h #define _GC_Colors_h 1 +#include "GoldenCheetah.h" #include #include @@ -35,6 +36,8 @@ struct Colors class GCColor : public QObject { Q_OBJECT + G_OBJECT + public: GCColor(MainWindow*); @@ -50,48 +53,59 @@ class GCColor : public QObject #define GColor(x) GCColor::getColor(x) #define CPLOTBACKGROUND 0 -#define CPLOTTHUMBNAIL 1 -#define CPLOTTITLE 2 -#define CPLOTSELECT 3 -#define CPLOTTRACKER 4 -#define CPLOTMARKER 5 -#define CPLOTGRID 6 -#define CINTERVALHIGHLIGHTER 7 -#define CHEARTRATE 8 -#define CSPEED 9 -#define CPOWER 10 -#define CCP 11 -#define CCADENCE 12 -#define CALTITUDE 13 -#define CALTITUDEBRUSH 14 -#define CWINDSPEED 15 -#define CTORQUE 16 -#define CSTS 17 -#define CLTS 18 -#define CSB 19 -#define CDAILYSTRESS 20 -#define CCALENDARTEXT 21 -#define CZONE1 22 -#define CZONE2 23 -#define CZONE3 24 -#define CZONE4 25 -#define CZONE5 26 -#define CZONE6 27 -#define CZONE7 28 -#define CZONE8 29 -#define CZONE9 30 -#define CZONE10 31 -#define CHZONE1 32 -#define CHZONE2 33 -#define CHZONE3 34 -#define CHZONE4 35 -#define CHZONE5 36 -#define CHZONE6 37 -#define CHZONE7 38 -#define CHZONE8 39 -#define CHZONE9 40 -#define CHZONE10 41 -#define CAEROVE 42 -#define CAEROEL 43 +#define CRIDEPLOTBACKGROUND 1 +#define CPLOTTHUMBNAIL 2 +#define CPLOTTITLE 3 +#define CPLOTSELECT 4 +#define CPLOTTRACKER 5 +#define CPLOTMARKER 6 +#define CPLOTGRID 7 +#define CINTERVALHIGHLIGHTER 8 +#define CHEARTRATE 9 +#define CSPEED 10 +#define CPOWER 11 +#define CCP 12 +#define CCADENCE 13 +#define CALTITUDE 14 +#define CALTITUDEBRUSH 15 +#define CWINDSPEED 16 +#define CTORQUE 17 +#define CSTS 18 +#define CLTS 19 +#define CSB 20 +#define CDAILYSTRESS 21 +#define CCALENDARTEXT 22 +#define CZONE1 23 +#define CZONE2 24 +#define CZONE3 25 +#define CZONE4 26 +#define CZONE5 27 +#define CZONE6 28 +#define CZONE7 29 +#define CZONE8 30 +#define CZONE9 31 +#define CZONE10 32 +#define CHZONE1 33 +#define CHZONE2 34 +#define CHZONE3 35 +#define CHZONE4 36 +#define CHZONE5 37 +#define CHZONE6 38 +#define CHZONE7 39 +#define CHZONE8 40 +#define CHZONE9 41 +#define CHZONE10 42 +#define CAEROVE 43 +#define CAEROEL 44 +#define CCALCELL 45 +#define CCALHEAD 46 +#define CCALCURRENT 47 +#define CCALACTUAL 48 +#define CCALPLANNED 49 +#define CCALTODAY 50 +#define CPOPUP 51 +#define CPOPUPTEXT 52 +#define CTILEBAR 53 +#define CTILEBARSELECT 54 #endif diff --git a/src/CommPort.cpp b/src/CommPort.cpp index c4ecdd311..a149c73de 100644 --- a/src/CommPort.cpp +++ b/src/CommPort.cpp @@ -1,16 +1,16 @@ -/* +/* * Copyright (c) 2008 Sean C. Rhea (srhea@srhea.net) * * 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 2 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., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA diff --git a/src/CommPort.h b/src/CommPort.h index b96048a1d..a907d26a9 100644 --- a/src/CommPort.h +++ b/src/CommPort.h @@ -1,16 +1,16 @@ -/* +/* * Copyright (c) 2008 Sean C. Rhea (srhea@srhea.net) * * 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 2 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., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA @@ -18,6 +18,7 @@ #ifndef _GC_CommPort_h #define _GC_CommPort_h 1 +#include "GoldenCheetah.h" #include #include diff --git a/src/Computrainer.h b/src/Computrainer.h index 4e8ef90af..2506ac6ef 100644 --- a/src/Computrainer.h +++ b/src/Computrainer.h @@ -31,6 +31,7 @@ #ifndef _GC_Computrainer_h #define _GC_Computrainer_h 1 +#include "GoldenCheetah.h" #include #include diff --git a/src/Computrainer3dpFile.h b/src/Computrainer3dpFile.h index 45aada646..643edfdd1 100644 --- a/src/Computrainer3dpFile.h +++ b/src/Computrainer3dpFile.h @@ -18,6 +18,7 @@ #ifndef _GC_COMPUTRAINER3DP_H #define _GC_COMPUTRAINER3DP_H +#include "GoldenCheetah.h" #include "RideFile.h" diff --git a/src/ComputrainerController.cpp b/src/ComputrainerController.cpp index 57e2db6ad..cb23bc3b1 100644 --- a/src/ComputrainerController.cpp +++ b/src/ComputrainerController.cpp @@ -77,6 +77,7 @@ ComputrainerController::getRealtimeData(RealtimeData &rtData) if(!myComputrainer->isRunning()) { + parent->Stop(); QMessageBox msgBox; msgBox.setText("Cannot Connect to Computrainer"); msgBox.setIcon(QMessageBox::Critical); diff --git a/src/ComputrainerController.h b/src/ComputrainerController.h index a2714b196..483b7e3b8 100644 --- a/src/ComputrainerController.h +++ b/src/ComputrainerController.h @@ -15,6 +15,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "GoldenCheetah.h" #include "RealtimeController.h" #include "Computrainer.h" diff --git a/src/ConfigDialog.cpp b/src/ConfigDialog.cpp index 64deb70a1..bf8006a8d 100644 --- a/src/ConfigDialog.cpp +++ b/src/ConfigDialog.cpp @@ -27,6 +27,7 @@ ConfigDialog::ConfigDialog(QDir _home, Zones *_zones, MainWindow *mainWindow) : mainWindow(mainWindow), zones(_zones) { setAttribute(Qt::WA_DeleteOnClose); + setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint); home = _home; @@ -44,14 +45,11 @@ ConfigDialog::ConfigDialog(QDir _home, Zones *_zones, MainWindow *mainWindow) : configPage = new ConfigurationPage(mainWindow); devicePage = new DevicePage(this); + pagesWidget = new QStackedWidget; pagesWidget->addWidget(configPage); pagesWidget->addWidget(cyclistPage); pagesWidget->addWidget(devicePage); - #ifdef GC_HAVE_LIBOAUTH - twitterPage = new TwitterPage(this); - pagesWidget->addWidget(twitterPage); - #endif closeButton = new QPushButton(tr("Close")); saveButton = new QPushButton(tr("Save")); @@ -59,16 +57,6 @@ ConfigDialog::ConfigDialog(QDir _home, Zones *_zones, MainWindow *mainWindow) : createIcons(); contentsWidget->setCurrentItem(contentsWidget->item(0)); - // connect(closeButton, SIGNAL(clicked()), this, SLOT(reject())); - // connect(saveButton, SIGNAL(clicked()), this, SLOT(accept())); - connect(closeButton, SIGNAL(clicked()), this, SLOT(accept())); - - // connect the pieces... - connect(devicePage->typeSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(changedType(int))); - connect(devicePage->addButton, SIGNAL(clicked()), this, SLOT(devaddClicked())); - connect(devicePage->delButton, SIGNAL(clicked()), this, SLOT(devdelClicked())); - connect(devicePage->pairButton, SIGNAL(clicked()), this, SLOT(devpairClicked())); - horizontalLayout = new QHBoxLayout; horizontalLayout->addWidget(contentsWidget); horizontalLayout->addWidget(pagesWidget, 1); @@ -93,6 +81,15 @@ ConfigDialog::ConfigDialog(QDir _home, Zones *_zones, MainWindow *mainWindow) : setWindowTitle(tr("Options")); #endif setFixedSize(QSize(800, 600)); + + connect(closeButton, SIGNAL(clicked()), this, SLOT(accept())); + connect(devicePage->typeSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(changedType(int))); + connect(devicePage->addButton, SIGNAL(clicked()), this, SLOT(devaddClicked())); + connect(devicePage->delButton, SIGNAL(clicked()), this, SLOT(devdelClicked())); + connect(devicePage->pairButton, SIGNAL(clicked()), this, SLOT(devpairClicked())); + connect(contentsWidget, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)), + this, SLOT(changePage(QListWidgetItem *, QListWidgetItem*))); + connect(saveButton, SIGNAL(clicked()), this, SLOT(save_Clicked())); } void ConfigDialog::createIcons() @@ -116,14 +113,6 @@ void ConfigDialog::createIcons() realtimeButton->setTextAlignment(Qt::AlignHCenter); realtimeButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); -#ifdef GC_HAVE_LIBOAUTH - QListWidgetItem *twitterButton = new QListWidgetItem(contentsWidget); - twitterButton->setIcon(QIcon(":images/twitter.png")); - twitterButton->setText(tr("Twitter")); - twitterButton->setTextAlignment(Qt::AlignHCenter); - twitterButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); -#endif - connect(contentsWidget, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)), this, SLOT(changePage(QListWidgetItem *, QListWidgetItem*))); @@ -145,46 +134,42 @@ void ConfigDialog::changePage(QListWidgetItem *current, QListWidgetItem *previou // ! new mode: change the CP associated with the present mode void ConfigDialog::save_Clicked() { - boost::shared_ptr settings = GetApplicationSettings(); - if (configPage->langCombo->currentIndex()==0) - settings->setValue(GC_LANG, "en"); + appsettings->setValue(GC_LANG, "en"); else if (configPage->langCombo->currentIndex()==1) - settings->setValue(GC_LANG, "fr"); + appsettings->setValue(GC_LANG, "fr"); else if (configPage->langCombo->currentIndex()==2) - settings->setValue(GC_LANG, "ja"); + appsettings->setValue(GC_LANG, "ja"); else if (configPage->langCombo->currentIndex()==3) - settings->setValue(GC_LANG, "pt-br"); + appsettings->setValue(GC_LANG, "pt-br"); else if (configPage->langCombo->currentIndex()==4) - settings->setValue(GC_LANG, "it"); + appsettings->setValue(GC_LANG, "it"); else if (configPage->langCombo->currentIndex()==5) - settings->setValue(GC_LANG, "de"); + appsettings->setValue(GC_LANG, "de"); if (configPage->unitCombo->currentIndex()==0) - settings->setValue(GC_UNIT, "Metric"); + appsettings->setValue(GC_UNIT, "Metric"); else if (configPage->unitCombo->currentIndex()==1) - settings->setValue(GC_UNIT, "Imperial"); + appsettings->setValue(GC_UNIT, "Imperial"); - settings->setValue(GC_ALLRIDES_ASCENDING, configPage->allRidesAscending->checkState()); - settings->setValue(GC_GARMIN_SMARTRECORD, configPage->garminSmartRecord->checkState()); - settings->setValue(GC_GARMIN_HWMARK, configPage->garminHWMarkedit->text()); - settings->setValue(GC_CRANKLENGTH, configPage->crankLengthCombo->currentText()); - settings->setValue(GC_BIKESCOREDAYS, configPage->BSdaysEdit->text()); - settings->setValue(GC_BIKESCOREMODE, configPage->bsModeCombo->currentText()); - settings->setValue(GC_WORKOUTDIR, configPage->workoutDirectory->text()); - settings->setValue(GC_INITIAL_STS, cyclistPage->perfManStart->text()); - settings->setValue(GC_INITIAL_LTS, cyclistPage->perfManStart->text()); - settings->setValue(GC_STS_DAYS, cyclistPage->perfManSTSavg->text()); - settings->setValue(GC_LTS_DAYS, cyclistPage->perfManLTSavg->text()); - settings->setValue(GC_SB_TODAY, (int) cyclistPage->showSBToday->isChecked()); + appsettings->setValue(GC_ALLRIDES_ASCENDING, configPage->allRidesAscending->checkState()); + appsettings->setValue(GC_CRANKLENGTH, configPage->crankLengthCombo->currentText()); + appsettings->setValue(GC_BIKESCOREDAYS, configPage->BSdaysEdit->text()); + appsettings->setValue(GC_BIKESCOREMODE, configPage->bsModeCombo->currentText()); + appsettings->setValue(GC_WORKOUTDIR, configPage->workoutDirectory->text()); + appsettings->setCValue(mainWindow->cyclist, GC_INITIAL_STS, cyclistPage->perfManStart->text()); + appsettings->setCValue(mainWindow->cyclist, GC_INITIAL_LTS, cyclistPage->perfManStart->text()); + appsettings->setCValue(mainWindow->cyclist, GC_STS_DAYS, cyclistPage->perfManSTSavg->text()); + appsettings->setCValue(mainWindow->cyclist, GC_LTS_DAYS, cyclistPage->perfManLTSavg->text()); + appsettings->setCValue(mainWindow->cyclist, GC_SB_TODAY, (int) cyclistPage->showSBToday->isChecked()); // set default stress names if not set: - settings->setValue(GC_STS_NAME, settings->value(GC_STS_NAME,tr("Short Term Stress"))); - settings->setValue(GC_STS_ACRONYM, settings->value(GC_STS_ACRONYM,tr("STS"))); - settings->setValue(GC_LTS_NAME, settings->value(GC_LTS_NAME,tr("Long Term Stress"))); - settings->setValue(GC_LTS_ACRONYM, settings->value(GC_LTS_ACRONYM,tr("LTS"))); - settings->setValue(GC_SB_NAME, settings->value(GC_SB_NAME,tr("Stress Balance"))); - settings->setValue(GC_SB_ACRONYM, settings->value(GC_SB_ACRONYM,tr("SB"))); + appsettings->setValue(GC_STS_NAME, appsettings->value(this, GC_STS_NAME,tr("Short Term Stress"))); + appsettings->setValue(GC_STS_ACRONYM, appsettings->value(this, GC_STS_ACRONYM,tr("STS"))); + appsettings->setValue(GC_LTS_NAME, appsettings->value(this, GC_LTS_NAME,tr("Long Term Stress"))); + appsettings->setValue(GC_LTS_ACRONYM, appsettings->value(this, GC_LTS_ACRONYM,tr("LTS"))); + appsettings->setValue(GC_SB_NAME, appsettings->value(this, GC_SB_NAME,tr("Stress Balance"))); + appsettings->setValue(GC_SB_ACRONYM, appsettings->value(this, GC_SB_ACRONYM,tr("SB"))); // Save Cyclist page stuff cyclistPage->saveClicked(); @@ -192,10 +177,6 @@ void ConfigDialog::save_Clicked() // save interval metrics and ride data pages configPage->saveClicked(); -#ifdef GC_HAVE_LIBOAUTH - //Call Twitter Save Dialog to get Access Token - twitterPage->saveClicked(); -#endif // Save the device configuration... DeviceConfigurations all; all.writeConfig(devicePage->deviceListModel->Configuration); diff --git a/src/ConfigDialog.h b/src/ConfigDialog.h index d9a6fd972..b586375d2 100644 --- a/src/ConfigDialog.h +++ b/src/ConfigDialog.h @@ -1,5 +1,6 @@ #ifndef CONFIGDIALOG_H #define CONFIGDIALOG_H +#include "GoldenCheetah.h" #include #include @@ -15,6 +16,8 @@ class Zones; class ConfigDialog : public QDialog { Q_OBJECT + G_OBJECT + public: ConfigDialog(QDir home, Zones *zones, MainWindow *mainWindow); @@ -38,7 +41,6 @@ class ConfigDialog : public QDialog ConfigurationPage *configPage; CyclistPage *cyclistPage; DevicePage *devicePage; - TwitterPage *twitterPage; QPushButton *saveButton; QStackedWidget *pagesWidget; QPushButton *closeButton; diff --git a/src/CpintPlot.cpp b/src/CpintPlot.cpp index 39a67be79..704349ceb 100644 --- a/src/CpintPlot.cpp +++ b/src/CpintPlot.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include "RideItem.h" @@ -33,30 +34,34 @@ #include "LogTimeScaleEngine.h" #include "RideFile.h" #include "Season.h" +#include "Settings.h" #include #include // for std::lower_bound #define USE_T0_IN_CP_MODEL 0 // added djconnel 08Apr2009: allow 3-parameter CP model -CpintPlot::CpintPlot(QString p, const Zones *zones) : +CpintPlot::CpintPlot(MainWindow *main, QString p, const Zones *zones) : needToScanRides(true), path(p), thisCurve(NULL), CPCurve(NULL), zones(zones), + mainWindow(main), energyMode_(false) { + setInstanceName("CP Plot"); assert(!USE_T0_IN_CP_MODEL); // doesn't work with energyMode=true - insertLegend(new QwtLegend(), QwtPlot::BottomLegend); + //insertLegend(new QwtLegend(), QwtPlot::BottomLegend); //XXX ugly in small, needs fixing setAxisTitle(yLeft, tr("Average Power (watts)")); setAxisTitle(xBottom, tr("Interval Length")); setAxisScaleDraw(xBottom, new LogTimeScaleDraw); setAxisScaleEngine(xBottom, new LogTimeScaleEngine); - setAxisScale(xBottom, 1.0 / 60.0, 60); + setAxisScale(xBottom, (double)0.017, (double)60); + plotLayout()->setAlignCanvasToScales(true); grid = new QwtPlotGrid(); - grid->enableX(false); + grid->enableX(true); grid->attach(this); configChanged(); // apply colors @@ -71,6 +76,20 @@ CpintPlot::configChanged() grid->setPen(gridPen); } +void +CpintPlot::setAxisTitle(int axis, QString label) +{ + // setup the default fonts + QFont stGiles; // hoho - Chart Font St. Giles ... ok you have to be British to get this joke + stGiles.fromString(appsettings->value(this, GC_FONT_CHARTLABELS, QFont().toString()).toString()); + stGiles.setPointSize(appsettings->value(NULL, GC_FONT_CHARTLABELS_SIZE, 8).toInt()); + + QwtText title(label); + title.setFont(stGiles); + QwtPlot::setAxisFont(axis, stGiles); + QwtPlot::setAxisTitle(axis, title); +} + struct cpi_file_info { QString file, inname, outname; }; @@ -118,13 +137,13 @@ struct cpint_data { }; static void -update_cpi_file(const cpi_file_info *info, QProgressDialog *progress, +update_cpi_file(MainWindow *mainWindow, const cpi_file_info *info, QProgressDialog *progress, double &progress_sum, double progress_max) { QFile file(info->inname); QStringList errors; boost::scoped_ptr rideFile( - RideFileFactory::instance().openRideFile(file, errors)); + RideFileFactory::instance().openRideFile(mainWindow, file, errors)); if (!rideFile || rideFile->dataPoints().isEmpty()) return; cpint_data data; @@ -448,9 +467,10 @@ CpintPlot::plot_CP_curve(CpintPlot *thisPlot, // the plot we're currently di #endif CPCurve = new QwtPlotCurve(curve_title); - CPCurve->setRenderHint(QwtPlotItem::RenderAntialiased); + if (appsettings->value(this, GC_ANTIALIAS, false).toBool() == true) + CPCurve->setRenderHint(QwtPlotItem::RenderAntialiased); QPen pen(GColor(CCP)); - pen.setWidth(2.0); + pen.setWidth(appsettings->value(this, GC_LINEWIDTH, 2.0).toDouble()); pen.setStyle(Qt::DashLine); CPCurve->setPen(pen); CPCurve->setData(cp_curve_time.data(), cp_curve_power.data(), curve_points); @@ -509,9 +529,10 @@ CpintPlot::plot_allCurve(CpintPlot *thisPlot, QColor color = zoneColor(zone, n_zones); QString name = zones->getDefaultZoneName(zone); QwtPlotCurve *curve = new QwtPlotCurve(name); - curve->setRenderHint(QwtPlotItem::RenderAntialiased); + if (appsettings->value(this, GC_ANTIALIAS, false).toBool() == true) + curve->setRenderHint(QwtPlotItem::RenderAntialiased); QPen pen(color); - pen.setWidth(2.0); + pen.setWidth(appsettings->value(this, GC_LINEWIDTH, 2.0).toDouble()); curve->setPen(pen); curve->attach(thisPlot); color.setAlpha(64); @@ -528,8 +549,8 @@ CpintPlot::plot_allCurve(CpintPlot *thisPlot, if (!energyMode_ || energyBests[high] > 100.0) { QwtText text(name); - text.setFont(QFont("Helvetica", 24, QFont::Bold)); - color.setAlpha(128); + text.setFont(QFont("Helvetica", 20, QFont::Bold)); + color.setAlpha(255); text.setColor(color); QwtPlotMarker *label_mark = new QwtPlotMarker(); // place the text in the geometric mean in time, at a decent power @@ -555,12 +576,13 @@ CpintPlot::plot_allCurve(CpintPlot *thisPlot, // no zones available: just plot the curve without zones else { QwtPlotCurve *curve = new QwtPlotCurve(tr("maximal power")); - curve->setRenderHint(QwtPlotItem::RenderAntialiased); + if (appsettings->value(this, GC_ANTIALIAS, false).toBool() == true) + curve->setRenderHint(QwtPlotItem::RenderAntialiased); QPen pen(GColor(CCP)); - pen.setWidth(2.0); + pen.setWidth(appsettings->value(this, GC_LINEWIDTH, 2.0).toDouble()); curve->setPen(pen); QColor brush_color = GColor(CCP); - brush_color.setAlpha(64); + brush_color.setAlpha(200); curve->setBrush(brush_color); // brush fills below the line if (energyMode_) curve->setData(time_values.data(), energyBests.data(), n_values); @@ -573,7 +595,7 @@ CpintPlot::plot_allCurve(CpintPlot *thisPlot, // Energy mode is really only interesting in the range where energy is // linear in interval duration--up to about 1 hour. double xmax = energyMode_ ? 60.0 : time_values[n_values - 1]; - thisPlot->setAxisScale(thisPlot->xBottom, 1.0 / 60, xmax); + thisPlot->setAxisScale(thisPlot->xBottom, (double) 0.017, (double)xmax); double ymax; if (energyMode_) { @@ -589,6 +611,8 @@ CpintPlot::plot_allCurve(CpintPlot *thisPlot, void CpintPlot::calculate(RideItem *rideItem) { + if (!rideItem) return; + QString fileName = rideItem->fileName; QDateTime dateTime = rideItem->dateTime; QDir dir(path); @@ -607,7 +631,7 @@ CpintPlot::calculate(RideItem *rideItem) QFile file(info.inname); QStringList errors; boost::scoped_ptr rideFile( - RideFileFactory::instance().openRideFile(file, errors)); + RideFileFactory::instance().openRideFile(mainWindow, file, errors)); if (rideFile) { double x = rideFile->dataPoints().size(); progress_max += x * (x + 1.0) / 2.0; @@ -628,7 +652,7 @@ CpintPlot::calculate(RideItem *rideItem) progress.setLabelText( existing + QString(tr("Processing %1...")).arg(info.file)); progress.setValue(count++); - update_cpi_file(&info, &progress, progress_sum, progress_max); + update_cpi_file(mainWindow, &info, &progress, progress_sum, progress_max); QCoreApplication::processEvents(); if (progress.wasCanceled()) { aborted = true; diff --git a/src/CpintPlot.h b/src/CpintPlot.h index df6112ad1..8b07be598 100644 --- a/src/CpintPlot.h +++ b/src/CpintPlot.h @@ -18,6 +18,7 @@ #ifndef _GC_CpintPlot_h #define _GC_CpintPlot_h 1 +#include "GoldenCheetah.h" #include #include @@ -27,16 +28,19 @@ class QwtPlotGrid; class QwtPlotMarker; class RideItem; class Zones; +class MainWindow; QString ride_filename_to_cpi_filename(const QString filename); class CpintPlot : public QwtPlot { Q_OBJECT + G_OBJECT + public: - CpintPlot(QString path, const Zones *zones); + CpintPlot(MainWindow *, QString path, const Zones *zones); bool needToScanRides; const QwtPlotCurve *getThisCurve() const { return thisCurve; } @@ -50,6 +54,7 @@ class CpintPlot : public QwtPlot void changeSeason(const QDate &start, const QDate &end); void setEnergyMode(bool value); bool energyMode() const { return energyMode_; } + void setAxisTitle(int axis, QString label); public slots: @@ -77,6 +82,7 @@ class CpintPlot : public QwtPlot // keys are CPI files contributing to bests (at least originally) QHash cpiDataInBests; bool energyMode_; + MainWindow *mainWindow; }; #endif // _GC_CpintPlot_h diff --git a/src/CriticalPowerWindow.cpp b/src/CriticalPowerWindow.cpp index f58ba3d6e..ba9557e9d 100644 --- a/src/CriticalPowerWindow.cpp +++ b/src/CriticalPowerWindow.cpp @@ -31,15 +31,23 @@ #include CriticalPowerWindow::CriticalPowerWindow(const QDir &home, MainWindow *parent) : - QWidget(parent), home(home), mainWindow(parent), currentRide(NULL) + GcWindow(parent), home(home), mainWindow(parent), currentRide(NULL) { + setInstanceName("Critical Power Window"); + + // main plot area QVBoxLayout *vlayout = new QVBoxLayout; - - cpintPlot = new CpintPlot(home.path(), mainWindow->zones()); + cpintPlot = new CpintPlot(mainWindow, home.path(), mainWindow->zones()); vlayout->addWidget(cpintPlot); + setLayout(vlayout); + // controls + QWidget *c = new QWidget; + QVBoxLayout *cl = new QVBoxLayout(c); + setControls(c); + + // picker details QFormLayout *cpintPickerLayout = new QFormLayout; - QFormLayout *cpintPickerLayout2 = new QFormLayout; QLabel *cpintTimeLabel = new QLabel(tr("Interval Duration:"), this); cpintTimeValue = new QLineEdit("0 s"); QLabel *cpintTodayLabel = new QLabel(tr("Today:"), this); @@ -50,9 +58,9 @@ CriticalPowerWindow::CriticalPowerWindow(const QDir &home, MainWindow *parent) : cpintCPValue = new QLineEdit(tr("no data")); QFontMetrics metrics(QApplication::font()); - int width = metrics.width("8888 watts (88/88/8888)") + 10; - cpintAllValue->setFixedWidth(width); - cpintCPValue->setFixedWidth(width); // so lines up nicely + //int width = metrics.width("8888 watts (88/88/8888)") + 10; + //cpintAllValue->setFixedWidth(width); + //cpintCPValue->setFixedWidth(width); // so lines up nicely cpintTimeValue->setReadOnly(true); cpintTodayValue->setReadOnly(true); @@ -60,31 +68,22 @@ CriticalPowerWindow::CriticalPowerWindow(const QDir &home, MainWindow *parent) : cpintCPValue->setReadOnly(true); cpintPickerLayout->addRow(cpintTimeLabel, cpintTimeValue); cpintPickerLayout->addRow(cpintTodayLabel, cpintTodayValue); - cpintPickerLayout2->addRow(cpintAllLabel, cpintAllValue); - cpintPickerLayout2->addRow(cpintCPLabel, cpintCPValue); + cpintPickerLayout->addRow(cpintAllLabel, cpintAllValue); + cpintPickerLayout->addRow(cpintCPLabel, cpintCPValue); + cl->addLayout(cpintPickerLayout); - QHBoxLayout *bottomLayout = new QHBoxLayout; - - bottomLayout->addLayout(cpintPickerLayout); - bottomLayout->addLayout(cpintPickerLayout2); - - QVBoxLayout *otherLayout = new QVBoxLayout; + // tools /properties cComboSeason = new QComboBox(this); addSeasons(); cpintSetCPButton = new QPushButton(tr("&Save CP value"), this); cpintSetCPButton->setEnabled(false); - otherLayout->addWidget(cpintSetCPButton); - otherLayout->addWidget(cComboSeason); - QComboBox *yAxisCombo = new QComboBox(this); + cl->addWidget(cpintSetCPButton); + cl->addWidget(cComboSeason); + yAxisCombo = new QComboBox(this); yAxisCombo->addItem(tr("Y Axis Shows Power")); yAxisCombo->addItem(tr("Y Axis Shows Energy")); - otherLayout->addWidget(yAxisCombo); - - bottomLayout->addLayout(otherLayout); - - vlayout->addLayout(bottomLayout); - - setLayout(vlayout); + cl->addWidget(yAxisCombo); + cl->addStretch(); picker = new QwtPlotPicker(QwtPlot::xBottom, QwtPlot::yLeft, QwtPicker::PointSelection, @@ -100,7 +99,8 @@ CriticalPowerWindow::CriticalPowerWindow(const QDir &home, MainWindow *parent) : this, SLOT(seasonSelected(int))); connect(yAxisCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(setEnergyMode(int))); - connect(mainWindow, SIGNAL(rideSelected()), this, SLOT(rideSelected())); + //connect(mainWindow, SIGNAL(rideSelected()), this, SLOT(rideSelected())); + connect(this, SIGNAL(rideItemChanged(RideItem*)), this, SLOT(rideSelected())); connect(mainWindow, SIGNAL(configChanged()), cpintPlot, SLOT(configChanged())); // redraw on config change -- this seems the simplest approach @@ -123,9 +123,9 @@ CriticalPowerWindow::deleteCpiFile(QString rideFilename) void CriticalPowerWindow::rideSelected() { - if (mainWindow->activeTab() != this) + if (!amVisible()) return; - currentRide = mainWindow->rideItem(); + currentRide = myRideItem; if (currentRide) { cpintPlot->calculate(currentRide); diff --git a/src/CriticalPowerWindow.h b/src/CriticalPowerWindow.h index 5f561d269..f13fa37b0 100644 --- a/src/CriticalPowerWindow.h +++ b/src/CriticalPowerWindow.h @@ -18,6 +18,7 @@ #ifndef _GC_CriticalPowerWindow_h #define _GC_CriticalPowerWindow_h 1 +#include "GoldenCheetah.h" #include #include "Season.h" @@ -27,9 +28,14 @@ class MainWindow; class RideItem; class QwtPlotPicker; -class CriticalPowerWindow : public QWidget +class CriticalPowerWindow : public GcWindow { Q_OBJECT + G_OBJECT + + // properties can be saved/restored/set by the layout manager + Q_PROPERTY(int season READ season WRITE setSeason USER true) + Q_PROPERTY(int mode READ mode WRITE setMode USER true) public: @@ -38,6 +44,12 @@ class CriticalPowerWindow : public QWidget void newRideAdded(); void deleteCpiFile(QString filename); + // set/get properties + int season() const { return cComboSeason->currentIndex(); } + void setSeason(int x) { cComboSeason->setCurrentIndex(x); } + int mode() const { return yAxisCombo->currentIndex(); } + void setMode(int x) { yAxisCombo->setCurrentIndex(x); } + protected slots: void cpintSetCPButtonClicked(); @@ -56,7 +68,8 @@ class CriticalPowerWindow : public QWidget QLineEdit *cpintAllValue; QLineEdit *cpintCPValue; QComboBox *cComboSeason; - QPushButton *cpintSetCPButton; + QComboBox *yAxisCombo; + QPushButton *cpintSetCPButton; QwtPlotPicker *picker; void addSeasons(); QList seasons; diff --git a/src/CsvRideFile.h b/src/CsvRideFile.h index bd5b14730..3fce2e5c5 100644 --- a/src/CsvRideFile.h +++ b/src/CsvRideFile.h @@ -1,16 +1,16 @@ -/* +/* * Copyright (c) 2007 Sean C. Rhea (srhea@srhea.net) * * 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 2 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., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA @@ -18,6 +18,7 @@ #ifndef _CsvRideFile_h #define _CsvRideFile_h +#include "GoldenCheetah.h" #include "RideFile.h" diff --git a/src/D2XX.cpp b/src/D2XX.cpp index 03a2f75cd..10ea71cf2 100644 --- a/src/D2XX.cpp +++ b/src/D2XX.cpp @@ -1,16 +1,16 @@ -/* +/* * Copyright (c) 2008 Sean C. Rhea (srhea@srhea.net) * * 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 2 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., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA @@ -143,7 +143,7 @@ void D2XX::close() { assert(isOpen); - lib->close(ftHandle); + lib->close(ftHandle); isOpen = false; } @@ -202,15 +202,15 @@ D2XX::myListCommPorts(QString &err) } } DWORD numDevs; - FT_STATUS ftStatus = lib->create_device_info_list(&numDevs); + FT_STATUS ftStatus = lib->create_device_info_list(&numDevs); if(ftStatus != FT_OK) { - err = QString("FT_CreateDeviceInfoList: %1").arg(ftStatus); + err = QString("FT_CreateDeviceInfoList: %1").arg(ftStatus); return result; } FT_DEVICE_LIST_INFO_NODE *devInfo = new FT_DEVICE_LIST_INFO_NODE[numDevs]; - ftStatus = lib->get_device_info_list(devInfo, &numDevs); + ftStatus = lib->get_device_info_list(devInfo, &numDevs); if (ftStatus != FT_OK) - err = QString("FT_GetDeviceInfoList: %1").arg(ftStatus); + err = QString("FT_GetDeviceInfoList: %1").arg(ftStatus); else { for (DWORD i = 0; i < numDevs; i++) result.append(CommPortPtr(new D2XX(devInfo[i]))); diff --git a/src/D2XX.h b/src/D2XX.h index 1ecb1333c..99d61a8ab 100644 --- a/src/D2XX.h +++ b/src/D2XX.h @@ -1,16 +1,16 @@ -/* +/* * Copyright (c) 2008 Sean C. Rhea (srhea@srhea.net) * * 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 2 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., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA @@ -18,6 +18,7 @@ #ifndef _GC_PT_D2XX_h #define _GC_PT_D2XX_h 1 +#include "GoldenCheetah.h" #include "CommPort.h" #ifdef WIN32 diff --git a/src/DBAccess.cpp b/src/DBAccess.cpp index 590edc838..964610a53 100644 --- a/src/DBAccess.cpp +++ b/src/DBAccess.cpp @@ -1,16 +1,16 @@ -/* +/* * Copyright (c) 2006 Justin Knotzke (jknotzke@shampoo.ca) * * 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 2 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., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA @@ -40,11 +40,17 @@ // DB Schema Version - YOU MUST UPDATE THIS IF THE SCHEMA VERSION CHANGES!!! // Schema version will change if a) the default metadata.xml is updated // or b) new metrics are added / old changed -static int DBSchemaVersion = 15; +static int DBSchemaVersion = 23; DBAccess::DBAccess(MainWindow* main, QDir home) : main(main), home(home) { initDatabase(home); + + // check we have one and use built in if not there + QString filename = main->home.absolutePath()+"/measures.xml"; + if (!QFile(filename).exists()) filename = ":/xml/measures.xml"; + RideMetadata::readXML(filename, mkeywordDefinitions, mfieldDefinitions); + } void DBAccess::closeConnection() @@ -60,8 +66,8 @@ DBAccess::~DBAccess() void DBAccess::initDatabase(QDir home) { - - + + if(dbconn.isOpen()) return; QString cyclist = QFileInfo(home.path()).baseName(); @@ -93,6 +99,30 @@ DBAccess::initDatabase(QDir home) } } +static int +computeFileCRC(QString filename) +{ + QFile file(filename); + QFileInfo fileinfo(file); + + // open file + if (!file.open(QFile::ReadOnly)) return 0; + + // allocate space + boost::scoped_array data(new char[file.size()]); + + // read entire file into memory + QDataStream *rawstream(new QDataStream(&file)); + rawstream->readRawData(&data[0], file.size()); + file.close(); + + // calculate the CRC + boost::crc_optimal<16, 0x1021, 0xFFFF, 0, false, false> CRC; + CRC.process_bytes(&data[0], file.size()); + + return CRC.checksum(); +} + bool DBAccess::createMetricsTable() { SpecialFields sp; @@ -124,6 +154,13 @@ bool DBAccess::createMetricsTable() for (int i=0; irideMetadata()->getFields()) { + if (!sp.isMetric(field.name) && (field.type < 3)) { + createMetricTable += QString(", Z%1 varchar").arg(sp.makeTechName(field.name)); + } + } + // And all the metadata metrics foreach(FieldDefinition field, main->rideMetadata()->getFields()) { if (!sp.isMetric(field.name) && (field.type == 3 || field.type == 4)) { @@ -133,9 +170,22 @@ bool DBAccess::createMetricsTable() createMetricTable += " )"; rc = query.exec(createMetricTable); - if (!rc) { - qDebug()<<"create table failed!" << query.lastError(); - } + //if (!rc) qDebug()<<"create table failed!" << query.lastError(); + + // add row to version database + QString metadataXML = QString(home.absolutePath()) + "/metadata.xml"; + int metadatacrcnow = computeFileCRC(metadataXML); + QDateTime timestamp = QDateTime::currentDateTime(); + + // wipe current version row + query.exec("DELETE FROM version where table_name = \"metrics\""); + + query.prepare("INSERT INTO version (table_name, schema_version, creation_date, metadata_crc ) values (?,?,?,?)"); + query.addBindValue("metrics"); + query.addBindValue(DBSchemaVersion); + query.addBindValue(timestamp.toTime_t()); + query.addBindValue(metadatacrcnow); + rc = query.exec(); } return rc; } @@ -143,8 +193,82 @@ bool DBAccess::createMetricsTable() bool DBAccess::dropMetricTable() { QSqlQuery query("DROP TABLE metrics", dbconn); + bool rc = query.exec(); + return rc; +} - return query.exec(); +bool DBAccess::createMeasuresTable() +{ + QSqlQuery query(dbconn); + bool rc; + bool createTables = true; + + // does the table exist? + rc = query.exec("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;"); + if (rc) { + while (query.next()) { + + QString table = query.value(0).toString(); + if (table == "measures") { + createTables = false; + break; + } + } + } + // we need to create it! + if (rc && createTables) { + + // read definitions from measures.xml + SpecialFields sp; // just for makeTechName function... + QList fieldDefinitions; + QList keywordDefinitions; //NOTE: not used in measures.xml + + // check we have one and use built in if not there + QString filename = main->home.absolutePath()+"/measures.xml"; + if (!QFile(filename).exists()) filename = ":/xml/measures.xml"; + RideMetadata::readXML(filename, keywordDefinitions, fieldDefinitions); + + QString createMeasuresTable = "create table measures (timestamp integer primary key," + "measure_date date"; + + // And all the metadata texts + foreach(FieldDefinition field, fieldDefinitions) + if (field.type < 3) createMeasuresTable += QString(", Z%1 varchar").arg(sp.makeTechName(field.name)); + + // And all the metadata measures + foreach(FieldDefinition field, fieldDefinitions) + if (field.type == 3 || field.type == 4) + createMeasuresTable += QString(", Z%1 double").arg(sp.makeTechName(field.name)); + + createMeasuresTable += " )"; + + rc = query.exec(createMeasuresTable); + //if (!rc) qDebug()<<"create table failed!" << query.lastError(); + + // add row to version database + QString measuresXML = QString(home.absolutePath()) + "/measures.xml"; + int measurescrcnow = computeFileCRC(measuresXML); + QDateTime timestamp = QDateTime::currentDateTime(); + + // wipe current version row + rc = query.exec("DELETE FROM version where table_name = \"measures\";"); + + // add row to version table + query.prepare("INSERT INTO version (table_name, schema_version, creation_date, metadata_crc ) values (?,?,?,?)"); + query.addBindValue("measures"); + query.addBindValue(DBSchemaVersion); + query.addBindValue(timestamp.toTime_t()); + query.addBindValue(measurescrcnow); + rc = query.exec(); + } + return rc; +} + +bool DBAccess::dropMeasuresTable() +{ + QSqlQuery query("DROP TABLE measures", dbconn); + bool rc = query.exec(); + return rc; } bool DBAccess::createDatabase() @@ -152,81 +276,84 @@ bool DBAccess::createDatabase() // check schema version and if missing recreate database checkDBVersion(); - // at present only one table! - bool rc = createMetricsTable(); - if(!rc) return rc; + // Ride metrics + createMetricsTable(); + + // Athlete measures + createMeasuresTable(); - // other tables here return true; } -static int -computeFileCRC(QString filename) -{ - QFile file(filename); - QFileInfo fileinfo(file); - - // open file - if (!file.open(QFile::ReadOnly)) return 0; - - // allocate space - boost::scoped_array data(new char[file.size()]); - - // read entire file into memory - QDataStream *rawstream(new QDataStream(&file)); - rawstream->readRawData(&data[0], file.size()); - file.close(); - - // calculate the CRC - boost::crc_optimal<16, 0x1021, 0xFFFF, 0, false, false> CRC; - CRC.process_bytes(&data[0], file.size()); - - return CRC.checksum(); -} - void DBAccess::checkDBVersion() { - int currentversion = 0; - int metadatacrc; // crc for metadata.xml when last refreshed - int metadatacrcnow; // current value for metadata.xml crc - int creationdate; - // get a CRC for metadata.xml QString metadataXML = QString(home.absolutePath()) + "/metadata.xml"; - metadatacrcnow = computeFileCRC(metadataXML); + int metadatacrcnow = computeFileCRC(metadataXML); + + // get a CRC for measures.xml + QString measuresXML = QString(home.absolutePath()) + "/measures.xml"; + int measurescrcnow = computeFileCRC(measuresXML); // can we get a version number? - QSqlQuery query("SELECT schema_version, creation_date, metadata_crc from version;", dbconn); + QSqlQuery query("SELECT table_name, schema_version, creation_date, metadata_crc from version;", dbconn); bool rc = query.exec(); - while (rc && query.next()) { - currentversion = query.value(0).toInt(); - creationdate = query.value(1).toInt(); - metadatacrc = query.value(2).toInt(); - } - // if its not up-to-date - if (!rc || currentversion != DBSchemaVersion || metadatacrc != metadatacrcnow) { + if (!rc) { + // we couldn't read the version table properly + // it must be out of date!! - // drop tables - QSqlQuery dropV("DROP TABLE version", dbconn); - dropV.exec(); - QSqlQuery dropM("DROP TABLE metrics", dbconn); + QSqlQuery dropM("DROP TABLE version", dbconn); dropM.exec(); // recreate version table and add one entry - QSqlQuery version("CREATE TABLE version ( schema_version integer primary key, creation_date date, metadata_crc integer );", dbconn); + QSqlQuery version("CREATE TABLE version ( table_name varchar primary key, schema_version integer, creation_date date, metadata_crc integer );", dbconn); version.exec(); - // insert current version number - QDateTime timestamp = QDateTime::currentDateTime(); - QSqlQuery insert("INSERT INTO version ( schema_version, creation_date, metadata_crc ) values (?,?,?)", dbconn); - insert.addBindValue(DBSchemaVersion); - insert.addBindValue(timestamp.toTime_t()); - insert.addBindValue(metadatacrcnow); - insert.exec(); + // wipe away whatever (if anything is there) + dropMetricTable(); + dropMeasuresTable(); + // create afresh createMetricsTable(); + createMeasuresTable(); + + return; + } + + // ok we checked out ok, so lets adjust db schema to reflect + // tne current version / crc + bool dropMetric = false; + bool dropMeasures = false; + + while (query.next()) { + QString table_name = query.value(0).toString(); + int currentversion = query.value(1).toInt(); + //int creationdate = query.value(2).toInt(); // not relevant anymore, we use version/crc + int crc = query.value(3).toInt(); + + if (table_name == "metrics" && (currentversion != DBSchemaVersion || crc != metadatacrcnow)) { + dropMetric = true; + } + + if (table_name == "measures" && crc != measurescrcnow) { + dropMeasures = true; + } + } + query.finish(); + + // "metrics" table, is it up-to-date? + if (dropMetric) { + dropMetricTable(); + createMetricsTable(); + } + + // "measures" table, is it up-to-date? - export - recreate - import .... + // XXX gets wiped away for now, will fix later + if (dropMeasures) { + dropMeasuresTable(); + createMeasuresTable(); } } @@ -252,7 +379,13 @@ bool DBAccess::importRide(SummaryMetrics *summaryMetrics, RideFile *ride, unsign for (int i=0; irideMetadata()->getFields()) { + if (!sp.isMetric(field.name) && (field.type < 3)) { + insertStatement += QString(", Z%1 ").arg(sp.makeTechName(field.name)); + } + } + // And all the metadata metrics foreach(FieldDefinition field, main->rideMetadata()->getFields()) { if (!sp.isMetric(field.name) && (field.type == 3 || field.type == 4)) { insertStatement += QString(", Z%1 ").arg(sp.makeTechName(field.name)); @@ -263,7 +396,7 @@ bool DBAccess::importRide(SummaryMetrics *summaryMetrics, RideFile *ride, unsign for (int i=0; irideMetadata()->getFields()) { - if (!sp.isMetric(field.name) && (field.type == 3 || field.type == 4)) { + if (!sp.isMetric(field.name) && field.type < 5) { insertStatement += ",?"; } } @@ -282,6 +415,13 @@ bool DBAccess::importRide(SummaryMetrics *summaryMetrics, RideFile *ride, unsign query.addBindValue(summaryMetrics->getForSymbol(factory.metricName(i))); } + // And all the metadata texts + foreach(FieldDefinition field, main->rideMetadata()->getFields()) { + + if (!sp.isMetric(field.name) && (field.type < 3)) { + query.addBindValue(ride->getTag(field.name, "")); + } + } // And all the metadata metrics foreach(FieldDefinition field, main->rideMetadata()->getFields()) { @@ -296,8 +436,7 @@ bool DBAccess::importRide(SummaryMetrics *summaryMetrics, RideFile *ride, unsign // go do it! bool rc = query.exec(); - if(!rc) - qDebug() << query.lastError(); + //if(!rc) qDebug() << query.lastError(); return rc; } @@ -342,7 +481,7 @@ QList DBAccess::getAllMetricsFor(QDateTime start, QDateTime end) for (int i=0; irideMetadata()->getFields()) { - if (!sp.isMetric(field.name) && (field.type == 3 || field.type == 4)) { + if (!sp.isMetric(field.name) && field.type < 5) { selectStatement += QString(", Z%1 ").arg(sp.makeTechName(field.name)); } } @@ -368,7 +507,12 @@ QList DBAccess::getAllMetricsFor(QDateTime start, QDateTime end) foreach(FieldDefinition field, main->rideMetadata()->getFields()) { if (!sp.isMetric(field.name) && (field.type == 3 || field.type == 4)) { QString underscored = field.name; - summaryMetrics.setForSymbol(underscored.replace(" ","_"), query.value(i+2).toDouble()); + summaryMetrics.setForSymbol(underscored.replace("_"," "), query.value(i+2).toDouble()); + i++; + } else if (!sp.isMetric(field.name) && field.type < 3) { + QString underscored = field.name; + // ignore texts for now XXX todo if want metadata from Summary Metrics + summaryMetrics.setText(underscored.replace("_"," "), query.value(i+2).toString()); i++; } } @@ -377,3 +521,160 @@ QList DBAccess::getAllMetricsFor(QDateTime start, QDateTime end) return metrics; } +SummaryMetrics DBAccess::getRideMetrics(QString filename) +{ + SpecialFields sp; + SummaryMetrics summaryMetrics; + + // construct the select statement + QString selectStatement = "SELECT filename"; + const RideMetricFactory &factory = RideMetricFactory::instance(); + for (int i=0; irideMetadata()->getFields()) { + if (!sp.isMetric(field.name) && field.type < 5) { + selectStatement += QString(", Z%1 ").arg(sp.makeTechName(field.name)); + } + } + selectStatement += " FROM metrics where filename == :filename ;"; + + // execute the select statement + QSqlQuery query(selectStatement, dbconn); + query.bindValue(":filename", filename); + query.exec(); + while(query.next()) + { + // filename and date + summaryMetrics.setFileName(query.value(0).toString()); + // the values + int i=0; + for (; irideMetadata()->getFields()) { + if (!sp.isMetric(field.name) && (field.type == 3 || field.type == 4)) { + QString underscored = field.name; + summaryMetrics.setForSymbol(underscored.replace(" ","_"), query.value(i+1).toDouble()); + i++; + } else if (!sp.isMetric(field.name) && field.type < 3) { + // ignore texts for now XXX todo if want metadata from Summary Metrics + QString underscored = field.name; + summaryMetrics.setText(underscored.replace("_"," "), query.value(i+1).toString()); + i++; + } + } + } + return summaryMetrics; +} + +/*---------------------------------------------------------------------- + * CRUD routines for Measures table + *----------------------------------------------------------------------*/ +bool DBAccess::importMeasure(SummaryMetrics *summaryMetrics) +{ + QSqlQuery query(dbconn); + + // construct an insert statement + QString insertStatement = "insert into measures (timestamp, measure_date"; + + // And all the metadata texts + foreach(FieldDefinition field, mfieldDefinitions) { + if (field.type < 3) { + insertStatement += QString(", Z%1 ").arg(msp.makeTechName(field.name)); + } + } + // And all the metadata metrics + foreach(FieldDefinition field, mfieldDefinitions) { + if (field.type == 3 || field.type == 4) { + insertStatement += QString(", Z%1 ").arg(msp.makeTechName(field.name)); + } + } + + insertStatement += " ) values (?,?"; // timestamp, measure_date + + foreach(FieldDefinition field, mfieldDefinitions) { + if (field.type < 5) { + insertStatement += ",?"; + } + } + insertStatement += ")"; + + query.prepare(insertStatement); + + // timestamp and date + query.addBindValue(summaryMetrics->getDateTime().toTime_t()); + query.addBindValue(summaryMetrics->getDateTime().date()); + + // And all the text measures + foreach(FieldDefinition field, mfieldDefinitions) { + if (field.type < 3) { + query.addBindValue(summaryMetrics->getText(field.name, "")); + } + } + // And all the numeric measures + foreach(FieldDefinition field, mfieldDefinitions) { + if (field.type == 3 || field.type == 4) { + query.addBindValue(summaryMetrics->getText(field.name, "nan").toDouble()); + } + } + // go do it! + bool rc = query.exec(); + + //if(!rc) qDebug() << query.lastError(); + + return rc; +} + +QList DBAccess::getAllMeasuresFor(QDateTime start, QDateTime end) +{ + SpecialFields sp; + QList fieldDefinitions; + QList keywordDefinitions; //NOTE: not used in measures.xml + + // check we have one and use built in if not there + QString filename = main->home.absolutePath()+"/measures.xml"; + if (!QFile(filename).exists()) filename = ":/xml/measures.xml"; + RideMetadata::readXML(filename, keywordDefinitions, fieldDefinitions); + + QList measures; + + // null date range fetches all, but not currently used by application code + // since it relies too heavily on the results of the QDateTime constructor + if (start == QDateTime()) start = QDateTime::currentDateTime().addYears(-10); + if (end == QDateTime()) end = QDateTime::currentDateTime().addYears(+10); + + // construct the select statement + QString selectStatement = "SELECT timestamp, measure_date"; + foreach(FieldDefinition field, fieldDefinitions) { + if (!sp.isMetric(field.name) && field.type < 5) { + selectStatement += QString(", Z%1 ").arg(sp.makeTechName(field.name)); + } + } + selectStatement += " FROM measures where DATE(measure_date) >=DATE(:start) AND DATE(measure_date) <=DATE(:end) " + " ORDER BY measure_date;"; + + // execute the select statement + QSqlQuery query(selectStatement, dbconn); + query.bindValue(":start", start.date()); + query.bindValue(":end", end.date()); + query.exec(); + while(query.next()) + { + SummaryMetrics add; + + // filename and date + add.setDateTime(query.value(1).toDateTime()); + // the values + int i=2; + foreach(FieldDefinition field, fieldDefinitions) { + if (field.type == 3 || field.type == 4) { + add.setText(field.name, query.value(i).toString()); + i++; + } else if (field.type < 3) { + add.setText(field.name, query.value(i).toString()); + i++; + } + } + measures << add; + } + return measures; +} diff --git a/src/DBAccess.h b/src/DBAccess.h index 77aeb0116..2f278b703 100644 --- a/src/DBAccess.h +++ b/src/DBAccess.h @@ -1,16 +1,16 @@ -/* +/* * Copyright (c) 2009 Justin F. Knotzke (jknotzke@shampoo.ca) * * 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 2 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., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA @@ -19,6 +19,7 @@ #ifndef _GC_DBAccess_h #define _GC_DBAccess_h 1 +#include "GoldenCheetah.h" #include @@ -28,13 +29,15 @@ #include "MainWindow.h" #include "Season.h" #include "RideFile.h" +#include "SpecialFields.h" +#include "RideMetadata.h" class RideFile; class Zones; class RideMetric; class DBAccess { - + public: // get connection name @@ -47,13 +50,20 @@ class DBAccess DBAccess(MainWindow *main, QDir home); ~DBAccess(); - // Create/Delete Records + // Create/Delete Metrics bool importRide(SummaryMetrics *summaryMetrics, RideFile *ride, unsigned long, bool); bool deleteRide(QString); + // Create/Delete Measures + bool importMeasure(SummaryMetrics *summaryMetrics); + // Query Records - QList getAllDates(); QList getAllMetricsFor(QDateTime start, QDateTime end); + QList getAllMeasuresFor(QDateTime start, QDateTime end); + + SummaryMetrics getRideMetrics(QString filename); // for a filename + + QList getAllDates(); QList getAllSeasons(); private: @@ -62,15 +72,18 @@ class DBAccess QSqlDatabase dbconn; QString sessionid; + SpecialFields msp; + QList mfieldDefinitions; + QList mkeywordDefinitions; //NOTE: not used in measures.xml + typedef QHash MetricMap; bool createDatabase(); void closeConnection(); bool createMetricsTable(); bool dropMetricTable(); - bool createIndex(); + bool createMeasuresTable(); + bool dropMeasuresTable(); void initDatabase(QDir home); - - }; #endif diff --git a/src/DanielsPoints.cpp b/src/DanielsPoints.cpp index 82fe26e2d..50b845834 100644 --- a/src/DanielsPoints.cpp +++ b/src/DanielsPoints.cpp @@ -51,8 +51,10 @@ class DanielsPoints : public RideMetric { setImperialUnits(""); setType(RideMetric::Total); } - void compute(const RideFile *ride, const Zones *zones, - int zoneRange, const HrZones *, int, const QHash &) { + void compute(const RideFile *ride, const Zones *zones, int zoneRange, + const HrZones *, int, + const QHash &, + const MainWindow *) { if (!zones || zoneRange < 0) { setValue(0); return; @@ -111,8 +113,10 @@ class DanielsEquivalentPower : public RideMetric { setType(RideMetric::Average); } - void compute(const RideFile *, const Zones *zones, int zoneRange, const HrZones *, int, - const QHash &deps) + void compute(const RideFile *, const Zones *zones, int zoneRange, + const HrZones *, int, + const QHash &deps, + const MainWindow *) { if (!zones || zoneRange < 0) { setValue(0); diff --git a/src/DataProcessor.cpp b/src/DataProcessor.cpp index 30c2660dc..695986950 100644 --- a/src/DataProcessor.cpp +++ b/src/DataProcessor.cpp @@ -24,7 +24,6 @@ #include DataProcessorFactory *DataProcessorFactory::instance_; - DataProcessorFactory &DataProcessorFactory::instance() { if (!instance_) instance_ = new DataProcessorFactory(); @@ -42,7 +41,6 @@ DataProcessorFactory::registerProcessor(QString name, DataProcessor *processor) bool DataProcessorFactory::autoProcess(RideFile *ride) { - boost::shared_ptr settings = GetApplicationSettings(); bool changed = false; // run through the processors and execute them! @@ -51,7 +49,7 @@ DataProcessorFactory::autoProcess(RideFile *ride) while (i.hasNext()) { i.next(); QString configsetting = QString("dp/%1/apply").arg(i.key()); - if (settings->value(configsetting, "Manual").toString() == "Auto") + if (appsettings->value(NULL, configsetting, "Manual").toString() == "Auto") i.value()->postProcess(ride); } diff --git a/src/DataProcessor.h b/src/DataProcessor.h index ead80461c..09c42f466 100644 --- a/src/DataProcessor.h +++ b/src/DataProcessor.h @@ -18,6 +18,7 @@ #ifndef _DataProcessor_h #define _DataProcessor_h +#include "GoldenCheetah.h" #include "RideFile.h" #include "RideFileCommand.h" @@ -53,6 +54,8 @@ class DataProcessorConfig : public QWidget { Q_OBJECT + G_OBJECT + public: DataProcessorConfig(QWidget *parent=0) : QWidget(parent) {} @@ -84,7 +87,6 @@ class DataProcessorFactory { public: static DataProcessorFactory &instance(); - bool registerProcessor(QString name, DataProcessor *processor); QMap getProcessors() const { return processors; } bool autoProcess(RideFile *); // run auto processes (after open rideFile) @@ -94,6 +96,8 @@ class MainWindow; class ManualDataProcessorDialog : public QDialog { Q_OBJECT + G_OBJECT + public: ManualDataProcessorDialog(MainWindow *, QString, RideItem *); diff --git a/src/DatePickerDialog.cpp b/src/DatePickerDialog.cpp index 4aa06f2cd..f730bf830 100644 --- a/src/DatePickerDialog.cpp +++ b/src/DatePickerDialog.cpp @@ -1,16 +1,16 @@ -/* +/* * Copyright (c) 2007 Justin F. Knotzke (jknotzke@shampoo.ca) * * 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 2 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., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA @@ -19,7 +19,7 @@ #include "DatePickerDialog.h" #include "Settings.h" #include - + void DatePickerDialog::setupUi(QDialog *DatePickerDialog) { if (DatePickerDialog->objectName().isEmpty()) @@ -37,7 +37,7 @@ void DatePickerDialog::setupUi(QDialog *DatePickerDialog) QDateTime *dt = new QDateTime; date = dt->currentDateTime(); dateTimeEdit->setDateTime(date); - + mainGrid->addWidget(dateTimeEdit,0,1); lblBrowse = new QLabel("Choose a CSV file to upload", this); mainGrid->addWidget(lblBrowse, 1,0); @@ -52,17 +52,17 @@ void DatePickerDialog::setupUi(QDialog *DatePickerDialog) mainGrid->addWidget(btnCancel, 3,1); DatePickerDialog->setWindowTitle( - QApplication::translate("DatePickerDialog", "Import CSV file", 0, + QApplication::translate("DatePickerDialog", "Import CSV file", 0, QApplication::UnicodeUTF8)); btnBrowse->setText( - QApplication::translate("DatePickerDialog", "File to import...", 0, + QApplication::translate("DatePickerDialog", "File to import...", 0, QApplication::UnicodeUTF8)); btnOK->setText( - QApplication::translate("DatePickerDialog", "OK", 0, + QApplication::translate("DatePickerDialog", "OK", 0, QApplication::UnicodeUTF8)); btnCancel->setText( - QApplication::translate("DatePickerDialog", "Cancel", 0, + QApplication::translate("DatePickerDialog", "Cancel", 0, QApplication::UnicodeUTF8)); connect(btnOK, SIGNAL(clicked()), this, SLOT(on_btnOK_clicked())); @@ -73,9 +73,9 @@ void DatePickerDialog::setupUi(QDialog *DatePickerDialog) dateTimeEdit->setEnabled(FALSE); lblOccur->setEnabled(FALSE); btnOK->setEnabled(FALSE); - + Q_UNUSED(DatePickerDialog); -} +} DatePickerDialog::DatePickerDialog(QWidget *parent) : QDialog(parent) @@ -93,18 +93,16 @@ void DatePickerDialog::on_btnOK_clicked() void DatePickerDialog::on_btnBrowse_clicked() { //First check to see if the Library folder exists where the executable is (for USB sticks) - boost::shared_ptr settings = GetApplicationSettings(); - - QVariant lastDirVar = settings->value(GC_SETTINGS_LAST_IMPORT_PATH); - QString lastDir = (lastDirVar != QVariant()) + QVariant lastDirVar = appsettings->value(this, GC_SETTINGS_LAST_IMPORT_PATH); + QString lastDir = (lastDirVar != QVariant()) ? lastDirVar.toString() : QDir::homePath(); fileName = QFileDialog::getOpenFileName( this, tr("Import CSV"), lastDir, tr("Comma Separated Values (*.csv)")); if (!fileName.isEmpty()) { lastDir = QFileInfo(fileName).absolutePath(); - settings->setValue(GC_SETTINGS_LAST_IMPORT_PATH, lastDir); - + appsettings->setValue(GC_SETTINGS_LAST_IMPORT_PATH, lastDir); + // Find the datetimestamp from the filename. // If we can't, use the creation time. // eg. GoldenCheetah YYYY_MM_DD_HH_MM_SS.csv @@ -122,10 +120,10 @@ void DatePickerDialog::on_btnBrowse_clicked() } // and put it into the datePicker dialog dateTimeEdit->setDateTime(date); - + } txtBrowse->setText(fileName); - + // allow date to be changed, and enable OK button dateTimeEdit->setEnabled(TRUE); lblOccur->setEnabled(TRUE); diff --git a/src/DatePickerDialog.h b/src/DatePickerDialog.h index 0be17c9f0..8f37d8d38 100644 --- a/src/DatePickerDialog.h +++ b/src/DatePickerDialog.h @@ -1,20 +1,21 @@ -/* +/* * Copyright (c) 2007 Justin F. Knotzke (jknotzke@shampoo.ca) * * 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 2 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., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "GoldenCheetah.h" #include #include @@ -22,6 +23,8 @@ class DatePickerDialog : public QDialog { Q_OBJECT + G_OBJECT + public: DatePickerDialog(QWidget *parent = 0); diff --git a/src/DaysScaleDraw.h b/src/DaysScaleDraw.h index a05cb57d2..efdf0eebd 100644 --- a/src/DaysScaleDraw.h +++ b/src/DaysScaleDraw.h @@ -5,12 +5,12 @@ * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 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., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA @@ -20,6 +20,7 @@ * * Provides specialized formatting for Plot axes */ +#include "GoldenCheetah.h" #include diff --git a/src/Device.cpp b/src/Device.cpp index 37443cded..9dba014f2 100644 --- a/src/Device.cpp +++ b/src/Device.cpp @@ -1,16 +1,16 @@ -/* +/* * Copyright (c) 2008 Sean C. Rhea (srhea@srhea.net) * * 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 2 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., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA @@ -50,4 +50,4 @@ Device::addDevice(const QString &deviceType, Device *device) devices().insert(deviceType, device); return true; } - + diff --git a/src/Device.h b/src/Device.h index 5d8c05c9f..798f83c8b 100644 --- a/src/Device.h +++ b/src/Device.h @@ -1,16 +1,16 @@ -/* +/* * Copyright (c) 2008 Sean C. Rhea (srhea@srhea.net) * * 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 2 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., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA @@ -18,6 +18,7 @@ #ifndef _GC_Device_h #define _GC_Device_h 1 +#include "GoldenCheetah.h" #include "CommPort.h" #include @@ -38,6 +39,6 @@ struct Device static Device &device(const QString &deviceType); static bool addDevice(const QString &deviceType, Device *device); }; - + #endif // _GC_Device_h diff --git a/src/DeviceConfiguration.cpp b/src/DeviceConfiguration.cpp index 8c1e7658b..9691904ad 100644 --- a/src/DeviceConfiguration.cpp +++ b/src/DeviceConfiguration.cpp @@ -59,10 +59,8 @@ DeviceConfigurations::readConfig() { int count; - boost::shared_ptr settings = GetApplicationSettings(); - // get count of devices - QVariant configVal = settings->value(GC_DEV_COUNT); + QVariant configVal = appsettings->value(NULL, GC_DEV_COUNT); if (configVal.isNull()) { count=0; } else { @@ -75,27 +73,27 @@ DeviceConfigurations::readConfig() DeviceConfiguration Entry; QString configStr = QString("%1%2").arg(GC_DEV_NAME).arg(i+1); - configVal = settings->value(configStr); + configVal = appsettings->value(NULL, configStr); Entry.name = configVal.toString(); configStr = QString("%1%2").arg(GC_DEV_SPEC).arg(i+1); - configVal = settings->value(configStr); + configVal = appsettings->value(NULL, configStr); Entry.portSpec = configVal.toString(); configStr = QString("%1%2").arg(GC_DEV_TYPE).arg(i+1); - configVal = settings->value(configStr); + configVal = appsettings->value(NULL, configStr); Entry.type = configVal.toInt(); configStr = QString("%1%2").arg(GC_DEV_PROF).arg(i+1); - configVal = settings->value(configStr); + configVal = appsettings->value(NULL, configStr); Entry.deviceProfile = configVal.toString(); configStr = QString("%1%2").arg(GC_DEV_DEFI).arg(i+1); - configVal = settings->value(configStr); + configVal = appsettings->value(NULL, configStr); Entry.isDefaultDownload = configVal.toInt(); configStr = QString("%1%2").arg(GC_DEV_DEFR).arg(i+1); - configVal = settings->value(configStr); + configVal = appsettings->value(NULL, configStr); Entry.isDefaultRealtime = configVal.toInt(); Entries.append(Entry); @@ -110,34 +108,32 @@ DeviceConfigurations::writeConfig(QList Configuration) // writing to the GC settings int i=0; - boost::shared_ptr settings = GetApplicationSettings(); - - settings->setValue(GC_DEV_COUNT, Configuration.count()); + appsettings->setValue(GC_DEV_COUNT, Configuration.count()); for (i=0; i < Configuration.count(); i++) { // name QString configStr = QString("%1%2").arg(GC_DEV_NAME).arg(i+1); - settings->setValue(configStr, Configuration.at(i).name); + appsettings->setValue(configStr, Configuration.at(i).name); // type configStr = QString("%1%2").arg(GC_DEV_TYPE).arg(i+1); - settings->setValue(configStr, Configuration.at(i).type); + appsettings->setValue(configStr, Configuration.at(i).type); // portSpec configStr = QString("%1%2").arg(GC_DEV_SPEC).arg(i+1); - settings->setValue(configStr, Configuration.at(i).portSpec); + appsettings->setValue(configStr, Configuration.at(i).portSpec); // deviceProfile configStr = QString("%1%2").arg(GC_DEV_PROF).arg(i+1); - settings->setValue(configStr, Configuration.at(i).deviceProfile); + appsettings->setValue(configStr, Configuration.at(i).deviceProfile); // isDefaultDownload configStr = QString("%1%2").arg(GC_DEV_DEFI).arg(i+1); - settings->setValue(configStr, Configuration.at(i).isDefaultDownload); + appsettings->setValue(configStr, Configuration.at(i).isDefaultDownload); // isDefaultRealtime configStr = QString("%1%2").arg(GC_DEV_DEFR).arg(i+1); - settings->setValue(configStr, Configuration.at(i).isDefaultRealtime); + appsettings->setValue(configStr, Configuration.at(i).isDefaultRealtime); } } diff --git a/src/DeviceConfiguration.h b/src/DeviceConfiguration.h index c5617561f..ac147bbaa 100644 --- a/src/DeviceConfiguration.h +++ b/src/DeviceConfiguration.h @@ -21,6 +21,7 @@ #ifndef _GC_DeviceConfiguration_h #define _GC_DeviceConfiguration_h +#include "GoldenCheetah.h" class DeviceConfiguration { diff --git a/src/DeviceTypes.cpp b/src/DeviceTypes.cpp index 3f1fc2418..2212a264e 100644 --- a/src/DeviceTypes.cpp +++ b/src/DeviceTypes.cpp @@ -30,7 +30,8 @@ static DeviceType SupportedDevices[] = { { DEV_CT, DEV_SERIAL, (char *) "Computrainer", true, false }, { DEV_ANTPLUS, DEV_ANT, (char *) "ANT+ via Quarqd", true, false }, -// { DEV_GSERVER, DEV_TCP, (char *) "Golden Cheetah Server", false, false }, + { DEV_GSERVER, DEV_TCP, (char *) "Golden Cheetah Server", false, false }, + { DEV_NULL, DEV_TCP, (char *) "Null device (testing)", false, false }, // { DEV_PT, DEV_SERIAL, (char *) "Powertap Head Unit", false, true }, // { DEV_SRM, DEV_SERIAL, (char *) "SRM PowerControl V/VI", false, true }, // { DEV_GCLIENT, DEV_TCP, (char *) "Golden Cheetah Client", false, false }, diff --git a/src/DeviceTypes.h b/src/DeviceTypes.h index c89eb01d8..76c92e453 100644 --- a/src/DeviceTypes.h +++ b/src/DeviceTypes.h @@ -21,6 +21,7 @@ #ifndef _GC_DeviceTypes_h #define _GC_DeviceTypes_h 1 +#include "GoldenCheetah.h" #include @@ -28,6 +29,7 @@ #define DEV_SRM 0x0002 #define DEV_CT 0x0010 #define DEV_ANTPLUS 0x0020 +#define DEV_NULL 0x0040 #define DEV_GSERVER 0x0100 // NOT IMPLEMENTED IN THIS RELEASE XXX #define DEV_GCLIENT 0x0200 // NOT IMPLEMENTED IN THIS RELEASE XXX diff --git a/src/DiaryWindow.cpp b/src/DiaryWindow.cpp new file mode 100644 index 000000000..bdd77f59d --- /dev/null +++ b/src/DiaryWindow.cpp @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2010 Mark Liversedge (liversedge@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 2 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., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include "DiaryWindow.h" + +DiaryWindow::DiaryWindow(MainWindow *mainWindow) : + GcWindow(mainWindow), mainWindow(mainWindow), active(false) +{ + setInstanceName("Diary Window"); + setControls(NULL); + + // get config + fieldDefinitions = mainWindow->rideMetadata()->getFields(); + + QVBoxLayout *vlayout = new QVBoxLayout(this); + + // controls + QHBoxLayout *controls = new QHBoxLayout; + QFont bold; + bold.setPointSize(14); + bold.setWeight(QFont::Bold); + title = new QLabel("", this); + title->setFont(bold); + title->setFixedWidth(250); + title->setAlignment(Qt::AlignCenter); + + QIcon prevIcon(":images/toolbar/back_alt.png"); + QIcon nextIcon(":images/toolbar/forward_alt.png"); + next = new QPushButton(nextIcon, "", this); + prev = new QPushButton(prevIcon, "", this); +#ifdef Q_OS_MAC + next->setFlat(true); + prev->setFlat(true); +#endif + + // viewMode - monthly or weekly + viewMode = new QComboBox; + viewMode->addItem("View List"); + viewMode->addItem("View Month"); + viewMode->addItem("View Week"); // we can add more later... + viewMode->addItem("View Ride"); // we can add more later... + viewMode->setFixedWidth(120); + + viewMode->setCurrentIndex(appsettings->cvalue(mainWindow->cyclist, GC_DIARY_VIEW, "1").toInt()); + + QLabel *spacer = new QLabel(this); + spacer->setFixedWidth(120); // same as viewMode, centering controls... + + controls->addWidget(spacer); + controls->addStretch(); + controls->addWidget(prev); + controls->addWidget(title); + controls->addWidget(next); + controls->addStretch(); + controls->addWidget(viewMode); + + vlayout->addLayout(controls); + + // list view RideNavigator + listView = new RideNavigator(mainWindow); + + // monthly view via QCalendarWidget + calendarModel = new GcCalendarModel(this, &fieldDefinitions, mainWindow); + calendarModel->setSourceModel(listView->sqlModel); + + monthlyView = new QTableView(this); + monthlyView->setItemDelegate(new GcCalendarDelegate); + monthlyView->setModel(calendarModel); + monthlyView->horizontalHeader()->setResizeMode(QHeaderView::Stretch); + monthlyView->verticalHeader()->setResizeMode(QHeaderView::Stretch); + monthlyView->viewport()->installEventFilter(this); + + // weekly view via QxtScheduleView + weeklyView = new QxtScheduleView; + weeklyViewProxy = new QxtScheduleViewProxy(this, &fieldDefinitions, mainWindow); + weeklyViewProxy->setSourceModel(listView->sqlModel); + weeklyView->setCurrentZoomDepth (30, Qxt::Minute); + weeklyView->setDateRange(QDate(2010,9,2), QDate(2010,9,8)); + weeklyView->setModel(weeklyViewProxy); + + RideSummaryWindow *rideSummary = new RideSummaryWindow(mainWindow); + allViews = new QStackedWidget(this); + allViews->addWidget(listView); + allViews->addWidget(monthlyView); + allViews->addWidget(weeklyView); + allViews->addWidget(rideSummary); + allViews->setCurrentIndex(viewMode->currentIndex()); + + vlayout->addWidget(allViews); + + connect(viewMode, SIGNAL(currentIndexChanged(int)), allViews, SLOT(setCurrentIndex(int))); + connect(viewMode, SIGNAL(currentIndexChanged(int)), this, SLOT(setDefaultView(int))); + connect(viewMode, SIGNAL(currentIndexChanged(int)), this, SLOT(rideSelected())); + connect(this, SIGNAL(rideItemChanged(RideItem*)), this, SLOT(rideSelected())); + //connect(mainWindow, SIGNAL(rideSelected()), this, SLOT(rideSelected())); + connect(mainWindow, SIGNAL(configChanged()), this, SLOT(configChanged())); + connect(weeklyView, SIGNAL(indexSelected(QModelIndex)), this, SLOT(weeklySelected(QModelIndex))); + connect(next, SIGNAL(clicked()), this, SLOT(nextClicked())); + connect(prev, SIGNAL(clicked()), this, SLOT(prevClicked())); +} + +void +DiaryWindow::configChanged() +{ + // get config + fieldDefinitions = mainWindow->rideMetadata()->getFields(); +} + +void +DiaryWindow::setDefaultView(int view) +{ + appsettings->setCValue(mainWindow->cyclist, GC_DIARY_VIEW, view); +} +void +DiaryWindow::rideSelected() +{ + if (active) { + return; + } + + RideItem *ride = myRideItem; + + // ignore if not active or null ride + if (!ride) { + return; + } + + // set the date range to put the current ride in view... + QDate when = ride->dateTime.date(); + int month = when.month(); + int year = when.year(); + int weekNumber = when.weekNumber(); + + // monthly view updates + calendarModel->setMonth(when.month(), when.year()); + + when = when.addDays(Qt::Monday - when.dayOfWeek()); + weeklyView->setDateRange(when, when.addDays(6)); + weeklyView->setViewMode(QxtScheduleView::DayView); + + // ok update title + switch (viewMode->currentIndex()) { + case 0 : // list + title->setText(""); + next->hide(); + prev->hide(); + break; + case 1 : // monthly + title->setText(QString("%1 %2").arg(QDate::longMonthName(month)).arg(year)); + next->show(); + prev->show(); + break; + case 2 : // weekly + title->setText(QString("Week %1 %2").arg(weekNumber).arg(year)); + next->show(); + prev->show(); + break; + + default: + case 3 : //ride + title->setText(""); + next->hide(); + prev->hide(); + break; + } +} + +void +DiaryWindow::prevClicked() +{ + switch (viewMode->currentIndex()) { + case 0 : // list - should be hidded! + break; + case 1 : // monthly + { + int month = calendarModel->getMonth(); + int year = calendarModel->getYear(); + QDate when = QDate(year, month, 1).addDays(-1); + calendarModel->setMonth(when.month(), when.year()); + title->setText(QString("%1 %2").arg(QDate::longMonthName(when.month())).arg(when.year())); + } + break; + case 2 : // weekly + { + QDateTime when = weeklyView->getStartTime(); + when = when.addDays(-7); + weeklyView->setDateRange(when.date(), when.addDays(6).date()); + weeklyView->setViewMode(QxtScheduleView::DayView); + title->setText(QString("Week %1 %2").arg(when.date().weekNumber()).arg(when.date().year())); + } + break; + } +} + +void +DiaryWindow::nextClicked() +{ + switch (viewMode->currentIndex()) { + case 0 : // list - should be hidded! + break; + case 1 : // monthly + { + int month = calendarModel->getMonth(); + int year = calendarModel->getYear(); + QDate when = QDate(year, month, 1).addMonths(1); + calendarModel->setMonth(when.month(), when.year()); + title->setText(QString("%1 %2").arg(QDate::longMonthName(when.month())).arg(when.year())); + } + break; + case 2 : // weekly + { + QDateTime when = weeklyView->getStartTime(); + when = when.addDays(7); + weeklyView->setDateRange(when.date(), when.addDays(6).date()); + weeklyView->setViewMode(QxtScheduleView::DayView); + title->setText(QString("Week %1 %2").arg(when.date().weekNumber()).arg(when.date().year())); + } + break; + } +} + +void +DiaryWindow::weeklySelected(QModelIndex index) +{ + if (active) return; + + // lets select it in the ride list then! + QString filename = weeklyViewProxy->data(index, QxtScheduleViewProxy::FilenameRole).toString(); + active = true; + mainWindow->selectRideFile(QFileInfo(filename).fileName()); + //weeklyView->setViewMode(QxtScheduleView::DayView); + active = false; + rideSelected(); +} + +bool +DiaryWindow::eventFilter(QObject *object, QEvent *e) +{ + + //if (object != (QObject *)monthlyView) return false; + + switch (e->type()) { + case QEvent::MouseButtonPress: + { + // Get a list of rides for the point clicked + QModelIndex index = monthlyView->indexAt(static_cast(e)->pos()); + QStringList files = calendarModel->data(index, GcCalendarModel::FilenamesRole).toStringList(); + + // worry about where we clicked in the cell + int y = static_cast(e)->pos().y(); + QRect c = monthlyView->visualRect(index); + + // clicked on heading + if (y <= (c.y()+15)) return true; // XXX clicked on heading we may need to trap this! + + // clicked on cell contents + if (files.count() == 1) { + if (files[0] == "calendar") ; // XXX handle planned rides + else mainWindow->selectRideFile(QFileInfo(files[0]).fileName()); + + } else if (files.count()) { + + // which ride? + int h = (c.height()-15) / files.count(); + int i; + for(i=files.count()-1; i>=0; i--) if (y > (c.y()+15+(h*i))) break; + + if (files[i] == "calendar") ; // XXX handle planned rides + else mainWindow->selectRideFile(QFileInfo(files[i]).fileName()); + } + + // force a repaint XXX this is a hack! + calendarModel->setMonth(calendarModel->getMonth(), calendarModel->getYear()); + return true; + } + break; + // ignore click, doubleclick + case QEvent::MouseButtonRelease: + case QEvent::MouseMove: + case QEvent::MouseButtonDblClick: + return true; + default: + return QObject::eventFilter(object, e); + } + return true; +} diff --git a/src/DiaryWindow.h b/src/DiaryWindow.h new file mode 100644 index 000000000..d96faca74 --- /dev/null +++ b/src/DiaryWindow.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2010 Mark Liversedge (liversedge@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 2 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., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _GC_DiaryWindow_h +#define _GC_DiaryWindow_h 1 +#include "GoldenCheetah.h" + +#include +#include +#include + +#include "MainWindow.h" +#include "RideMetadata.h" + +// list view +#include "RideNavigator.h" + +// weekly view +#include "qxtscheduleview.h" +#include "QxtScheduleViewProxy.h" +#include "WeeklyViewItemDelegate.h" + +// monthly view +#include +#include "GcCalendarModel.h" + +// ride view +#include "RideSummaryWindow.h" + + +class DiaryWindow : public GcWindow +{ + Q_OBJECT + G_OBJECT + + Q_PROPERTY(int view READ view WRITE setView USER true) + + public: + + DiaryWindow(MainWindow *); + + int view() const { return viewMode->currentIndex(); } + void setView(int x) { viewMode->setCurrentIndex(x); } + + public slots: + void rideSelected(); + void configChanged(); + void nextClicked(); + void prevClicked(); + void weeklySelected(QModelIndex); + void setDefaultView(int); + bool eventFilter(QObject *object, QEvent *e); // traps monthly view + + protected: + MainWindow *mainWindow; + + QLabel *title; + QPushButton *prev, *next; + + QComboBox *viewMode; + QStackedWidget *allViews; + RideNavigator *listView; + + QTableView *monthlyView; + GcCalendarModel *calendarModel; + + QxtScheduleView *weeklyView; + QxtScheduleViewProxy *weeklyViewProxy; + + bool active; + QList fieldDefinitions; +}; +#endif // _GC_DiaryWindow_h diff --git a/src/DownloadRideDialog.cpp b/src/DownloadRideDialog.cpp index 304f0c2e0..484352a45 100644 --- a/src/DownloadRideDialog.cpp +++ b/src/DownloadRideDialog.cpp @@ -1,4 +1,4 @@ -/* +/* * $Id: DownloadRideDialog.cpp,v 1.4 2006/08/11 20:02:13 srhea Exp $ * * Copyright (c) 2006-2008 Sean C. Rhea (srhea@srhea.net) @@ -7,12 +7,12 @@ * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 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., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA @@ -28,7 +28,7 @@ #include DownloadRideDialog::DownloadRideDialog(MainWindow *mainWindow, - const QDir &home) : + const QDir &home) : mainWindow(mainWindow), home(home), cancelled(false), downloadInProgress(false) { @@ -58,11 +58,11 @@ DownloadRideDialog::DownloadRideDialog(MainWindow *mainWindow, connect(rescanButton, SIGNAL(clicked()), this, SLOT(scanCommPorts())); connect(cancelButton, SIGNAL(clicked()), this, SLOT(cancelClicked())); - QHBoxLayout *buttonLayout = new QHBoxLayout; - buttonLayout->addWidget(downloadButton); + QHBoxLayout *buttonLayout = new QHBoxLayout; + buttonLayout->addWidget(downloadButton); buttonLayout->addWidget(eraseRideButton); - buttonLayout->addWidget(rescanButton); - buttonLayout->addWidget(cancelButton); + buttonLayout->addWidget(rescanButton); + buttonLayout->addWidget(cancelButton); QVBoxLayout *mainLayout = new QVBoxLayout(this); mainLayout->addWidget(new QLabel(tr("Select port:"), this)); @@ -76,7 +76,7 @@ DownloadRideDialog::DownloadRideDialog(MainWindow *mainWindow, scanCommPorts(); } -void +void DownloadRideDialog::setReadyInstruct() { if (portCombo->count() == 0) { @@ -108,7 +108,7 @@ DownloadRideDialog::scanCommPorts() if (err != "") { QString msg = "Warning(s):\n\n" + err + "\n\nYou may need to (re)install " "the FTDI or PL2303 drivers before downloading."; - QMessageBox::warning(0, "Error Loading Device Drivers", msg, + QMessageBox::warning(0, "Error Loading Device Drivers", msg, QMessageBox::Ok, QMessageBox::NoButton); } for (int i = 0; i < devList.size(); ++i) { @@ -135,7 +135,7 @@ DownloadRideDialog::statusCallback(const QString &statusText) return !cancelled; } -void +void DownloadRideDialog::downloadClicked() { downloadButton->setEnabled(false); @@ -178,7 +178,7 @@ DownloadRideDialog::downloadClicked() tr("This ride appears to have already ") + tr("been downloaded. Do you want to ") + tr("overwrite the previous download?"), - tr("&Overwrite"), tr("&Cancel"), + tr("&Overwrite"), tr("&Cancel"), QString(), 1, 1) == 1) { reject(); return; @@ -190,9 +190,9 @@ DownloadRideDialog::downloadClicked() if (QFile::exists(filepath)) { QFile old(filepath); if (!old.remove()) { - QMessageBox::critical(this, tr("Error"), - tr("Failed to remove existing file ") - + filepath + ": " + old.error()); + QMessageBox::critical(this, tr("Error"), + tr("Failed to remove existing file ") + + filepath + ": " + old.error()); QFile::remove(tmpname); reject(); } @@ -201,7 +201,7 @@ DownloadRideDialog::downloadClicked() // Use ::rename() instead of QFile::rename() to get forced overwrite. if (rename(QFile::encodeName(tmpname), QFile::encodeName(filepath)) < 0) { - QMessageBox::critical(this, tr("Error"), + QMessageBox::critical(this, tr("Error"), tr("Failed to rename ") + tmpname + tr(" to ") + filepath + ": " + strerror(errno)); QFile::remove(tmpname); @@ -239,7 +239,7 @@ DownloadRideDialog::eraseClicked() accept(); } -void +void DownloadRideDialog::cancelClicked() { if (!downloadInProgress) diff --git a/src/DownloadRideDialog.h b/src/DownloadRideDialog.h index 3586b0113..20b03e5f2 100644 --- a/src/DownloadRideDialog.h +++ b/src/DownloadRideDialog.h @@ -1,16 +1,16 @@ -/* +/* * Copyright (c) 2006-2008 Sean C. Rhea (srhea@srhea.net) * * 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 2 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., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA @@ -18,15 +18,18 @@ #ifndef _GC_DownloadRideDialog_h #define _GC_DownloadRideDialog_h 1 +#include "GoldenCheetah.h" #include "CommPort.h" #include class MainWindow; -class DownloadRideDialog : public QDialog +class DownloadRideDialog : public QDialog { Q_OBJECT + G_OBJECT + public: DownloadRideDialog(MainWindow *mainWindow, const QDir &home); @@ -42,7 +45,7 @@ class DownloadRideDialog : public QDialog void scanCommPorts(); private: - + MainWindow *mainWindow; QDir home; QPushButton *downloadButton, *eraseRideButton, *rescanButton, *cancelButton; diff --git a/src/ErgFile.h b/src/ErgFile.h index be3ade371..9338963fc 100644 --- a/src/ErgFile.h +++ b/src/ErgFile.h @@ -18,6 +18,7 @@ #ifndef _ErgFile_h #define _ErgFile_h +#include "GoldenCheetah.h" #include #include diff --git a/src/ErgFilePlot.cpp b/src/ErgFilePlot.cpp index 6fd52cac7..b7b7cbacf 100644 --- a/src/ErgFilePlot.cpp +++ b/src/ErgFilePlot.cpp @@ -40,6 +40,8 @@ QwtData *NowData::copy() const { return new NowData(); } ErgFilePlot::ErgFilePlot(QList *data) { + setInstanceName("ErgFile Plot"); + //insertLegend(new QwtLegend(), QwtPlot::BottomLegend); setCanvasBackground(Qt::white); courseData = data; // what we plot diff --git a/src/ErgFilePlot.h b/src/ErgFilePlot.h index 1fa2dc301..2fba7da9e 100644 --- a/src/ErgFilePlot.h +++ b/src/ErgFilePlot.h @@ -18,6 +18,7 @@ #ifndef _GC_ErgFilePlot_h #define _GC_ErgFilePlot_h 1 +#include "GoldenCheetah.h" #include #include @@ -54,6 +55,8 @@ class NowData : public QwtData class ErgFilePlot : public QwtPlot { Q_OBJECT + G_OBJECT + public: diff --git a/src/FitRideFile.h b/src/FitRideFile.h index 4e061f460..c5ff2a8e6 100644 --- a/src/FitRideFile.h +++ b/src/FitRideFile.h @@ -18,6 +18,7 @@ #ifndef _FitRideFile_h #define _FitRideFile_h +#include "GoldenCheetah.h" #include "RideFile.h" diff --git a/src/FixGaps.cpp b/src/FixGaps.cpp index f24e07fb4..0451fd4db 100644 --- a/src/FixGaps.cpp +++ b/src/FixGaps.cpp @@ -89,17 +89,15 @@ class FixGapsConfig : public DataProcessorConfig } void readConfig() { - boost::shared_ptr settings = GetApplicationSettings(); - double tol = settings->value(GC_DPFG_TOLERANCE, "1.0").toDouble(); - double stop = settings->value(GC_DPFG_STOP, "1.0").toDouble(); + double tol = appsettings->value(NULL, GC_DPFG_TOLERANCE, "1.0").toDouble(); + double stop = appsettings->value(NULL, GC_DPFG_STOP, "1.0").toDouble(); tolerance->setValue(tol); beerandburrito->setValue(stop); } void saveConfig() { - boost::shared_ptr settings = GetApplicationSettings(); - settings->setValue(GC_DPFG_TOLERANCE, tolerance->value()); - settings->setValue(GC_DPFG_STOP, beerandburrito->value()); + appsettings->setValue(GC_DPFG_TOLERANCE, tolerance->value()); + appsettings->setValue(GC_DPFG_STOP, beerandburrito->value()); } }; @@ -131,9 +129,8 @@ FixGaps::postProcess(RideFile *ride, DataProcessorConfig *config=0) // get settings double tolerance, stop; if (config == NULL) { // being called automatically - boost::shared_ptr settings = GetApplicationSettings(); - tolerance = settings->value(GC_DPFG_TOLERANCE, "1.0").toDouble(); - stop = settings->value(GC_DPFG_STOP, "1.0").toDouble(); + tolerance = appsettings->value(NULL, GC_DPFG_TOLERANCE, "1.0").toDouble(); + stop = appsettings->value(NULL, GC_DPFG_STOP, "1.0").toDouble(); } else { // being called manually tolerance = ((FixGapsConfig*)(config))->tolerance->value(); stop = ((FixGapsConfig*)(config))->beerandburrito->value(); diff --git a/src/FixSpikes.cpp b/src/FixSpikes.cpp index bc166a40f..702369352 100644 --- a/src/FixSpikes.cpp +++ b/src/FixSpikes.cpp @@ -90,17 +90,15 @@ class FixSpikesConfig : public DataProcessorConfig } void readConfig() { - boost::shared_ptr settings = GetApplicationSettings(); - double tol = settings->value(GC_DPFS_MAX, "1500").toDouble(); - double stop = settings->value(GC_DPFS_VARIANCE, "1000").toDouble(); + double tol = appsettings->value(NULL, GC_DPFS_MAX, "1500").toDouble(); + double stop = appsettings->value(NULL, GC_DPFS_VARIANCE, "1000").toDouble(); max->setValue(tol); variance->setValue(stop); } void saveConfig() { - boost::shared_ptr settings = GetApplicationSettings(); - settings->setValue(GC_DPFS_MAX, max->value()); - settings->setValue(GC_DPFS_VARIANCE, variance->value()); + appsettings->setValue(GC_DPFS_MAX, max->value()); + appsettings->setValue(GC_DPFS_VARIANCE, variance->value()); } }; @@ -135,9 +133,8 @@ FixSpikes::postProcess(RideFile *ride, DataProcessorConfig *config=0) // get settings double variance, max; if (config == NULL) { // being called automatically - boost::shared_ptr settings = GetApplicationSettings(); - max = settings->value(GC_DPFS_MAX, "1500").toDouble(); - variance = settings->value(GC_DPFS_VARIANCE, "1000").toDouble(); + max = appsettings->value(NULL, GC_DPFS_MAX, "1500").toDouble(); + variance = appsettings->value(NULL, GC_DPFS_VARIANCE, "1000").toDouble(); } else { // being called manually max = ((FixSpikesConfig*)(config))->max->value(); variance = ((FixSpikesConfig*)(config))->variance->value(); diff --git a/src/FixTorque.cpp b/src/FixTorque.cpp index bb8433835..bdddb2dd2 100644 --- a/src/FixTorque.cpp +++ b/src/FixTorque.cpp @@ -67,13 +67,11 @@ class FixTorqueConfig : public DataProcessorConfig } void readConfig() { - boost::shared_ptr settings = GetApplicationSettings(); - ta->setText(settings->value(GC_DPTA, "0 nm").toString()); + ta->setText(appsettings->value(NULL, GC_DPTA, "0 nm").toString()); } void saveConfig() { - boost::shared_ptr settings = GetApplicationSettings(); - settings->setValue(GC_DPTA, ta->text()); + appsettings->setValue(GC_DPTA, ta->text()); } }; @@ -110,8 +108,7 @@ FixTorque::postProcess(RideFile *ride, DataProcessorConfig *config=0) double nmAdjust; if (config == NULL) { // being called automatically - boost::shared_ptr settings = GetApplicationSettings(); - ta = settings->value(GC_DPTA, "0 nm").toString(); + ta = appsettings->value(NULL, GC_DPTA, "0 nm").toString(); } else { // being called manually ta = ((FixTorqueConfig*)(config))->ta->text(); } diff --git a/src/GcCalendarModel.h b/src/GcCalendarModel.h new file mode 100644 index 000000000..38076b2c8 --- /dev/null +++ b/src/GcCalendarModel.h @@ -0,0 +1,435 @@ +/* + * Copyright (c) 2010 Mark Liversedge (liversedge@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 2 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., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/********************* WARNING !!! ********************* + * + * PLEASE DO NOT EDIT THIS SOURCE FILE IF YOU WANT TO + * CHANGE THE WAY ROWS ARE GROUPED. THERE IS A FUNCTION + * IN QxtScheduleView.cpp CALLED groupFromValue() WHICH + * IS CALLED TO GET THE GROUP NAME FOR A COLUMN HEADING + * VALUE COMBINATION. THIS IS CALLED FROM whichGroup() + * BELOW. + * + * Of course, if there is a bug in this ProxyModel you + * are welcome to fix it! + * But do take care. Specifically with the index() + * parent() mapToSource() and mapFromSource() functions. + * + *******************************************************/ + +#ifndef _GC_GcCalendarModel_h +#define _GC_GcCalendarModel_h 1 +#include "GoldenCheetah.h" + +#include +#include +#include "qxtscheduleview.h" +#include "MainWindow.h" +#include "RideMetadata.h" +#include "ICalendar.h" +#include "Colors.h" +#include "Settings.h" + +Q_DECLARE_METATYPE(QList) // for passing back colors for each workout + +// Proxy model for doing groupBy +class GcCalendarModel : public QAbstractProxyModel +{ + Q_OBJECT + G_OBJECT + + +private: + int month, year; // current month and year, default to this month + QVector dates; // dates for each cell from zero onwards + int rows; + + QMap * > dateToRows; // map a date to SQL rows + + QxtScheduleView *rideNavigator; + QList *fieldDefinitions; + QList columns; // what columns in the sql model + MainWindow *mainWindow; + int filenameIndex, durationIndex, dateIndex, textIndex; + +public slots: + void refresh() { + + if (!sourceModel()) return; // no model yet! + + QDate first = QDate(year, month, 1); + // Date array + int monthDays = first.daysTo(first.addMonths(1)); // how many days in this month? + QDate firstDate = first.addDays((first.dayOfWeek()-1)*-1); // date in cell 0,0 + int ndays = firstDate.daysTo(QDate(year, month, monthDays)); + ndays += 7 - ndays % 7; + + dates.clear(); + dates.resize(ndays); + for(int i=0; icolumnCount(); i++) { + QString column = sourceModel()->headerData (i, Qt::Horizontal, Qt::DisplayRole).toString(); + columns << column; + if (column == tr("Duration")) durationIndex = i; + if (column == tr("Date")) dateIndex = i; + if (column == tr("Filename")) filenameIndex = i; + if (column == tr("Calendar Text")) textIndex = i; + } + + // we need to build a list of all the rides + // in the source model for the dates we have + dateToRows.clear(); //XXX mem leak, need to delete vectors... + for (int j=0; jrowCount(); j++) { + QVector *arr; + + // get ride date + QDateTime dateTime = sourceModel()->data(sourceModel()->index(j, dateIndex), Qt::DisplayRole).toDateTime(); + if ((arr = dateToRows.value(dateTime.date(), NULL)) == NULL) + arr = new QVector(); + + arr->append(j); + dateToRows.insert(dateTime.date(), arr); + } + reset(); + } + +public: + + GcCalendarModel(QWidget *parent, QList *fields, MainWindow *main) : QAbstractProxyModel(parent), fieldDefinitions(fields), mainWindow(main) { + setParent(parent); + + QDate today = QDateTime::currentDateTime().date(); + setMonth(today.month(), today.year()); + } + ~GcCalendarModel() {} + + void setMonth(int month, int year) { + this->month = month; + this->year = year; + refresh(); + } + + int getMonth() { return month; } + int getYear() { return year; } + + QDate date(QModelIndex index) const { + return dates[index.row()*7+index.column()]; + } + + void setSourceModel(QAbstractItemModel *model) { + QAbstractProxyModel::setSourceModel(model); + connect(model, SIGNAL(modelReset()), this, SLOT(refresh())); + connect(model, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SLOT(refresh())); + connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(refresh())); + connect(model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), this, SLOT(refresh())); + connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(refresh())); + connect(mainWindow->rideCalendar, SIGNAL(dataChanged()), this, SLOT(refresh())); + refresh(); + } + + QModelIndex index(int row, int column, const QModelIndex &/*parent*/ = QModelIndex()) const { + return createIndex(row,column,(void *)NULL); + } + + QModelIndex parent(const QModelIndex &index) const { + // parent should be encoded in the index if we supplied it, if + // we didn't then return a duffer + if (index == QModelIndex() || index.internalPointer() == NULL) { + return QModelIndex(); + } else if (index.column()) { + return QModelIndex(); + } else { + return *static_cast(index.internalPointer()); + } + } + + QModelIndex mapToSource(const QModelIndex &proxyIndex) const { + return sourceModel()->index(proxyIndex.row(), proxyIndex.column(), QModelIndex()); + } + + QModelIndex mapFromSource(const QModelIndex &sourceIndex) const { + + return createIndex(sourceIndex.row(), sourceIndex.column(), (void *)NULL); // accomodate virtual column + } + + // we override the standard version to make our virtual column zero + // selectable. If we don't do that then the arrow keys don't work + // since there are no valid rows to cursor up or down to. + Qt::ItemFlags flags (const QModelIndex &/*index*/) const { + return Qt::ItemIsSelectable | Qt::ItemIsEnabled; + } + + enum UserRoles { + DateStringRole = Qt::UserRole + 1, + HeaderColorRole = DateStringRole + 1, + CellColorRole = HeaderColorRole + 1, + EventCountRole = CellColorRole + 1, + FilenamesRole = EventCountRole + 1 + }; + + QVariant data(const QModelIndex &proxyIndex, int role = Qt::DisplayRole) const { + + if (!proxyIndex.isValid()) return QVariant(); + + QVariant returning; + + switch (role) { + + case Qt::BackgroundRole: + { + QList colors; + QVector *arr = dateToRows.value(date(proxyIndex), NULL); + if (arr) { + foreach (int i, *arr) + // we have rides on this day... + if (sourceModel()->data(index(i, dateIndex, QModelIndex())).toDateTime() == mainWindow->rideItem()->dateTime) + colors.append(GColor(CCALCURRENT)); + else + colors.append(GColor(CCALACTUAL)); + } + // added planned workouts + for (int k= mainWindow->rideCalendar->data(date(proxyIndex), EventCountRole).toInt(); k>0; k--) + colors.append(GColor(CCALPLANNED)); + + return QVariant::fromValue >(colors); + } + break; + + case Qt::ForegroundRole: // XXX Should return a list for each ride + // XXX within the cell + return Qt::black; + break; + + case Qt::FontRole: // XXX Should return a list for each ride + // XXX within the cell + { + QFont font; + font.fromString(appsettings->value(this, GC_FONT_CALENDAR, QFont().toString()).toString()); + font.setPointSize(appsettings->value(this, GC_FONT_CALENDAR_SIZE, 12).toInt()); + return font; + } + break; + + case CellColorRole: // what color for the cell? + if (date(proxyIndex) == QDate::currentDate()) + return GColor(CCALTODAY); + if (date(proxyIndex).month() == month) + return GColor(CCALCELL); + else + return GColor(CCALHEAD); + break; + + case HeaderColorRole: // what color for the cell heading + if (date(proxyIndex).month() == month) + return GColor(CCALHEAD); + else + return GColor(CCALHEAD).darker(200); + break; + + case FilenamesRole: + { + QStringList filenames; + // is there an entry? + QVector *arr = dateToRows.value(date(proxyIndex), NULL); + QStringList strings; + + if (arr) + foreach (int i, *arr) + filenames << sourceModel()->data(index(i, filenameIndex, QModelIndex())).toString(); + + // fold in planned workouts + if (mainWindow->rideCalendar->data(date(proxyIndex), EventCountRole).toInt()) { + foreach(QString x, mainWindow->rideCalendar->data(date(proxyIndex), Qt::DisplayRole).toStringList()) + filenames << "calendar"; + } + + return filenames; + } + + case Qt::EditRole: + case Qt::DisplayRole: // returns the string to display + { + // is there an entry? + QVector *arr = dateToRows.value(date(proxyIndex), NULL); + QStringList strings; + + if (arr) + foreach (int i, *arr) + strings << sourceModel()->data(index(i, textIndex, QModelIndex())).toString(); + + // fold in planned workouts + if (mainWindow->rideCalendar->data(date(proxyIndex), EventCountRole).toInt()) { + QStringList planned; + planned = mainWindow->rideCalendar->data(date(proxyIndex), Qt::DisplayRole).toStringList(); + strings << planned; + } + return strings; + } + break; + + case DateStringRole: // how should this date be described in the + // cell heading + { + QDate today = date(proxyIndex); + if (today.month() != month && + (today.addDays(1).month() == month || today.addDays(-1).month() == month)) + return QString("%1 %2").arg(today.day()).arg(QDate::shortMonthName(today.month())); + else + return QString("%1").arg(today.day()); + } + break; + default: + return QVariant(); + break; + } + return QVariant(); + } + + QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const { + + if (role == Qt::DisplayRole) { + if (orientation == Qt::Horizontal) { + return QDate::longDayName(section+1); + } else { + return QString("%1").arg(date(index(section,0)).weekNumber()); + } + } + return QVariant(); + } + + bool setHeaderData (int , Qt::Orientation , const QVariant & , int = Qt::EditRole) { + return true; + } + + int columnCount(const QModelIndex & = QModelIndex()) const { + return 7; + } + + int rowCount(const QModelIndex & = QModelIndex()) const { + return rows; + } + + // does this index have children? + bool hasChildren(const QModelIndex &) const { + return false; + } +}; + +// lets do the calendar delegate too since whilst we're at it... +class GcCalendarDelegate : public QItemDelegate +{ + Q_OBJECT + G_OBJECT + + + public: + GcCalendarDelegate(QTableView *parent = 0) : QItemDelegate(parent) {} + + void createPainterPath(QPainterPath & emptyPath, const QRect & fullItemRect, const int iRoundTop, const int iRoundBottom) const { + emptyPath = QPainterPath(); + bool bRoundTop = iRoundTop > 0; + bool bRountBottom = iRoundBottom > 0; + + if (bRoundTop) { + emptyPath.moveTo(fullItemRect.topLeft() + QPoint(0, iRoundTop)); + emptyPath.quadTo(fullItemRect.topLeft(), fullItemRect.topLeft() + QPoint(iRoundTop, 0)); + + } else emptyPath.moveTo(fullItemRect.topLeft()); + + emptyPath.lineTo(fullItemRect.topRight() - QPoint(iRoundTop, 0)); + + if (bRoundTop) + emptyPath.quadTo(fullItemRect.topRight(), fullItemRect.topRight() + QPoint(0, iRoundTop)); + + emptyPath.lineTo(fullItemRect.bottomRight() - QPoint(0, iRoundBottom)); + + if (bRountBottom) + emptyPath.quadTo(fullItemRect.bottomRight(), fullItemRect.bottomRight() - QPoint(iRoundBottom, 0)); + + emptyPath.lineTo(fullItemRect.bottomLeft() + QPoint(iRoundBottom, 0)); + + if (bRountBottom) + emptyPath.quadTo(fullItemRect.bottomLeft(), fullItemRect.bottomLeft() - QPoint(0, iRoundBottom)); + + emptyPath.closeSubpath(); + } + + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const { + + // font + QVariant vfont = index.data(Qt::FontRole); + painter->setFont(vfont.value()); + + // cell decoration + QColor bg = index.data(GcCalendarModel::CellColorRole).value(); + QColor hg = index.data(GcCalendarModel::HeaderColorRole).value(); + + + // If selected then use selection color anyway... + if (option.state & QStyle::State_Selected) bg = option.palette.highlight().color(); + painter->fillRect(option.rect, bg); + + // still paint header + QRect hd(option.rect.x(), option.rect.y(), option.rect.width(), 15); + painter->fillRect(hd, hg); + + // date... + QString datestring = index.data(GcCalendarModel::DateStringRole).toString(); + QTextOption textOption(Qt::AlignRight); + painter->drawText(hd, datestring, textOption); + + // text + QStringList texts = index.data(Qt::DisplayRole).toStringList(); + QList colors = index.data(Qt::BackgroundRole).value >(); + if (texts.count()) { + + int height = (option.rect.height()-21) / texts.count(); + int y = option.rect.y()+17; + int i=0; + foreach (QString text, texts) { + + QRect bd(option.rect.x()+2, y, option.rect.width()-4, height); + y += height+2; + + QPainterPath cachePath; + createPainterPath(cachePath, bd, 4, 4); + + QPen pen; + pen.setColor(Qt::black); + pen.setWidth(2); + QColor fillColor = colors[i++]; + fillColor.setAlpha(250); + painter->setBrush(fillColor); + painter->setPen(pen); + painter->setRenderHint(QPainter::Antialiasing); + painter->drawPath(cachePath); + painter->drawText(bd, text); + } + } + + } + +}; + +#endif diff --git a/src/GcPane.cpp b/src/GcPane.cpp new file mode 100644 index 000000000..a9c054b88 --- /dev/null +++ b/src/GcPane.cpp @@ -0,0 +1,446 @@ +/* + * Copyright (c) 2010 Mark Liversedge (liversedge@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 2 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., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "GcPane.h" + +// on a mac we remove the drop shadows, since +// they leave nasty artefacts for the close icon +#ifdef Q_OS_MAC +#include "/Developer/Headers/FlatCarbon/MacWindows.h" +#endif + +GcPane::GcPane(QLayout *p) +{ + GcPane(); + setLayout(p); +} + +GcPane::GcPane() : QWidget(NULL, Qt::FramelessWindowHint), + borderWidth(4), dragState(None) +{ + closeImage = QPixmap(":images/toolbar/popbutton.png"); + flipImage = QPixmap(":images/toolbar/flipbutton.png"); +#ifdef Q_OS_MAC + HIViewRef v = (HIViewRef)winId(); + WindowRef w = HIViewGetWindow(v); + ChangeWindowAttributes(w, kWindowNoShadowAttribute, kWindowNoAttributes); +#endif + setAttribute(Qt::WA_DeleteOnClose); + setAttribute(Qt::WA_TranslucentBackground); + setAttribute(Qt::WA_NoSystemBackground); + setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint); + setMouseTracking(true); + setContentsMargins(10,10,10,10); + setStyleSheet(QString::fromUtf8( + "border-color: rgba(255,255,255,0); \ + background-color: rgba(255, 255, 255, 0);" + )); + // from the graphics stuff to a normal widget + widget = new QGraphicsProxyWidget(); // proxy from graphics to normal widgets + widget->setContentsMargins(0,0,0,0); + //widget->setAutoFillBackground(false); + + window = new QWidget(); // a normal widget we layout into + //window->setAutoFillBackground(false); + window->setAttribute(Qt::WA_TranslucentBackground); + window->setContentsMargins(0,0,0,0); + widget->setWidget(window); // link them + + // setup the scene and view + scene = new QGraphicsScene(this); + scene->addItem(widget); + + view = new QGraphicsView(scene); // we made our own to resize nicely + //view->setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers))); + view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + view->setAutoFillBackground(false); + view->setFrameStyle(QFrame::NoFrame); + view->viewport()->installEventFilter(this); + view->viewport()->setMouseTracking(true); + + // we just display a view containing + // all the other crap + QVBoxLayout *mainLayout = new QVBoxLayout(this); + QWidget::setLayout(mainLayout); + mainLayout->setSpacing(0); + mainLayout->addWidget(view); +} + +void +GcPane::setLayout(QLayout *layout) +{ + window->setLayout(layout); +} + +bool +GcPane::eventFilter(QObject *object, QEvent *e) +{ + + //if (object != (QObject *)plot()->canvas() ) + //return false; + if (dragState != None) { + switch (e->type()) { + case QEvent::MouseMove: + mouseMoveEvent((QMouseEvent*)e); + break; + case QEvent::MouseButtonRelease: + mouseReleaseEvent((QMouseEvent*)e); + break; + default: + return QObject::eventFilter(object, e); + } + } else { + return QObject::eventFilter(object, e); + } + return true; +} + +void +GcPane::paintEvent(QPaintEvent *) +{ + + // Init paint settings + QPainter painter(this); + //painter.setRenderHint(QPainter::Antialiasing); + QColor color = GColor(CPOPUP); + QPen pen(color); + + // border color + if (dragState != None) pen.setColor(Qt::black); + pen.setWidth(borderWidth); + pen.color().setAlpha(40); + painter.setPen(pen); + + // background color + painter.setBrush(Qt::NoBrush); + + // draw border and background + painter.drawRoundedRect(3, 0 + (closeImage.height()/2), + width()-(closeImage.width()/2), height()-(closeImage.height()/2)-3, + closeImage.width()/2, closeImage.height()/2); + + pen.setColor(Qt::white); + if (underMouse()) + color.setAlpha(200); + else + color.setAlpha(150); + pen.setWidth(1); + painter.setPen(pen); + painter.setBrush(color); + // draw border and background + painter.drawRoundedRect(4, 1 + (closeImage.height()/2), + width()-2-(closeImage.width()/2), height()-2-(closeImage.height()/2)-3, + closeImage.width()/2, closeImage.height()/2); + // close button + if (underMouse()) + painter.drawPixmap(width()-closeImage.width(), 0, closeImage.width(), closeImage.height(), closeImage); + + // close button + if (underMouse()) + painter.drawPixmap(width()-(closeImage.width()+5+flipImage.width()), 0, + flipImage.width(), flipImage.height(), flipImage); + +} + +// for the mouse position, are we in a hotspot? +// if so, what would the drag state become if we +// clicked? +GcPane::DragState +GcPane::spotHotSpot(QMouseEvent *e) +{ + // corner + int corner = closeImage.width()/2; + + // account for offset + int _y = e->y() - (closeImage.height()/2); + int _x = e->x(); + int _height = height() - (closeImage.height()/2); + int _width = width() - (closeImage.width()/2); + + if (e->x() > (2 + width() - closeImage.width()) && e->y() < (closeImage.height()-2)) + return Close; + else if (e->x() > (width() - 5 - closeImage.width() - flipImage.width()) && + e->x() < (width() - 5 - closeImage.width()) && + e->y() < (closeImage.height()-2)) + return Flip; + else if (_x <= corner && _y <= corner) return (TLCorner); + else if (_x >= (_width-corner) && _y <= corner) return (TRCorner); + else if (_x <= corner && _y >= (_height-corner)) return (BLCorner); + else if (_x >= (_width-corner) && _y >= (_height-corner)) return (BRCorner); + else if (_x <= borderWidth) return (Left); + else if (_x >= (_width-borderWidth)) return (Right); + else if (_y <= borderWidth) return (Top); + else if (_y >= (_height-borderWidth)) return (Bottom); + else return (Move); +} + +void +GcPane::enterEvent(QEvent *) +{ + repaint(); +} + +void +GcPane::leaveEvent(QEvent *) +{ + repaint(); +} + +void +GcPane::mousePressEvent(QMouseEvent *e) +{ + if (e->button() == Qt::NoButton || isHidden()) { + setDragState(None); + return; + } + + DragState h = spotHotSpot(e); + + // is it on the close icon? + if (h == Close) { + setDragState(None); + hide(); + //emit exit(); + return; + } else if (h == Flip) { + setDragState(None); + flip(); + } + + // get current window state + oWidth = width(); + oHeight = height(); + oX = pos().x(); + oY = pos().y(); + mX = e->globalX(); + mY = e->globalY(); + + setDragState(h); // set drag state then! + + repaint(); +} + +void +GcPane::mouseReleaseEvent(QMouseEvent *) +{ + setDragState(None); + repaint(); +} + +void +GcPane::mouseMoveEvent(QMouseEvent *e) +{ + if (dragState == None) { + // set the cursor shape + setCursorShape(spotHotSpot(e)); + return; + } + + // work out the relative move x and y + int relx = e->globalX() - mX; + int rely = e->globalY() - mY; + + switch (dragState) { + + default: + case Move : + move(oX + relx, oY + rely); + break; + + case TLCorner : + { + int newWidth = oWidth - relx; + int newHeight = oHeight - rely; + + // need to move and resize + if (newWidth > 30 && newHeight > 30) { + move(oX + relx, oY + rely); + resize(newWidth, newHeight); + } + } + break; + + case TRCorner : + { + int newWidth = oWidth + relx; + int newHeight = oHeight - rely; + + // need to move and resize if changes on y plane + if (newWidth > 30 && newHeight > 30) { + move(oX, oY + rely); + resize(newWidth, newHeight); + } + } + break; + + case BLCorner : + { + int newWidth = oWidth - relx; + int newHeight = oHeight + rely; + + // need to move and resize + if (newWidth > 30 && newHeight > 30) { + move(oX + relx, oY); + resize(newWidth, newHeight); + } + } + break; + + case BRCorner : + { + int newWidth = oWidth + relx; + int newHeight = oHeight + rely; + + // need to move and resize + if (newWidth > 30 && newHeight > 30) { + resize(newWidth, newHeight); + } + } + break; + + case Top : + { + int newHeight = oHeight - rely; + + // need to move and resize + if (newHeight > 30) { + move (oX, oY + rely); + resize(oWidth, newHeight); + } + } + break; + + case Bottom : + { + int newHeight = oHeight + rely; + + // need to move and resize + if (newHeight > 30) { + resize(oWidth, newHeight); + } + } + break; + + case Left : + { + int newWidth = oWidth - relx; + + // need to move and resize + if (newWidth > 30) { + move (oX + relx, oY); + resize(newWidth, oHeight); + } + } + break; + + case Right : + { + int newWidth = oWidth + relx; + + // need to move and resize + if (newWidth > 30) { + resize(newWidth, oHeight); + } + } + break; + } + + //repaint(); + //QApplication::processEvents(); // flicker... +} + +void +GcPane::setDragState(DragState d) +{ + dragState = d; + setCursorShape(d); +} + +void +GcPane::setCursorShape(DragState d) +{ + // set cursor + switch (d) { + case Bottom: + case Top: + setCursor(Qt::SizeVerCursor); + break; + case Left: + case Right: + setCursor(Qt::SizeHorCursor); + break; + case TLCorner: + case BRCorner: + setCursor(Qt::SizeFDiagCursor); + break; + case TRCorner: + case BLCorner: + setCursor(Qt::SizeBDiagCursor); + break; + case Move: + //setCursor(Qt::OpenHandCursor); //XXX sub widgets don't set the cursor... + setCursor(Qt::ArrowCursor); + break; + default: + case Close: + case None: + setCursor(Qt::ArrowCursor); + break; + } +} + +void +GcPane::resizeEvent (QResizeEvent *) +{ + setUpdatesEnabled(false); + + // resize the scene, view and widget to fit + widget->resize(width()-50, height()-50); + view->scene()->setSceneRect(0, 0, width()-50, height()-50); + view->resize(width()-50, height()-50); + + setUpdatesEnabled(true); +} + +void +GcPane::flip() +{ +#if 0 +// XXX broken! + QStateMachine *machine = new QStateMachine(this); + QState *s0 = new QState(); + s0->assignProperty(scene, "rotation", 0); + QState *s1 = new QState(); + s1->assignProperty(scene, "rotation", 90); + QAbstractTransition *t1 = s0->addTransition(widget, SIGNAL(flipRequest()), s1); + QPropertyAnimation *yRotationAnim = new QPropertyAnimation(scene, "rotation"); + yRotationAnim->setDuration(250); + t1->addAnimation(yRotationAnim); + QState *s2 = new QState(); + //QObject::connect(s2, SIGNAL(entered()), this, SLOT(togglePage())); + s2->assignProperty(scene, "rotation", -90); + s1->addTransition(s1, SIGNAL(polished()), s2); + QAbstractTransition *t2 = s2->addTransition(s0); + t2->addAnimation(yRotationAnim); + machine->addState(s0); + machine->addState(s1); + machine->addState(s2); + machine->start(); +#endif +} diff --git a/src/GcPane.h b/src/GcPane.h new file mode 100644 index 000000000..a9fa32a6b --- /dev/null +++ b/src/GcPane.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2010 Mark Liversedge (liversedge@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 2 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., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _GC_GcPane_h +#define _GC_GcPane_h 1 +#include "GoldenCheetah.h" + +#include +#include "Colors.h" + +class GcPane : public QWidget +{ + Q_OBJECT + G_OBJECT + + + enum drag { None, Close, Flip, Move, Left, Right, Top, Bottom, TLCorner, TRCorner, BLCorner, BRCorner }; + typedef enum drag DragState; + + public: + GcPane(); + GcPane(QLayout *p); + + void setLayout(QLayout *layout); // override standard setLayout + + protected: + + signals: + void exit(); + + private slots: + + protected: + virtual void paintEvent(QPaintEvent *); + virtual void mousePressEvent(QMouseEvent *); + virtual void mouseReleaseEvent(QMouseEvent *); + virtual void mouseMoveEvent(QMouseEvent *); + virtual void enterEvent(QEvent *); + virtual void leaveEvent(QEvent *); + bool eventFilter(QObject *object, QEvent *e); + virtual void resizeEvent (QResizeEvent * e); + void flip(); + + private: + void setDragState(DragState); + void setCursorShape(DragState); + DragState spotHotSpot(QMouseEvent *); + + // member vars + QPixmap closeImage, flipImage; + int borderWidth; + + // drag / resize information + DragState dragState; + int oWidth, oHeight, oX, oY, mX, mY; + + // graphics + QGraphicsView *view; + QGraphicsScene *scene; + QGraphicsProxyWidget *widget; + QGraphicsGridLayout *layout; + + // normal + QWidget *window; +}; + +#endif // _GC_GcPane_h diff --git a/src/GcRideFile.cpp b/src/GcRideFile.cpp index 70835d789..e230b84a3 100644 --- a/src/GcRideFile.cpp +++ b/src/GcRideFile.cpp @@ -28,7 +28,7 @@ static int gcFileReaderRegistered = RideFileFactory::instance().registerReader( - "gc", "GoldenCheetah Native Format", new GcFileReader()); + "gc", "GoldenCheetah XML Format", new GcFileReader()); RideFile * GcFileReader::openRideFile(QFile &file, QStringList &errors) const diff --git a/src/GcRideFile.h b/src/GcRideFile.h index 4649665e7..c5699fdc9 100644 --- a/src/GcRideFile.h +++ b/src/GcRideFile.h @@ -18,6 +18,7 @@ #ifndef _GcRideFile_h #define _GcRideFile_h +#include "GoldenCheetah.h" #include "RideFile.h" diff --git a/src/GcWindowLayout.cpp b/src/GcWindowLayout.cpp new file mode 100644 index 000000000..464c05bf3 --- /dev/null +++ b/src/GcWindowLayout.cpp @@ -0,0 +1,167 @@ +/* +* Copyright (c) 2006 Sean C. Rhea (srhea@srhea.net) +* +* 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 2 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., 51 +* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include "GcWindowLayout.h" + +GcWindowLayout::GcWindowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing) + : QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing) +{ + setContentsMargins(margin, margin, margin, margin); +} + +GcWindowLayout::GcWindowLayout(int margin, int hSpacing, int vSpacing) + : m_hSpace(hSpacing), m_vSpace(vSpacing) +{ + setContentsMargins(margin, margin, margin, margin); +} + +GcWindowLayout::~GcWindowLayout() +{ + QLayoutItem *item; + while ((item = takeAt(0))) + delete item; +} + +void GcWindowLayout::addItem(QLayoutItem *item) +{ + itemList.append(item); +} + +int GcWindowLayout::horizontalSpacing() const +{ + if (m_hSpace >= 0) { + return m_hSpace; + } else { + return smartSpacing(QStyle::PM_LayoutHorizontalSpacing); + } +} + +int GcWindowLayout::verticalSpacing() const +{ + if (m_vSpace >= 0) { + return m_vSpace; + } else { + return smartSpacing(QStyle::PM_LayoutVerticalSpacing); + } +} + +int GcWindowLayout::count() const +{ + return itemList.size(); +} + +QLayoutItem *GcWindowLayout::itemAt(int index) const +{ + return itemList.value(index); +} + +QLayoutItem *GcWindowLayout::takeAt(int index) +{ + if (index >= 0 && index < itemList.size()) + return itemList.takeAt(index); + else + return 0; +} + +Qt::Orientations GcWindowLayout::expandingDirections() const +{ + return 0; +} + +bool GcWindowLayout::hasHeightForWidth() const +{ + return true; +} + +int GcWindowLayout::heightForWidth(int width) const +{ + int height = doLayout(QRect(0, 0, width, 0), true); + return height; +} + +void GcWindowLayout::setGeometry(const QRect &rect) +{ + QLayout::setGeometry(rect); + doLayout(rect, false); +} + +QSize GcWindowLayout::sizeHint() const +{ + return minimumSize(); +} + +QSize GcWindowLayout::minimumSize() const +{ + QSize size; + QLayoutItem *item; + foreach (item, itemList) + size = size.expandedTo(item->minimumSize()); + + size += QSize(2*margin(), 2*margin()); + return size; +} + +int GcWindowLayout::doLayout(const QRect &rect, bool testOnly) const +{ + int left, top, right, bottom; + getContentsMargins(&left, &top, &right, &bottom); + QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom); + int x = effectiveRect.x(); + int y = effectiveRect.y(); + int lineHeight = 0; + + QLayoutItem *item; + foreach (item, itemList) { + QWidget *wid = item->widget(); + int spaceX = horizontalSpacing(); + if (spaceX == -1) + spaceX = wid->style()->layoutSpacing( + QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal); + int spaceY = verticalSpacing(); + if (spaceY == -1) + spaceY = wid->style()->layoutSpacing( + QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical); + int nextX = x + item->sizeHint().width() + spaceX; + if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) { + x = effectiveRect.x(); + y = y + lineHeight + spaceY; + nextX = x + item->sizeHint().width() + spaceX; + lineHeight = 0; + } + + if (!testOnly) + item->setGeometry(QRect(QPoint(x, y), item->sizeHint())); + + x = nextX; + lineHeight = qMax(lineHeight, item->sizeHint().height()); + } + return y + lineHeight - rect.y() + bottom; +} +int GcWindowLayout::smartSpacing(QStyle::PixelMetric pm) const +{ + QObject *parent = this->parent(); + if (!parent) { + return -1; + } else if (parent->isWidgetType()) { + QWidget *pw = static_cast(parent); + return pw->style()->pixelMetric(pm, 0, pw); + } else { + return static_cast(parent)->spacing(); + } +} diff --git a/src/GcWindowLayout.h b/src/GcWindowLayout.h new file mode 100644 index 000000000..f7e55688d --- /dev/null +++ b/src/GcWindowLayout.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2006 Sean C. Rhea (srhea@srhea.net) + * + * 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 2 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., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _GC_GcWindowLayout_h +#define _GC_GcWindowLayout_h + +#include +#include +#include +#include + +// A simple layout manager that lays out widgets +// left to right and top to bottom. So as widgets +// are resized and their neighbour won't fit to +// the right they move down. +class GcWindowLayout : public QLayout +{ +public: + GcWindowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1); + GcWindowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1); + ~GcWindowLayout(); + + void addItem(QLayoutItem *item); + int horizontalSpacing() const; + int verticalSpacing() const; + Qt::Orientations expandingDirections() const; + bool hasHeightForWidth() const; + int heightForWidth(int) const; + int count() const; + QLayoutItem *itemAt(int index) const; + QSize minimumSize() const; + void setGeometry(const QRect &rect); + QSize sizeHint() const; + QLayoutItem *takeAt(int index); + +private: + int doLayout(const QRect &rect, bool testOnly) const; + int smartSpacing(QStyle::PixelMetric pm) const; + + QList itemList; + int m_hSpace; + int m_vSpace; +}; + +#endif diff --git a/src/GcWindowRegistry.cpp b/src/GcWindowRegistry.cpp new file mode 100644 index 000000000..86fc15e2e --- /dev/null +++ b/src/GcWindowRegistry.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2010 Mark Liversedge (liversedge@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 2 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., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "GoldenCheetah.h" +#include "GcWindowRegistry.h" + +// all the windows we have defined +#include "AerolabWindow.h" +#include "AllPlotWindow.h" +#include "CriticalPowerWindow.h" +#include "DiaryWindow.h" +#include "GoogleMapControl.h" +#include "HistogramWindow.h" +#include "LTMWindow.h" +#include "ModelWindow.h" +#include "PerformanceManagerWindow.h" +#include "PfPvWindow.h" +#include "RaceWindow.h" // XXX not done +#include "RealtimeWindow.h" +#include "RideEditor.h" +#include "RideSummaryWindow.h" +#include "ScatterWindow.h" +#include "SummaryWindow.h" +#include "TrainWindow.h" // XXX not done +#include "TreeMapWindow.h" +#include "WeeklySummaryWindow.h" + +GcWindowRegistry GcWindows[] = { + // name GcWinID + { "Aerolab", GcWindowTypes::Aerolab }, + { "Ride", GcWindowTypes::AllPlot }, + { "Critical Power", GcWindowTypes::CriticalPower }, + { "Diary", GcWindowTypes::Diary }, + { "Map", GcWindowTypes::GoogleMap }, + { "Histogram", GcWindowTypes::Histogram }, + { "Metrics", GcWindowTypes::LTM }, + { "3d", GcWindowTypes::Model }, + { "Performance Manager", GcWindowTypes::PerformanceManager }, + { "PfPv", GcWindowTypes::PfPv }, + { "Training", GcWindowTypes::Training }, + { "Ride Editor", GcWindowTypes::RideEditor }, + { "Ride Summary", GcWindowTypes::RideSummary }, + { "Scatter", GcWindowTypes::Scatter }, + { "Ride Summary & Fields", GcWindowTypes::Summary }, + { "Treemap", GcWindowTypes::TreeMap }, + { "Weekly Summary", GcWindowTypes::WeeklySummary }, + { "", GcWindowTypes::None }}; + +// instantiate a new window +GcWindow * +GcWindowRegistry::newGcWindow(GcWinID id, MainWindow *main) //XXX mainWindow will go soon +{ + GcWindow *returning = NULL; + + switch(id) { + case GcWindowTypes::Aerolab: returning = new AerolabWindow(main); break; + case GcWindowTypes::AllPlot: returning = new AllPlotWindow(main); break; + case GcWindowTypes::CriticalPower: returning = new CriticalPowerWindow(main->home, main); break; + case GcWindowTypes::Diary: returning = new DiaryWindow(main); break; + case GcWindowTypes::GoogleMap: returning = new GoogleMapControl(main); break; + case GcWindowTypes::Histogram: returning = new HistogramWindow(main); break; + case GcWindowTypes::LTM: returning = new LTMWindow(main, main->useMetricUnits, main->home); break; +#ifdef GC_HAVE_QWTPLOT3D + case GcWindowTypes::Model: returning = new ModelWindow(main, main->home); break; +#else + case GcWindowTypes::Model: returning = new GcWindow(); break; +#endif + case GcWindowTypes::PerformanceManager: returning = new PerformanceManagerWindow(main); break; + case GcWindowTypes::PfPv: returning = new PfPvWindow(main); break; + case GcWindowTypes::Training: returning = new TrainWindow(main, main->home); break; + case GcWindowTypes::RideEditor: returning = new RideEditor(main); break; + case GcWindowTypes::RideSummary: returning = new RideSummaryWindow(main); break; + case GcWindowTypes::Scatter: returning = new ScatterWindow(main, main->home); break; + case GcWindowTypes::Summary: returning = new SummaryWindow(main); break; + case GcWindowTypes::TreeMap: returning = new TreeMapWindow(main, main->useMetricUnits, main->home); break; + case GcWindowTypes::WeeklySummary: returning = new WeeklySummaryWindow(main->useMetricUnits, main); break; + default: return NULL; break; + } + if (returning) returning->setProperty("type", QVariant::fromValue(id)); + return returning; +} diff --git a/src/GcWindowRegistry.h b/src/GcWindowRegistry.h new file mode 100644 index 000000000..1542aea88 --- /dev/null +++ b/src/GcWindowRegistry.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2010 Mark Liversedge (liversedge@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 2 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., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _GC_GcWindowRegistry_h +#define _GC_GcWindowRegistry_h + +#include "GoldenCheetah.h" + +class MainWindow; + +// all the windows we have defined +namespace GcWindowTypes { +enum gcwinid { + None = 0, + Aerolab = 1, + AllPlot = 2, + CriticalPower =3, + Diary =4, + GoogleMap =5, + Histogram =6, + LTM =7, + Model =8, + PerformanceManager =9, + PfPv =10, + Race =11, + Training =12, + RideEditor =13, + RideSummary =14, + Scatter =15, + Summary =16, + Train =17, + TreeMap =18, + WeeklySummary =19, +}; +}; +typedef enum GcWindowTypes::gcwinid GcWinID; + +struct GcWindowRegistry { + QString name; + GcWinID id; + + static GcWindow *newGcWindow(GcWinID id, MainWindow *main); //XXX main is gonna go +}; + +extern GcWindowRegistry GcWindows[]; +#endif diff --git a/src/GcWindowTool.cpp b/src/GcWindowTool.cpp new file mode 100644 index 000000000..22643c11e --- /dev/null +++ b/src/GcWindowTool.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2010 Mark Liversedge (liversedge@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 2 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., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "GoldenCheetah.h" +#include "GcWindowRegistry.h" +#include "GcWindowTool.h" +#include + +GcWindowTool::GcWindowTool(QWidget *parent) : QWidget(parent) +{ + QVBoxLayout *mainLayout = new QVBoxLayout(this); + mainLayout->setContentsMargins(0,0,0,0); + setLayout(mainLayout); + + chartTree = new QTreeWidget; + mainLayout->addWidget(chartTree); + + // setup the chart list + chartTree->setColumnCount(1); + chartTree->setSelectionMode(QAbstractItemView::SingleSelection); + chartTree->header()->hide(); + chartTree->setAlternatingRowColors (true); + chartTree->setIndentation(5); + chartTree->setDragDropMode(QAbstractItemView::DragOnly); + allCharts = new QTreeWidgetItem(chartTree, ROOT_TYPE); + allCharts->setText(0, tr("Chart")); + + // initialise the metrics catalogue and user selector + for (int i = 0; GcWindows[i].id != GcWindowTypes::None; i++) { + + // add to the tree + QTreeWidgetItem *add; + add = new QTreeWidgetItem(allCharts, static_cast(GcWindows[i].id)); + add->setText(0, GcWindows[i].name); + add->setFlags(add->flags() | Qt::ItemIsDragEnabled); + } + chartTree->expandItem(allCharts); + + connect(chartTree,SIGNAL(itemSelectionChanged()), this, SLOT(chartTreeWidgetSelectionChanged())); +} + +/*---------------------------------------------------------------------- + * Selections Made + *----------------------------------------------------------------------*/ + +void +GcWindowTool::chartTreeWidgetSelectionChanged() +{ + chartSelected(); +} diff --git a/src/GcWindowTool.h b/src/GcWindowTool.h new file mode 100644 index 000000000..2a80f5d6e --- /dev/null +++ b/src/GcWindowTool.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2010 Mark Liversedge (liversedge@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 2 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., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _GC_GcWindowTool_h +#define _GC_GcWindowTool_h 1 +#include "GoldenCheetah.h" + +#include "MainWindow.h" +#include "Season.h" +#include "RideMetric.h" +#include "LTMSettings.h" + +#include +#include + +// tree widget types +#define ROOT_TYPE 1 +#define CHART_TYPE 3 + +// list of charts to add to a window +class GcWindowTool : public QWidget +{ + Q_OBJECT + G_OBJECT + + + public: + + GcWindowTool(QWidget *parent = 0); + + QTreeWidgetItem *selectedChart() { return chartTree->selectedItems().first(); } + + signals: + + void chartSelected(); + + private slots: + void chartTreeWidgetSelectionChanged(); + + private: + + QTreeWidget *chartTree; + QTreeWidgetItem *allCharts; + +}; + +#endif // _GC_GcWindowTool_h + diff --git a/src/GoldenCheetah.cpp b/src/GoldenCheetah.cpp new file mode 100644 index 000000000..08810662c --- /dev/null +++ b/src/GoldenCheetah.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2010 Mark Liversedge (liversedge@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 2 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., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "GoldenCheetah.h" +#include "Colors.h" +#include +#include +#include + +QWidget *GcWindow::controls() const +{ + return _controls; +} + +void GcWindow::setControls(QWidget *x) +{ + _controls = x; + emit controlsChanged(_controls); +} + +QString GcWindow::instanceName() const +{ + return _instanceName; +} + +void GcWindow::_setInstanceName(QString x) +{ + _instanceName = x; +} + +QString GcWindow::title() const +{ + return _title; +} + +void GcWindow::setTitle(QString x) +{ + _title = x; + emit titleChanged(_title); +} + +RideItem* GcWindow::rideItem() const +{ + return _rideItem; +} + +void GcWindow::setRideItem(RideItem* x) +{ + _rideItem = x; + emit rideItemChanged(_rideItem); +} + +int GcWindow::widthFactor() const +{ + return _widthFactor; +} + +void GcWindow::setWidthFactor(int x) +{ + _widthFactor = x; + emit widthFactorChanged(x); +} + +int GcWindow::heightFactor() const +{ + return _heightFactor; +} + +void GcWindow::setHeightFactor(int x) +{ + _heightFactor = x; + emit heightFactorChanged(x); +} + +GcWindow::GcWindow() +{ +} + +GcWindow::GcWindow(QWidget *parent) : QFrame(parent) { + qRegisterMetaType("controls"); + qRegisterMetaType("ride"); + qRegisterMetaType("type"); + setParent(parent); + setControls(NULL); + setRideItem(NULL); + setTitle(""); + setContentsMargins(0,0,0,0); +} + +GcWindow::~GcWindow() +{ + //qDebug()<<"deleting.."<