From def7b1a0316fb7a2d38cca61088a65e8be1d8142 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jaakko=20Kyr=C3=B6?= Date: Fri, 7 May 2010 21:03:03 +0300 Subject: [PATCH] Initial code --- .gitignore | 4 + LICENCE.GPL | 340 ++++++++++++++++++++ debian/changelog | 6 + debian/compat | 1 + debian/control | 12 + debian/dirs | 2 + debian/files | 1 + debian/rules | 98 ++++++ .../qmaemo5homescreenadaptor.cpp | 287 +++++++++++++++++ .../qmaemo5homescreenadaptor.h | 90 ++++++ .../qmaemo5homescreenadaptor.pri | 4 + src/eveaccount.cpp | 96 ++++++ src/eveaccount.h | 38 +++ src/evehomescreen | Bin 0 -> 148545 bytes src/evehomescreen.desktop | 10 + src/evehomescreen.pro | 64 ++++ src/evemodel.cpp | 87 +++++ src/evemodel.h | 36 +++ src/evesettingsdialog.cpp | 127 ++++++++ src/evesettingsdialog.h | 43 +++ src/evesettingsdialog.ui | 221 +++++++++++++ src/eveskilltraining.cpp | 98 ++++++ src/eveskilltraining.h | 43 +++ src/main.cpp | 21 ++ src/mainwindow.cpp | 59 ++++ src/mainwindow.h | 33 ++ src/mainwindow.ui | 74 +++++ src/skilltree.cpp | 109 +++++++ src/skilltree.h | 29 ++ src/ui_evesettingsdialog.h | 132 ++++++++ src/ui_mainwindow.h | 84 +++++ src/widget.cpp | 114 +++++++ src/widget.h | 43 +++ tests/main.cpp | 82 +++++ tests/tests.pro | 26 ++ 35 files changed, 2514 insertions(+) create mode 100644 .gitignore create mode 100644 LICENCE.GPL create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/dirs create mode 100644 debian/docs create mode 100644 debian/files create mode 100755 debian/rules create mode 100644 qmaemo5homescreenadaptor/qmaemo5homescreenadaptor.cpp create mode 100644 qmaemo5homescreenadaptor/qmaemo5homescreenadaptor.h create mode 100644 qmaemo5homescreenadaptor/qmaemo5homescreenadaptor.pri create mode 100644 src/eveaccount.cpp create mode 100644 src/eveaccount.h create mode 100755 src/evehomescreen create mode 100644 src/evehomescreen.desktop create mode 100644 src/evehomescreen.pro create mode 100644 src/evemodel.cpp create mode 100644 src/evemodel.h create mode 100644 src/evesettingsdialog.cpp create mode 100644 src/evesettingsdialog.h create mode 100644 src/evesettingsdialog.ui create mode 100644 src/eveskilltraining.cpp create mode 100644 src/eveskilltraining.h create mode 100644 src/main.cpp create mode 100644 src/mainwindow.cpp create mode 100644 src/mainwindow.h create mode 100644 src/mainwindow.ui create mode 100644 src/skilltree.cpp create mode 100644 src/skilltree.h create mode 100644 src/ui_evesettingsdialog.h create mode 100644 src/ui_mainwindow.h create mode 100644 src/widget.cpp create mode 100644 src/widget.h create mode 100644 tests/main.cpp create mode 100644 tests/tests.pro diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dc780df --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*~ +*.o +moc_* +Makefile diff --git a/LICENCE.GPL b/LICENCE.GPL new file mode 100644 index 0000000..b7b5f53 --- /dev/null +++ b/LICENCE.GPL @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..7491f18 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,6 @@ +evehomescreen (0.0.1-1) unstable; urgency=low + + * Initial release (Closes: #nnnn) + + -- Jaakko Kyro Wed, 5 May 2010 22:46:36 +0300 + diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..7ed6ff8 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +5 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..468efd7 --- /dev/null +++ b/debian/control @@ -0,0 +1,12 @@ +Source: evehomescreen +Section: user/Network +Priority: extra +Maintainer: Jaakko Kyro +Build-Depends: debhelper (>= 5) +Standards-Version: 3.7.2 + +Package: evehomescreen +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends}, qt4-maemo5-homescreen-loader +Description: Homescreen widget to display Eve character training status + diff --git a/debian/dirs b/debian/dirs new file mode 100644 index 0000000..ca882bb --- /dev/null +++ b/debian/dirs @@ -0,0 +1,2 @@ +usr/bin +usr/sbin diff --git a/debian/docs b/debian/docs new file mode 100644 index 0000000..e69de29 diff --git a/debian/files b/debian/files new file mode 100644 index 0000000..06915e6 --- /dev/null +++ b/debian/files @@ -0,0 +1 @@ +evehomescreen_0.0.1-1_i386.deb user/Network extra diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..3d6a228 --- /dev/null +++ b/debian/rules @@ -0,0 +1,98 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Sample debian/rules that uses debhelper. +# This file was originally written by Joey Hess and Craig Small. +# As a special exception, when this file is copied by dh-make into a +# dh-make output file, you may use that output file without restriction. +# This special exception was added by Craig Small in version 0.37 of dh-make. + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + + + + +CFLAGS = -Wall -g + +ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) + CFLAGS += -O0 +else + CFLAGS += -O2 +endif + +configure: configure-stamp +configure-stamp: + dh_testdir + # Add here commands to configure the package. + cd src && /opt/qt4-maemo5/bin/qmake "PREFIX = /usr" + touch configure-stamp + + +build: build-stamp + +build-stamp: configure-stamp + dh_testdir + + # Add here commands to compile the package. + cd src && $(MAKE) + #docbook-to-man debian/evehomescreen.sgml > qt-eve-homescreen.1 + + touch $@ + +clean: + dh_testdir + dh_testroot + rm -f build-stamp configure-stamp + + # Add here commands to clean up after the build process. + -cd src && $(MAKE) clean + + dh_clean + +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + + # Add here commands to install the package into debian/qt-eve-homescreen. + cd src && $(MAKE) INSTALL_ROOT=$(CURDIR)/debian/evehomescreen install + + +# Build architecture-independent files here. +binary-indep: build install +# We have nothing to do by default. + +# Build architecture-dependent files here. +binary-arch: build install + dh_testdir + dh_testroot + dh_installchangelogs + dh_installdocs + dh_installexamples +# dh_install +# dh_installmenu +# dh_installdebconf +# dh_installlogrotate +# dh_installemacsen +# dh_installpam +# dh_installmime +# dh_python +# dh_installinit +# dh_installcron +# dh_installinfo + dh_installman + dh_link + dh_strip + dh_compress + dh_fixperms +# dh_perl +# dh_makeshlibs + dh_installdeb + dh_shlibdeps + dh_gencontrol + dh_md5sums + dh_builddeb + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install configure diff --git a/qmaemo5homescreenadaptor/qmaemo5homescreenadaptor.cpp b/qmaemo5homescreenadaptor/qmaemo5homescreenadaptor.cpp new file mode 100644 index 0000000..b473fea --- /dev/null +++ b/qmaemo5homescreenadaptor/qmaemo5homescreenadaptor.cpp @@ -0,0 +1,287 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmaemo5homescreenadaptor.h" + +#include + +#include +#include +#include + +#include +#include + +static QCoreApplication::EventFilter oldEventFilter; +static QList allDesktopItems; + +static Atom atomByName(const char *name) +{ + Atom atom = XInternAtom(QX11Info::display(), name, False); + if (!atom) + qWarning("Unable to obtain %s atom. This class requires a running Hildon session.", name); + + return atom; +} + +enum HomescreenAtoms +{ + HildonAppletId = 0, + NetWmWindowType = 1, + Utf8String = 2, + HildonTypeHomeApplet = 3, + HildonAppletSettings = 4, + HildonAppletShowSettings = 5, + HildonAppletOnCurrentDesktop = 6, + EnumCount = 7 +}; + +static Atom hsAtoms[EnumCount] = { 0, 0, 0, 0, 0, 0, 0 }; + +static void initAtoms() +{ + hsAtoms[HildonAppletId] = atomByName("_HILDON_APPLET_ID"); + hsAtoms[NetWmWindowType] = atomByName("_NET_WM_WINDOW_TYPE"); + hsAtoms[Utf8String] = atomByName("UTF8_STRING"); + hsAtoms[HildonTypeHomeApplet] = atomByName("_HILDON_WM_WINDOW_TYPE_HOME_APPLET"); + hsAtoms[HildonAppletSettings] = atomByName("_HILDON_APPLET_SETTINGS"); + hsAtoms[HildonAppletShowSettings] = atomByName("_HILDON_APPLET_SHOW_SETTINGS"); + hsAtoms[HildonAppletOnCurrentDesktop] = atomByName("_HILDON_APPLET_ON_CURRENT_DESKTOP"); +} + +/*! \class QMaemo5HomescreenAdaptor + + \brief The QMaemo5HomescreenAdaptor flags a top-level QWidget as homescreen widget + + QMaemo5HomescreenAdaptor is used in conjunction with the Qt for Maemo homescreen + loader. It evaluates the two command line arguments "-plugin-id" and "-write-pipe" + to set up a Qt top-level widget as Maemo 5 homescreen widget. + + Note: By default, the widget will have a black background. In order to make the + widget transparent, set the Qt::WA_TranslucentBackground widget attribute. + + Example: + + \code + QLabel *label = new QLabel("Hello Homescreen"); + new QMaemo5HomescreenAdaptor(label); + label->show(); + \endcode + + Maemo 5 supports homescreen widgets with settings dialogs. To use it, set + the settingsAvailable() property and show a settings dialog when the + settingsRequested() signal is emitted. + + Maemo 5 supports more than one homescreen. In order to determine whether + the homescreen widget is on the currently visible homescreen, connect to + the homescreenChanged() signal. +*/ + +/*! \property QMaemo5HomescreenAdaptor::settingsAvailable + + Set this property to true if the widget can make use of a settings dialog, + otherwise to false. When this property is set to true, the Maemo 5 homescreen + renders a small settings icon on top of the homescreen widget when the + user enters the desktop menu. When the user clicks that settings icon, the + settingsRequested() signal is emitted. + + The default is false. + + \sa settingsRequested() + */ + +/*! \fn void settingsRequested() + + This signal is emitted every time the homescreen widget's settings icon is + invoked by the user. Note that this icon is only visible when the settingsAvailable() + property is set. + + \sa settingsAvailable() + */ + +/*! \fn void homescreenChanged(bool isOnCurrentHomescreen) + + This is signal is emitted when current homescreen changes and the homescreen + widget becomes visible or invisible. The \a isOnCurrentHomescreen argument + indicates whether the homescreen widget is on the current homescreen or not. + + This signal can be used to start/stop background processing in order to save + battery life. + */ + +/*! + Constructs a new QMaemo5HomescreenAdaptor for the given \a widget. + + Note: The widget must be a top-level widget, and must not be reparented + during the lifetime of this adaptor. + + Note: \a widget is also the parent of this class, if the widget is destroyed, + so is this adaptor. + */ +QMaemo5HomescreenAdaptor::QMaemo5HomescreenAdaptor(QWidget *widget) + : QObject(widget), + hasSettings(false) +{ + Q_ASSERT(widget->isWindow()); + + if (!hsAtoms[0]) + initAtoms(); + + Display *display = QX11Info::display(); + + const QStringList args = QApplication::arguments(); + + // parse the command line arguments. + int idx; + if ((idx = args.indexOf(QLatin1String("-plugin-id"))) != -1) { + appletId = args.value(idx + 1); + const QByteArray pluginId = appletId.toUtf8(); + if (!pluginId.isEmpty()) { + XChangeProperty(display, + widget->winId(), + hsAtoms[HildonAppletId], + hsAtoms[Utf8String], 8, PropModeReplace, + reinterpret_cast(pluginId.constData()), + pluginId.length()); + } + } + if ((idx = args.indexOf(QLatin1String("-write-pipe"))) != -1) { + bool ok; + int sockId = args.value(idx + 1).toInt(&ok); + if (ok) { + socketNotifier = new QSocketNotifier(sockId, QSocketNotifier::Exception, this); + connect(socketNotifier, SIGNAL(activated(int)), this, SLOT(socketException())); + } + } + + // set the X11 atoms to flag our widget as homescreen widget + if (!appletId.isEmpty()) { + XChangeProperty(display, + widget->winId(), + hsAtoms[NetWmWindowType], + XA_ATOM, 32, PropModeReplace, + reinterpret_cast(&hsAtoms[HildonTypeHomeApplet]), + 1); + + updateStatus(); + + // --- make this window a child of root + XSetTransientForHint(display, widget->winId(), + RootWindow(display, widget->x11Info().screen())); + + // --- add an x11 event filter + if (!oldEventFilter) + oldEventFilter = QCoreApplication::instance()->setEventFilter(applicationEventFilter); + + allDesktopItems.append(this); + } +} + +QMaemo5HomescreenAdaptor::~QMaemo5HomescreenAdaptor() +{ + allDesktopItems.removeOne(this); +} + +/*! \internal */ +void QMaemo5HomescreenAdaptor::updateStatus() +{ + if (appletId.isEmpty()) + return; + + Display *display = QX11Info::display(); + + // Set or remove settings property + if (hasSettings) + XChangeProperty(display, + appletWidget()->winId(), + hsAtoms[HildonAppletSettings], + XA_CARDINAL, 32, PropModeReplace, + (const unsigned char *) &(hasSettings), 1); + else + XDeleteProperty(display, + appletWidget()->winId(), + hsAtoms[HildonAppletSettings]); +} + +/*! \internal */ +void QMaemo5HomescreenAdaptor::socketException() +{ + socketNotifier->setEnabled(false); + appletWidget()->close(); +} + +/*! \internal */ +bool QMaemo5HomescreenAdaptor::applicationEventFilter(void *message, long *result) +{ + bool retval = false; + + if (oldEventFilter) + retval = oldEventFilter(message, result); + + if (allDesktopItems.isEmpty()) + return retval; + + XEvent *ev = reinterpret_cast(message); + + if (ev->type == ClientMessage) { + XClientMessageEvent *cm = (XClientMessageEvent *)message; + if (cm->message_type == hsAtoms[HildonAppletShowSettings]) { + for (int i = 0; i < allDesktopItems.count(); ++i) { + if (allDesktopItems.at(i)->appletWidget()->winId() == ev->xproperty.window) { + emit allDesktopItems.at(i)->settingsRequested(); + retval = true; + } + } + } + } else if (ev->type == PropertyNotify) { + if (ev->xproperty.atom == hsAtoms[HildonAppletOnCurrentDesktop]) { + for (int i = 0; i < allDesktopItems.count(); ++i) { + if (allDesktopItems.at(i)->appletWidget()->winId() == ev->xproperty.window) { + emit allDesktopItems.at(i)->homescreenChanged(ev->xproperty.window == 0); + retval = true; + } + } + } + } + + return retval; +} + diff --git a/qmaemo5homescreenadaptor/qmaemo5homescreenadaptor.h b/qmaemo5homescreenadaptor/qmaemo5homescreenadaptor.h new file mode 100644 index 0000000..68c4d5b --- /dev/null +++ b/qmaemo5homescreenadaptor/qmaemo5homescreenadaptor.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMAEMO5HOMESCREENADAPTOR_H +#define QMAEMO5HOMESCREENADAPTOR_H + +#include +#include + +class QWidget; +class QSocketNotifier; + +class QMaemo5HomescreenAdaptor : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool settingsAvailable READ settingsAvailable WRITE setSettingsAvailable) + +public: + QMaemo5HomescreenAdaptor(QWidget *widget); + ~QMaemo5HomescreenAdaptor(); + + inline void setSettingsAvailable(bool available) + { + hasSettings = available; + updateStatus(); + } + + inline bool settingsAvailable() const + { + return hasSettings; + } + +Q_SIGNALS: + void settingsRequested(); + void homescreenChanged(bool isOnCurrentHomescreen); + +private Q_SLOTS: + void socketException(); + +private: + inline QWidget *appletWidget() const { return static_cast(parent()); } + + void updateStatus(); + + static bool applicationEventFilter(void *message, long *result); + + bool hasSettings; + QString appletId; + QSocketNotifier *socketNotifier; +}; + +#endif diff --git a/qmaemo5homescreenadaptor/qmaemo5homescreenadaptor.pri b/qmaemo5homescreenadaptor/qmaemo5homescreenadaptor.pri new file mode 100644 index 0000000..73d41c3 --- /dev/null +++ b/qmaemo5homescreenadaptor/qmaemo5homescreenadaptor.pri @@ -0,0 +1,4 @@ +HEADERS += $$PWD/qmaemo5homescreenadaptor.h +SOURCES += $$PWD/qmaemo5homescreenadaptor.cpp + +INCLUDEPATH += $$PWD diff --git a/src/eveaccount.cpp b/src/eveaccount.cpp new file mode 100644 index 0000000..414999f --- /dev/null +++ b/src/eveaccount.cpp @@ -0,0 +1,96 @@ +#include "eveaccount.h" +#include "QXmlStreamReader" +#include +#include +#include +#include +EveCharacter::EveCharacter(QObject *parent) : + QObject(parent), + characterId(0), + corpId(0), + characterIcon(NULL) +{ +} + +EveCharacter::EveCharacter(const EveCharacter &aOther): + QObject(aOther.parent()), + name(aOther.name), + corpName(aOther.corpName), + characterId(aOther.characterId), + corpId(aOther.corpId), + characterIcon(NULL) +{ + if (aOther.characterIcon) { + characterIcon = new QPixmap(*(aOther.characterIcon)); + } +} + +EveCharacter::~EveCharacter() +{ + if (characterIcon) + delete characterIcon; + characterIcon = NULL; +} + +EveCharacter &EveCharacter::operator =(const EveCharacter &other) +{ + name = other.name; + characterId = other.characterId; + corpId = other.corpId; + corpName = other.corpName; + qDebug() << "Assignment, characterIcon " << characterIcon; + if (characterIcon != NULL) { + delete characterIcon; + characterIcon = NULL; + } + qDebug() << " after deletion"; + qDebug() << " other icon" << other.characterIcon; + if (other.characterIcon != NULL) + characterIcon = new QPixmap(*(other.characterIcon)); + qDebug() << "Assignment done"; + return *this; +} + +bool EveCharacter::fromXml(QXmlStreamReader &xml) +{ + if (xml.name() != "row") + return false; + name = xml.attributes().value("","name").toString(); + characterId = xml.attributes().value("","characterID").toString().toInt(); + corpName = xml.attributes().value("","corporationName").toString(); + corpId = xml.attributes().value("","corporationID").toString().toInt(); + return true; +} + +bool EveCharacter::fetchImage() +{ + if (characterId == 0) { + qDebug() << "No character"; + return false; + } + if (characterIcon != NULL) { + return true; + + } + qDebug() << "Requesting image"; + QNetworkRequest req(QUrl(QString("http://img.eve.is/serv.asp?c=%1&s=64").arg(characterId))); + m_reply = mgr.get(req); + connect(m_reply,SIGNAL(finished()),this,SLOT(imageReady())); + return true; +} + +// Construct the pixmap from the reply data +// and delete the reply +void EveCharacter::imageReady() +{ + qDebug() << "Image ready"; + characterIcon = new QPixmap; + if (!characterIcon->loadFromData(m_reply->readAll())) { + delete characterIcon; + characterIcon = NULL; + qDebug() << "Cannot load image"; + return; + } + m_reply->deleteLater(); + emit imageLoaded(); +} diff --git a/src/eveaccount.h b/src/eveaccount.h new file mode 100644 index 0000000..1e1ec38 --- /dev/null +++ b/src/eveaccount.h @@ -0,0 +1,38 @@ +#ifndef EVEACCOUNT_H +#define EVEACCOUNT_H + +#include +#include +#include +#include +#include +#include + +#include "eveskilltraining.h" +class EveCharacter : public QObject +{ + Q_OBJECT +public: + explicit EveCharacter(QObject *parent = 0); + EveCharacter(const EveCharacter &aOther); + EveCharacter &operator=(const EveCharacter&other); + ~EveCharacter(); + bool fromXml(QXmlStreamReader &xml); + bool fetchImage(); +signals: + void imageLoaded(); +public slots: + void imageReady(); +public: + QString name; + QString corpName; + int characterId; + int corpId; + QPixmap *characterIcon; + EveSkillTraining currentSkill; +private: + QNetworkReply *m_reply; + QNetworkAccessManager mgr; +}; + +#endif // EVEACCOUNT_H diff --git a/src/evehomescreen b/src/evehomescreen new file mode 100755 index 0000000000000000000000000000000000000000..92960a96eeacef356abe5c0814ce09b43526942f GIT binary patch literal 148545 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 cP}O!D!<*>@rn$xKr8k13Uq#GLdi7HLAHWnheE +#include +#include +#include +#include +#include +static QUrl imageUrl("http://img.eve.is/serv.asp"); + +EveModel::EveModel(QObject *parent) : + QObject(parent), + m_userId(0) +{ +} + +void EveModel::fetchAccounts() +{ + QNetworkRequest fetch(QUrl(QString("http://api.eveonline.com/account/Characters.xml.aspx?apiKey=%1&userID=%2") + .arg(apiKey()) + .arg(userId()))); + m_reply = mgr.get(fetch); + qDebug() << "Fetch accounts, reply " << m_reply; + connect(m_reply,SIGNAL(finished()),this,SLOT(replyReady())); + connect(m_reply,SIGNAL(error(QNetworkReply::NetworkError)),this,SLOT(networkError(QNetworkReply::NetworkError))); +} + +void EveModel::replyReady() +{ + if (m_reply->error()) { + qDebug() << "Failed! " << m_reply->errorString(); + return; + } + QByteArray reply = m_reply->readAll(); + qDebug() << "Reply ready"; + qDebug() << reply; + QXmlStreamReader reader(reply); + + reader.readNextStartElement(); + if (reader.error()) { + qDebug() << "Parse error: " << reader.errorString(); + } + qDebug() << "Relement: " << reader.name(); + if (reader.name() != "eveapi") + return; + reader.readNextStartElement(); + if (reader.error()) { + qDebug() << "Parse error: " << reader.errorString(); + } + qDebug() << "1 element: " << reader.name(); + if (reader.name() != "currentTime") + return; + reader.readNextStartElement(); + reader.readNextStartElement(); + if (reader.error()) { + qDebug() << "Parse error: " << reader.errorString(); + } + qDebug() << "2 element: " << reader.name(); + if (reader.name() != "result") + return; + reader.readNextStartElement(); + qDebug() << "Relement: " << reader.name(); + if (reader.name() != "rowset") + return; + reader.readNextStartElement(); + qDebug() << "3 element: " << reader.name(); + if (reader.name() == "row") + m_characters.clear(); + while (reader.name() == "row") { + EveCharacter chara; + if (!chara.fromXml(reader)) { + qDebug() << "Error parsing character data"; + break; + } + m_characters.append(chara); + reader.readNextStartElement(); // end current row + reader.readNextStartElement(); // start next row + } + m_reply->deleteLater(); + emit accountsReady(); +} + +void EveModel::networkError(QNetworkReply::NetworkError error) +{ + qDebug() << "Network error " << error; +} + diff --git a/src/evemodel.h b/src/evemodel.h new file mode 100644 index 0000000..1d09ce1 --- /dev/null +++ b/src/evemodel.h @@ -0,0 +1,36 @@ +#ifndef EVEMODEL_H +#define EVEMODEL_H + +#include "eveaccount.h" +#include +#include +#include +#include +class EveModel : public QObject +{ + Q_OBJECT +public: + explicit EveModel(QObject *parent = 0); + void setApiKey(QString aKey) { m_apiKey = aKey; } + QString apiKey() const { return m_apiKey; } + + void setUserId(int aId) { m_userId = aId; } + int userId() const { return m_userId; } + QList &characters() { return m_characters; } +signals: + void accountsReady(); + +public slots: + void fetchAccounts(); +private slots: + void replyReady(); + void networkError(QNetworkReply::NetworkError); +private: + QString m_apiKey; + int m_userId; + QNetworkAccessManager mgr; + QNetworkReply *m_reply; + QList m_characters; +}; + +#endif // EVEMODEL_H diff --git a/src/evesettingsdialog.cpp b/src/evesettingsdialog.cpp new file mode 100644 index 0000000..0352654 --- /dev/null +++ b/src/evesettingsdialog.cpp @@ -0,0 +1,127 @@ +#include "evesettingsdialog.h" +#include "ui_evesettingsdialog.h" +#include +#include +#include +#include +#include +#include +#include + +EveSettingsDialog::EveSettingsDialog(QWidget *parent) : + QDialog(parent), + m_reply(0), + m_selection(0), + userIdEdit(new QLineEdit(this)), + apiKeyEdit(new QLineEdit(this)), + characterPicker(new QMaemo5ValueButton(this)), + characterSelector(new QMaemo5ListPickSelector), + characterModel(new QStandardItemModel(0,1,this)), + m_layout(new QVBoxLayout(this)) +{ + setWindowTitle("EVE account"); + //QHBoxLayout *hbox = new QHBoxLayout(this); + //QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel,Qt::Vertical,this); + //hbox->addLayout(m_layout); + //hbox->addWidget(buttons); + setLayout(m_layout); + QLabel *label1 = new QLabel("User ID"); + m_layout->addWidget(label1); + m_layout->addWidget(userIdEdit); + QLabel *label2 = new QLabel("API key"); + m_layout->addWidget(label2); + m_layout->addWidget(apiKeyEdit); + m_layout->addWidget(characterPicker); + characterSelector->setModel(characterModel); + characterSelector->setModelColumn(0); + characterPicker->setValueLayout(QMaemo5ValueButton::ValueBesideText); + characterPicker->setText("Character"); + + QPushButton *button = new QPushButton("Done"); + m_layout->addWidget(button); + characterPicker->setPickSelector(characterSelector); + + connect(button,SIGNAL(pressed()),this,SLOT(accept())); + //connect(buttons,SIGNAL(rejected()),this,SLOT(reject())); + //connect(ui->getCharactersButton,SIGNAL(pressed()),this,SLOT(tryLoadCharacters())); + //connect(ui->characterCombo,SIGNAL(currentIndexChanged(int)),this,SLOT(characterSelected(int))); + //connect(apiKeyEdit,SIGNAL(editingFinished()),this,SLOT(tryLoadCharacters())); + //connect(userIdEdit,SIGNAL(editingFinished()),this,SLOT(tryLoadCharacters())); + //connect(characterSelector->view(),SIGNAL(activated(const QModelIndex &)),this,SLOT(characterSelected())); + connect(characterPicker,SIGNAL(pressed()),this,SLOT(tryLoadCharacters())); + connect(characterSelector,SIGNAL(selected(const QString &)),characterPicker,SLOT(setValuetext(const QString &))); +} + +void EveSettingsDialog::changeEvent(QEvent *e) +{ + QDialog::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + //retranslateUi(this); + break; + default: + break; + } +} + +void EveSettingsDialog::tryLoadCharacters() +{ + if (!apiKeyEdit->text().isEmpty() && + !userIdEdit->text().isEmpty()) { + qDebug() << "Load characters, API key " << apiKeyEdit->text() << " user ID: " << userIdEdit->text(); + characterModel->clear(); + QStandardItem *item = new QStandardItem("Retrieving characters, please wait..."); + + characterModel->appendRow(item); + if (m_model.isNull()) { + m_model = QSharedPointer(new EveModel); + } + m_model->setApiKey(apiKeyEdit->text()); + m_model->setUserId(userIdEdit->text().toInt()); + connect(m_model.data(),SIGNAL(accountsReady()),this,SLOT(charactersLoaded())); + m_model->fetchAccounts(); + setAttribute(Qt::WA_Maemo5ShowProgressIndicator, true); + } +} + +// Setting the model also sets the +// API key and user info since +// Widget is responsible for loading and saving + +void EveSettingsDialog::setModel(QSharedPointer &model) +{ + m_model = model; + apiKeyEdit->setText(model->apiKey()); + userIdEdit->setText(QString::number(model->userId())); + charactersLoaded(); +} + +void EveSettingsDialog::charactersLoaded() +{ + setAttribute(Qt::WA_Maemo5ShowProgressIndicator, false); + EveCharacter c; + qDebug() << "Characters loaded"; + characterModel->clear(); + foreach (c,m_model->characters()) { + qDebug() << "Found " << c.name; + QStandardItem *item = new QStandardItem(c.name); + item->setTextAlignment(Qt::AlignCenter); + item->setEditable(false); + characterModel->appendRow(item); + } +} + +EveCharacter *EveSettingsDialog::selectedCharacter() { + int idx = characterSelector->currentIndex(); + qDebug() << "Selected index is " << idx; + if ( idx >=0 && idx < m_model->characters().size() ) { + qDebug() << "Selecting " << m_model->characters().at(idx).name; + return &(m_model->characters()[idx]); + } + return NULL; +} + +void EveSettingsDialog::characterSelected() +{ + +} diff --git a/src/evesettingsdialog.h b/src/evesettingsdialog.h new file mode 100644 index 0000000..47832c5 --- /dev/null +++ b/src/evesettingsdialog.h @@ -0,0 +1,43 @@ +#ifndef EVESETTINGSDIALOG_H +#define EVESETTINGSDIALOG_H + +#include +#include "evemodel.h" +#include +#include +#include + +class QNetworkReply; +class EveCharacter; +namespace Ui { + class EveSettingsDialog; +} + +class EveSettingsDialog : public QDialog { + Q_OBJECT +public: + EveSettingsDialog(QWidget *parent = 0); + + EveCharacter *selectedCharacter(); + QSharedPointer model() { return m_model; } + void setModel(QSharedPointer &model); +protected: + void changeEvent(QEvent *e); +public slots: + void tryLoadCharacters(); + void charactersLoaded(); + void characterSelected(); +private: + QSharedPointer m_model; + QNetworkReply *m_reply; + EveCharacter *m_selection; + QLineEdit *userIdEdit; + QLineEdit *apiKeyEdit; + QMaemo5ValueButton *characterPicker; + QMaemo5ListPickSelector *characterSelector; + QStandardItemModel *characterModel; + QVBoxLayout *m_layout; + +}; + +#endif // EVESETTINGSDIALOG_H diff --git a/src/evesettingsdialog.ui b/src/evesettingsdialog.ui new file mode 100644 index 0000000..4637455 --- /dev/null +++ b/src/evesettingsdialog.ui @@ -0,0 +1,221 @@ + + + EveSettingsDialog + + + + 0 + 0 + 800 + 300 + + + + EVE settings + + + + + 710 + 20 + 81 + 241 + + + + Qt::Vertical + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + 20 + 10 + 171 + 34 + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'DejaVu Sans'; font-size:8pt; font-weight:400; font-style:normal;"> +<table style="-qt-table-type: root; margin-top:4px; margin-bottom:4px; margin-left:4px; margin-right:4px;"> +<tr> +<td style="border: none;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">To see character info visit<br /><a href="http://www.eveonline.com/api/"><span style=" text-decoration: underline; color:#0057ae;">EVE Online account page</span></a></p></td></tr></table></body></html> + + + true + + + + + + 10 + 50 + 701 + 241 + + + + true + + + + + 0 + 0 + 695 + 235 + + + + + + 10 + 10 + 671 + 61 + + + + + 0 + 0 + + + + + 24 + + + + true + + + + + + 20 + 0 + 39 + 16 + + + + API key + + + + + + 20 + 70 + 34 + 16 + + + + UserID + + + + + + 10 + 90 + 661 + 61 + + + + + 0 + 0 + + + + + 24 + + + + + + + 20 + 170 + 70 + 24 + + + + + + + 20 + 153 + 87 + 16 + + + + Choose character + + + + + + 540 + 180 + 121 + 22 + + + + Fetch characters + + + + + + + + + buttonBox + accepted() + EveSettingsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + EveSettingsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/eveskilltraining.cpp b/src/eveskilltraining.cpp new file mode 100644 index 0000000..4ee0c31 --- /dev/null +++ b/src/eveskilltraining.cpp @@ -0,0 +1,98 @@ +#include "eveskilltraining.h" +#include "evemodel.h" +#include "eveaccount.h" + +#include +#include +#include +#include +#include +EveSkillTraining::EveSkillTraining(QObject *parent) : + QObject(parent), + training(false), + typeId(0), + startSkillpoints(0), + destSkillpoints(0), + level(0), + m_character(NULL), + m_account(NULL), + m_reply(NULL) +{ +} + +bool EveSkillTraining::fromXml(QXmlStreamReader &xml) +{ + if (xml.name() != "result") { + qDebug() << "Wrong element:" << xml.name(); + return false; + } + qDebug() << "passed, element is result"; + bool inResult = true; + while (inResult) { + inResult = xml.readNextStartElement(); + qDebug() << "element:" << xml.name(); + if (xml.name() == "trainingEndTime") { + qDebug() << "Parse end time"; + endTime = QDateTime::fromString(xml.readElementText(),"yyyy-MM-dd hh:mm:ss");//2008-08-17 06:43:00 + qDebug()<< endTime; + + } else if (xml.name() == "trainingStartTime") { + qDebug() << "Parse start time"; + startTime = QDateTime::fromString(xml.readElementText(),"yyyy-MM-dd hh:mm:ss");//2008-08-17 06:43:00 + qDebug() << startTime; + + } else if (xml.name() == "trainingTypeID") { + typeId = xml.readElementText().toInt(); + + } else if (xml.name() == "trainingStartSP"){ + startSkillpoints = xml.readElementText().toInt(); + + } else if (xml.name() == "trainingDestinationSP"){ + destSkillpoints = xml.readElementText().toInt(); + + } else if (xml.name() == "trainingToLevel"){ + level = xml.readElementText().toInt(); + } else + // Noop + xml.skipCurrentElement(); + + } // while + qDebug() << "Parsing finished"; + return true; +} + +void EveSkillTraining::fetchInfo() +{ + if (m_character == NULL || m_account == NULL) + return; + QNetworkRequest req(QUrl(QString("http://api.eveonline.com/char/SkillInTraining.xml.aspx?apiKey=%1&userID=%2&characterID=%3") + .arg(m_account->apiKey()) + .arg(m_account->userId()) + .arg(m_character->characterId))); + m_reply = m_mgr.get(req); + connect(m_reply,SIGNAL(finished()),this,SLOT(infoReady())); +} + +void EveSkillTraining::infoReady() +{ + if (m_reply->error()) { + qDebug() << "Failed! " << m_reply->errorString(); + return; + } + qDebug() << "Skill reply"; + QByteArray reply = m_reply->readAll(); + qDebug() << "Reply ready"; + qDebug() << reply; + QXmlStreamReader reader(reply); + + reader.readNextStartElement(); + if (reader.name() != "eveapi") + return; + reader.readNextStartElement(); + if (reader.name() != "currentTime") + return; + reader.readNextStartElement(); // end currentTime element + reader.readNextStartElement(); // start result element + fromXml(reader); + emit finished(); +} diff --git a/src/eveskilltraining.h b/src/eveskilltraining.h new file mode 100644 index 0000000..1ddc9ce --- /dev/null +++ b/src/eveskilltraining.h @@ -0,0 +1,43 @@ +#ifndef EVESKILLTRAINING_H +#define EVESKILLTRAINING_H + +#include +#include +#include +#include +#include + +class EveCharacter; +class EveModel; + +class EveSkillTraining : public QObject +{ + Q_OBJECT +public: + explicit EveSkillTraining(QObject *parent = 0); + bool fromXml(QXmlStreamReader &xml); + void setCharacter(EveCharacter *character) { m_character = character; } + void setAccount(EveModel *aModel) { m_account = aModel; } +signals: + void finished(); + +public slots: + void fetchInfo(); + void infoReady(); + +public: + bool training; + QDateTime startTime; + QDateTime endTime; + int typeId; + int startSkillpoints; + int destSkillpoints; + int level; +private: + EveCharacter *m_character; + EveModel *m_account; + QNetworkAccessManager m_mgr; + QNetworkReply *m_reply; +}; + +#endif // EVESKILLTRAINING_H diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..6ab2198 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,21 @@ +#include +#include "widget.h" +#include "qmaemo5homescreenadaptor.h" +#include "mainwindow.h" + +int main(int argc, char *argv[]) +{ + QApplication::setGraphicsSystem("native"); + QApplication a(argc, argv); + //MainWindow w; + Widget w; + QMaemo5HomescreenAdaptor *adaptor = new QMaemo5HomescreenAdaptor(&w); + adaptor->setSettingsAvailable(true); + QObject::connect(adaptor,SIGNAL(settingsRequested()),&w,SLOT(showSettingsDialog())); +#if defined(Q_WS_S60) + w.showMaximized(); +#else + w.show(); +#endif + return a.exec(); +} diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp new file mode 100644 index 0000000..0e09abb --- /dev/null +++ b/src/mainwindow.cpp @@ -0,0 +1,59 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" +#include "evesettingsdialog.h" +#include +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::MainWindow), + m_settings(new EveSettingsDialog(this)) +{ + ui->setupUi(this); + connect(ui->fetchButton,SIGNAL(pressed()),this,SLOT(settingsRequested())); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + +void MainWindow::changeEvent(QEvent *e) +{ + QMainWindow::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } +} + +void MainWindow::settingsRequested() +{ + int result = m_settings->exec(); + if (result == QDialog::Accepted) { + m_character = m_settings->selectedCharacter(); + + if (m_character != NULL ) { + qDebug() << "Selected character:" << m_character->name; + connect(m_character,SIGNAL(imageLoaded()),this,SLOT(setLabelImage())); + m_character->fetchImage(); + + skill.setCharacter(m_character); + skill.setAccount(m_settings->model().data()); + connect(&skill, SIGNAL(finished()),this,SLOT(setSkillTraining())); + skill.fetchInfo(); + } + } +} + +void MainWindow::setLabelImage() +{ + ui->iconLabel->setPixmap(*(m_character->characterIcon)); +} + +void MainWindow::setSkillTraining() +{ + ui->trainingLabel->setText(QString("Skill ready at %1").arg(skill.endTime.toString(Qt::SystemLocaleShortDate))); +} + diff --git a/src/mainwindow.h b/src/mainwindow.h new file mode 100644 index 0000000..23f9170 --- /dev/null +++ b/src/mainwindow.h @@ -0,0 +1,33 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include "evesettingsdialog.h" +#include "eveaccount.h" + +namespace Ui { + class MainWindow; +} + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); +public slots: + void settingsRequested(); + void setLabelImage(); + void setSkillTraining(); +protected: + void changeEvent(QEvent *e); + +private: + Ui::MainWindow *ui; + EveSettingsDialog *m_settings; + EveCharacter *m_character; + EveSkillTraining skill; +}; + +#endif // MAINWINDOW_H diff --git a/src/mainwindow.ui b/src/mainwindow.ui new file mode 100644 index 0000000..e738d8e --- /dev/null +++ b/src/mainwindow.ui @@ -0,0 +1,74 @@ + + + MainWindow + + + + 0 + 0 + 394 + 236 + + + + MainWindow + + + + + + 280 + 150 + 90 + 22 + + + + Fetch! + + + + + + 80 + 60 + 70 + 70 + + + + TextLabel + + + + + + 10 + 150 + 221 + 20 + + + + + + + Qt::AlignCenter + + + + + + + 0 + 0 + 394 + 20 + + + + + + + + diff --git a/src/skilltree.cpp b/src/skilltree.cpp new file mode 100644 index 0000000..0bdfbe4 --- /dev/null +++ b/src/skilltree.cpp @@ -0,0 +1,109 @@ +#include "skilltree.h" +#include +#include +#include +#include +#include +#include +#include +#include + +SkillTree::SkillTree(QObject *parent) : + QObject(parent), + mgr(0), + reply(0) +{ + QFile cacheFile("/var/tmp/skillCache"); + if (cacheFile.exists()) { + QDataStream input(&cacheFile); + input >> skillNames; + } else { + // load from network + + } + +} + +void SkillTree::loadSkills() +{ + QFile cacheFile("/var/tmp/skillCache"); + if (cacheFile.exists()) { + cacheFile.open(QIODevice::ReadOnly); + qDebug() << "Load from file"; + QDataStream input(&cacheFile); + input >> skillNames; + emit skillsLoaded(); + } else { + // load from network + qDebug() << "load skills from network"; + mgr = new QNetworkAccessManager(this); + QNetworkRequest req(QUrl("http://api.eveonline.com//eve/SkillTree.xml.aspx")); + reply = mgr->get(req); + connect(reply,SIGNAL(finished()),this,SLOT(replyReady())); + } +} + +void SkillTree::replyReady() +{ + qDebug() << "Reply ready"; + if (!reply->error()) { + QByteArray replyContent = reply->readAll(); + qDebug() << replyContent; + fromXml(replyContent); + emit skillsLoaded(); + } else { + qDebug() << "fetch error:" << reply->errorString(); + } +} + +void SkillTree::fromXml(QByteArray &content) +{ + QXmlStreamReader xml(content); + xml.readNextStartElement(); //eveapi + //assert(xml.name() == "eveapi"); + xml.readNextStartElement(); //currentTime + xml.skipCurrentElement(); + //assert(xml.name() == "currentTime"); + xml.readNextStartElement(); //result + xml.readNextStartElement(); // rowset for skill groups + bool groupsLeft = xml.readNextStartElement(); // row for skill group + while (groupsLeft) { + xml.readNextStartElement(); // rowset for skills + bool skillsLeft = xml.readNextStartElement(); // row for skill + while (skillsLeft) { + assert(xml.name() == "row"); + QString name = xml.attributes().value("","typeName").toString(); + int typeId = xml.attributes().value("","typeID").toString().toInt(); + qDebug() << "Found skill " << name << ":" << typeId; + skillNames[typeId] = name; + + xml.skipCurrentElement(); // description + skillsLeft = xml.readNextStartElement(); // next skill row + } + xml.readNextStartElement(); // rowset ends + groupsLeft = xml.readNextStartElement(); // next skill group row + } + int len = 0; + QString name; + QString longestName; + foreach (name,skillNames) { + if (name.length() > len) { + longestName = name; + len = name.length(); + } + } + + qDebug() << skillNames; + qDebug() << longestName; +} + +void SkillTree::save() +{ + QFile cache("/var/tmp/skillCache"); + cache.open(QIODevice::WriteOnly); + QDataStream output(&cache); + output << skillNames; + cache.flush(); + cache.close(); +} + diff --git a/src/skilltree.h b/src/skilltree.h new file mode 100644 index 0000000..d39710e --- /dev/null +++ b/src/skilltree.h @@ -0,0 +1,29 @@ +#ifndef SKILLTREE_H +#define SKILLTREE_H + +#include +#include + +class QNetworkAccessManager; +class QNetworkReply; + +class SkillTree : public QObject +{ + Q_OBJECT +public: + explicit SkillTree(QObject *parent = 0); + QString skillName(const int code) const { return skillNames.value(code); } +signals: + void skillsLoaded(); +public slots: + void loadSkills(); + void replyReady(); + void save(); +private: + void fromXml(QByteArray &content); + QHash skillNames; + QNetworkAccessManager *mgr; + QNetworkReply *reply; +}; + +#endif // SKILLTREE_H diff --git a/src/ui_evesettingsdialog.h b/src/ui_evesettingsdialog.h new file mode 100644 index 0000000..aa555a4 --- /dev/null +++ b/src/ui_evesettingsdialog.h @@ -0,0 +1,132 @@ +/******************************************************************************** +** Form generated from reading UI file 'evesettingsdialog.ui' +** +** Created: Fri May 7 20:57:37 2010 +** by: Qt User Interface Compiler version 4.6.2 +** +** WARNING! All changes made in this file will be lost when recompiling UI file! +********************************************************************************/ + +#ifndef UI_EVESETTINGSDIALOG_H +#define UI_EVESETTINGSDIALOG_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Ui_EveSettingsDialog +{ +public: + QDialogButtonBox *buttonBox; + QLabel *label_4; + QScrollArea *scrollArea; + QWidget *scrollAreaWidgetContents; + QLineEdit *apiKeyEdit; + QLabel *label; + QLabel *label_2; + QLineEdit *userIdEdit; + QComboBox *characterCombo; + QLabel *label_3; + QPushButton *getCharactersButton; + + void setupUi(QDialog *EveSettingsDialog) + { + if (EveSettingsDialog->objectName().isEmpty()) + EveSettingsDialog->setObjectName(QString::fromUtf8("EveSettingsDialog")); + EveSettingsDialog->resize(800, 300); + buttonBox = new QDialogButtonBox(EveSettingsDialog); + buttonBox->setObjectName(QString::fromUtf8("buttonBox")); + buttonBox->setGeometry(QRect(710, 20, 81, 241)); + buttonBox->setOrientation(Qt::Vertical); + buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); + label_4 = new QLabel(EveSettingsDialog); + label_4->setObjectName(QString::fromUtf8("label_4")); + label_4->setGeometry(QRect(20, 10, 171, 34)); + label_4->setOpenExternalLinks(true); + scrollArea = new QScrollArea(EveSettingsDialog); + scrollArea->setObjectName(QString::fromUtf8("scrollArea")); + scrollArea->setGeometry(QRect(10, 50, 701, 241)); + scrollArea->setWidgetResizable(true); + scrollAreaWidgetContents = new QWidget(); + scrollAreaWidgetContents->setObjectName(QString::fromUtf8("scrollAreaWidgetContents")); + scrollAreaWidgetContents->setGeometry(QRect(0, 0, 695, 235)); + apiKeyEdit = new QLineEdit(scrollAreaWidgetContents); + apiKeyEdit->setObjectName(QString::fromUtf8("apiKeyEdit")); + apiKeyEdit->setGeometry(QRect(10, 10, 671, 61)); + QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(apiKeyEdit->sizePolicy().hasHeightForWidth()); + apiKeyEdit->setSizePolicy(sizePolicy); + QFont font; + font.setPointSize(24); + apiKeyEdit->setFont(font); + apiKeyEdit->setFrame(true); + label = new QLabel(scrollAreaWidgetContents); + label->setObjectName(QString::fromUtf8("label")); + label->setGeometry(QRect(20, 0, 39, 16)); + label_2 = new QLabel(scrollAreaWidgetContents); + label_2->setObjectName(QString::fromUtf8("label_2")); + label_2->setGeometry(QRect(20, 70, 34, 16)); + userIdEdit = new QLineEdit(scrollAreaWidgetContents); + userIdEdit->setObjectName(QString::fromUtf8("userIdEdit")); + userIdEdit->setGeometry(QRect(10, 90, 661, 61)); + sizePolicy.setHeightForWidth(userIdEdit->sizePolicy().hasHeightForWidth()); + userIdEdit->setSizePolicy(sizePolicy); + userIdEdit->setFont(font); + characterCombo = new QComboBox(scrollAreaWidgetContents); + characterCombo->setObjectName(QString::fromUtf8("characterCombo")); + characterCombo->setGeometry(QRect(20, 170, 70, 24)); + label_3 = new QLabel(scrollAreaWidgetContents); + label_3->setObjectName(QString::fromUtf8("label_3")); + label_3->setGeometry(QRect(20, 153, 87, 16)); + getCharactersButton = new QPushButton(scrollAreaWidgetContents); + getCharactersButton->setObjectName(QString::fromUtf8("getCharactersButton")); + getCharactersButton->setGeometry(QRect(540, 180, 121, 22)); + scrollArea->setWidget(scrollAreaWidgetContents); + + retranslateUi(EveSettingsDialog); + QObject::connect(buttonBox, SIGNAL(accepted()), EveSettingsDialog, SLOT(accept())); + QObject::connect(buttonBox, SIGNAL(rejected()), EveSettingsDialog, SLOT(reject())); + + QMetaObject::connectSlotsByName(EveSettingsDialog); + } // setupUi + + void retranslateUi(QDialog *EveSettingsDialog) + { + EveSettingsDialog->setWindowTitle(QApplication::translate("EveSettingsDialog", "EVE settings", 0, QApplication::UnicodeUTF8)); + label_4->setText(QApplication::translate("EveSettingsDialog", "\n" +"\n" +"\n" +"\n" +"
\n" +"

To see character info visit
EVE Online account page

", 0, QApplication::UnicodeUTF8)); + label->setText(QApplication::translate("EveSettingsDialog", "API key", 0, QApplication::UnicodeUTF8)); + label_2->setText(QApplication::translate("EveSettingsDialog", "UserID", 0, QApplication::UnicodeUTF8)); + label_3->setText(QApplication::translate("EveSettingsDialog", "Choose character", 0, QApplication::UnicodeUTF8)); + getCharactersButton->setText(QApplication::translate("EveSettingsDialog", "Fetch characters", 0, QApplication::UnicodeUTF8)); + } // retranslateUi + +}; + +namespace Ui { + class EveSettingsDialog: public Ui_EveSettingsDialog {}; +} // namespace Ui + +QT_END_NAMESPACE + +#endif // UI_EVESETTINGSDIALOG_H diff --git a/src/ui_mainwindow.h b/src/ui_mainwindow.h new file mode 100644 index 0000000..da7e401 --- /dev/null +++ b/src/ui_mainwindow.h @@ -0,0 +1,84 @@ +/******************************************************************************** +** Form generated from reading UI file 'mainwindow.ui' +** +** Created: Fri May 7 20:57:37 2010 +** by: Qt User Interface Compiler version 4.6.2 +** +** WARNING! All changes made in this file will be lost when recompiling UI file! +********************************************************************************/ + +#ifndef UI_MAINWINDOW_H +#define UI_MAINWINDOW_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Ui_MainWindow +{ +public: + QWidget *centralwidget; + QPushButton *fetchButton; + QLabel *iconLabel; + QLabel *trainingLabel; + QMenuBar *menubar; + QStatusBar *statusbar; + + void setupUi(QMainWindow *MainWindow) + { + if (MainWindow->objectName().isEmpty()) + MainWindow->setObjectName(QString::fromUtf8("MainWindow")); + MainWindow->resize(394, 236); + centralwidget = new QWidget(MainWindow); + centralwidget->setObjectName(QString::fromUtf8("centralwidget")); + fetchButton = new QPushButton(centralwidget); + fetchButton->setObjectName(QString::fromUtf8("fetchButton")); + fetchButton->setGeometry(QRect(280, 150, 90, 22)); + iconLabel = new QLabel(centralwidget); + iconLabel->setObjectName(QString::fromUtf8("iconLabel")); + iconLabel->setGeometry(QRect(80, 60, 70, 70)); + trainingLabel = new QLabel(centralwidget); + trainingLabel->setObjectName(QString::fromUtf8("trainingLabel")); + trainingLabel->setGeometry(QRect(10, 150, 221, 20)); + trainingLabel->setAlignment(Qt::AlignCenter); + MainWindow->setCentralWidget(centralwidget); + menubar = new QMenuBar(MainWindow); + menubar->setObjectName(QString::fromUtf8("menubar")); + menubar->setGeometry(QRect(0, 0, 394, 20)); + MainWindow->setMenuBar(menubar); + statusbar = new QStatusBar(MainWindow); + statusbar->setObjectName(QString::fromUtf8("statusbar")); + MainWindow->setStatusBar(statusbar); + + retranslateUi(MainWindow); + + QMetaObject::connectSlotsByName(MainWindow); + } // setupUi + + void retranslateUi(QMainWindow *MainWindow) + { + MainWindow->setWindowTitle(QApplication::translate("MainWindow", "MainWindow", 0, QApplication::UnicodeUTF8)); + fetchButton->setText(QApplication::translate("MainWindow", "Fetch!", 0, QApplication::UnicodeUTF8)); + iconLabel->setText(QApplication::translate("MainWindow", "TextLabel", 0, QApplication::UnicodeUTF8)); + trainingLabel->setText(QString()); + } // retranslateUi + +}; + +namespace Ui { + class MainWindow: public Ui_MainWindow {}; +} // namespace Ui + +QT_END_NAMESPACE + +#endif // UI_MAINWINDOW_H diff --git a/src/widget.cpp b/src/widget.cpp new file mode 100644 index 0000000..29b80be --- /dev/null +++ b/src/widget.cpp @@ -0,0 +1,114 @@ +#include "widget.h" +#include +#include "eveaccount.h" +#include "evesettingsdialog.h" +#include "eveskilltraining.h" +#include "skilltree.h" +#include +#include + +QTM_USE_NAMESPACE + +Widget::Widget(QWidget *parent) + : QLabel(parent), + m_character(NULL), + m_settings(new EveSettingsDialog(this)), + m_training(NULL), + m_net(new QNetworkConfigurationManager(this)), + m_skills(new SkillTree(this)), + m_model(NULL) +{ + setAlignment(Qt::AlignCenter); + setAttribute(Qt::WA_TranslucentBackground); + setGeometry(0,0,150,180); + connect(m_net,SIGNAL(onlineStateChanged(bool)),this,SLOT(onlineStateChanged(bool))); + m_skills->loadSkills(); +} + +Widget::~Widget() +{ + delete m_character; +} + + +QSize Widget::sizeHint() const +{ + return 2 * QLabel::sizeHint(); +} + +void Widget::paintEvent(QPaintEvent *event) +{ + QPainter p(this); + p.setBrush(QColor(0, 0, 0, 128)); + p.setPen(Qt::NoPen); + p.drawRoundedRect(rect(), 25, 25); + p.setPen(Qt::white); + QFont dFont(p.font()); + dFont.setPixelSize(15); + p.setFont(dFont); + if (m_character != NULL) { + QPoint iconLoc((150-64)/2,(150-64)/2); + if (m_character->characterIcon) { + p.drawPixmap(iconLoc,*(m_character->characterIcon)); + } + QRect nameLoc(0,10,150,20); + p.drawText(nameLoc,Qt::AlignCenter,m_character->name); + if (m_training) { + QRect skillTitle(0,110,150,50); + p.drawText(skillTitle,Qt::AlignCenter|Qt::TextWordWrap, + QString("%1 %2").arg(m_skills->skillName(m_training->typeId)) + .arg(m_training->level)); + QRect skillLoc(0,155,150,20); + p.drawText(skillLoc,Qt::AlignCenter,m_training->endTime.toString(Qt::SystemLocaleShortDate)); + } + + } + p.end(); + + QLabel::paintEvent(event); +} + +void Widget::showSettingsDialog() +{ + + int result = m_settings->exec(); + if (result == QDialog::Accepted) { + m_character = m_settings->selectedCharacter(); + m_model = m_settings->model(); + qDebug() << "Got character " << m_character->name; + connect(m_character,SIGNAL(imageLoaded()),this,SLOT(skillReady())); + m_character->fetchImage(); + qDebug() << "Fetch skills"; + m_training = new EveSkillTraining(this); + m_training->setAccount(m_settings->model().data()); + m_training->setCharacter(m_character); + connect(m_training,SIGNAL(finished()),this,SLOT(update())); + + } +} + +void Widget::skillReady() +{ + update(); + m_training->fetchInfo(); +} + +void Widget::onlineStateChanged(bool online) +{ + qDebug() << "Online status changed, reloading info"; + if (online) { + m_training->fetchInfo(); + } +} + +// TODO +void Widget::loadSettings() +{ + +} + +// TODO +void Widget::saveSettings() +{ + +} diff --git a/src/widget.h b/src/widget.h new file mode 100644 index 0000000..5100df3 --- /dev/null +++ b/src/widget.h @@ -0,0 +1,43 @@ +#ifndef WIDGET_H +#define WIDGET_H + +#include +#include +#include + +QTM_USE_NAMESPACE + +class EveCharacter; +class EveSettingsDialog; +class EveSkillTraining; +class SkillTree; +class EveModel; + +class Widget : public QLabel +{ + Q_OBJECT + +public: + Widget(QWidget *parent = 0); + ~Widget(); + QSize sizeHint() const; + void setCharacter(EveCharacter *aChar) { m_character = aChar; } + EveCharacter *character() { return m_character; } +public slots: + void showSettingsDialog(); + void paintEvent(QPaintEvent *event); + void skillReady(); + void onlineStateChanged(bool online); + void loadSettings(); + void saveSettings(); + +private: + EveCharacter *m_character; + EveSettingsDialog *m_settings; + EveSkillTraining *m_training; + QNetworkConfigurationManager *m_net; + SkillTree *m_skills; + QSharedPointer m_model; +}; + +#endif // WIDGET_H diff --git a/tests/main.cpp b/tests/main.cpp new file mode 100644 index 0000000..d123dc7 --- /dev/null +++ b/tests/main.cpp @@ -0,0 +1,82 @@ +#include +#include +#include +#include +#include +#include "skilltree.h" +#include +class EveModelTest: public QObject +{ + Q_OBJECT +private slots: + void testXml(); + void testSkills(); + void testSkillTree(); +}; + +#define GONTH_ID 1525655037 + +void EveModelTest::testXml() +{ + EveModel model; + model.setApiKey("2A671E53172C4F8A878BB770BBABFBD3CFF31815927F4E5591EA6D5576456E50"); + model.setUserId(4079475); + model.fetchAccounts(); + QSignalSpy spy(&model,SIGNAL(accountsReady())); + QTest::qWait(1000); + QCOMPARE(spy.length(),1); + QCOMPARE(model.characters().length(),2); + EveCharacter e; + foreach (e,model.characters()) { + qDebug() << e.name << ":" << e.characterId; + } + e = model.characters()[0]; + qDebug() << "e assigned"; + QSignalSpy imagespy(&e,SIGNAL(imageLoaded())); + qDebug() << "spy installed"; + e.fetchImage(); + qDebug() << "fetchImage called"; + QTest::qWait(3000); + qDebug() << "waited"; + qDebug() << e.characterIcon; + QVERIFY(imagespy.length() == 1); + qDebug() << "Test done"; +} + +void EveModelTest::testSkills() +{ + EveModel model; + model.setApiKey("2A671E53172C4F8A878BB770BBABFBD3CFF31815927F4E5591EA6D5576456E50"); + model.setUserId(4079475); + model.fetchAccounts(); + QTest::qWait(1000); + EveCharacter e = model.characters()[0]; + EveSkillTraining skill; + skill.setCharacter(&e); + skill.setAccount(&model); + skill.fetchInfo(); + QTest::qWait(1000); + qDebug() << skill.startTime + << skill.endTime + << skill.typeId + << skill.startSkillpoints + << skill.destSkillpoints + << skill.level; +} + +void EveModelTest::testSkillTree() +{ + SkillTree stree; + QSignalSpy spy(&stree,SIGNAL(skillsLoaded())); + stree.loadSkills(); + QTest::qWait(5000); + QCOMPARE(spy.count(),1); + QCOMPARE(stree.skillName(3442),QString("Drone Interfacing")); + stree.save(); + QVERIFY(QFile::exists("/var/tmp/skillCache")); + QFile::remove("/var/tmp/skillCache"); +} + +QTEST_MAIN(EveModelTest) + +#include "main.moc" diff --git a/tests/tests.pro b/tests/tests.pro new file mode 100644 index 0000000..91a933a --- /dev/null +++ b/tests/tests.pro @@ -0,0 +1,26 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2010-05-02T08:36:06 +# +#------------------------------------------------- + +QT += core network + +TARGET = tests +CONFIG += console qtestlib +CONFIG -= app_bundle + +TEMPLATE = app + +INCLUDEPATH += ../src + +SOURCES += main.cpp +SOURCES += ../src/eveaccount.cpp \ +../src/evemodel.cpp \ +../src/eveskilltraining.cpp \ +../src/skilltree.cpp + +HEADERS += ../src/eveaccount.h \ + ../src/evemodel.h \ + ../src/eveskilltraining.h \ +../src/skilltree.h -- 1.7.9.5