From 50a14534a51f892500ee82f867e8ab2f85b936ae Mon Sep 17 00:00:00 2001 From: Evan Battaglia Date: Thu, 1 Sep 2005 17:07:20 +0000 Subject: [PATCH] Initial revision --- COPYING | 340 +++++ ChangeLog | 457 ++++++ HACKING | 23 + INSTALL | 27 + Makefile | 2 + NEWGOOGLE | 14 + README | 20 + TODO | 534 +++++++ TODO-REID | 158 +++ VIKING-REMOTE | 2 + doc/GEOCODED-PHOTOS | 26 + doc/GETTING-STARTED | 31 + doc/GPSMAPPER | 13 + doc/dev/CLEANUPS | 21 + doc/dev/Clipboard | 20 + doc/dev/FF2 | 119 ++ doc/dev/FF2-Implementation | 91 ++ doc/dev/File-Format-Outline | 118 ++ doc/dev/GRRM | 12 + doc/dev/MergeLayers | 25 + doc/dev/README | 3 + doc/dev/Track-Tool-Interfaces | 27 + doc/dev/TrackEditing | 23 + src/.compile_config | 1 + src/.depend | 111 ++ src/.link_config | 1 + src/Makefile | 47 + src/Makefile_GTK_2_2 | 47 + src/Makefile_windows | 47 + src/background.c | 226 +++ src/background.h | 37 + src/clipboard.c | 107 ++ src/clipboard.h | 29 + src/config.h | 11 + src/coords.c | 224 +++ src/coords.h | 57 + src/dialog.c | 433 ++++++ src/dialog.h | 52 + src/expedia.c | 172 +++ src/expedia.h | 29 + src/file.c | 509 +++++++ src/file.h | 35 + src/globals.h | 54 + src/google.c | 141 ++ src/google.h | 38 + src/googlemaps.c | 78 + src/googlemaps.h | 36 + src/gpsmapper.c | 141 ++ src/gpsmapper.h | 23 + src/gpspoint.c | 442 ++++++ src/gpspoint.h | 28 + src/gtkcellrendererprogress.c | 297 ++++ src/gtkcellrendererprogress.h | 64 + src/gtkcolorbutton.c | 41 + src/http.c | 309 ++++ src/http.h | 29 + src/khmaps.c | 126 ++ src/khmaps.h | 36 + src/main.c | 110 ++ src/mapcache.c | 172 +++ src/mapcache.h | 31 + src/mapcoord.h | 29 + src/terraserver.c | 108 ++ src/terraserver.h | 36 + src/thumbnails.c | 544 +++++++ src/thumbnails.h | 31 + src/thumbnails_pixbuf.h | 231 +++ src/usgs.c | 106 ++ src/usgs.h | 29 + src/vikaggregatelayer.c | 349 +++++ src/vikaggregatelayer.h | 64 + src/vikaggregatelayer_pixmap.h | 55 + src/vikcoord.c | 117 ++ src/vikcoord.h | 60 + src/vikcoordlayer.c | 282 ++++ src/vikcoordlayer.h | 51 + src/vikcoordlayer_pixmap.h | 73 + src/vikfileentry.c | 98 ++ src/vikfileentry.h | 51 + src/vikgeoreflayer.c | 543 +++++++ src/vikgeoreflayer.h | 42 + src/vikgeoreflayer_pixmap.h | 60 + src/viking.h | 62 + src/viklayer.c | 526 +++++++ src/viklayer.h | 260 ++++ src/viklayerspanel.c | 513 +++++++ src/viklayerspanel.h | 69 + src/vikmapslayer.c | 875 ++++++++++++ src/vikmapslayer.h | 41 + src/vikmapslayer_pixmap.h | 44 + src/vikradiogroup.c | 128 ++ src/vikradiogroup.h | 52 + src/vikstatus.c | 109 ++ src/vikstatus.h | 51 + src/viktrack.c | 426 ++++++ src/viktrack.h | 73 + src/viktreeview.c | 455 ++++++ src/viktreeview.h | 103 ++ src/viktrwlayer.c | 2431 ++++++++++++++++++++++++++++++++ src/viktrwlayer.h | 69 + src/viktrwlayer_pixmap.h | 57 + src/viktrwlayer_propwin.c | 172 +++ src/viktrwlayer_propwin.h | 32 + src/viktrwlayer_tpwin.c | 275 ++++ src/viktrwlayer_tpwin.h | 63 + src/vikviewport.c | 824 +++++++++++ src/vikviewport.h | 142 ++ src/vikwaypoint.c | 82 ++ src/vikwaypoint.h | 52 + src/vikwindow.c | 1091 ++++++++++++++ src/vikwindow.h | 55 + viking-remote | 120 ++ win32/README | 9 + win32/link.bat | 1 + win32/make.bat | 1 + win32/makeall.bat | 34 + win32/setpath.bat | 1 + 117 files changed, 18734 insertions(+) create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 HACKING create mode 100644 INSTALL create mode 100644 Makefile create mode 100644 NEWGOOGLE create mode 100644 README create mode 100644 TODO create mode 100644 TODO-REID create mode 100644 VIKING-REMOTE create mode 100644 doc/GEOCODED-PHOTOS create mode 100644 doc/GETTING-STARTED create mode 100644 doc/GPSMAPPER create mode 100644 doc/dev/CLEANUPS create mode 100644 doc/dev/Clipboard create mode 100644 doc/dev/FF2 create mode 100644 doc/dev/FF2-Implementation create mode 100644 doc/dev/File-Format-Outline create mode 100644 doc/dev/GRRM create mode 100644 doc/dev/MergeLayers create mode 100644 doc/dev/README create mode 100644 doc/dev/Track-Tool-Interfaces create mode 100644 doc/dev/TrackEditing create mode 100644 src/.compile_config create mode 100644 src/.depend create mode 100644 src/.link_config create mode 100644 src/Makefile create mode 100644 src/Makefile_GTK_2_2 create mode 100644 src/Makefile_windows create mode 100644 src/background.c create mode 100644 src/background.h create mode 100644 src/clipboard.c create mode 100644 src/clipboard.h create mode 100644 src/config.h create mode 100644 src/coords.c create mode 100644 src/coords.h create mode 100644 src/dialog.c create mode 100644 src/dialog.h create mode 100644 src/expedia.c create mode 100644 src/expedia.h create mode 100644 src/file.c create mode 100644 src/file.h create mode 100644 src/globals.h create mode 100644 src/google.c create mode 100644 src/google.h create mode 100644 src/googlemaps.c create mode 100644 src/googlemaps.h create mode 100644 src/gpsmapper.c create mode 100644 src/gpsmapper.h create mode 100644 src/gpspoint.c create mode 100644 src/gpspoint.h create mode 100644 src/gtkcellrendererprogress.c create mode 100644 src/gtkcellrendererprogress.h create mode 100644 src/gtkcolorbutton.c create mode 100644 src/http.c create mode 100644 src/http.h create mode 100644 src/khmaps.c create mode 100644 src/khmaps.h create mode 100644 src/main.c create mode 100644 src/mapcache.c create mode 100644 src/mapcache.h create mode 100644 src/mapcoord.h create mode 100644 src/terraserver.c create mode 100644 src/terraserver.h create mode 100644 src/thumbnails.c create mode 100644 src/thumbnails.h create mode 100644 src/thumbnails_pixbuf.h create mode 100644 src/usgs.c create mode 100644 src/usgs.h create mode 100644 src/vikaggregatelayer.c create mode 100644 src/vikaggregatelayer.h create mode 100644 src/vikaggregatelayer_pixmap.h create mode 100644 src/vikcoord.c create mode 100644 src/vikcoord.h create mode 100644 src/vikcoordlayer.c create mode 100644 src/vikcoordlayer.h create mode 100644 src/vikcoordlayer_pixmap.h create mode 100644 src/vikfileentry.c create mode 100644 src/vikfileentry.h create mode 100644 src/vikgeoreflayer.c create mode 100644 src/vikgeoreflayer.h create mode 100644 src/vikgeoreflayer_pixmap.h create mode 100644 src/viking.h create mode 100644 src/viklayer.c create mode 100644 src/viklayer.h create mode 100644 src/viklayerspanel.c create mode 100644 src/viklayerspanel.h create mode 100644 src/vikmapslayer.c create mode 100644 src/vikmapslayer.h create mode 100644 src/vikmapslayer_pixmap.h create mode 100644 src/vikradiogroup.c create mode 100644 src/vikradiogroup.h create mode 100644 src/vikstatus.c create mode 100644 src/vikstatus.h create mode 100644 src/viktrack.c create mode 100644 src/viktrack.h create mode 100644 src/viktreeview.c create mode 100644 src/viktreeview.h create mode 100644 src/viktrwlayer.c create mode 100644 src/viktrwlayer.h create mode 100644 src/viktrwlayer_pixmap.h create mode 100644 src/viktrwlayer_propwin.c create mode 100644 src/viktrwlayer_propwin.h create mode 100644 src/viktrwlayer_tpwin.c create mode 100644 src/viktrwlayer_tpwin.h create mode 100644 src/vikviewport.c create mode 100644 src/vikviewport.h create mode 100644 src/vikwaypoint.c create mode 100644 src/vikwaypoint.h create mode 100644 src/vikwindow.c create mode 100644 src/vikwindow.h create mode 100755 viking-remote create mode 100644 win32/README create mode 100644 win32/link.bat create mode 100644 win32/make.bat create mode 100644 win32/makeall.bat create mode 100644 win32/setpath.bat diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..eeb586b3 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 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) 19yy + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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) 19yy 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/ChangeLog b/ChangeLog new file mode 100644 index 00000000..55459a84 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,457 @@ +2005-08-18 +Map cache size based on memory usage, not number of images. Some flaws to be worked out still in this. + +2005-08-16 +Accepted USGS maps support from Clark McKines. Pretty slick, I'll have to take a look at it. + +2005-08-02 +Quite easily added support for Mercator projection & Google Mercator maps (regular, transparent etc) +Chaned waypoint types a little bit (size of waypoint) + +2005-07-31 +Hopefully fixed background.c item # count +Fixed KH Maps (v=2) + +2005-06-20 +A little research & added KH Maps support. + +2005-06-19 +Fixed right-click waypoint edit -> crash bug, hopefully. +Fixed "everyone lives in zone 11" assumption for vikgeoreflayer.c; still monozone though ... +Made it so when you paste or add a new layer, it pastes it before the selected layer, not to the top. +Fixed the bug where pasting a map layer messed up the name. +Made dragging middle mouse button pan. Removed pan tool. Thanks Reid for the idea. +Made different options for drawing waypoint dots. Not very good but we can make better-looking ones later. +Side view of tracks à la Garmin MapSource, nice ... +Fixed Google street maps simply by changing "v=.1" to "v=.3" ; the newest version of gmaps is completely different, I'm unsure how long this will work for. + +2005-05-05 +Experimenting with stops + +2005-05-01 +Fixed cheap ruler bug (use ruler, change draw mode, use ruler, crash). That's really not good... but hey, it _is_ alpha software. + +2005-04-24 +Added "how many maps currently downloading" status bar hack + +2005-03-11 +What the heck? Viking-remote didn't work? Fixing --/- stuff. Also fixing hang if can't open. Did this by putting gdk_threads_enter() before file opening in main.c. +Update viking-remote to work _right_ with maps, also geo://GC1234/ works, nice... + +2005-03-08 +Uffda, I forgot Oggdeir's gpsmapper.c patch. +Also fixed Windows compiling. + +2005-02-28 +Fixed Windows compiling, hopefully. +Removed TopoZone support per topozone.com request. + +2005-02-27 +Instituted a really nice background downloading system with a separate system, works like a charm. +Fixed (hopefully) a bug where a download failed the filename wasn't deleted, leaving an empty file. +Misc cleanups/testing surrounding downloading. +Autodownload +Fix other stuff, including moving a waypoint (delete while moving -> crash) + +2005-02-26 +X and Y zoom levels for maps so factors don't have to be equal. +Fixed maps zoomed out, added an combo box for it by adding a combo box in viklayer.c, fixed maptype to use map id and not internal array index +Changed VikTrwLayer properties dialog to add an image tab, fixed GtkTable layout so as not to stretch out stuff in the y direction. +Fixed /var/cache/maps stuff +Safety for maps zoomed out +Fixed mapzoom for cases where factors aren't equal +Got the basic download in background code on, now just minor nicities +Got the genius idea that someone might want to open up more than one file at one time ;) + +2005-02-25 +No time tonight, gotta get some sleep. Did work on hooking up Google Maps with GPSBabel's module into viking with a wget & a sed command. Totally awesome. +Also, added support for the highest google zoom level which I didn't know existed. + +2005-02-23 +I'm not really supposed to be working on this ... but it get's addictive, so close to a release... +Added back alpha support. It works for all map types now. Mostly useless for other types, unless you want to see tracks under your map, since you can't show more than one non-Terraserver map at the same time. +Started work on maps for different zoom levels. You can do all different maps at all different zoom levels, even zoom in. Don't zoom in with expedia maps though, it could create a 2000px x 3000px size image or so, and cache up to 10,000 of those. Yeah. + +2005-02-22 +Made it easier to use all these maps -- switching between them, correct zoom levels, etc. +Expedia for Europe (thanks GPSDrive people again) +Kit Transue's UTM zone patch is back in for the new MapLayer. This marks the first time I have seen cross-UTM zone Topozone maps. Interestingly enough it seems that they patch the maps together. Viking messes this up a little bit but in general it works fairly well. I'm still amazed it's possible ;) +Minor cleanups etc. + +2005-02-21 +Tons of work today, too much to list really. Basicly I threw away VikMapLayer and started over. This one's nice, really easy to add new types. Google, Expedia, and Terraserver maps are now functioning, but the layer isn't completed yet, many things such as alpha, download in the background, etc. aren't quite there yet. +Topozone support, heh ;) Of course none of these maps can be used together. Especially since scaling isn't done yet. I'm also going to have to create some handy ways to switch between layers. + +2005-02-20 +Finally got the brilliant notion that a GSList isn't the smartest way to do memory map caching. In fact it's probably one of the stupidest. Increased speed (when all maps loaded) by 30x or so by using a GHashTable shared between layers. How much of the slowdown was accumulated somehow since 0.0.8 is unknown. +New map caching file naming scheme. Hierarchial directory structure for faster lookup. +Tweaked the above memory caching scheme to use a linked list in conjunction with a hashtable to allow for a maximum size limit to the cache. Currently you have to edit the file to set this upper limit. And it's in tiles -- doesn't matter the size of the tiles. +Added some code in http.c of all places to make 2 directories above if they don't exist (new caching filename scheme) + +2005-02-19 +Fixed params[3] -> params[2] l1738 to fix click image +Fixed delete a waypoint->crash bug +Fixed TOPLAYER_FROM_WIDGET() error on right click wp -> properties (no VLP to use this on; use FROM_LAYER()) + +2005-02-18 +Wow, Haven't worked on this in a while... +Sorry, just adding a hack to allow me use mapzoom at 0.25 quality. + +2005-02-02 +Changed pan, a little bit, to move the map. Not good enough though, it leaves behind stuff now. Couldn't get it to not flicker, and run at a decent speed. + +2005-01-29 +Fixed waypoint pango drawing. +Compatibility layer for GtkColorButton to use an entry for GTK 2.2 +Move Waypoints by dragging +Snap to WP (shift-) for TP & WP moving. Snap to TP (ctrl-) for WP moving and still TP moving. +Select WP on the side when editing (if the layer is selected that is) + +2005-01-28 +Changed over to Pango, finally. Just experimentally so far, though; gotta cement it and fix it over. +Changed the properties/parameters system to allow tabs (groups) in properties boxes. +Tracks & waypoints now alphabetized! Three cheers! I can't believe how relatively easy that was. Two new functions in viktreewview.c and two more lines of code in viktrwlayer.c, I thought it was going to be hard for some reason... +Added a GtkColorButton thing for layer properties. This officially boosts the minimum requirements to GTK+ 2.4. This shouldn't be too much of a problem since 2.6 is out already. +Settable waypoint background & text colors. Different ones for different layers is incredibly snazzy. +Settable track bg color. Also fix some switch/case stuff... forgot to break... +So you can do plain transparent (no WP background) by choosing white and selecting color translucency... Not exactly clear. But wow, these colors are looking really awesome. + +2005-01-23 +Expedia maps working now. Don't *exactly* match up on the top and bottom sides, but I'm not sure I can get much better. Good enough. More fixes, added autodownload (thought that was already in here? maybe only in my topozone version). +Fixes, lots of small stuff -- e.g. a bug I introduced where it wasn't getting the correct utm zone for terraserver maps when downloading. +Fixed a weird GTK bug where updating the coord mode menu radio button would change the coord mode. See only_updating_coord_mode_ui +Implemented "invisble auto-downloads". Thought up a new way of doing downloads -- a queue and only one other process -- but probably won't implement that for a while. +Wow, auto-download + street maps is really neat! + +2005-01-22 +Working on implementing Expedia maps .. Lots of stuff, had to change HTTP to hande forwarding, that took a while just to test. Had to restructure some vikmaplayer code to know about expedia maps and download differently. LOTS more to go however. It now downloads maps, but that's about it. Lots of work to go. +OK, now it cuts off the sides (ads) and stuff. Snazzy. I made a test "draw" function but it isn't working perfectly yet. It is working and piecing together maps though. They look OK pieced together but street names are messed up at borders sometimes to I'm going to make the pieces bigger. +Did I mention it draws the maps in the wrong places? + +2005-01-21 +Many little things tonight: +Much cleaner way of doing alpha/bleach. Slightly less functionality (can't do bleach and alpha) but cleaner and should be faster. +Made it so it saves coordinate mode in the .vik file. +Made it so you can change the background. Saves this to the .vik file. +White background under tracks. Inefficient but it works. + +2005-01-20 +Added support for Urban Areas color photograph maps from TerraServer. +Just a note: TopoZone isn't going to work. I had added support for it in my own prviate version, and used it happily for areas where TerraServer was messed up. But one day it didn't work. They changed their site so it's hard (if not impossible) to get to the image directly. I could always do indirect, though ... + +2005-01-17 +Made it so you can change modes +Optimized a little tiny bit, drawing still slow +GREATLY Improved UTM drawing speed by remember zone width in VikViewport. I think kit meant to do that, there was a zonie_width item in VikViewport; it went unused. +Re-instituted UTM ce1, cn2, etc. checks that went out the door when multiple zones became available. It now does these checks only if UTM is used and there is one zone. Performance is now comparable to 0.0.5 speeds. +Fixed minor UTM 2 zones bug I (apparently) introduced above where some things are on zoom. + +2005-01-16 +Added "Visit Geocache Webpage" right-click waypoint option. Not very useful until I can click on waypoints, though. + +2005-01-15 +Started massive UTM -> VikCoord abstraction project +Wow. +Biggest. Change. Ever. +Worked on it about 5 hours. It seems to be going excellent! Most UTM's have been made into VikCoord's, except for some things like VikCoordLayer which I simply disabled. I just plugged in the Lat/Lon coord to screen code and it works! Now I have to work out all the bugs, add a change coordinate / conversion system, and fix a lot of stuff. WOO HOO!!! +Update: added coord_to_screen and screen_to_coord for Lat/Lon. works FLAWLESSLY!!! AWESOME!!! + +--- viking 0.0.8 --- + +2004-12-25 +Added UTM zone glue-together by Kit Transue +Fixed various things regarding this patch (distance, lines, etc.) +Packages & uploaded changes + +2004-11-27 +Back, but not really ... +Added a fix by Peter Jones (gpspoint.c glitch) +Added CYGWIN/Makefile fixes by Alex Stohr + +--- viking 0.0.8 --- + +2004-04-13 +Back from a 3-day GPS data gathering mission. +Fixed bug wherein track GC's weren't getting updated when line_thickness was updated. +Only waypoints and trackpoints if they're in the right UTM zone. +Use g_spawn_command_line_async for showing pictures; support for Windows for this too. +Rather drastically changed background.c so as not to use pthread's detach() and cancel() functions which are strangely absent in gthread. +Changed background.c to gthread. +Auto-switch UTM if you go too far. +Draw little star on tracks if it was cut off due to UTM zones. +Show human-readable time in trackpoint window. +Fix trackpoint window so as to not show time of '0' if the TP has no timestamp. +I didn't want to take this long with 0.0.8. 0.0.7 was released in just 27 days but 0.0.8 took 50. Oh well, I'll have to work doubly hard for 0.0.9. Here is Viking 0.0.8 all. Enjoy. + +2004-04-09 +Prevent loading partially-downloaded file by first writing to a temp file and them writing that data to the image file. +g_error_free () where neccessary. +Changed "Map Download All" to clearer, more direct and generic code. +Very easily used this code to make downloading lots of maps with the "map download" tool. Just drag over the square you want to download. +Added map size parameter. Had to change viklayer.[ch], add an extra_widget_data field in the param struct and change viklayer.c to support different uint values for options + +2004-04-08 +Some experimental work with maps at a different zoom level (e.g. 4mpp maps viewed at 16mpp) + +2004-04-04 +Load thumbs in a background thread. Thumbs show up as soon as they are loaded and you redraw. +Cleanup function for maps so incomplete map downloads will be deleted, woo hoo! +Snap to TP for edit TP and new track. Hold down control. +Added Max Speed, Average Speed, Average Dist Between Trackpoints in track property window. +Added "Diff Speed" in TPWin so you can tell speed between one trackpoint to the next. + +2004-04-03 +Put maps in a background thread while updating the UI, but you can still use viking. I have to duplicate all necessary data passed to the thread (what threads to download and where) in case that layer is deleted. I still have to a couple things (actually canceling the download operation) but it works very nicely, I can download lots of maps at the same time! It feels very liberating... +Right click to redownload a map. Regretably some messed up maps have been showing up with the above method, it's a big mystery how exactly... +Did a little test: disconnected my Internet connection and tried to download a map. Viking hanged for 22 seconds. I consider that acceptable, for now at least. + +2004-04-02 +Fixed trw_layer_new_track_gcs getting called twice -- post_read link and in vik_trw_layer_create +Just got finished loading thumbnails in the background. Aye, what a bother. But it works nice. (However once I got it to crash with an Xlib async error? Sigh, that's not good...) + +2004-03-31 +Added support for clicking images to show it, using eog -- this currently cannot be changed. (!) + +2004-03-23 +Fix crash on export code (Mike Davison) +Use $VIKING_MAPS for default map directory if available (Mike Davison) + +2004-03-08 +Move image to use when alpha drawing to VikViewport. That simplifies a lot. +Little changes, here and there. Lots of options for images. + +2004-03-07 +More work on dynamic Properties boxes, it will make adding new options _so_ much easier... +A lot of work on this. Wow, Viking is getting big. And ugly. +Also fixed a bug similar to another old bug -- forgot to copy waypoint image filename when a waypoint is copied. +You can now choose the image size, via the new dynamic properties box. + +2004-03-06 +Fixed a dumb bug that prevented you from opening GPSPoint files after Viking files. +Working on dynamic Properties boxes, getting nowhere fast... + +2004-03-05 +Wrote a tiny bit of code to further shrink and cache thumbnails. + +2004-02-29 +background.c to use pthread to run stuff in the background (think -- downloading maps) and give a dialog to cancel (d'uh!) +The ability to use pthread opens up a lot of possibilities about how to handle map downloads. For instance, you could have a little dialog where you have different groups of maps downloading. Lots of possibilities, I just have to think about how I want to implement them. + +2004-02-28 +Lately I've been working on a GUI tool written in Python to link up pictures and track logs to figure out where a picture was taken. That's my excuse, anyways, for not working on Viking. +Thought a little bit about how I'm going to do pictures in Viking. Looked over ROX-Filer's thumbnailing code. +Added a bit of picture thumbnail code. You can now see photos in Viking. + +--- viking 0.0.7 --- + +2004-02-22 +Fixed edit TP, then rename track -> updates current_tp_track_name glitch. +Took quite a bit of time to change the way tools work. Instead of returning TRUE to redraw, tools return TRUE if they "accept" the tool call. If they don't, then it will look for another layer that will accept the tool call. That way you can edit trackpoints on different layers. It's nice, actually. +Fixed "edit layer name, then move it" bug the crept in somehow. It wasn't in 0.0.5-pre4, but I don't see any differences in the code, oh well, fixed it this time. +One month and almost 4000 LOC later... Viking 0.0.7 "Gonna Move Those Mountains Aside" + +2004-02-21 +Fixed track join deleting problem mentioned yesterday by keeping track of individual GtkTreeIter's for all tracks in a hash_table. Whenever a track is added/realized it's iter is added to the hash. Actually a rather pleasant (and fairly easy) solution. This simplifies deleting tracks in other places too, since we now don't need to know the iter. +Very easily made it so that when you edit a trackpoint it selects the appropriate track in the layers panel. Press Shift+10 for fast deleting. Evidently the tracks list must be expanded. +Fixed last_tpl (VikTrwLayer) / cur_tp (VikTrwLayerTpwin) confusion. +Changed split up track to change current tp to first of new track rather than last of old. +For some reason I felt I needed to add a thing in track properties which would count the number of duplicate TPs (same coords, one after the other) and make a button to remove them. +You can now change latitude, longitude, and altitude of each TP in the track window. Good for setting exact location rather then moving. +Added _real_ transparency support. I've tried before, but this time it actually wasn't hard it all. It was harder adding the UI stuff and layer attribute and all that. Anyway it might be a tad slow wise but it looks FANTASTIC! +Fixed open and save dialogs (including VikFileEntry, but excluding render to image and export as gps* dialogs) so that the dialogs are not deleted and created each time but the same one is used. This means that it will stay in the same directory. +How does a release tomorrow sound? + +2004-02-20 +Can't seem to find the time for Viking these days. Today I managed to get a little work in. +I did manage to add support for moving trackpoints. It's awesome. You just choose the edit TP tool, select a TP, and drag it. +After experimenting with arcs (arrows) for TPs (it didn't work), I changed track drawing to draw a triangle for the startpoint and a circle for the endpoint. +OK, So you can now join tracks (kind of ugly solution, but that was how it worked out), but the really ugly part is that it won't show up as deleted in the layers panel, and to make it do that, I need the track's layers panel Iter which I don't have from the tpwin. I sense major hackage ahead. + +2004-02-16 +Thursday through Sunday was another "data gathering mission". I also though up a _lot_ of great ideas for Viking. Now I just have to find the time to implement them all. +Today I added support in file.c for opening from stdin, aka "-". I also changed the magic number determining code to use ungetc() rather than rewind() so it will work correctly with stdin. +Not part of viking, but I wrote a quickie viking-remote script. + +2004-02-10 +Gotta get back to work. Sigh. Saturday and Sunday I went on a "data gathering mission", so there's my excuse. Yesterday I was away hacking on gpsbabel to support gpspoint (Viking) files. +Fixing little tiny things, like making the tpwin not close when you delete a waypoint, fixing little mistakes in trwlayer, graying buttons, etc. +Added little things to tpwin -- forward, previous, diff, makind diff work for delete button, etc. +Added a_coord_utm_diff for tpwin diff & ruler tool & track length +Added a_dialog_create_vbox_label function, converted vik_trw_layer_tpwin_new() +Added track length, tp count, segment count in properties box +Fixed if try to rename waypoint or track to one that already exists, crash bug (had to uppercase user input) +Added split up into segments. Had to change a_dialog_new_track, do above change, and a million other things. Sigh. +Fixed split up segments so it first makes a copy, then deletes the old copy. That way if the track was on the clipboard you can paste to get the original track again. Nifty eh? +Added reverse track. +That's all folks. I'm a-goin to bed. + +2004-02-06 +Just got through fixing the hardest-to-track-bug I've ever tackled. Viking would crash when I copied a +pasted a layer in a certain file, but it seemed fairly random and gdb's backtraces were no help because +it was crashing on totally unrelated things (free'ing memory twice does weird things). I finally narrowed +it down to do a certain sequence to give me the warning: viking in free() chunk already freed, etc. Then +I used step (starting from vik_trw_layer_free) to determine exactly where it was free'ing something +twice. It was the waypoint comment. I noticed comments were different from the original waypoint (on the +original layer that had been copied) so I tried stepping thru vik_waypoint_copy to see whether the +waypoint was being copied correctly. It was then when I something wrong. I had assigned one waypoint to +another, then called the function vik_waypoint_set_comment(), which, upon seeing that the comment of this +new waypoint was not null, proceeded to free it, whilst the old waypoint still carried that comment +pointer around. The ironic part is one line of code (new_wp->comment = NULL;) fixed everything. + +Well, glad to get that out of the way. Now I can proceed to work on new stuff. + +I also made sure to delete the trackpoint window if the track gets deleted. +The long-awaited SPLIT UP TRACK is finally in Viking. Goodie. + +2004-02-03 +Set up signals for VikLayer. Constructed a super-cool yet slightly-slow chain of signals using VikAggregateLayer. It's wacky. +All my hard internal work has finally payed off. I just added code for deleting trackpoints. It works, the signal is given to update and all is good. Yay. + +2004-02-02 +Back to work. I hit a snag and had to switch VikLayer to a GObject to give off signals for updating for the track editing tool. + +2004-01-30 +Very very small amount of code to report export image write errors to stderr. +Some various work on track editing: + Made another file/class viktrwlayer_tpwin.c + Added labels to show info when you click a trackpoint + Made it draw a big TP for the current TP +I don't know why it's not in this ChangeLog, but some experimental Track[point] "editing" (realling finding) support was already there: the tool, the code for finding the nearest TP, and a line of code showing a dialog with the buttons which would be useful (I obviously did some planning.) +Oh, Also, cut and paste waypoints support has been in for quite a while, before September, I'd say, somehow that escaped this log. +Anywho, the road looks clear as to how to procede with respect to track editing: make sure to reset current_tp if track is deleted, add on features, etc. + +2004-01-29 +Added a little thing to tell you how much are the map(s) will cover. +FINALLY fixed coord layer so it shows ALL necessary coord lines (it actually checks your zoom level now!!!) + +2004-01-28 +Slowly added "Generate directory of Images" support. +Fixed pan tool. + +2004-01-27 +You can now choose the name of the image to export to, choose the zoom level, and choose to get everything in the current viewing window. +Also took 15 minutes to add a pan tool. +Also made it remember your choices of width and height and image format for draw to image file. + +2004-01-26 +I'm celebrating Australia Day by working on the Vik for the first time in ... My fingers don't go up that high, anyway. +Adding code for exporting to image. + +2003-9-22 +Took a short two month break. Hehe. +Finally got around to checking whether server returns "200". Sometimes terraserver has erras. + +--- viking 0.0.5 -- + +2003-7-15 +Simplified vikfileentry.c by using gtk_dialog_run +Loading from World files +Saving to World files + +2003-7-14 +Map Layer -- default cache directory ~/.viking-maps (on UNIX) or C:\VIKING-MAPS (on Windows). This way people don't have to worry at all about cache directories, they can just use the default and it will automatically be created. +Create default cache directory if it doesn't exist. Automatically add file separator on cache directory name. +Don't write cache directory name to Viking file if it is default; if no directory use default (that way people can share files without having to worry about someone elses cache directory) +Readability/Naming: InitGcs -> PostRead + +2003-7-11 +More work on GeorefLayer. It's now pretty much working. +Made vikfileentry.c, changed vikgeoreflayer and vikmaplayer to use it. + +2003-7-10 +Changed zoom to two variables -- xmpp and ympp and implemented necessary changes in the layers. +Added a "Goto Any Zoom" kind of dialog/function. +Changed status bar a tad to reflect this. +Started some work (mainly UI/semantics/program interface) on GeorefLayer. Will make it actually do something later. + +2003-7-09 +Changed zoom limit from 32 mpp to 512 mpp. +Changed zoom to a double to allow any zoom level. + +2003-7-07 +Sorry ... "Real Life" (tm) has been taking up a lot of time lately. Anyway today I managed to implement track visibility in Viking GPS files. + +2003-6-27 +Don't show an empty menu for a sublayer if it doesn't add anything to the empty menu +Little bugfixes + +2003-6-26 +Put all file stuff in one file. +Removed old binary fileformat support. 3 days of work deleted in no time. +Track -> VikTrack, Waypoint -> VikWaypoint, Trackpoint -> VikTrackpoint, vikwaypoint.c, viktrack.c +Comment support, comment support for gpsmapper export +Comment for gpspoint, backslashed quotes, etc. in comment in gpspoint.c. Lot of work... I could easily backslash track/wp name, but they usually dont have quotes in +OK, I've been programming for over 4 hours. Break time. +OK, back to work. Comment support complete, I've also added altitude output support for gpspoint. +Switched various button order around in dialog.c to comply with HIG +Track undo support. Double-click to end, right click to undo. Crazy day, huh? + +2003-6-25 +Support for reading map and trackwaypoint layers. Also support for trackwaypoint data (in GPSPoint format) +Whew! Write support is complete! Man, that's a lot of work. Now I just gotta iron out any wrinkles (such as getting rid of old file format support) + +2003-6-24 +Half hour's worth of coding has made copying and pasting TrackWaypoint layers a reality. +Started working on FF2. TODO: implement individual layer getters/setters functions, write support +I've done an incredible amount of work today. +Mission: By the weekend I must have FF2 support out the door. + +2003-6-23 +Created Viking 0.0.5 branch. +Started work on copy & paste layers. With just two hours or so worth of code, you can now cut, copy and paste coord and map layers and aggregate layers made up of these. +You can even copy and paste between windows. +Next up -- trackwaypoint layers, then we're off to copying items. +Onward -- 0.0.5! + +--- viking 0.0.4 --- + +2003-6-23 +Viking 0.0.4 "Lame Duck Release" +Alright, I haven't been doing anything for the last two days. Guess I'll just release what I have... + +2003-6-20 +Added GpsMapper export support to make it easy to make Garmin maps. +Made it so you can now rename tracks. +Implemented map fade into white (alpha)! This should help distinguish tracks while still seeing maps. + +2003-6-19 +Fixed a map caching problem that occurs when you lower the size of the memory cache (it won't really be lowered). +Started experimenting with map semi-transparency. It's pretty cool. + +2003-6-18 +HIG document compliance (Open, New, Close, modified flag, etc.) I probably won't be making a "Exit" menu item, because that is the dumbest and most confusing aspect of the HIG. +Tearoff item for tools. +Made a_file_basename and replaced all places where I previously just copied and pasted the code. +Fixed _another_ "forgot to initialize" bug (layer.realized) +Simplified interface by having one Open menu item for opening any type of file and an Append menu item to do the same +In doing this, "./viking my.vik my2.vik my.gps my2.gps" etc. now works (concatenates into an unamed file.) +Removed "Properties" from Aggregate Layer popup, and added info message when running "Properties" from the Layers menu on an Aggregate +Provided a info message that tells the user the Top Layer cannot be deleted. + +2003-6-17: +Added error message for failed writes, fixed various bugs such as not free()ing any of the layers on exit (!) +Also fixed some crashes such as when you try to export a trw layer and you can't write to it. +Fixed button order in model dialogs to comply with GNOME HIG. +Added a "Delete All Layers" item from the menu. Change menu names a little bit. +Added "Save" support (current filename) +Started thinking about HIG compliance in the way of documents. Will probably get rid of delete all layers and add new, open (in new window) + +2003-6-16: +Added VikTrwLayer serialization support, added "open" and "save" dialog boxes. +Added error messages for failed reads, etc. + +2003-6-15: +More serialization work. The basics are now working, I have to add VikTrwlayer, maybe add some things in to make +it less vulnerable to crashes and really big mallocs(), and maintain compatibility between versions. + +2003-6-14: +Got some more sample data for Viking via motorcycle riding ;) + +2003-6-13: +Fixed serious glitch that crashes on creating a new track. +Fixed viking crash that happens when a new track is being created, then deleted. +Designed file format & started on serialization of layers. +Moved Layer Icon loading stuff to VikTreeview, as poor designed as it sounds, to get ready for Serialization code. + +2003-6-12: +Viking 0.0.3, first public release diff --git a/HACKING b/HACKING new file mode 100644 index 00000000..6fdb36dc --- /dev/null +++ b/HACKING @@ -0,0 +1,23 @@ +Naming: +A "module" is a .c file. + +Functions starting with "vik_" operate on an object/structure with the module name (see layer.c for an example). +Functions starting with "a_" do not, these modules may have static variables. +Both are followed by the module name. Unless of course the function is static, in which case it may be called anything. + +All (well, practically all) global constants and macros start with "VIK_" and then the module name. + +---- + +The layers panel holds all the layers. Layers connect to the layers panel via what I call "interfaces" which are really just a +structure of function pointers and pointers to other bits such as icons. viklayer.c switches between the layers like +polymorhpism. Anything specific to the layer should (in theory) be found solely in that layer's source file. + +There are some ugly hacks in here, like the layers panel holding the viewport and each layer holding the viewport and a +GtkTreeIter. Unfortunately it was the only way I could figure out to do some things. It would be _much_ easier with a +object-oriented language. + +--- + +"xmpp" and "ympp" are really misnomers, as they only represent the Meters Per Pixel in UTM mode. Otherwise they are an artificial +zooming system. In expedia mode it represents the "Alti", in Google mode it represents 2^(scale), e.g. 2^0 = 1, 2^8 = 256. diff --git a/INSTALL b/INSTALL new file mode 100644 index 00000000..3a34cefd --- /dev/null +++ b/INSTALL @@ -0,0 +1,27 @@ +UNIX: +You must have GTK+ 2.2.0 or greater installed. + +cd src +make +strip --strip-all viking # optional) +cp viking /usr/local/bin # as root) +cp ../viking-remote /usr/local/bin # optional) + +Or, if you are using GTK 2.2 (as opposed to 2.4 or greater): + +cd src +make -f Makefile_GTK_2_2 +strip --strip-all viking +cp viking /usr/local/bin +cp ../viking-remote /usr/local/bin + +Try the "example.vik" file to get started. + +Windows: +(Outdated info) +You must have Mingw and MSYS (?) installed. Set the path to include the GTK LIB +directory and the bin directory for mingw. Then just gcc -c each of source files +and link 'em together along with a bunch of GTK libs. I have batch files to do +all of this, I will include them in here soon. Cross-compiling with Linux +might also be possible but I've never tried it. + diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..7a1c520e --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +all: src + cd src && make diff --git a/NEWGOOGLE b/NEWGOOGLE new file mode 100644 index 00000000..8b0aa35e --- /dev/null +++ b/NEWGOOGLE @@ -0,0 +1,14 @@ +Mercator Projection +zoom=17 -- 200x200 +sides just reach each other (+-180 long) +middle is 0 degrees -- Greenwich Meridian +middle is equator +zooming in is 2x +The northwestern most tile is always 0,0 and x,y increase by one + +* easy to convert x,y -> ttqs etc. + +highest is 1 "mpp" MERCATOR. + + +therefore top must be 85.051128779806582259 = DEMARCLAT(180) diff --git a/README b/README new file mode 100644 index 00000000..748a5266 --- /dev/null +++ b/README @@ -0,0 +1,20 @@ +To compile: + +make + +Or if you're using GTK 2.2: + +make -f Makefile_GTK_2_2 + +... and ignore the GtkColorButton warnings etc. + +Viking needs documentation! Any volunteers? +After I get some core features down I'll start on documentation. I promise. + +To export as GPSMapper, the track/waypoint name should be in the format of: + +Name RGN## 0x#### + +or + +Name RGN## 0x## diff --git a/TODO b/TODO new file mode 100644 index 00000000..144a2e9c --- /dev/null +++ b/TODO @@ -0,0 +1,534 @@ +FIXES: + +Mapcache: checks if only a few images in cache (list_shift) +what happens if image is bigger than cache size! fix this... + +del from cache when updating image + +check out bgitemcount stuff in background.c +open jobs window a 2nd time after clearing (mess around a bit), crashes + +TOO MANY OPEN FILES!!!!!!!!!!!!!!!!!!! + +FIX HOLLISTER-RELATED DOWNLAOD TOPOS -> COORD NOT IN UTM ASSERT/CRASH (???) + + smarter caching (size-based), too many open files bug? + + what if try to REDOWLOAD while RE/DOWNLOADing? + remove from CACHE after REDOWNLOADing + Reid's 23, including waypoints, GPX/XML Viking files. + Fix map downloading/HTTP code. + Redraw when becomes available, somehow? + Speed up waypoint drawing; option for faster drawing. + FIX CACHE TOO BIG/SMALL STUFF!!! Just add a 'size' field to each tile, add and subtract as ness. + Then convert to MB and set this a program option. + map loading/memory caching in the background? (for zoom out) + modular plotting styles? just an idea... + optimize Lat/Lon performance with its own CE1, CE2 etc. + make ce1, cn2 etc for lat/lon & utm; maybe get out the checking code in vik_viewport (see draw_rectange for instance) + no alphabetize in the layer itself option for opening tons of GC's? (optimization tag for wp drawing and alphab.) + Progressive Loading (GdkPixbufLoader) + CACHING WHOLE CHUNKS? (USEFUL FOR MAP ZOOM TOO!) RELOAD BUTTON? + * instead of using "emit"s in tools, do this: + + * "add-only" return value -- only redraw this layer and layers above it. + OR maybe even more advanced -- only redraw certain portions of screen. + what about ALPHA??? uh oh... + "always redraw" return value -- if layer is visible or not -- e.g. goto wp + "redraw if visible" return value + WHITE UNDER TRACKS! DONE FASTER! CLEAN THIS UP! + replace HTTP code!!! + relative photo pathnames (relative to GPS data file) + Optimizations (compare to 0.0.5) +OTHER + Editing/Moving waypoints: if didn't move enough + settable defaults for layers (sigh...) OR at least config.h stuff + hover over pic + Clipboard done right -- GtkClipboard, between processes + Better annotation for tracks, etc. Fields, etc. + Waypoints -- exactly change how it looks like -- shape, size of point. + \-- make drawing name OPTIONAL. + Filled polygons and we'll have a QuasiGIS!!! + merge layers -OR- copy & paste >1 item (better! not too hard?) + New UI: "Track Tool" => just analyze/pick out, right click or button to edit + right click to do new track + or "Track/Waypoint Tool" + I18N +THUMBNAILS + * settable viewer: xv, kview, eog, gnome-open, kfmclient, etc. custom (& web browser for geocaches) + +0.1.2 + * Data sources: google, gpsbabel + * gpsbabel plugin -- wired up to get directions from google maps. + * address plugin. + * direct GPS support (?) + GPS navigation (speed, etc, etc) + * advanced downloader? +0.1.3 + * stops + whole list: length, max movement, icons. can make more. right click -> stops... + * symbols + * draw by altitude, altitude side thingy. +0.1.4 + * selections (?) + * NASA crazhy maps + * scale +0.1.6 + * new UI & documentation + * internationalization +0.2.0 + * TIGER data, vector maps +0.3.0 + * altitude stuff, goodies + * major UI changes, direct manipulation + * sit down for a day or two and design the bestest bestest UI + * threads, etc + * docs, lots of docs +1.0 + * more sophisticated vector data, almost GIS stuff. garmin maps, whatever + * party + +-------------------------------- + +0.0.9 / 0.1.0 + SELECTIONS (?) + * square & track-based + * select everything in, everything out + * download maps in + WAYPOINTS + * pango for text; text background with settable colors & options + * move & edit waypoints!!! auto-select for that + * maybe an option to turn on auto-select: off, on, only for same layer (default) + MAPS + * some of the above optimizations + MERGE TRW + * auto-rename dupes + * delete dupes by name + * delete dupes by location; name & location + TRACKS + * track editing -- everything on that idea sheet on my desk. + * insert into track + * show list of pics in one location + * maybe track-by-elevation + MISC + * alphabetize waypoints & tracks + * d&d layers (not worth it?) + FIXES + * crazhy-crazhy auto-switch UTM? + * figure out save image in background stuff, sigh... + * "widget properties set value param data" + -> check if has properties params + -> in layer_prop_fact: hold value's check if changed. + -> return TRUE only if value affects look + -> beautify dynamic properties boxes (checkboxes please?) + -> groups + -> maybe get rid of post_read (use set_param to update gc's -- but maybe not...) + * TODOs & FIXMEs + * due <= 2004-6-12 (1 yr after 0.0.3) + +DONE: + * do alpha _right_ (to see what I mean, try drawing a transparent PNG) +----------------------- + +POSSIBLE MAP OPTIMIZATIONS: +save conglomerates of shrunken images [to disk]? +static cache.c shared between everything -- SHARE MAP CACHES BETWEEN LAYERS AND WINDOWS!!! +background loading thread +load zoomed out images into conglomerates + -or- +Sort images by some sort of checksum (maybe sum of all digits) into 10 groups +makes finding cached image 10 times faster +if we somehow know we're not going to have enough room to store all maps for 1 redraw in cache, don't cache at all. +better cache management -- maybe every map in memory? +--> cache should fit more small maps than big ones!!! + maybe number for cache_size, keep track of it manuall, calculate from shrinkfactor? <-- good idea +pixmap (not pixbuf) cache? +vikmaplayer.c:520, don't run g_slist_length who-knows-howmany times (only noticable when shrinking cache) +stat images to make sure the size isn't zero (vs. trying to load them). then show errors for bad ones (?) +don't lookup IP each time (?) + +------------------------------ + + + Then, focus on Lat/Lon and mapquest maps. (????) + Then, focus on Lat/Lon and mapquest maps. + +0.0.9 "260 million maps... I can't hear you at all" release: +finalize pthread/map download UI. +export to image fix bug +viking running -> use this thread ( good luckers !!! ) +merge trw layers + +0.1.0 "where we're going, we don't need roads" release (concept idea): +Select a waypoint, delete to delete it, real tools, a toolbar, etc. +Drag and drop layers +alphabetatize waypoints & tracks +"area selections" -- select tool -> select waypoints in/outside area, download maps in area, etc. etc. + +-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/-/ + +0.1.0: the "fixes, patches, improvements, and goodies" release + "Where we're going, we don't need roads..." + + * move a_dialog_new_track to viktrwlayer.c. Clean up VikTrwLayer, separating into several more-managable functions. + * get rids of pass_along, layer_and_vlp junk, just use update signal and track_iters (maybe make waypoint_iters) (?) + * TODO longones + * general cleanups, what you have time for (vik_trackpoint_free etc, draw_track, etc.) + * do we really have to have all those global funcs in vik_trw_layer ? +right click->new track here, new waypoint here. general TRWEdit tool with popup on vikviewport, it can be done. +general edit->delete to delete whatever is selected. ugly stuff ahead. +show human-readable timestamp +toolbar/toolbox, tool opts win (for tpwin) like in gimp (?) +Maybe fix "private" ref_count in clipboard.c (?) +clean up viktrwlayer.c, call track_realize() when adding track with add_track(), del dup. code, esp. in track_draw +move/edit by click for wp? +toolbar/toolbox, tool opts win (for tpwin) like in gimp (?) +only draw if in UTM zone !!! +Track color-by-elevation ( go all over an area, make tracks thicker/zoom out to create DEM color map ?! ) +maps zoomed out +Thing to download lotsa maps (script or builtin -- ?) +Merge layers (what a say we have 2 layers, one with wp's, one with tracks) + merge: auto rename (everything begins with "layer name: ") + right click agg -> merge trw children -> super trw + or multiple slect/copy & paste +picture layer, thumbnails and all... +move to gpsbabel default altitude, reflect it everywhere, include viking-remote +gpspoint.c -- use new easy gpsbabel code, abc, 123. + +hard things to think about that need to be done sometime (mainly hard/impossible): +1) map downloader process / map download queue +2) if viking already running, use that process. cut and paste will work much better. + +EVERYTHING ABOVE THIS LINE SHOULD BE IN 0.1.0! 0.2.0 WILL FOCUS ON LAT/LON AND STREET MAPS + +----- + +0.2.0 +* Plot as lat/lon + For mapquest & georef maps: vikviewport "mode" to draw in (utm or straightened lat/lon), check before drawing, convert for TP, etc. +* full georef support, release minnesota/manganese +* mapquest maps +* smart drawing -- determine if line segment passes thru area, fancy math... +* map downloading manager, smarter ways -- e.g. "download maps within polygon" +* maybe a gpsbabel plug-in +* topos zoomed out if it didn't make it into 0.1.0 +* maybe better wp drawing +* wp's alphabetically (good luckers) or at least when loading +* wp editing +* direct gps support + +0.3.0 (still here?) +* new viking -> not new process +* wp icons +* routes +* improve direct manipulation +* bugfixes +* smart labeling so we can see 2 wp's (nearly) in the same place +* UTM zones (?) + +1.0 +* DEM maps +* mapsource files +* anything else possible, plausible and desirable. +* lots and lots of docs +* UI cleanups + +Uses: +* planning +* viewing -- showing places/tracks on topo or street maps or scanned maps +* map download & printing -- your own, TOPO-thomas guides or city thomas guies +* adding coord lines to scanning maps -- print 'em out or see where they are +* editing tracks +* managing & organizing data +* creating maps and gps maps + +SCALING: +* maybe downn scaling of higher-quality topos to accommodate odd zoom resolutions. this would also be useful for downloading _only_ high + quality topos, or zooming out and seeing what topos we are missing, or downloading LOTS more topos at once (w/ download all on screen) + again, we will have to see how good GTK scaling is. +* maybe scaling of weird-scale (georeferenced) maps + +Other +* georeferenced raster layer: + Optional: + - fix off-by-one-pixel bug (probably corner stuff) (?) + - utm zones (?) + - tool (maybe BUTTON 4,5 thing), and maybe a better zoom tool. but for release it's OK. + +ROTATION/MESSED UP MAPS: +* Track/Waypoint Plotting as Lat/Lon (or artificial rotation, maybe preferred) for those kind of maps + and/or rotation of maps (not as good) +* maybe option "draw as lat long -- UTM center +- 1000 or something to find degrees/pixel (or use some other formula a la odu) +* or just rotation in screen_to_utm and utm_to_screen +* "anchoring down" one part of a georeferenced map for rotation and scaling. + +* fix coord layer, it is SO horribly broken for high zoom levels. + +* Investigate "chunk already freed" +* investigate if layer type doesnt have icon wont start bug +* Cleanups and fixes until 0.0.5-final: +* at least two of these things + * Merge TRW Layers + * copy and paste items + * MapBlast + +These Cleanups: + +-> These Cleanups +maybe "max size" thing for each param +--> maybe address within layer (struct offset) so wwe can just do away with set_param (in most cases). put stuff in realize or after_read +cleanup interface <---- IMPORTANT + +-> cleanups below + +-> sometime: + has_altitude or NO_ALTITUDE -> 999999 + +-> 0.0.6 + +C*L*E*A*N*U*P*S + +dialog.c -> misc.c, viktrwlayer.c +export a layer from the file menu, some how. Export Layer -> choose layer. or Export Selected Layer (disabled as need be +--- +*MAYBE* + +RGN type and other type in something of its own, not name. +that way, we can use the same file for waypoints to gps and for maps. + +solutions to this problem: +File->Properties: + Position: + Save current position + Save this position: + UTM/LATLON ZOOM + Save no position + +------- +------- +------- +------- + +(fixes + cleanups + scaling + rotation + ruler widgets (?) + topos & orthos in one (?) + copy&past items + merge layers) = 0.0.6 + +then 0.0.7, 0.0.8, 0.0.9 focus + +things before "major" release: +direct manipulation (major feature/use) +use current viking (maybe) + +to second "major" release: +libgpspoint2 and/or gpsd (show current pos) +map ui (rough edge) +analyzing stuff better, exact analyzing. + +to 1.0 +UTM ZONE!!! + +> Although almost all of these are on your todo list I thought I would add my thoughts. +> +> 1. Being able to choose the map units. The metric system still baffles +> some of us. +> 2. Modular set up for map sources. The urls tend to change/disapear +> when companies get bought up by micro$oft (mapblast). The ability to +> import/export georefrenced maps. +> 3. Manipulations of track data...distance, altitude etc. +> 4. Export/printing of maps. +> 5. Eyecandy. Colors and symbols for tracks, waypoints and routes. Shade +> topo maps with DEM data. + +file format + hig compliance + bug fixes + MAYBE track undo = 0.0.4 +FF2 + ruler widget + topos&orthos in one +comment field + bugfixes = 0.0.5 +if viking already running, use that process. + copy and paste layers + merge layers + + copy and paste items + bugfixes = 0.0.6 +Direct manipulation, WYSIWYG moving + track connecting + etc = 0.0.7 +track segment selection, deletion, track point deletion (create two segments or skip point) + + waypoint symbols + bugfixes = 0.0.8 +toolbar + layer properties boxes changes, etc. + misc = 0.0.9 +routes + misc + code cleanups = 0.0.10 +map download ui + code cleanups + bugfixes = 0.0.11 +other goodies, fixes, break release = 0.0.12 +misc, etc. fixes, cleanups = 0.0.14 +autotools, I18n + etc. = 0.0.15 +fixes = 0.1.0 + +(NOT IMPORTANT. FF2 will come.) +functions write_utm, read_utm to save space +sizeof(Waypoint) != sizeof(components)! try to save disk space +len of layer for each layer so older versions can skip over new layers +Make it so if layertype >= vik_layer_num_types, skip layer. (in aggregatelayer.c) +Use magic numbers, and lengths to better prevent version messing ups (len of layer data, if extra, + rest skip, if not enough, ignore, etc.) 9in layer file themselves) +--- + +Viking 0.1.0 "It's been a long road..." + +> 0.1.0: anylyzing speed and time, libgpspoint, misc goodies +> 0.2.0: garmap layers, utm zones, etc. + +0.0.4 "Lame Duck Tape" +-------------------- +0.0.6 "Where we're going, we don't need roads..." +-------------------- +--Definitely: +UNDO DELETE LAYER!!! Or ask, maybe? For now, ask: later, maybe an undo "stack of commands" where things at the bottom of the stack are done only as new things push them down? +more "Forgive the user" kind of stuff. + +FIXES: + Acceptable chars in dialog.c & gpspoint.c + file check if it's a directory for load & save. + +Route support! + +Toolbar +Do something similar with tools. layer_tools func in interface, then a tools structure, array, or add2menu func. humm + +Track Colors -- PROBABLY. + +viktrwlayer_dialog.c -- we MUST do this one. + No, even better Idea. framework where properties are stored as RESOURCES for each type of layer. + Each type of layer defines it's own resources and the viking layer dialog builder picks it up and draws the dialog. + Comments? Format? Fields? + Type: check, double/spin (including range, and all other params), string, double/entry, int/entry + And then subdivisions (tabs) of that. Track drawing, track colors, waypoints. or Drawing, Colors, ... + +fix layers_menu_add_layers + I've made a mess of the add menu stuff. The add submeny is bad. + maybe a itemfactory thing in layer interface (for now, it's OK... but...) at the very least a serializedicon thing in + interface so I don't have to serialize it every time... draining resources. + + -- what if i have one menu with the icons and everything already made, and both vlp and wikwindow adds that? no, then i + could't have custom callback data. what if all layer types' itemfactory items were modeled as "Add Layer/TRW Layer" Then + I'd have an add layer menu in each? + -- think about it. + + +MAYBE: + MOVE A WAYPOINT + Waypoint Symbols, get started. symbols.c where i have a Name->ID mapping, then an ID->icon mapping. Get Started on Icons. + Merge 2 TRW Layer + Better CoordLayer. + Fix Some Bugs, Clean up Some Code. + Bulk Map Downloader (maybe another project) + renaming waypoints -- overwriting + + +And There's Your Release. + +Other Ideas: +fix editing / move bug (ideas?) +copy & paste individual tracks & waypoints +tracks & waypoint ordering (?!) +don't forget about track SEGMENTs !!! (shoot for 0.0.5; should be in by 0.0.6) +vik_trw_layernew_track like waypoint. or maybe not (need starting point?) +I18N -- at least fake ( all strs _() ized) +HIG compliance... +maybe export all visible trw's (nah, merge) +select waypoint; break up a track +Coordlayer color chooser +Coordlayer: UTM +trw_layer centerize: randompoints & average +maybe in item_add get visible and name from the layer / track / whatever itself. +ALSO, maybe chane set_name to update_name. +show tracks and waypoints (dynamic stuff)!! Copying between layers! YEAH + +do some TODOs + +--------------------------------------- +| 0.0.5 | +--------------------------------------- +| "Not all those who wander are lost" | +--------------------------------------- +Track & Waypoint sorting +Convert to GLIB Gobjects for layers +I18N +AutoTools +Track Colors +select waypoints; select trackpoints; selet track segments; measuring track & segment length; analyzing trackpoint altitude +& time by selecting it; color by altitude/ +Copy & Paste Layer (see below) +legend (length) + +copying between layers: sublayer_add_menu_items () -> copy -> static pointer for layers panel, or whole process. +two ways of doing it: layer2copy & sublayer2copy, or general purpose "cliboard" var. on paste, layer / aggregate layer figures it out. +I vote for "layer2copy" and "sublayer2copy". That way, each layer can check if it can paste the sublayer and paste layer will always be there. +then you need a duplicate function for each thing you want to copy. shouldn't be too bad. + + +~-~-~-~-~-~-~-~ +0.0.6 "Where no man has gone before" +--------------- +DnD +Map Download Background Thread +LibGpsPoint +Settabl eFonts +UTM zones +Waypoint Symbols +Edit Timestamps +show different fields on side: altitude, time, etc. sort by these. maybe a layer edit dialog box. + +------ +0.1.0 +------ +Garmap Layer! + + +Other: +-------------- +waypoint symbols +make your own timestamp +CACHE MAP SERVER IP; RETURN ERROR FOR WHY DOESNT WORK +MAP DOWNLOAD PROGERESS (!) +more g_asserts ... lots more +UTM zones, aye... + +---------------------------------------- +======================================== +++++++++++++++++++++++++++++++++++++++++ +fix tracks thing: STILL BUGS ! and clean up function maybe? +start noplace, when open file start in middle of there +clarification of new / open ( add to or what? maybe add to in layer; merge layer stuff ) +status bar: don't overlap stuff + +i18n!!! + +coordlayer -- ticks only +tick marks or lines for utm lines so you can tell where you are (TickLayer) + +Intelligent Map Caching: knows which way you're going ... just something to think about. + +Download all maps + +routes +choosable gcs including background +Maybe each track has it's own GC? Just a thought. +Tools, explore etc. tools menu. tools window / bar +save stuff +status bar for track info, etc. + ++TODO from otu ... + +glist for routes (pointer directly to string used in ghashtable key? possible?) and tracks. + +"Viking -- can you handle the power?" +"Viking -- where do you want to go today?" +"Viking -- where have you been today?" +"Viking -- where did you go today?" + +"Vikingis Conqueris" + +"The Viking Conquereth" +Viking 0.0.1pre15 + +Viking "the fake GIS" +Viking "the wannabee GIS" +Viking "CasiGIS" + +Viking QuasiGIS (tm) + +GPX support, loc.gz +So Many Possibilities... + +OTU can do Viking can't: + * routes + * instant GPS location (sorta) diff --git a/TODO-REID b/TODO-REID new file mode 100644 index 00000000..1af69d07 --- /dev/null +++ b/TODO-REID @@ -0,0 +1,158 @@ +STATUS + +1. I'm not doing this; someone else can mess with all that crap +2. Trivial, can do if want +3. Already done; needs some reworking +4. I tried; I can always try again! +5. a. done + b. Brilliant! + c. impossible +6. + +Dear Evan and list, + +I spent the afternoon today tinkering with Viking 0.1.0, and came up with +the following list of criticisms. I hope that you all will find these +constructive, and I do regret that I have time only to criticize and not +contribute myself. Also, let me emphasize that Viking is damn cool, and +these are only ways to make it even better. + +In terms of context, my current use of Viking is to plan backpacking trips. +I basically download the topos of areas of interest, and then draw in +potential routes using the track tool. I then export the images to the GIMP +and hack them further. Here is one example: + + http://reidster.net/trips/halls-ck-lower-escalante-loop/ + +Comments, of course, welcome. + +Again, thanks for all the hard work on Viking! I hope to contribute myself +one day. + +Reid + + +Constructive Criticism for Viking +--------------------------------- + + 1. Use autoconf/automake. Currently, if things (e.g. gtk, glib) are missing, + the build simply fails, sometimes quite spectacularly. This will also + bring in all sorts of handy niceties such as an "install" make target, + etc. + + 2. The top-level map directories appear to have the form "tZsXXzYY". Does tZ + stand for "type Z" where type is terraserver, google, expedia, etc.? If + so, I suggest using a word rather than a number for greater transparency. + + 3. There ought to be a way to re-download map tiles, in case you mess with + the files on disk like I did and download some aerial photos into the + topo area. + + 4. Panning (which is scads faster than v0.0.9, BTW) leaves screen junk in the + panned-into region. Perhaps this area could be filled in with a solid + color (say, a light steel gray?) until the new image can be drawn? + + I do really like that panning is lightning-fast, and drawing new data is + deferred until I stop dragging. + + 5. I like that downloading maps doesn't bring up a new window anymore. + However, there needs to be more feedback on what is going on. I'd like to + see the following: + + a. Status indicator somewhere saying how many maps are waiting to be + downloaded. + + b. Map tiles which are blank but queued for download are highlighted in + some way (say, color them pink?). + + c. Map tiles are drawn as soon as they are available. Currently, new tiles + aren't drawn in until another redraw event comes in. + + 6. Changing zoom level to a large scale (e.g. zoom out 64x) is slow, esp. if + you only use the 4x4 meter resolution topos, as I do. This slowness is + fine, but I think there ought to be an indicator that Viking is thinking + -- the watch cursor, perhaps. I don't think a full-blown percentage bar + window is necessary. + + 7. Resizes to the layer info panel (or, the window config in general) should + stored in the Viking file so they're persistent across sessions. + + 8. It should be clearer which tool is active. Each tool should have its own + cursor. + + 9. Visibility of the start/end trackpoints of a track should be controllable + independently of the visibility of interior points. I think that it would + also be handy if, near the start/end trackpoints, "Foobar start" and + "foobar end" could be drawn, since otherwise I forget the tracks' names + and I don't remember which little symbol is the end and which is the + beginning. + +10. There should be a concept of a "current track". The current track should + be drawn in a different color, and it should be highlighted in the list in + the same color. The "Create Track" tool should extend the current track by + adding on to the end point. + + Also, it's important to be able to have no current track; in this case the + Create Track tool will start a new track. At the moment, I can't figure + out how to start a brand new track after I've been editing one without + restarting the program. Perhaps separate Begin Track and Extend Track + tools would be a good idea? + +11. Track colors are kind of strange under "Track drawing mode: draw by + track". Here are some suggestions: + + a. Once a track is assigned a color, it should never change except by the + user changing its color directly. (There might be a global "recolor + tracks" command, in case the user wants help.) + + b. There needs to be more contrast between the auto-assigned colors, and + the number of colors doesn't need to be too big. I would say, pick + three or four contrasting colors and rotate between them. + + c. When a path is split at a trackpoint, the portion closer to the end + point should be the one to change color. Perhaps a secondary dialog + box listing the two new tracks, their names, and their colors, with + opportunity to edit the last two items? + + d. A path's color should appear in its list entry, too. Perhaps a small + square by the name? + +12. I might further increase the contrast of the current trackpoint (selected + with the edit trackpoint tool). Perhaps rather than changing its size, + draw a big bold crosshair centered on it? + +13. Tracks names should permit mixed-case. + +14. IMO, being able to customize the background color is an unnecessary + feature, and should be removed to ease maintenance. + +15. I would rename the menu item "Zoom To..." to "Zoom to Custom..." and the + "Zoom" submenu to "Zoom To". + +16. Type size in the layer list should be configurable. + +17. Perhaps dragging with the middle button should pan. I think this would + complement well the existing center at location of middle click. + +18. Deleting something big, like a track or a layer, should have a + confirmation box. + +19. It would be nice to have more information in the interface about which + maps will be used depending on the "zoom level" of a map layer. For + example, in a Terraserver topo layer, I had to discover by trial and + error that <= 8x means "use 24,000:1 topo maps" and >= 16x means "use + 100,000:1 topo maps". + +20. Consider an XML file format rather than the existing ad-hoc one. + +21. The ability to rotate and move waypoint text would be fabulous. + +22. When moving waypoints, it would be great if a greyed copy of the waypoint + text and dot moved with the mouse. + +23. I'd like the ability to customize the waypoint dot symbol. Personally, I'd + prefer an X to the dot. + + + + diff --git a/VIKING-REMOTE b/VIKING-REMOTE new file mode 100644 index 00000000..6a886bde --- /dev/null +++ b/VIKING-REMOTE @@ -0,0 +1,2 @@ +gconftool-2 -s -t bool /desktop/gnome/url-handlers/geo/enabled "true" +gconftool-2 -s -t string /desktop/gnome/url-handlers/geo/command "viking-remote \"%s\"" diff --git a/doc/GEOCODED-PHOTOS b/doc/GEOCODED-PHOTOS new file mode 100644 index 00000000..2ed8d72e --- /dev/null +++ b/doc/GEOCODED-PHOTOS @@ -0,0 +1,26 @@ +HOWTO GEOCODE YOUR PHOTOS AND SEE THEM IN VIKING + +1) Synchronize your camera's clock with your GPS clock. If your camera's clock doesn't have a seconds value you can change, you might try setting the minute value just when the minute changes. + +2) Activate the track log on your GPS. + +3) Get outside and take some pictures with your camera while making sure your GPS has reception. If you forgot to turn your GPS on or forgot to bring it when you took a picture but you know where you took the picture, you can make a waypoint with a name of the format "YYMMDDhhmm", representing the time the picture was taken, to record where you were at that time. + +4) Get home and download your pictures and GPS data. You can download the GPS data several ways. The first is using gpspoint to download from a Garmin GPS: + +gpspoint -p /dev/ttyS0 -dw -dt -of 2004-04-02-trip1 + +Where /dev/ttyS0 is the serial port the GPS is connected to. +If you don't have a Garmin GPS, or if you don't feel like install gpspoint, you can use my version of gpsbabel with support for gpspoint files. You can use gpsbabel to either translate the GPS data from a format you already have the data in or download it directly from your GPS reciever. A typical command line would something like this: + +gpsbabel -i garmin -f /dev/ttyS0 -o gpspoint -F 2004-04-02-trip1 + +5) Launch GPSPhoto. + +6) You must now bring your photos into GPSPhoto. Either drag them (or the directory containing them) from your favorite file manager to the list on the left of the GPSPhoto window, or use the buttons below the list to find them. + +7) Do the same for your GPS data file(s), dragging them to the list on the right of the GPSPhoto Window. + +8) Click the "Execute" button, and choose a location to save the gpspoint file containing the waypoints for your photos. Then click OK to write the waypoints to this file. + +9) Open Viking and open both your original GPS data file and your image waypoint file just created. diff --git a/doc/GETTING-STARTED b/doc/GETTING-STARTED new file mode 100644 index 00000000..350ed57a --- /dev/null +++ b/doc/GETTING-STARTED @@ -0,0 +1,31 @@ +To quickly get started using Viking, follow these instructions. + +1) Get some data from your GPS device. There are a couple ways to do this. +You can use gpspoint or you can use another program and translate it with +my version GPSBabel with gpspoint write support (or use the GPSBabel program +itself to download and translate at once.) + +2) Open up viking and use File -> Open GPS Data to open the gpspoint file. +Viking will try to change its viewing area to the center of the layer (file) +but sometimes if you have waypoints and tracks which are very far apart you +won't see anything. If this is the case, expand the layer (on the Layers +Panel to the left), then expand "Waypoints", find a waypoint and right-click +on it and click "Goto". + +3) Now add a map layer ( Layers -> New Map Layer ), choose a suitable cache +directory where you would like to store your tracks and click OK. Zoom to a +comfortable level (I suggest 4 mpp) by clicking the left and right mouse +buttons on the gray area where your tracks are. Now from the menu choose +Tools -> Map Download, Click where you would like to download a map. +Regardless of what tool you are using, the middle mouse button always pans. + +4) You will notice the maps are being drawn on top of the tracks, which is +probably not what you want. Look at the Layers Panel to the left. Notice how +the layer named "Map" is ABOVE your TrackWaypoint layer that your GPS data is +in. This means that it will be drawn last, or on top of the other layers. +Select the Map layer from the list and click the down arrow button beneath +the list. Your tracks will now be drawn over the map. + +---- + +TODO: some explanation of the layers, etc. is required. diff --git a/doc/GPSMAPPER b/doc/GPSMAPPER new file mode 100644 index 00000000..4813ee75 --- /dev/null +++ b/doc/GPSMAPPER @@ -0,0 +1,13 @@ +A word on exporting TrackWaypoint layers as GPSMapper files. +(NOTE: You should always save your data as a gpspoint or Viking file also because Viking cannot open gpsmapper files) +The comment field for each track/waypoint you want to export to GPSMapper should be in the following format: + +Name RGN## 0x#### + - or - +Name RGN## 0x## + +Where the first '##' is 10, 20, 40, or 80, and the second group of '#'s is the type of region. +See the GPSmapper ( http://gps.chrisb.org/ ) docs for more details. +By the way, if anyone knows of an open-source program to make compiled maps for (preferably Garmin, +but I would appreciate info on other brand) GPS receivers, please tell me! + diff --git a/doc/dev/CLEANUPS b/doc/dev/CLEANUPS new file mode 100644 index 00000000..4d8366ac --- /dev/null +++ b/doc/dev/CLEANUPS @@ -0,0 +1,21 @@ +Cleanups: +Split up viktrwlayer.c, etc. +get rid of duplicate code (including new 1mpp topo stuff & fix that hack) in vikmaplayer.c + +VikTRWlayer.c cleanup: + and maybe viktrwlayer_dialog.c + as much as you can fit in a week. + file.c >> viktrwlayer.c (yeah, viktrwlayer.c could really use some extra lines of code...)? + is there a way to split up viktrwlayer.c? it is a MESS! (split it up) + instead of get_tracks and get_waypoints, have lookup's and insert's (actually, only gpspoint.c uses it now) + waypoint.c with some dialog.c stuff. tool code in maybe even another file, viktrwlayer_tools.c or maybe +viktrwlayer_menu.c + all layer files: there are things that nolonger need to be non-static (sublayer_add_menu, properties, etc.) + +AUDITS & CLEANUPS: + no more dialog.c -- scatter it to layers' files. + audit viklayerspanel.c, viktrwlayer.c, dialog.c, and maybe others. + resolve nameing and semi-static stuff: dialog.c, http.c, file.c (put into viktrwlayer.c, maybe) + +do a BUNCH of TODOs + diff --git a/doc/dev/Clipboard b/doc/dev/Clipboard new file mode 100644 index 00000000..f1bc2742 --- /dev/null +++ b/doc/dev/Clipboard @@ -0,0 +1,20 @@ +new file -- clipboard.c + +Copy layer -> layer address +on paste, layer_copy_func in Interface. returns new layer with new data, etc. + +-- items -- + +if selected thing something to be copied call +layer_copy_item_func +then +layer_paste_item_func +If same layer is selected. + +The only problem is determining if selected item can be copied. +No, wait, piece of cake. Hand back sublayer_type to the layer in a new interface func + +gboolean layer_sublayer_is_copyable_func ( sublayer_type ) + +to determine if copyable + diff --git a/doc/dev/FF2 b/doc/dev/FF2 new file mode 100644 index 00000000..243cf11b --- /dev/null +++ b/doc/dev/FF2 @@ -0,0 +1,119 @@ +If I use the GPSPoint code, I can concatenate gpspoint files and make them into layers just with a text editor. +Also, any new features I just add to that. +However, be mindful that GPSPoint looks for EOF. +I could modify the code to stop on a line that begins with "-" + +Style 1: Use GPSPoint code + +type="layer" id="aggregate" name="My \"Name\"" +type="layer" id="trackwaypoint" name="cool" +type="waypoint" ... +... +type="layerend" +type="layerend" + +Style 2: GPSPoint++ + ++Layer Aggregate +=This Is A "Name" with "Quotes" and WEIRD *(#$% chars + ++Layer TrackWaypoint +=My CoolLayer + +type="waypoint" .. +type="track" + +-EndLayer + +-EndLayer + +comments (#) ignored +each layer parser must return control if line begins with '-' +However, this is to freestyle. + +Style 3: GPSPoint to the next Level + ++Layer Aggregate +=This Is A "Name" + ++Layer Coord +=My Cool Chord Liar + +type="param" name="deg_inc" value="5" + +-EndLayer + +-EndLayer + +I'm a bit foggy on the parsing, does each layer do that? 'cause TRWLayer will have to. +also, if we do that, we may as well use #1 with \\'d quotes as it is more standardized. + +Style #4: Happy-Medium + ++Layer Aggregate +name=This Is A "Name" with funny =-10!@$#* chars + ++Layer TRW +name=My Cool Layer + +draw_tracks=1 +draw_waypoints=0 +data_format=gpspoint + ++LayerData # parsed by Layer itself + +type="waypoint" ... + +-LayerData + +-Layer + ++Layer Coord +name=My Cool Chord Liar + +deg_inc=5 + +-Layer + +-Layer + +Every thing is done by the file parser, EXCEPT LayerData blocks. +Everything else is done by some sort of settable resources. +Layertypes would have to list resources and types. +Unknown resource are simply skipped. +Unknown Layers are simply skipped until the next '-Layer', include their LayerData blocks if they have one. + +comments? + +Issues: + +#1 +One start character would be nice, like +!startlayer +!endlayer + +etc. + +all i can think of for now. + +Style 5: Win Conf Style + +[Layer Type=Aggregate] +name=Hello World + +[Layer Type=TRW] +name=My Cool Layer +draw_tracks=1 +draw_waypoints=0 +data_format=gpspoint +[LayerData] +type=waypoint ... +[EndLayerData] +[EndLayer] + +[Layer Type=Coord] +name=Chord Liar +deg_inc=2 +[EndLayer] + +[EndLayer] diff --git a/doc/dev/FF2-Implementation b/doc/dev/FF2-Implementation new file mode 100644 index 00000000..8c88bb73 --- /dev/null +++ b/doc/dev/FF2-Implementation @@ -0,0 +1,91 @@ + +ff2.c +reads thru thing +if layer, makes new layer of type +adds to aggregate + +looks at line +if starts with '[' creates layer (new interface func?) +fgets line with 'this=this' in +calls + +interface { +... + params { + { "this", PARAM_THIS }, + { "that", PARAM_THAT }, + }, + num params = 2, + +} + +set_param(VikLayer*l,gint type_id,gchar *stuff) { switch(type_id) { +case PARAM_THIS: + layer_set_this(l,stuff); +case PARAM_THAT: + l->that = atol(stuff) +} } + +checks thru params strlen() check and strncmp() +good -> set param + +*now*, what if I could use all this set_param stuff for dynamic dialog box creation? +and default (non-interactive) new layers? + +param name ID explanation type widget type min/min len max/max len default val +colorvelocity VEL lower velocity range CUSTOM CUSTOM +line_thickness THICK Track Line Thickness INT SPINBUTTON 1 15 1 +# on read, if outside range, gives error. + +special stuff like color velocity range? + +union param_val { + gchar *s; + long l; + double d; +} + +color velocity range: +[ ] to [ ] + +so we need a special get_widget_for_param() thingy. +then set_param_from_widget() -> gets widget data does its thing + +tabs? +radio boxes? (names) + +---- + +ff2.c + +typedef struct { + Stack *under; + gpointer data; +} Stack; + +void pop(Stack **stack) { + Stack *tmp = (*stack)->under; + g_free ( *stack ); + *stack = tmp; +} + +void push(Stack **stack) +{ + Stack *tmp = g_malloc ( sizeof ( Stack ) ); + tmp->under = *stack; + *stack = tmp; +} + +/* +read thru file +if "[Layer Type=" + push(&stack) + new default layer of type (str_to_type) (check interface->name) +if "[EndLayer]" + VikLayer *vl = stack->data; + pop(&stack); + vik_aggregate_layer_add_layer(stack->data, vl); +if "[LayerData]" + vik_layer_data ( VIK_LAYER_DATA(stack->data), f, vp ); + +*/ diff --git a/doc/dev/File-Format-Outline b/doc/dev/File-Format-Outline new file mode 100644 index 00000000..cb57a2c9 --- /dev/null +++ b/doc/dev/File-Format-Outline @@ -0,0 +1,118 @@ +WARNING: OLD file format. Will be discarded. Read FF2 +---------- +Magic Number: +6c45e131 + +lE.. + +at beginning of file. I love magic numbers. + +For Serialization, we need to separate realizing a layer and adding it to a aggregate. An aggregate +layer, when realized, should realize all of its children, and add all of its children to the treeview. +An aggregate should realize a layer and add it to the treeview in add_layer only if it is already +realized (we cannot assume that it is.) This will pave the road for serialization. + +Tree + Child + Child + + +START +(TopLayer) +layer type ID +layer name len + layer name +Layer Data. Call Layer serialize-in func. + +(if type == aggregate, len = # of layers) + +Example: + +Top ++ Aggy + + Aggy2 + - TRW + - MAP + - COORD ++ Aggy1 + - TRW + - TRW + - TRW + +File: + +0 (Aggregate) +4 +Aggy +2 (2 Sublayers) + +0 (Aggregate) +5 +Aggy2 +2 (2 Subs) + +1 (TRW) +3 +TRW +(TRW DATA) + +2 (MAP) +3 +MAP +(MAP DATA) + +3 (COORD) +5 +COORD +(COORD DATA) + +1 +5 +Aggy1 +3 (3 subs) + +1 +3 +TRW +100 +(TRW DATA) + +1 +3 +TRW +100 +(TRW DATA) + +1 +3 +TRW +100 +(TRW DATA) + +(END) + +Parsing +Call agg parse function (top layer) with file format. +agg parse function parses, calls appropriate function. +repeats add infinitum. + +---- + +Functions: + +Aggregate Read: +for loop thru # of layers: + read a byte, get id, create a layer of such type via the "serialize_read" func which + returns a layer I can then read. + repeat, enjoy. + +Ahhh, the joys of Recursion. + +Aggregate Write: +write length of layers array. +for each layer: + write ID. + run serialize_write. + + +MAX # of layers: 2^16 + diff --git a/doc/dev/GRRM b/doc/dev/GRRM new file mode 100644 index 00000000..8969e326 --- /dev/null +++ b/doc/dev/GRRM @@ -0,0 +1,12 @@ +Geo Referenced Raster Map Layer (Operation Gremlin) vikgrmlayer.c +Tasks: +*) read and understand World (JGW,etc) files. +*) new layer type, world data inside later, on save written into viking file as JGW/whatever. +*) new layer dialog choose image and type in world data, or button "load from file" +*) reference to map. we sh/could also store whole map in memory. but there also must be a filename to write to viking file. +*) only draws on proper zoom level, or perhaps very very close to proper zoom level +*) right click -> go to this zoom level, goto corner or center of map, export to world file + +Later Versions: +*) edit mode where we can move map, resize (really resize tracks), etc. +*) rotation diff --git a/doc/dev/MergeLayers b/doc/dev/MergeLayers new file mode 100644 index 00000000..916fb29b --- /dev/null +++ b/doc/dev/MergeLayers @@ -0,0 +1,25 @@ +Merge down -> check if another TRWLayer below (HOW) +Actual merge operation: +Take one layer, go thru each waypoint/track of another. +Take old name and value pointers, add to new layer, steal from old layer. +Name conflict: + + ______________________________________________________ +|_____n_a_m_e_____________c_o_n_f_l_i_c_t______________| +| | +| The track 'x' exists in both the top layer ('l1') | +| and the bottom layer ('l2'). What do you want to do? | +| | +| Delete From : top layer ('l1') | +| Rename to | +| [ ] in: | +| Autorename | +| Delete All bottom layer ('l2') | +| Autorename all | ++------------------------------------------------------+ + +TODO +* check if another TRWLayer below (HOW) +* Better/less confusing UI + +UI (name conflict) suggestions would be appreciated! diff --git a/doc/dev/README b/doc/dev/README new file mode 100644 index 00000000..662c2b9f --- /dev/null +++ b/doc/dev/README @@ -0,0 +1,3 @@ +This is a place for me to think out how I am going to implement new features. Comments appreciated. + +UPDATE: 2004-04-13: A lot of the stuff in this directory is historical. diff --git a/doc/dev/Track-Tool-Interfaces b/doc/dev/Track-Tool-Interfaces new file mode 100644 index 00000000..0080b54c --- /dev/null +++ b/doc/dev/Track-Tool-Interfaces @@ -0,0 +1,27 @@ +static struct trwlayer_interface = { + ..., + trwlayer_tools, + sizeof ( trwlayer_tools ) / sizeof ( LayerTool ), /* gshort */ +}; + +static struct trwlayer_tools = { + { "Add Waypoint", trwlayer_add_wp }, + { "Add Track", trwlayer_add_track }, +}; + +---- + +Tools Adding: +ItemFactory args: +1) VikWindow +2) 4-byte variable containing ID and ID of tool. + +struct VikLayerToolId { + gshort layer_id; + gshort tool_id; +}; + +Then we just need to assert (or #warning, etc.) that +sizeof(VikLayerToolId) <= sizeof(gpointer) +OR, I could use guint8 to be on the safe size, for a maximum of 256 +layer types. But I think guint16 will be file. diff --git a/doc/dev/TrackEditing b/doc/dev/TrackEditing new file mode 100644 index 00000000..96c62b01 --- /dev/null +++ b/doc/dev/TrackEditing @@ -0,0 +1,23 @@ +Track editing will be the most powerful yet most difficult to design/implement feature in Viking + +Basic Requirements: +1) Dragging trackpoints by mouse +2) Examining & Setting exact lat/lon (and time) + +Things we need to be able to do: +* Join and separate Tracks, and segments of tracks. +* Delete segments of tracks +* Delete individual segments, preferably both leaving a gap and directly jump to the next point. +* Add on to tracks, on end, middle, or insert trackpoint, with option 'new segment' flag. +* Select track segments to copy & create a new track +* Move/pan whole tracks +* Maybe by hovering over a trackpoint, displays diff in time and space (displacement AND distance + along track) from selected trackpoint + +With these things, we can accommodate most common procedures. For example: user wants to change +one part of a track by going a different route, but rest of track is OK. User: + +1) Loads old track and new data. +2) selects part of old track that is no longer valid, deletes that segment. +3) Clips of any excess data or adds any trackpoints that are needed to join tracks. +4) joins two track segments and new track data together to form one track. diff --git a/src/.compile_config b/src/.compile_config new file mode 100644 index 00000000..d7cead66 --- /dev/null +++ b/src/.compile_config @@ -0,0 +1 @@ +-DXTHREADS -isystem/usr/include/gtk-2.0 -isystem/usr/lib/gtk-2.0/include -isystem/usr/X11R6/include -isystem/usr/include/atk-1.0 -isystem/usr/include/pango-1.0 -isystem/usr/include/freetype2 -isystem/usr/include/glib-2.0 -isystem/usr/lib/glib-2.0/include diff --git a/src/.depend b/src/.depend new file mode 100644 index 00000000..6edeb4f2 --- /dev/null +++ b/src/.depend @@ -0,0 +1,111 @@ +viktrack.o: viktrack.c coords.h vikcoord.h viktrack.h +vikwaypoint.o: vikwaypoint.c coords.h vikcoord.h vikwaypoint.h +clipboard.o: clipboard.c viking.h config.h globals.h coords.h vikcoord.h \ + http.h vikwaypoint.h viktrack.h vikviewport.h viktreeview.h viklayer.h \ + vikaggregatelayer.h viklayerspanel.h vikcoordlayer.h vikgeoreflayer.h \ + vikstatus.h vikfileentry.h viktrwlayer.h clipboard.h dialog.h file.h \ + vikwindow.h gpspoint.h gpsmapper.h +coords.o: coords.c viking.h config.h globals.h coords.h vikcoord.h http.h \ + vikwaypoint.h viktrack.h vikviewport.h viktreeview.h viklayer.h \ + vikaggregatelayer.h viklayerspanel.h vikcoordlayer.h vikgeoreflayer.h \ + vikstatus.h vikfileentry.h viktrwlayer.h clipboard.h dialog.h file.h \ + vikwindow.h gpspoint.h gpsmapper.h +gpsmapper.o: gpsmapper.c viking.h config.h globals.h coords.h vikcoord.h \ + http.h vikwaypoint.h viktrack.h vikviewport.h viktreeview.h viklayer.h \ + vikaggregatelayer.h viklayerspanel.h vikcoordlayer.h vikgeoreflayer.h \ + vikstatus.h vikfileentry.h viktrwlayer.h clipboard.h dialog.h file.h \ + vikwindow.h gpspoint.h gpsmapper.h +gpspoint.o: gpspoint.c viking.h config.h globals.h coords.h vikcoord.h \ + http.h vikwaypoint.h viktrack.h vikviewport.h viktreeview.h viklayer.h \ + vikaggregatelayer.h viklayerspanel.h vikcoordlayer.h vikgeoreflayer.h \ + vikstatus.h vikfileentry.h viktrwlayer.h clipboard.h dialog.h file.h \ + vikwindow.h gpspoint.h gpsmapper.h +file.o: file.c viking.h config.h globals.h coords.h vikcoord.h http.h \ + vikwaypoint.h viktrack.h vikviewport.h viktreeview.h viklayer.h \ + vikaggregatelayer.h viklayerspanel.h vikcoordlayer.h vikgeoreflayer.h \ + vikstatus.h vikfileentry.h viktrwlayer.h clipboard.h dialog.h file.h \ + vikwindow.h gpspoint.h gpsmapper.h +main.o: main.c viking.h config.h globals.h coords.h vikcoord.h http.h \ + vikwaypoint.h viktrack.h vikviewport.h viktreeview.h viklayer.h \ + vikaggregatelayer.h viklayerspanel.h vikcoordlayer.h vikgeoreflayer.h \ + vikstatus.h vikfileentry.h viktrwlayer.h clipboard.h dialog.h file.h \ + vikwindow.h gpspoint.h gpsmapper.h mapcache.h background.h +dialog.o: dialog.c viking.h config.h globals.h coords.h vikcoord.h http.h \ + vikwaypoint.h viktrack.h vikviewport.h viktreeview.h viklayer.h \ + vikaggregatelayer.h viklayerspanel.h vikcoordlayer.h vikgeoreflayer.h \ + vikstatus.h vikfileentry.h viktrwlayer.h clipboard.h dialog.h file.h \ + vikwindow.h gpspoint.h gpsmapper.h thumbnails.h +http.o: http.c http.h +viktreeview.o: viktreeview.c viking.h config.h globals.h coords.h \ + vikcoord.h http.h vikwaypoint.h viktrack.h vikviewport.h viktreeview.h \ + viklayer.h vikaggregatelayer.h viklayerspanel.h vikcoordlayer.h \ + vikgeoreflayer.h vikstatus.h vikfileentry.h viktrwlayer.h clipboard.h \ + dialog.h file.h vikwindow.h gpspoint.h gpsmapper.h +viktrwlayer.o: viktrwlayer.c viking.h config.h globals.h coords.h \ + vikcoord.h http.h vikwaypoint.h viktrack.h vikviewport.h viktreeview.h \ + viklayer.h vikaggregatelayer.h viklayerspanel.h vikcoordlayer.h \ + vikgeoreflayer.h vikstatus.h vikfileentry.h viktrwlayer.h clipboard.h \ + dialog.h file.h vikwindow.h gpspoint.h gpsmapper.h viktrwlayer_pixmap.h \ + viktrwlayer_tpwin.h viktrwlayer_propwin.h thumbnails.h background.h +viklayer.o: viklayer.c viking.h config.h globals.h coords.h vikcoord.h \ + http.h vikwaypoint.h viktrack.h vikviewport.h viktreeview.h viklayer.h \ + vikaggregatelayer.h viklayerspanel.h vikcoordlayer.h vikgeoreflayer.h \ + vikstatus.h vikfileentry.h viktrwlayer.h clipboard.h dialog.h file.h \ + vikwindow.h gpspoint.h gpsmapper.h vikradiogroup.h +viklayerspanel.o: viklayerspanel.c viking.h config.h globals.h coords.h \ + vikcoord.h http.h vikwaypoint.h viktrack.h vikviewport.h viktreeview.h \ + viklayer.h vikaggregatelayer.h viklayerspanel.h vikcoordlayer.h \ + vikgeoreflayer.h vikstatus.h vikfileentry.h viktrwlayer.h clipboard.h \ + dialog.h file.h vikwindow.h gpspoint.h gpsmapper.h +vikcoordlayer.o: vikcoordlayer.c viking.h config.h globals.h coords.h \ + vikcoord.h http.h vikwaypoint.h viktrack.h vikviewport.h viktreeview.h \ + viklayer.h vikaggregatelayer.h viklayerspanel.h vikcoordlayer.h \ + vikgeoreflayer.h vikstatus.h vikfileentry.h viktrwlayer.h clipboard.h \ + dialog.h file.h vikwindow.h gpspoint.h gpsmapper.h \ + vikcoordlayer_pixmap.h +vikstatus.o: vikstatus.c vikstatus.h +vikwindow.o: vikwindow.c viking.h config.h globals.h coords.h vikcoord.h \ + http.h vikwaypoint.h viktrack.h vikviewport.h viktreeview.h viklayer.h \ + vikaggregatelayer.h viklayerspanel.h vikcoordlayer.h vikgeoreflayer.h \ + vikstatus.h vikfileentry.h viktrwlayer.h clipboard.h dialog.h file.h \ + vikwindow.h gpspoint.h gpsmapper.h background.h +vikviewport.o: vikviewport.c coords.h vikcoord.h vikviewport.h mapcoord.h \ + globals.h googlemaps.h +vikaggregatelayer.o: vikaggregatelayer.c viking.h config.h globals.h \ + coords.h vikcoord.h http.h vikwaypoint.h viktrack.h vikviewport.h \ + viktreeview.h viklayer.h vikaggregatelayer.h viklayerspanel.h \ + vikcoordlayer.h vikgeoreflayer.h vikstatus.h vikfileentry.h \ + viktrwlayer.h clipboard.h dialog.h file.h vikwindow.h gpspoint.h \ + gpsmapper.h vikaggregatelayer_pixmap.h +vikgeoreflayer.o: vikgeoreflayer.c viking.h config.h globals.h coords.h \ + vikcoord.h http.h vikwaypoint.h viktrack.h vikviewport.h viktreeview.h \ + viklayer.h vikaggregatelayer.h viklayerspanel.h vikcoordlayer.h \ + vikgeoreflayer.h vikstatus.h vikfileentry.h viktrwlayer.h clipboard.h \ + dialog.h file.h vikwindow.h gpspoint.h gpsmapper.h \ + vikgeoreflayer_pixmap.h +vikfileentry.o: vikfileentry.c vikfileentry.h +viktrwlayer_tpwin.o: viktrwlayer_tpwin.c coords.h vikcoord.h viktrack.h \ + viktrwlayer_tpwin.h vikwaypoint.h dialog.h +viktrwlayer_propwin.o: viktrwlayer_propwin.c coords.h vikcoord.h \ + viktrack.h viktrwlayer_propwin.h vikwaypoint.h dialog.h +thumbnails.o: thumbnails.c viking.h config.h globals.h coords.h \ + vikcoord.h http.h vikwaypoint.h viktrack.h vikviewport.h viktreeview.h \ + viklayer.h vikaggregatelayer.h viklayerspanel.h vikcoordlayer.h \ + vikgeoreflayer.h vikstatus.h vikfileentry.h viktrwlayer.h clipboard.h \ + dialog.h file.h vikwindow.h gpspoint.h gpsmapper.h thumbnails.h \ + thumbnails_pixbuf.h +background.o: background.c background.h gtkcellrendererprogress.h +vikradiogroup.o: vikradiogroup.c vikradiogroup.h +vikcoord.o: vikcoord.c coords.h vikcoord.h +expedia.o: expedia.c globals.h coords.h vikcoord.h mapcoord.h http.h +mapcache.o: mapcache.c mapcache.h config.h +vikmapslayer.o: vikmapslayer.c globals.h coords.h vikcoord.h \ + viktreeview.h config.h vikviewport.h viklayer.h vikmapslayer.h \ + vikmapslayer_pixmap.h mapcache.h vikwaypoint.h dialog.h background.h \ + vikaggregatelayer.h viklayerspanel.h mapcoord.h terraserver.h \ + googlemaps.h expedia.h +terraserver.o: terraserver.c coords.h vikcoord.h mapcoord.h http.h +googlemaps.o: googlemaps.c coords.h vikcoord.h mapcoord.h http.h \ + googlemaps.h +gtkcellrendererprogress.o: gtkcellrendererprogress.c \ + gtkcellrendererprogress.h diff --git a/src/.link_config b/src/.link_config new file mode 100644 index 00000000..173374d6 --- /dev/null +++ b/src/.link_config @@ -0,0 +1 @@ +-Wl,--export-dynamic -pthread -lgtk-x11-2.0 -lgdk-x11-2.0 -latk-1.0 -lgdk_pixbuf-2.0 -lm -lpangoxft-1.0 -lpangox-1.0 -lpango-1.0 -lgobject-2.0 -lgmodule-2.0 -ldl -lgthread-2.0 -lglib-2.0 diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 00000000..96205d03 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,47 @@ +# This is the Makefile for the viking gps viewer program. +# This file is licensed through the GPL version 2 or newer. + +ifeq ($shell uname -o),Cygwin) +CYGWIN = 1 +EXE = .exe +endif + +TARGET=viking$(EXE) +OBJECTS=viktrack.o vikwaypoint.o clipboard.o coords.o gpsmapper.o gpspoint.o file.o main.o dialog.o http.o viktreeview.o viktrwlayer.o viklayer.o viklayerspanel.o vikcoordlayer.o vikstatus.o vikwindow.o vikviewport.o vikaggregatelayer.o vikgeoreflayer.o vikfileentry.o viktrwlayer_tpwin.o viktrwlayer_propwin.o thumbnails.o background.o vikradiogroup.o vikcoord.o expedia.o mapcache.o vikmapslayer.o terraserver.o google.o googlemaps.o gtkcellrendererprogress.o khmaps.o usgs.o + +CCFLAGS = -Wall -g +LINKFLAGS = + +ifdef DEBUG +DFLAGS += -g +endif + +all:: viking + +.PHONY: all clean realclean depend + +-include .depend + +.compile_config: + pkg-config --cflags gtk+-2.0 | sed -e 's/ -I/ -isystem/g' -e 's/^-I/-isystem/g' >$@.tmp + mv $@.tmp $@ +.link_config: + pkg-config --libs gtk+-2.0 gthread-2.0 >$@.tmp + mv $@.tmp $@ + +clean:: + rm -f *.o core $(TARGET).core *.tmp +realclean:: clean + rm -f $(TARGET) .depend .compile_config .link_config + +$(TARGET): $(OBJECTS) .link_config + $(CC) $(LINKFLAGS) -o $@ $(OBJECTS) `cat .link_config` +%.o: %.c .compile_config + $(CC) $(CCFLAGS) -o $@ -c $< `cat .compile_config` + +.depend: .compile_config .link_config + $(CC) $(CFLAGS) -MM -MG $(OBJECTS:.o=.c) `cat .compile_config` >$@.tmp + mv $@.tmp $@ + @echo dependencys got built. +depend:: + @# diff --git a/src/Makefile_GTK_2_2 b/src/Makefile_GTK_2_2 new file mode 100644 index 00000000..749155f0 --- /dev/null +++ b/src/Makefile_GTK_2_2 @@ -0,0 +1,47 @@ +# This is the Makefile for the viking gps viewer program. +# This file is licensed through the GPL version 2 or newer. + +ifeq ($shell uname -o),Cygwin) +CYGWIN = 1 +EXE = .exe +endif + +TARGET=viking$(EXE) +OBJECTS=gtkcolorbutton.o viktrack.o vikwaypoint.o clipboard.o coords.o gpsmapper.o gpspoint.o file.o main.o dialog.o http.o viktreeview.o viktrwlayer.o viklayer.o viklayerspanel.o vikcoordlayer.o vikstatus.o vikwindow.o vikviewport.o vikaggregatelayer.o vikgeoreflayer.o vikfileentry.o viktrwlayer_tpwin.o viktrwlayer_propwin.o thumbnails.o background.o vikradiogroup.o vikcoord.o expedia.o mapcache.o vikmapslayer.o terraserver.o googlemaps.o gtkcellrendererprogress.o + +CCFLAGS = -DGTK_2_2 -Wall -g +LINKFLAGS = + +ifdef DEBUG +DFLAGS += -g +endif + +all:: viking + +.PHONY: all clean realclean depend + +-include .depend + +.compile_config: + pkg-config --cflags gtk+-2.0 | sed -e 's/ -I/ -isystem/g' -e 's/^-I/-isystem/g' >$@.tmp + mv $@.tmp $@ +.link_config: + pkg-config --libs gtk+-2.0 gthread-2.0 >$@.tmp + mv $@.tmp $@ + +clean:: + rm -f *.o core $(TARGET).core *.tmp +realclean:: clean + rm -f $(TARGET) .depend .compile_config .link_config + +$(TARGET): $(OBJECTS) .link_config + $(CC) $(LINKFLAGS) -o $@ $(OBJECTS) `cat .link_config` +%.o: %.c .compile_config + $(CC) $(CCFLAGS) -o $@ -c $< `cat .compile_config` + +.depend: .compile_config .link_config + $(CC) $(CFLAGS) -MM -MG $(OBJECTS:.o=.c) `cat .compile_config` >$@.tmp + mv $@.tmp $@ + @echo dependencys got built. +depend:: + @# diff --git a/src/Makefile_windows b/src/Makefile_windows new file mode 100644 index 00000000..ba389945 --- /dev/null +++ b/src/Makefile_windows @@ -0,0 +1,47 @@ +# This is the Makefile for the viking gps viewer program. +# This file is licensed through the GPL version 2 or newer. + +ifeq ($shell uname -o),Cygwin) +CYGWIN = 1 +EXE = .exe +endif + +TARGET=viking$(EXE) +OBJECTS=viktrack.o vikwaypoint.o clipboard.o coords.o gpsmapper.o gpspoint.o file.o main.o dialog.o http.o viktreeview.o viktrwlayer.o viklayer.o viklayerspanel.o vikcoordlayer.o vikstatus.o vikwindow.o vikviewport.o vikaggregatelayer.o vikgeoreflayer.o vikfileentry.o viktrwlayer_tpwin.o viktrwlayer_propwin.o thumbnails.o background.o vikradiogroup.o vikcoord.o expedia.o mapcache.o vikmapslayer.o terraserver.o googlemaps.o gtkcellrendererprogress.o + +CCFLAGS = -DWINDOWS -mms-bitfields -Wall -g +LINKFLAGS = -lwsock32 + +ifdef DEBUG +DFLAGS += -g +endif + +all:: viking + +.PHONY: all clean realclean depend + +-include .depend + +.compile_config: + pkg-config --cflags gtk+-2.0 | sed -e 's/ -I/ -isystem/g' -e 's/^-I/-isystem/g' >$@.tmp + mv $@.tmp $@ +.link_config: + pkg-config --libs gtk+-2.0 gthread-2.0 >$@.tmp + mv $@.tmp $@ + +clean:: + rm -f *.o core $(TARGET).core *.tmp +realclean:: clean + rm -f $(TARGET) .depend .compile_config .link_config + +$(TARGET): $(OBJECTS) .link_config + $(CC) $(LINKFLAGS) -o $@ $(OBJECTS) `cat .link_config` +%.o: %.c .compile_config + $(CC) $(CCFLAGS) -o $@ -c $< `cat .compile_config` + +.depend: .compile_config .link_config + $(CC) $(CFLAGS) -MM -MG $(OBJECTS:.o=.c) `cat .compile_config` >$@.tmp + mv $@.tmp $@ + @echo dependencys got built. +depend:: + @# diff --git a/src/background.c b/src/background.c new file mode 100644 index 00000000..e83ff645 --- /dev/null +++ b/src/background.c @@ -0,0 +1,226 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include "vikstatus.h" +#include "background.h" +#include "gtkcellrendererprogress.h" + +static GtkWidget *bgwindow = NULL; +static GtkWidget *bgtreeview = NULL; +static GtkListStore *bgstore = NULL; + +static GSList *statusbars_to_update = NULL; + +static gint bgitemcount = 0; + +enum +{ + TITLE_COLUMN, + PROGRESS_COLUMN, + DATA_COLUMN, + N_COLUMNS, +}; + +void a_background_update_status ( VikStatusbar *vs, gchar *str ) +{ + gdk_threads_enter (); + vik_statusbar_set_message ( vs, 1, str ); + gdk_threads_leave (); +} + +static void background_thread_update () +{ + static gchar buf[20]; + g_snprintf(buf, sizeof(buf), "%d items", bgitemcount); + g_slist_foreach ( statusbars_to_update, (GFunc) a_background_update_status, buf ); +} + +void a_background_thread_progress ( gpointer callbackdata, gdouble fraction ) +{ + gpointer *args = (gpointer *) callbackdata; + a_background_testcancel ( callbackdata ); + gdk_threads_enter(); + gtk_list_store_set( GTK_LIST_STORE(bgstore), (GtkTreeIter *) args[5], PROGRESS_COLUMN, fraction, -1 ); + gdk_threads_leave(); + + args[6] = GINT_TO_POINTER(GPOINTER_TO_INT(args[6])-1); + bgitemcount--; + background_thread_update(); +} + +static void thread_die ( gpointer args[6] ) +{ + vik_thr_free_func userdata_free_func = args[3]; + + userdata_free_func ( args[2] ); + + if ( GPOINTER_TO_INT(args[6]) ) + { + bgitemcount -= GPOINTER_TO_INT(args[6]); + background_thread_update (); + } + + g_free ( args[5] ); /* free iter */ + g_free ( args ); + + g_thread_exit ( NULL ); +} + +void a_background_testcancel ( gpointer callbackdata ) +{ + gpointer *args = (gpointer *) callbackdata; + if ( args[0] ) + { + vik_thr_free_func cleanup = args[4]; + if ( cleanup ) + cleanup ( args[2] ); + thread_die( args ); + } +} + +void thread_helper ( gpointer args[6] ) +{ + /* unpack args */ + vik_thr_func func = args[1]; + gpointer userdata = args[2]; + + func ( userdata, args ); + + /* need MUTEX ? */ + gdk_threads_enter(); + if ( ! args[0] ) + gtk_list_store_remove ( bgstore, (GtkTreeIter *) args[5] ); + gdk_threads_leave(); + + thread_die ( args ); +} + +void a_background_thread ( GtkWindow *parent, const gchar *message, vik_thr_func func, gpointer userdata, vik_thr_free_func userdata_free_func, vik_thr_free_func userdata_cancel_cleanup_func, gint number_items ) +{ + GtkTreeIter *piter = g_malloc ( sizeof ( GtkTreeIter ) ); + gpointer *args = g_malloc ( sizeof(gpointer) * 7 ); + + args[0] = GINT_TO_POINTER(0); + args[1] = func; + args[2] = userdata; + args[3] = userdata_free_func; + args[4] = userdata_cancel_cleanup_func; + args[5] = piter; + args[6] = GINT_TO_POINTER(number_items); + + bgitemcount += number_items; + + gtk_list_store_append ( bgstore, piter ); + gtk_list_store_set ( bgstore, piter, TITLE_COLUMN, message, PROGRESS_COLUMN, 0.0, DATA_COLUMN, args, -1 ); + + /* run the thread in the background */ + g_thread_create( (GThreadFunc) thread_helper, args, FALSE, NULL ); +} + +void a_background_show_window () +{ + gtk_widget_show_all ( bgwindow ); +} + +static void cancel_job_with_iter ( GtkTreeIter *piter ) +{ + gpointer *args; + gtk_tree_model_get( GTK_TREE_MODEL(bgstore), piter, DATA_COLUMN, &args, -1 ); + + /* we know args still exists because it is free _after_ the list item is destroyed */ + /* need MUTEX ? */ + args[0] = GINT_TO_POINTER(1); /* set killswitch */ + + bgitemcount -= GPOINTER_TO_INT(args[6]); + + gtk_list_store_remove ( bgstore, piter ); +} + +static void bgwindow_response (GtkDialog *dialog, gint arg1 ) +{ + if ( arg1 == 1 ) /* cancel */ + { + GtkTreeIter iter; + if ( gtk_tree_selection_get_selected ( gtk_tree_view_get_selection ( GTK_TREE_VIEW(bgtreeview) ), NULL, &iter ) ) + cancel_job_with_iter ( &iter ); + background_thread_update(); + } + else if ( arg1 == 2 ) /* clear */ + { + GtkTreeIter iter; + while ( gtk_tree_model_get_iter_first ( GTK_TREE_MODEL(bgstore), &iter ) ) + cancel_job_with_iter ( &iter ); + bgitemcount = 0; + background_thread_update(); + } + else /* OK */ + gtk_widget_hide ( bgwindow ); +} + +void a_background_init() +{ + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + GtkWidget *scrolled_window; + + /* store & treeview */ + bgstore = gtk_list_store_new ( N_COLUMNS, G_TYPE_STRING, G_TYPE_DOUBLE, G_TYPE_POINTER ); + bgtreeview = gtk_tree_view_new_with_model ( GTK_TREE_MODEL(bgstore) ); + gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (bgtreeview), TRUE); + gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (bgtreeview)), + GTK_SELECTION_SINGLE); + + /* add columns */ + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ( "Job", renderer, "text", TITLE_COLUMN, NULL ); + gtk_tree_view_append_column ( GTK_TREE_VIEW(bgtreeview), column ); + + renderer = gtk_cell_renderer_progress_new (); + column = gtk_tree_view_column_new_with_attributes ( "Progress", renderer, "percentage", PROGRESS_COLUMN, NULL ); + gtk_tree_view_append_column ( GTK_TREE_VIEW(bgtreeview), column ); + + /* setup window */ + scrolled_window = gtk_scrolled_window_new ( NULL, NULL ); + gtk_container_add ( GTK_CONTAINER(scrolled_window), bgtreeview ); + gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW(scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC ); + + bgwindow = gtk_dialog_new_with_buttons ( "", NULL, 0, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_DELETE, 1, GTK_STOCK_CLEAR, 2, NULL ); + gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(bgwindow)->vbox), scrolled_window, TRUE, TRUE, 0 ); + gtk_window_set_default_size ( GTK_WINDOW(bgwindow), 400, 400 ); + gtk_window_set_title ( GTK_WINDOW(bgwindow), "Viking Background Jobs" ); + /* don't destroy win */ + g_signal_connect ( G_OBJECT(bgwindow), "delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL ); + + g_signal_connect ( G_OBJECT(bgwindow), "response", G_CALLBACK(bgwindow_response), 0 ); + +} + +void a_background_add_status(VikStatusbar *vs) +{ + statusbars_to_update = g_slist_prepend(statusbars_to_update,vs); +} + +void a_background_remove_status(VikStatusbar *vs) +{ + statusbars_to_update = g_slist_remove(statusbars_to_update,vs); +} + diff --git a/src/background.h b/src/background.h new file mode 100644 index 00000000..0e474973 --- /dev/null +++ b/src/background.h @@ -0,0 +1,37 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __VIKING_BACKGROUND_H +#define __VIKING_BACKGROUND_H + +typedef void(*vik_thr_free_func)(gpointer); +typedef void(*vik_thr_func)(gpointer,gpointer); + +/* the new way */ +void a_background_thread ( GtkWindow *parent, const gchar *message, vik_thr_func func, gpointer userdata, vik_thr_free_func userdata_free_func, vik_thr_free_func userdata_cancel_cleanup_func, gint number_items ); +void a_background_thread_progress ( gpointer callbackdata, gdouble fraction ); +void a_background_testcancel ( gpointer callbackdata ); +void a_background_show_window (); +void a_background_init (); +void a_background_add_status(VikStatusbar *vs); +void a_background_remove_status(VikStatusbar *vs); + +#endif diff --git a/src/clipboard.c b/src/clipboard.c new file mode 100644 index 00000000..5b6d97f8 --- /dev/null +++ b/src/clipboard.c @@ -0,0 +1,107 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "viking.h" + +/* static for a very good reason -- share between windows */ + +#define DATA_NONE 0 +#define DATA_LAYER 1 +#define DATA_SUBLAYER 2 + +guint8 type = DATA_NONE; +gint subtype; +guint16 layer_type; +gpointer clipboard; + +void a_clipboard_copy ( VikLayersPanel *vlp ) +{ + VikLayer *sel = vik_layers_panel_get_selected ( vlp ); + GtkTreeIter iter; + if ( ! sel ) + return; + + vik_treeview_get_selected_iter ( sel->vt, &iter ); + + if ( clipboard ) + a_clipboard_uninit(); + + if ( vik_treeview_item_get_type ( sel->vt, &iter ) == VIK_TREEVIEW_TYPE_SUBLAYER ) + { + layer_type = sel->type; + if ( vik_layer_get_interface(layer_type)->copy_item && (clipboard = vik_layer_get_interface(layer_type)-> + copy_item(sel,subtype=vik_treeview_item_get_data(sel->vt,&iter),vik_treeview_item_get_pointer(sel->vt,&iter)) )) + type = DATA_SUBLAYER; + } + else + { + clipboard = sel; + g_object_ref ( G_OBJECT(sel) ); + type = DATA_LAYER; + } +} + +gboolean a_clipboard_paste ( VikLayersPanel *vlp ) +{ + if ( clipboard && type == DATA_LAYER ) + { + /* oooh, _private_ ... so sue me, there's no other way to do this. */ + if ( G_OBJECT(clipboard)->ref_count == 1 ) + { /* optimization -- if layer has been deleted, don't copy the layer. */ + g_object_ref ( G_OBJECT(clipboard) ); + vik_layers_panel_add_layer ( vlp, VIK_LAYER(clipboard) ); + return TRUE; + } + else + { + VikLayer *new_layer = vik_layer_copy ( VIK_LAYER(clipboard), vik_layers_panel_get_viewport(vlp) ); + if ( new_layer ) + { + vik_layers_panel_add_layer ( vlp, new_layer ); + return TRUE; + } + } + } + else if ( clipboard && type == DATA_SUBLAYER ) + { + VikLayer *sel = vik_layers_panel_get_selected ( vlp ); + if ( sel && sel->type == layer_type) + { + if ( vik_layer_get_interface(layer_type)->paste_item ) + return vik_layer_get_interface(layer_type)->paste_item ( sel, subtype, clipboard ); + } + else + a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_WIDGET(GTK_WIDGET(vlp)), "The clipboard contains sublayer data for a %s layers. You must select a layer of this type to paste the clipboard data.", vik_layer_get_interface(layer_type)->name ); + return FALSE; + } + return FALSE; +} + +void a_clipboard_uninit () +{ + if ( clipboard && type == DATA_LAYER ) + g_object_unref ( G_OBJECT(clipboard) ); + else if ( clipboard && type == DATA_SUBLAYER ) + if ( vik_layer_get_interface(layer_type)->free_copied_item ) + vik_layer_get_interface(layer_type)->free_copied_item(subtype,clipboard); + clipboard = NULL; + type = DATA_NONE; +} diff --git a/src/clipboard.h b/src/clipboard.h new file mode 100644 index 00000000..56e6a648 --- /dev/null +++ b/src/clipboard.h @@ -0,0 +1,29 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _VIKING_CLIPBOARD_H +#define _VIKING_CLIPBOARD_H + +void a_clipboard_copy ( VikLayersPanel *vlp ); +gboolean a_clipboard_paste ( VikLayersPanel *vlp ); +void a_clipboard_uninit (); + +#endif diff --git a/src/config.h b/src/config.h new file mode 100644 index 00000000..43094736 --- /dev/null +++ b/src/config.h @@ -0,0 +1,11 @@ +#ifndef VIK_CONFIG_H +#define VIK_CONFIG_H + +#define VIK_CONFIG_ALPHABETIZED_TRW +// #define VIK_CONFIG_MAPCACHE_SIZE 50331648 /* 48 meggerbytes */ +#define VIK_CONFIG_MAPCACHE_SIZE 1331648 /* 48 meggerbytes */ + + +/* suggestions for what else you want here? */ + +#endif diff --git a/src/coords.c b/src/coords.c new file mode 100644 index 00000000..df2391bc --- /dev/null +++ b/src/coords.c @@ -0,0 +1,224 @@ +/* +coords.c +borrowed from: +http://acme.com/software/coords/ +I (Evan Battaglia ) have only made some small changes such as +renaming functions and defining LatLon and UTM structs. +2004-02-10 -- I also added a function of my own -- a_coords_utm_diff() -- that I felt belonged in coords.c +2004-02-21 -- I also added a_coords_utm_equal(). +*/ +/* coords.h - include file for coords routines +** +** Copyright © 2001 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#include +#include +#include + +#include "viking.h" + +#ifdef WINDOWS +#define M_PI 3.14159265358979 +#endif + +#define PIOVER180 0.01745329252 + +#define K0 0.9996 + +/* WGS-84 */ +#define EquatorialRadius 6378137 +#define EccentricitySquared 0.00669438 + +static char coords_utm_letter( double latitude ); + +int a_coords_utm_equal( const struct UTM *utm1, const struct UTM *utm2 ) +{ + return ( utm1->easting == utm2->easting && utm1->northing == utm2->northing && utm1->zone == utm2->zone ); +} + +double a_coords_utm_diff( const struct UTM *utm1, const struct UTM *utm2 ) +{ + static struct LatLon tmp1, tmp2; + if ( utm1->zone == utm2->zone ) { + return sqrt ( pow ( utm1->easting - utm2->easting, 2 ) + pow ( utm1->northing - utm2->northing, 2 ) ); + } else { + a_coords_utm_to_latlon ( utm1, &tmp1 ); + a_coords_utm_to_latlon ( utm2, &tmp2 ); + return a_coords_latlon_diff ( &tmp1, &tmp2 ); + } +} + +double a_coords_latlon_diff ( const struct LatLon *ll1, const struct LatLon *ll2 ) +{ + static struct LatLon tmp1, tmp2; + tmp1.lat = ll1->lat * PIOVER180; + tmp1.lon = ll1->lon * PIOVER180; + tmp2.lat = ll2->lat * PIOVER180; + tmp2.lon = ll2->lon * PIOVER180; + return EquatorialRadius * acos(sin(tmp1.lat)*sin(tmp2.lat)+cos(tmp1.lat)*cos(tmp2.lat)*cos(tmp1.lon-tmp2.lon)); +} + +void a_coords_latlon_to_utm( const struct LatLon *latlon, struct UTM *utm ) + { + double latitude; + double longitude; + double lat_rad, long_rad; + double long_origin, long_origin_rad; + double eccPrimeSquared; + double N, T, C, A, M; + int zone; + double northing, easting; + + longitude = latlon->lon; + latitude = latlon->lat; + + /* We want the longitude within -180..180. */ + if ( longitude < -180.0 ) + longitude += 360.0; + if ( longitude > 180.0 ) + longitude -= 360.0; + + /* Now convert. */ + lat_rad = latitude * M_PI / 180.0; + long_rad = longitude * M_PI / 180.0; + zone = (int) ( ( longitude + 180 ) / 6 ) + 1; + if ( latitude >= 56.0 && latitude < 64.0 && + longitude >= 3.0 && longitude < 12.0 ) + zone = 32; + /* Special zones for Svalbard. */ + if ( latitude >= 72.0 && latitude < 84.0 ) + { + if ( longitude >= 0.0 && longitude < 9.0 ) zone = 31; + else if ( longitude >= 9.0 && longitude < 21.0 ) zone = 33; + else if ( longitude >= 21.0 && longitude < 33.0 ) zone = 35; + else if ( longitude >= 33.0 && longitude < 42.0 ) zone = 37; + } + long_origin = ( zone - 1 ) * 6 - 180 + 3; /* +3 puts origin in middle of zone */ + long_origin_rad = long_origin * M_PI / 180.0; + eccPrimeSquared = EccentricitySquared / ( 1.0 - EccentricitySquared ); + N = EquatorialRadius / sqrt( 1.0 - EccentricitySquared * sin( lat_rad ) * sin( lat_rad ) ); + T = tan( lat_rad ) * tan( lat_rad ); + C = eccPrimeSquared * cos( lat_rad ) * cos( lat_rad ); + A = cos( lat_rad ) * ( long_rad - long_origin_rad ); + M = EquatorialRadius * ( ( 1.0 - EccentricitySquared / 4 - 3 * EccentricitySquared * EccentricitySquared / 64 - 5 * EccentricitySquared * EccentricitySquared * EccentricitySquared / 256 ) * lat_rad - ( 3 * EccentricitySquared / 8 + 3 * EccentricitySquared * EccentricitySquared / 32 + 45 * EccentricitySquared * EccentricitySquared * EccentricitySquared / 1024 ) * sin( 2 * lat_rad ) + ( 15 * EccentricitySquared * EccentricitySquared / 256 + 45 * EccentricitySquared * EccentricitySquared * EccentricitySquared / 1024 ) * sin( 4 * lat_rad ) - ( 35 * EccentricitySquared * EccentricitySquared * EccentricitySquared / 3072 ) * sin( 6 * lat_rad ) ); + easting = + K0 * N * ( A + ( 1 - T + C ) * A * A * A / 6 + ( 5 - 18 * T + T * T + 72 * C - 58 * eccPrimeSquared ) * A * A * A * A * A / 120 ) + 500000.0; + northing = + K0 * ( M + N * tan( lat_rad ) * ( A * A / 2 + ( 5 - T + 9 * C + 4 * C * C ) * A * A * A * A / 24 + ( 61 - 58 * T + T * T + 600 * C - 330 * eccPrimeSquared ) * A * A * A * A * A * A / 720 ) ); + if ( latitude < 0.0 ) + northing += 10000000.0; /* 1e7 meter offset for southern hemisphere */ + + utm->northing = northing; + utm->easting = easting; + utm->zone = zone; + utm->letter = coords_utm_letter( latitude ); + + /* All done. */ + } + + +static char coords_utm_letter( double latitude ) + { + /* This routine determines the correct UTM letter designator for the + ** given latitude. It returns 'Z' if the latitude is outside the UTM + ** limits of 84N to 80S. + */ + if ( latitude <= 84.0 && latitude >= 72.0 ) return 'X'; + else if ( latitude < 72.0 && latitude >= 64.0 ) return 'W'; + else if ( latitude < 64.0 && latitude >= 56.0 ) return 'V'; + else if ( latitude < 56.0 && latitude >= 48.0 ) return 'U'; + else if ( latitude < 48.0 && latitude >= 40.0 ) return 'T'; + else if ( latitude < 40.0 && latitude >= 32.0 ) return 'S'; + else if ( latitude < 32.0 && latitude >= 24.0 ) return 'R'; + else if ( latitude < 24.0 && latitude >= 16.0 ) return 'Q'; + else if ( latitude < 16.0 && latitude >= 8.0 ) return 'P'; + else if ( latitude < 8.0 && latitude >= 0.0 ) return 'N'; + else if ( latitude < 0.0 && latitude >= -8.0 ) return 'M'; + else if ( latitude < -8.0 && latitude >= -16.0 ) return 'L'; + else if ( latitude < -16.0 && latitude >= -24.0 ) return 'K'; + else if ( latitude < -24.0 && latitude >= -32.0 ) return 'J'; + else if ( latitude < -32.0 && latitude >= -40.0 ) return 'H'; + else if ( latitude < -40.0 && latitude >= -48.0 ) return 'G'; + else if ( latitude < -48.0 && latitude >= -56.0 ) return 'F'; + else if ( latitude < -56.0 && latitude >= -64.0 ) return 'E'; + else if ( latitude < -64.0 && latitude >= -72.0 ) return 'D'; + else if ( latitude < -72.0 && latitude >= -80.0 ) return 'C'; + else return 'Z'; + } + + + +void a_coords_utm_to_latlon( const struct UTM *utm, struct LatLon *latlon ) + { + double northing, easting; + int zone; + char letter[100]; + double x, y; + double eccPrimeSquared; + double e1; + double N1, T1, C1, R1, D, M; + double long_origin; + double mu, phi1_rad; + int northernHemisphere; /* 1 for northern hemisphere, 0 for southern */ + double latitude, longitude; + + northing = utm->northing; + easting = utm->easting; + zone = utm->zone; + letter[0] = utm->letter; + + /* Now convert. */ + x = easting - 500000.0; /* remove 500000 meter offset */ + y = northing; + if ( ( *letter - 'N' ) >= 0 ) + northernHemisphere = 1; /* northern hemisphere */ + else + { + northernHemisphere = 0; /* southern hemisphere */ + y -= 10000000.0; /* remove 1e7 meter offset */ + } + long_origin = ( zone - 1 ) * 6 - 180 + 3; /* +3 puts origin in middle of zone */ + eccPrimeSquared = EccentricitySquared / ( 1.0 - EccentricitySquared ); + e1 = ( 1.0 - sqrt( 1.0 - EccentricitySquared ) ) / ( 1.0 + sqrt( 1.0 - EccentricitySquared ) ); + M = y / K0; + mu = M / ( EquatorialRadius * ( 1.0 - EccentricitySquared / 4 - 3 * EccentricitySquared * EccentricitySquared / 64 - 5 * EccentricitySquared * EccentricitySquared * EccentricitySquared / 256 ) ); + phi1_rad = mu + ( 3 * e1 / 2 - 27 * e1 * e1 * e1 / 32 )* sin( 2 * mu ) + ( 21 * e1 * e1 / 16 - 55 * e1 * e1 * e1 * e1 / 32 ) * sin( 4 * mu ) + ( 151 * e1 * e1 * e1 / 96 ) * sin( 6 *mu ); + N1 = EquatorialRadius / sqrt( 1.0 - EccentricitySquared * sin( phi1_rad ) * sin( phi1_rad ) ); + T1 = tan( phi1_rad ) * tan( phi1_rad ); + C1 = eccPrimeSquared * cos( phi1_rad ) * cos( phi1_rad ); + R1 = EquatorialRadius * ( 1.0 - EccentricitySquared ) / pow( 1.0 - EccentricitySquared * sin( phi1_rad ) * sin( phi1_rad ), 1.5 ); + D = x / ( N1 * K0 ); + latitude = phi1_rad - ( N1 * tan( phi1_rad ) / R1 ) * ( D * D / 2 -( 5 + 3 * T1 + 10 * C1 - 4 * C1 * C1 - 9 * eccPrimeSquared ) * D * D * D * D / 24 + ( 61 + 90 * T1 + 298 * C1 + 45 * T1 * T1 - 252 * eccPrimeSquared - 3 * C1 * C1 ) * D * D * D * D * D * D / 720 ); + latitude = latitude * 180.0 / M_PI; + longitude = ( D - ( 1 + 2 * T1 + C1 ) * D * D * D / 6 + ( 5 - 2 * C1 + 28 * T1 - 3 * C1 * C1 + 8 * eccPrimeSquared + 24 * T1 * T1 ) * D * D * D * D * D / 120 ) / cos( phi1_rad ); + longitude = long_origin + longitude * 180.0 / M_PI; + + /* Show results. */ + + latlon->lat = latitude; + latlon->lon = longitude; + + } diff --git a/src/coords.h b/src/coords.h new file mode 100644 index 00000000..baaf21aa --- /dev/null +++ b/src/coords.h @@ -0,0 +1,57 @@ +/* +coords.h +borrowed from: +http://acme.com/software/coords/ +I (Evan Battaglia have only made some small changes such as +renaming functions and defining LatLon and UTM structs. +*/ +/* coords.h - include file for coords routines +** +** Copyright © 2001 by Jef Poskanzer . +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +*/ + +#ifndef _VIKING_COORDS_H +#define _VIKING_COORDS_H + +struct UTM { + gdouble northing; + gdouble easting; + gchar zone; + gchar letter; +}; + +struct LatLon { + gdouble lat; + gdouble lon; +}; + +int a_coords_utm_equal( const struct UTM *utm1, const struct UTM *utm2 ); +void a_coords_latlon_to_utm ( const struct LatLon *latlon, struct UTM *utm ); +void a_coords_utm_to_latlon ( const struct UTM *utm, struct LatLon *latlon ); +double a_coords_utm_diff( const struct UTM *utm1, const struct UTM *utm2 ); +double a_coords_latlon_diff ( const struct LatLon *ll1, const struct LatLon *ll2 ); + + +#endif diff --git a/src/dialog.c b/src/dialog.c new file mode 100644 index 00000000..e89c640c --- /dev/null +++ b/src/dialog.c @@ -0,0 +1,433 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "viking.h" +#include "thumbnails.h" + +#include +#include +#include + +void a_dialog_msg ( GtkWindow *parent, gint type, const gchar *info, const gchar *extra ) +{ + GtkWidget *msgbox = gtk_message_dialog_new ( parent, GTK_DIALOG_DESTROY_WITH_PARENT, type, GTK_BUTTONS_OK, info, extra ); + gtk_dialog_run ( GTK_DIALOG(msgbox) ); + gtk_widget_destroy ( msgbox ); +} + +gboolean a_dialog_goto_latlon ( GtkWindow *parent, struct LatLon *ll, const struct LatLon *old ) +{ + GtkWidget *dialog = gtk_dialog_new_with_buttons ("Go to Lat/Lon", + parent, + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_CANCEL, + GTK_RESPONSE_REJECT, + GTK_STOCK_OK, + GTK_RESPONSE_ACCEPT, + NULL); + GtkWidget *latlabel, *lonlabel; + GtkWidget *lat, *lon; + gchar *tmp_lat, *tmp_lon; + + latlabel = gtk_label_new ("Latitude:"); + lat = gtk_entry_new (); + tmp_lat = g_strdup_printf ( "%f", old->lat ); + gtk_entry_set_text ( GTK_ENTRY(lat), tmp_lat ); + g_free ( tmp_lat ); + + lonlabel = gtk_label_new ("Longitude:"); + lon = gtk_entry_new (); + tmp_lon = g_strdup_printf ( "%f", old->lon ); + gtk_entry_set_text ( GTK_ENTRY(lon), tmp_lon ); + g_free ( tmp_lon ); + + gtk_widget_show ( latlabel ); + gtk_widget_show ( lonlabel ); + gtk_widget_show ( lat ); + gtk_widget_show ( lon ); + + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), latlabel, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), lat, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), lonlabel, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), lon, FALSE, FALSE, 0); + + if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) + { + ll->lat = atof ( gtk_entry_get_text ( GTK_ENTRY(lat) ) ); + ll->lon = atof ( gtk_entry_get_text ( GTK_ENTRY(lon) ) ); + gtk_widget_destroy ( dialog ); + return TRUE; + } + + gtk_widget_destroy ( dialog ); + return FALSE; +} + +gboolean a_dialog_goto_utm ( GtkWindow *parent, struct UTM *utm, const struct UTM *old ) +{ + GtkWidget *dialog = gtk_dialog_new_with_buttons ("Go to Lat/Lon", + parent, + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_CANCEL, + GTK_RESPONSE_REJECT, + GTK_STOCK_OK, + GTK_RESPONSE_ACCEPT, + NULL); + GtkWidget *norlabel, *easlabel, *nor, *eas; + GtkWidget *zonehbox, *zonespin, *letterentry; + gchar *tmp_eas, *tmp_nor; + gchar tmp_letter[2]; + + norlabel = gtk_label_new ("Northing:"); + nor = gtk_entry_new (); + tmp_nor = g_strdup_printf("%ld", (long) old->northing ); + gtk_entry_set_text ( GTK_ENTRY(nor), tmp_nor ); + g_free ( tmp_nor ); + + easlabel = gtk_label_new ("Easting:"); + eas = gtk_entry_new (); + tmp_eas = g_strdup_printf("%ld", (long) old->easting ); + gtk_entry_set_text ( GTK_ENTRY(eas), tmp_eas ); + g_free ( tmp_eas ); + + zonehbox = gtk_hbox_new ( FALSE, 0 ); + gtk_box_pack_start ( GTK_BOX(zonehbox), gtk_label_new ( "Zone:" ), FALSE, FALSE, 5 ); + zonespin = gtk_spin_button_new ( (GtkAdjustment *) gtk_adjustment_new ( old->zone, 1, 60, 1, 5, 5 ), 1, 0 ); + gtk_box_pack_start ( GTK_BOX(zonehbox), zonespin, TRUE, TRUE, 5 ); + gtk_box_pack_start ( GTK_BOX(zonehbox), gtk_label_new ( "Letter:" ), FALSE, FALSE, 5 ); + letterentry = gtk_entry_new (); + gtk_entry_set_max_length ( GTK_ENTRY(letterentry), 1 ); + gtk_entry_set_width_chars ( GTK_ENTRY(letterentry), 2 ); + tmp_letter[0] = old->letter; + tmp_letter[1] = '\0'; + gtk_entry_set_text ( GTK_ENTRY(letterentry), tmp_letter ); + gtk_box_pack_start ( GTK_BOX(zonehbox), letterentry, FALSE, FALSE, 5 ); + + gtk_widget_show ( norlabel ); + gtk_widget_show ( easlabel ); + gtk_widget_show ( nor ); + gtk_widget_show ( eas ); + + gtk_widget_show_all ( zonehbox ); + + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), norlabel, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), nor, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), easlabel, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), eas, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), zonehbox, FALSE, FALSE, 0); + + if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) + { + const gchar *letter; + utm->northing = atof ( gtk_entry_get_text ( GTK_ENTRY(nor) ) ); + utm->easting = atof ( gtk_entry_get_text ( GTK_ENTRY(eas) ) ); + utm->zone = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(zonespin) ); + letter = gtk_entry_get_text ( GTK_ENTRY(letterentry) ); + if (*letter) + utm->letter = toupper(*letter); + gtk_widget_destroy ( dialog ); + return TRUE; + } + + gtk_widget_destroy ( dialog ); + return FALSE; +} + +void a_dialog_response_accept ( GtkDialog *dialog ) +{ + gtk_dialog_response ( dialog, GTK_RESPONSE_ACCEPT ); +} + +/* todo: less on this side, like add track */ +gboolean a_dialog_new_waypoint ( GtkWindow *parent, gchar **dest, VikWaypoint *wp, GHashTable *waypoints, VikCoordMode coord_mode ) +{ + GtkWidget *dialog = gtk_dialog_new_with_buttons ("Create", + parent, + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_CANCEL, + GTK_RESPONSE_REJECT, + GTK_STOCK_OK, + GTK_RESPONSE_ACCEPT, + NULL); + struct LatLon ll; + GtkWidget *latlabel, *lonlabel, *namelabel, *latentry, *lonentry, *altentry, *altlabel, *nameentry, *commentlabel, *commententry, *imagelabel, *imageentry; + + gchar *lat, *lon, *alt; + + vik_coord_to_latlon ( &(wp->coord), &ll ); + + lat = g_strdup_printf ( "%f", ll.lat ); + lon = g_strdup_printf ( "%f", ll.lon ); + alt = g_strdup_printf ( "%f", wp->altitude ); + + if ( dest != NULL ) + { + namelabel = gtk_label_new ("Name:"); + nameentry = gtk_entry_new (); + g_signal_connect_swapped ( nameentry, "activate", G_CALLBACK(a_dialog_response_accept), GTK_DIALOG(dialog) ); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), namelabel, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), nameentry, FALSE, FALSE, 0); + } + + latlabel = gtk_label_new ("Latitude:"); + latentry = gtk_entry_new (); + gtk_entry_set_text ( GTK_ENTRY(latentry), lat ); + g_free ( lat ); + + lonlabel = gtk_label_new ("Longitude:"); + lonentry = gtk_entry_new (); + gtk_entry_set_text ( GTK_ENTRY(lonentry), lon ); + g_free ( lon ); + + altlabel = gtk_label_new ("Altitude:"); + altentry = gtk_entry_new (); + gtk_entry_set_text ( GTK_ENTRY(altentry), alt ); + g_free ( alt ); + + commentlabel = gtk_label_new ("Comment:"); + commententry = gtk_entry_new (); + + imagelabel = gtk_label_new ("Image:"); + imageentry = vik_file_entry_new (); + + if ( dest == NULL && wp->comment ) + gtk_entry_set_text ( GTK_ENTRY(commententry), wp->comment ); + + if ( dest == NULL && wp->image ) + vik_file_entry_set_filename ( VIK_FILE_ENTRY(imageentry), wp->image ); + + + + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), latlabel, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), latentry, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), lonlabel, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), lonentry, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), altlabel, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), altentry, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), commentlabel, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), commententry, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), imagelabel, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), imageentry, FALSE, FALSE, 0); + + gtk_widget_show_all ( GTK_DIALOG(dialog)->vbox ); + + while ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) + { + if ( dest ) + { + const gchar *constname = gtk_entry_get_text ( GTK_ENTRY(nameentry) ); + if ( strlen(constname) == 0 ) /* TODO: other checks (isalpha or whatever ) */ + a_dialog_info_msg ( parent, "Please enter a name for the waypoint." ); + else { + int i; + gchar *name = g_strdup ( constname ); + + for ( i = strlen ( name ) - 1; i >= 0; i-- ) + name[i] = toupper(name[i]); /* all caps for stardandization */ + + if ( g_hash_table_lookup ( waypoints, name ) && !a_dialog_overwrite ( parent, "The waypoint \"%s\" exists, do you want to overwrite it?", name ) ) + g_free ( name ); + else + { + /* Do It */ + *dest = name; + ll.lat = atof ( gtk_entry_get_text ( GTK_ENTRY(latentry) ) ); + ll.lon = atof ( gtk_entry_get_text ( GTK_ENTRY(lonentry) ) ); + vik_coord_load_from_latlon ( &(wp->coord), coord_mode, &ll ); + wp->altitude = atof ( gtk_entry_get_text ( GTK_ENTRY(altentry) ) ); + vik_waypoint_set_comment ( wp, gtk_entry_get_text ( GTK_ENTRY(commententry) ) ); + vik_waypoint_set_image ( wp, vik_file_entry_get_filename ( VIK_FILE_ENTRY(imageentry) ) ); + if ( wp->image && *(wp->image) && (!a_thumbnails_exists(wp->image)) ) + a_thumbnails_create ( wp->image ); + gtk_widget_destroy ( dialog ); + return TRUE; + } + } /* else (valid name) */ + } + else + { + ll.lat = atof ( gtk_entry_get_text ( GTK_ENTRY(latentry) ) ); + ll.lon = atof ( gtk_entry_get_text ( GTK_ENTRY(lonentry) ) ); + vik_coord_load_from_latlon ( &(wp->coord), coord_mode, &ll ); + wp->altitude = atof ( gtk_entry_get_text ( GTK_ENTRY(altentry) ) ); + if ( (! wp->comment) || strcmp ( wp->comment, gtk_entry_get_text ( GTK_ENTRY(commententry) ) ) != 0 ) + vik_waypoint_set_comment ( wp, gtk_entry_get_text ( GTK_ENTRY(commententry) ) ); + if ( (! wp->image) || strcmp ( wp->image, vik_file_entry_get_filename ( VIK_FILE_ENTRY ( imageentry ) ) ) != 0 ) + { + vik_waypoint_set_image ( wp, vik_file_entry_get_filename ( VIK_FILE_ENTRY(imageentry) ) ); + if ( wp->image && *(wp->image) && (!a_thumbnails_exists(wp->image)) ) + a_thumbnails_create ( wp->image ); + } + + gtk_widget_destroy ( dialog ); + + return TRUE; + } + } + gtk_widget_destroy ( dialog ); + return FALSE; +} + +gchar *a_dialog_new_track ( GtkWindow *parent, GHashTable *tracks ) +{ + GtkWidget *dialog = gtk_dialog_new_with_buttons ("Add Track", + parent, + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_CANCEL, + GTK_RESPONSE_REJECT, + GTK_STOCK_OK, + GTK_RESPONSE_ACCEPT, + NULL); + GtkWidget *label = gtk_label_new ( "Track Name:" ); + GtkWidget *entry = gtk_entry_new (); + + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), label, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), entry, FALSE, FALSE, 0); + + g_signal_connect_swapped ( entry, "activate", G_CALLBACK(a_dialog_response_accept), GTK_DIALOG(dialog) ); + + gtk_widget_show ( label ); + gtk_widget_show ( entry ); + + while ( gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT ) + { + const gchar *constname = gtk_entry_get_text ( GTK_ENTRY(entry) ); + if ( *constname == '\0' ) + a_dialog_info_msg ( parent, "Please enter a name for the track." ); + else { + gchar *name = g_strdup ( constname ); + gint i; + + for ( i = strlen ( name ) - 1; i >= 0; i-- ) + name[i] = toupper(name[i]); /* all caps for stardandization */ + + if ( g_hash_table_lookup( tracks, name ) && !a_dialog_overwrite ( parent, "The track \"%s\" exists, do you want to overwrite it?", gtk_entry_get_text ( GTK_ENTRY(entry) ) ) ) + { + g_free ( name ); + } + else + { + gtk_widget_destroy ( dialog ); + return name; + } + } + } + gtk_widget_destroy ( dialog ); + return NULL; +} + +/* creates a vbox full of labels */ +GtkWidget *a_dialog_create_label_vbox ( gchar **texts, int label_count ) +{ + GtkWidget *vbox, *label; + int i; + vbox = gtk_vbox_new( TRUE, 3 ); + + for ( i = 0; i < label_count; i++ ) + { + label = gtk_label_new(NULL); + gtk_label_set_markup ( GTK_LABEL(label), texts[i] ); + gtk_box_pack_start ( GTK_BOX(vbox), label, FALSE, TRUE, 5 ); + } + return vbox; +} + +gboolean a_dialog_overwrite ( GtkWindow *parent, const gchar *message, const gchar *extra ) +{ + GtkWidget *dia; + dia = gtk_message_dialog_new ( parent, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_YES_NO, + message, extra ); + + if ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_YES ) + { + gtk_widget_destroy ( dia ); + return TRUE; + } + else + { + gtk_widget_destroy ( dia ); + return FALSE; + } +} + +static void zoom_spin_changed ( GtkSpinButton *spin, GtkWidget *pass_along[3] ) +{ + if ( gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(pass_along[2]) ) ) + gtk_spin_button_set_value ( + GTK_SPIN_BUTTON(pass_along[GTK_WIDGET(spin) == pass_along[0] ? 1 : 0]), + gtk_spin_button_get_value ( spin ) ); +} + +gboolean a_dialog_custom_zoom ( GtkWindow *parent, gdouble *xmpp, gdouble *ympp ) +{ + GtkWidget *dialog = gtk_dialog_new_with_buttons ("Zoom Factors...", + parent, + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_CANCEL, + GTK_RESPONSE_REJECT, + GTK_STOCK_OK, + GTK_RESPONSE_ACCEPT, + NULL); + GtkWidget *table, *label, *xlabel, *xspin, *ylabel, *yspin, *samecheck; + GtkWidget *pass_along[3]; + + table = gtk_table_new ( 4, 2, FALSE ); + gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), table, TRUE, TRUE, 0 ); + + label = gtk_label_new ( "Zoom factor (in meters per pixel:" ); + xlabel = gtk_label_new ( "X (easting): "); + ylabel = gtk_label_new ( "Y (northing): "); + + pass_along[0] = xspin = gtk_spin_button_new ( (GtkAdjustment *) gtk_adjustment_new ( *xmpp, VIK_VIEWPORT_MIN_ZOOM, VIK_VIEWPORT_MAX_ZOOM, 1, 5, 5 ), 1, 8 ); + pass_along[1] = yspin = gtk_spin_button_new ( (GtkAdjustment *) gtk_adjustment_new ( *ympp, VIK_VIEWPORT_MIN_ZOOM, VIK_VIEWPORT_MAX_ZOOM, 1, 5, 5 ), 1, 8 ); + + pass_along[2] = samecheck = gtk_check_button_new_with_label ( "X and Y zoom factors must be equal" ); + /* TODO -- same factor */ + /* samecheck = gtk_check_button_new_with_label ( "Same x/y zoom factor" ); */ + + if ( *xmpp == *ympp ) + gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(samecheck), TRUE ); + + gtk_table_attach_defaults ( GTK_TABLE(table), label, 0, 2, 0, 1 ); + gtk_table_attach_defaults ( GTK_TABLE(table), xlabel, 0, 1, 1, 2 ); + gtk_table_attach_defaults ( GTK_TABLE(table), xspin, 1, 2, 1, 2 ); + gtk_table_attach_defaults ( GTK_TABLE(table), ylabel, 0, 1, 2, 3 ); + gtk_table_attach_defaults ( GTK_TABLE(table), yspin, 1, 2, 2, 3 ); + gtk_table_attach_defaults ( GTK_TABLE(table), samecheck, 0, 2, 3, 4 ); + + gtk_widget_show_all ( table ); + + g_signal_connect ( G_OBJECT(xspin), "value-changed", G_CALLBACK(zoom_spin_changed), pass_along ); + g_signal_connect ( G_OBJECT(yspin), "value-changed", G_CALLBACK(zoom_spin_changed), pass_along ); + + if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) + { + *xmpp = gtk_spin_button_get_value ( GTK_SPIN_BUTTON(xspin) ); + *ympp = gtk_spin_button_get_value ( GTK_SPIN_BUTTON(yspin) ); + gtk_widget_destroy ( dialog ); + return TRUE; + } + gtk_widget_destroy ( dialog ); + return FALSE; +} diff --git a/src/dialog.h b/src/dialog.h new file mode 100644 index 00000000..93aa6a98 --- /dev/null +++ b/src/dialog.h @@ -0,0 +1,52 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _VIKING_DIALOG_H +#define _VIKING_DIALOG_H + +/* most of this file is an architechtural flaw. */ + +#define a_dialog_info_msg(win,info) a_dialog_msg(win,GTK_MESSAGE_INFO,info,NULL) +#define a_dialog_error_msg(win,info) a_dialog_msg(win,GTK_MESSAGE_ERROR,info,NULL) + +#define a_dialog_info_msg_extra(win,info,extra) a_dialog_msg(win,GTK_MESSAGE_INFO,info,extra) +#define a_dialog_error_msg_extra(win,info,extra) a_dialog_msg(win,GTK_MESSAGE_ERROR,info,extra) + +GtkWidget *a_dialog_create_label_vbox ( gchar **texts, int label_count ); + +void a_dialog_msg ( GtkWindow *parent, gint type, const gchar *info, const gchar *extra ); + +void a_dialog_response_accept ( GtkDialog *dialog ); + +/* okay, everthing below here is an architechtural flaw. */ +gboolean a_dialog_goto_latlon ( GtkWindow *parent, struct LatLon *ll, const struct LatLon *old ); +gboolean a_dialog_goto_utm ( GtkWindow *parent, struct UTM *utm, const struct UTM *old ); + +gboolean a_dialog_new_waypoint ( GtkWindow *parent, gchar **dest, VikWaypoint *wp, GHashTable *waypoints, VikCoordMode coord_mode ); + +gchar *a_dialog_new_track ( GtkWindow *parent, GHashTable *tracks ); + +gboolean a_dialog_overwrite ( GtkWindow *parent, const gchar *message, const gchar *extra ); +gboolean a_dialog_custom_zoom ( GtkWindow *parent, gdouble *xmpp, gdouble *ympp ); + +void a_dialog_choose_dir ( GtkWidget *entry ); + +#endif diff --git a/src/expedia.c b/src/expedia.c new file mode 100644 index 00000000..1d3b1c2e --- /dev/null +++ b/src/expedia.c @@ -0,0 +1,172 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2005, Evan Battaglia + * + * Some formulas or perhaps even code derived from GPSDrive + * GPSDrive Copyright (C) 2001-2004 Fritz Ganter + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include "globals.h" +#include "coords.h" +#include "vikcoord.h" +#include "mapcoord.h" +#include "http.h" + + +#define EXPEDIA_SITE "expedia.com" +#define MPP_MARGIN_OF_ERROR 0.01 +#define DEGREES_TO_RADS 0.0174532925 +#define HEIGHT_OF_LAT_DEGREE (111318.84502/ALTI_TO_MPP) +#define HEIGHT_OF_LAT_MINUTE (1855.3140837/ALTI_TO_MPP) +#define WIDTH_BUFFER 0 +#define HEIGHT_BUFFER 25 +#define REAL_WIDTH_BUFFER 1 +#define REAL_HEIGHT_BUFFER 26 + +/* first buffer is to cut off the expedia/microsoft logo. Annoying little buggers ;) */ +/* second is to allow for a 1-pixel overlap on each side. this is a good thing (tm) */ + +static const guint expedia_altis[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512 }; +/* square this number to find out how many per square degree. */ +static const gdouble expedia_altis_degree_freq[] = { 120, 60, 30, 15, 8, 4, 2, 1, 1, 1 }; +static const guint expedia_altis_count = sizeof(expedia_altis) / sizeof(expedia_altis[0]); + +gdouble expedia_altis_freq ( gint alti ) +{ + static gint i; + for ( i = 0; i < expedia_altis_count; i++ ) + if ( expedia_altis[i] == alti ) + return expedia_altis_degree_freq [ i ]; + + g_error ( "Invalid expedia altitude" ); + return 0; +} + +/* returns -1 if none of the above. */ +gint expedia_zoom_to_alti ( gdouble zoom ) +{ + guint i; + for ( i = 0; i < expedia_altis_count; i++ ) + if ( fabs(expedia_altis[i] - zoom) / zoom < MPP_MARGIN_OF_ERROR ) + return expedia_altis[i]; + return -1; +} + +/* +gint expedia_pseudo_zone ( gint alti, gint x, gint y ) +{ + return (int) (x/expedia_altis_freq(alti)*180) + (int) (y/expedia_altis_freq(alti)*90); +} +*/ + +void expedia_snip ( const gchar *file ) +{ + /* Load the pixbuf */ + GError *gx = NULL; + GdkPixbuf *old, *cropped; + gint width, height; + + old = gdk_pixbuf_new_from_file ( file, &gx ); + if (gx) + { + g_warning ( "Couldn't open EXPEDIA image file (right after successful download! Please report and delete image file!): %s", gx->message ); + g_error_free ( gx ); + return; + } + + width = gdk_pixbuf_get_width ( old ); + height = gdk_pixbuf_get_height ( old ); + + cropped = gdk_pixbuf_new_subpixbuf ( old, WIDTH_BUFFER, HEIGHT_BUFFER, + width - 2*WIDTH_BUFFER, height - 2*HEIGHT_BUFFER ); + + gdk_pixbuf_save ( cropped, file, "png", NULL, NULL, &gx ); + if ( gx ) { + g_warning ( "Couldn't save EXPEDIA image file (right after successful download! Please report and delete image file!): %s", gx->message ); + g_error_free ( gx ); + } + + g_object_unref ( cropped ); + g_object_unref ( old ); +} + +/* if degree_freeq = 60 -> nearest minute (in middle) */ +/* everything starts at -90,-180 -> 0,0. then increments by (1/degree_freq) */ +gboolean expedia_coord_to_mapcoord ( const VikCoord *src, gdouble xzoom, gdouble yzoom, MapCoord *dest ) +{ + gint alti; + + g_assert ( src->mode == VIK_COORD_LATLON ); + + if ( xzoom != yzoom ) + return FALSE; + + alti = expedia_zoom_to_alti ( xzoom ); + if ( alti != -1 ) + { + dest->scale = alti; + dest->x = (int) (((src->east_west+180) * expedia_altis_freq(alti))+0.5); + dest->y = (int) (((src->north_south+90) * expedia_altis_freq(alti))+0.5); + /* + 0.5 to round off and not floor */ + + /* just to space out tiles on the filesystem */ + dest->z = 0; + return TRUE; + } + return FALSE; +} + +void expedia_xy_to_latlon_middle ( gint alti, gint x, gint y, struct LatLon *ll ) +{ + ll->lon = (((gdouble)x) / expedia_altis_freq(alti)) - 180; + ll->lat = (((gdouble)y) / expedia_altis_freq(alti)) - 90; +} + +void expedia_mapcoord_to_center_coord ( MapCoord *src, VikCoord *dest ) +{ + dest->mode = VIK_COORD_LATLON; + dest->east_west = (((gdouble)src->x) / expedia_altis_freq(src->scale)) - 180; + dest->north_south = (((gdouble)src->y) / expedia_altis_freq(src->scale)) - 90; +} + +void expedia_download ( MapCoord *src, const gchar *dest_fn ) +{ + gint height, width; + struct LatLon ll; + gchar *uri; + + expedia_xy_to_latlon_middle ( src->scale, src->x, src->y, &ll ); + + height = HEIGHT_OF_LAT_DEGREE / expedia_altis_freq(src->scale) / (src->scale); + width = height * cos ( ll.lat * DEGREES_TO_RADS ); + + height += 2*REAL_HEIGHT_BUFFER; + width += 2*REAL_WIDTH_BUFFER; + + uri = g_strdup_printf ( "/pub/agent.dll?qscr=mrdt&ID=3XNsF.&CenP=%lf,%lf&Lang=%s&Alti=%d&Size=%d,%d&Offs=0.000000,0.000000&BCheck&tpid=1", + ll.lat, ll.lon, (ll.lon > -30) ? "EUR0809" : "USA0409", src->scale, width, height ); + + a_http_download_get_url_nohostname ( "expedia.com", uri, dest_fn ); + + expedia_snip ( dest_fn ); +} + + diff --git a/src/expedia.h b/src/expedia.h new file mode 100644 index 00000000..f518ef2f --- /dev/null +++ b/src/expedia.h @@ -0,0 +1,29 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __VIKING_EXPEDIA_H +#define __VIKING_EXPEDIA_H + +gboolean expedia_coord_to_mapcoord ( const VikCoord *src, gdouble xzoom, gdouble yzoom, MapCoord *dest ); +void expedia_mapcoord_to_center_coord ( MapCoord *src, VikCoord *dest ); +void expedia_download ( MapCoord *src, const gchar *dest_fn ); + +#endif diff --git a/src/file.c b/src/file.c new file mode 100644 index 00000000..491eabbb --- /dev/null +++ b/src/file.c @@ -0,0 +1,509 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "viking.h" + +#include +#include + +#define TEST_BOOLEAN(str) (! ((str)[0] == '\0' || (str)[0] == '0' || (str)[0] == 'n' || (str)[0] == 'N' || (str)[0] == 'f' || (str)[0] == 'F') ) +#define VIK_MAGIC "#VIK" +#define VIK_MAGIC_LEN 4 + +#ifdef WINDOWS +#define FILE_SEP '\\' +#else +#define FILE_SEP '/' +#endif + +typedef struct _Stack Stack; + +struct _Stack { + Stack *under; + gpointer *data; +}; + +static void pop(Stack **stack) { + Stack *tmp = (*stack)->under; + g_free ( *stack ); + *stack = tmp; +} + +static void push(Stack **stack) +{ + Stack *tmp = g_malloc ( sizeof ( Stack ) ); + tmp->under = *stack; + *stack = tmp; +} + +static gboolean is_viking_file ( FILE *f ) +{ + gchar magic[VIK_MAGIC_LEN]; + gboolean rv = FALSE; + gint8 i; + if ( fread(magic, 1, sizeof(magic), f) == sizeof(magic) && + strncmp(magic, VIK_MAGIC, sizeof(magic)) == 0 ) + rv = TRUE; + for ( i = sizeof(magic)-1; i >= 0; i-- ) /* the ol' pushback */ + ungetc(magic[i],f); + return rv; +} + + +static gboolean str_starts_with ( const gchar *haystack, const gchar *needle, guint16 len_needle, gboolean must_be_longer ) +{ + if ( strlen(haystack) > len_needle - (!must_be_longer) && strncasecmp ( haystack, needle, len_needle ) == 0 ) + return TRUE; + return FALSE; +} + +static guint16 layer_type_from_string ( const gchar *str ) +{ + guint8 i; + for ( i = 0; i < VIK_LAYER_NUM_TYPES; i++ ) + if ( strcasecmp ( str, vik_layer_get_interface(i)->name ) == 0 ) + return i; + return -1; +} + +static void write_layer_params_and_data ( VikLayer *l, FILE *f ) +{ + VikLayerParam *params = vik_layer_get_interface(l->type)->params; + VikLayerFuncGetParam get_param = vik_layer_get_interface(l->type)->get_param; + + fprintf ( f, "name=%s\n", l->name ? l->name : "" ); + if ( !l->visible ) + fprintf ( f, "visible=f\n" ); + + if ( params && get_param ) + { + VikLayerParamData data; + guint16 i, params_count = vik_layer_get_interface(l->type)->params_count; + for ( i = 0; i < params_count; i++ ) + { + data = get_param(l,i); + fprintf ( f, "%s=", params[i].name ); + switch ( params[i].type ) + { + case VIK_LAYER_PARAM_DOUBLE: fprintf ( f, "%f\n", data.d ); break; + case VIK_LAYER_PARAM_UINT: fprintf ( f, "%d\n", data.u ); break; + case VIK_LAYER_PARAM_INT: fprintf ( f, "%d\n", data.i ); break; + case VIK_LAYER_PARAM_BOOLEAN: fprintf ( f, "%c\n", data.b ? 't' : 'f' ); break; + case VIK_LAYER_PARAM_STRING: fprintf ( f, "%s\n", data.s ); break; + case VIK_LAYER_PARAM_COLOR: fprintf ( f, "#%.2x%.2x%.2x\n", (int)(data.c.red/256),(int)(data.c.green/256),(int)(data.c.blue/256)); break; + } + } + } + if ( vik_layer_get_interface(l->type)->write_file_data ) + { + fprintf ( f, "\n\n~LayerData\n" ); + vik_layer_get_interface(l->type)->write_file_data ( l, f ); + fprintf ( f, "~EndLayerData\n" ); + } + /* foreach param: + write param, and get_value, etc. + then run layer data, and that's it. + */ +} + +static void file_write ( VikAggregateLayer *top, FILE *f, gpointer vp ) +{ + Stack *stack = NULL; + VikLayer *current_layer; + struct LatLon ll; + gchar *modestring; + + push(&stack); + stack->data = (gpointer) vik_aggregate_layer_get_children(VIK_AGGREGATE_LAYER(top)); + stack->under = NULL; + + /* crazhy CRAZHY */ + vik_coord_to_latlon ( vik_viewport_get_center ( VIK_VIEWPORT(vp) ), &ll ); + + switch ( vik_viewport_get_drawmode ( VIK_VIEWPORT(vp) ) ) { + case VIK_VIEWPORT_DRAWMODE_UTM: modestring = "utm"; break; + case VIK_VIEWPORT_DRAWMODE_EXPEDIA: modestring = "expedia"; break; + case VIK_VIEWPORT_DRAWMODE_GOOGLE: modestring = "google"; break; + case VIK_VIEWPORT_DRAWMODE_KH: modestring = "kh"; break; + default: modestring = "mercator"; + } + + fprintf ( f, "#VIKING GPS Data file http://gpsmaps.org/viking/\n\nxmpp=%f\nympp=%f\nlat=%f\nlon=%f\nmode=%s\ncolor=%s\n", + vik_viewport_get_xmpp ( VIK_VIEWPORT(vp) ), vik_viewport_get_ympp ( VIK_VIEWPORT(vp) ), ll.lat, ll.lon, + modestring, vik_viewport_get_background_color(VIK_VIEWPORT(vp)) ); + + if ( ! VIK_LAYER(top)->visible ) + fprintf ( f, "visible=f\n" ); + + while (stack && stack->data) + { + current_layer = VIK_LAYER(((GList *)stack->data)->data); + fprintf ( f, "\n~Layer %s\n", vik_layer_get_interface(current_layer->type)->name ); + write_layer_params_and_data ( current_layer, f ); + if ( current_layer->type == VIK_LAYER_AGGREGATE && !vik_aggregate_layer_is_empty(VIK_AGGREGATE_LAYER(current_layer)) ) + { + push(&stack); + stack->data = (gpointer) vik_aggregate_layer_get_children(VIK_AGGREGATE_LAYER(current_layer)); + } + else + { + stack->data = (gpointer) ((GList *)stack->data)->next; + fprintf ( f, "~EndLayer\n\n" ); + while ( stack && (!stack->data) ) + { + pop(&stack); + if ( stack ) + { + stack->data = (gpointer) ((GList *)stack->data)->next; + fprintf ( f, "~EndLayer\n\n" ); + } + } + } + } +/* + get vikaggregatelayer's children (?) + foreach write ALL params, + then layer data (IF function exists) + then endlayer + + impl: + stack of layers (LIST) we are working on + when layer->next == NULL ... + we move on. +*/ +} + +static void file_read ( VikAggregateLayer *top, FILE *f, gpointer vp ) +{ + Stack *stack; + struct LatLon ll = { 0.0, 0.0 }; + gchar buffer[4096]; + gchar *line; + guint16 len; + long line_num = 0; + + VikLayerParam *params; /* for current layer, so we don't have to keep on looking up interface */ + guint8 params_count; + + push(&stack); + stack->under = NULL; + stack->data = (gpointer) top; + + while ( fgets ( buffer, 4096, f ) ) + { + line_num++; + + line = buffer; + while ( *line == ' ' || *line =='\t' ) + line++; + + if ( line[0] == '#' ) + continue; + + + len = strlen(line); + if ( len > 0 && line[len-1] == '\n' ) + line[--len] = '\0'; + if ( len > 0 && line[len-1] == '\r' ) + line[--len] = '\0'; + + if ( len == 0 ) + continue; + + + if ( line[0] == '~' ) + { + line++; len--; + if ( *line == '\0' ) + continue; + else if ( str_starts_with ( line, "Layer ", 6, TRUE ) ) + { + if ( ( ! stack->data ) || VIK_LAYER(stack->data)->type != VIK_LAYER_AGGREGATE ) + { + g_warning ( "Line %ld: Layer command inside non-Aggregate Layer", line_num ); + push(&stack); /* inside INVALID layer */ + stack->data = NULL; + continue; + } + else + { + gint16 type = layer_type_from_string ( line+6 ); + push(&stack); + if ( type == -1 ) + { + g_warning ( "Line %ld: Unknown type %s\n", line_num, line+6 ); + stack->data = NULL; + } + else + { + stack->data = (gpointer) vik_layer_create ( type, vp, NULL, FALSE ); + params = vik_layer_get_interface(type)->params; + params_count = vik_layer_get_interface(type)->params_count; + } + } + } + else if ( str_starts_with ( line, "EndLayer", 8, FALSE ) ) + { + if ( stack->under == NULL ) + g_warning ( "Line %ld: Mismatched ~EndLayer command", line_num ); + else + { + if ( stack->data && stack->under->data ) + { + vik_aggregate_layer_add_layer ( VIK_AGGREGATE_LAYER(stack->under->data), VIK_LAYER(stack->data) ); + vik_layer_post_read ( VIK_LAYER(stack->data), vp ); + } + pop(&stack); + } + } + else if ( str_starts_with ( line, "LayerData", 9, FALSE ) ) + { + if ( stack->data && vik_layer_get_interface(VIK_LAYER(stack->data)->type)->read_file_data ) + vik_layer_get_interface(VIK_LAYER(stack->data)->type)->read_file_data ( VIK_LAYER(stack->data), f ); + /* must read until hits ~EndLayerData */ + + else + { /* simply skip layer data over */ + while ( fgets ( buffer, 4096, f ) ) + { + line_num++; + + line = buffer; + + len = strlen(line); + if ( len > 0 && line[len-1] == '\n' ) + line[--len] = '\0'; + if ( len > 0 && line[len-1] == '\r' ) + line[--len] = '\0'; + if ( strcasecmp ( line, "~EndLayerData" ) == 0 ) + break; + } + continue; + } + } + else + { + g_warning ( "Line %ld: Unknown tilde command", line_num ); + } + } + else + { + gint32 eq_pos = -1; + guint16 i; + if ( ! stack->data ) + continue; + + for ( i = 0; i < len; i++ ) + if ( line[i] == '=' ) + eq_pos = i; + + if ( stack->under == NULL && eq_pos == 4 && strncasecmp ( line, "xmpp", eq_pos ) == 0) /* "hard coded" params: global & for all layer-types */ + vik_viewport_set_xmpp ( VIK_VIEWPORT(vp), strtod ( line+5, NULL ) ); + else if ( stack->under == NULL && eq_pos == 4 && strncasecmp ( line, "ympp", eq_pos ) == 0) + vik_viewport_set_ympp ( VIK_VIEWPORT(vp), strtod ( line+5, NULL ) ); + else if ( stack->under == NULL && eq_pos == 3 && strncasecmp ( line, "lat", eq_pos ) == 0 ) + ll.lat = strtod ( line+4, NULL ); + else if ( stack->under == NULL && eq_pos == 3 && strncasecmp ( line, "lon", eq_pos ) == 0 ) + ll.lon = strtod ( line+4, NULL ); + else if ( stack->under == NULL && eq_pos == 4 && strncasecmp ( line, "mode", eq_pos ) == 0 && strcasecmp ( line+5, "utm" ) == 0) + vik_viewport_set_drawmode ( VIK_VIEWPORT(vp), VIK_VIEWPORT_DRAWMODE_UTM); + else if ( stack->under == NULL && eq_pos == 4 && strncasecmp ( line, "mode", eq_pos ) == 0 && strcasecmp ( line+5, "expedia" ) == 0) + vik_viewport_set_drawmode ( VIK_VIEWPORT(vp), VIK_VIEWPORT_DRAWMODE_EXPEDIA ); + else if ( stack->under == NULL && eq_pos == 4 && strncasecmp ( line, "mode", eq_pos ) == 0 && strcasecmp ( line+5, "google" ) == 0) + vik_viewport_set_drawmode ( VIK_VIEWPORT(vp), VIK_VIEWPORT_DRAWMODE_GOOGLE ); + else if ( stack->under == NULL && eq_pos == 4 && strncasecmp ( line, "mode", eq_pos ) == 0 && strcasecmp ( line+5, "kh" ) == 0) + vik_viewport_set_drawmode ( VIK_VIEWPORT(vp), VIK_VIEWPORT_DRAWMODE_KH ); + else if ( stack->under == NULL && eq_pos == 4 && strncasecmp ( line, "mode", eq_pos ) == 0 && strcasecmp ( line+5, "mercator" ) == 0) + vik_viewport_set_drawmode ( VIK_VIEWPORT(vp), VIK_VIEWPORT_DRAWMODE_MERCATOR ); + else if ( stack->under == NULL && eq_pos == 5 && strncasecmp ( line, "color", eq_pos ) == 0 ) + vik_viewport_set_background_color ( VIK_VIEWPORT(vp), line+6 ); + else if ( stack->under && eq_pos == 4 && strncasecmp ( line, "name", eq_pos ) == 0 ) + vik_layer_rename ( VIK_LAYER(stack->data), line+5 ); + else if ( eq_pos == 7 && strncasecmp ( line, "visible", eq_pos ) == 0 ) + VIK_LAYER(stack->data)->visible = TEST_BOOLEAN(line+8); + else if ( eq_pos != -1 && stack->under ) + { + gboolean found_match = FALSE; + /* go thru layer params. if len == eq_pos && starts_with jazz, set it. */ + /* also got to check for name and visible. */ + + if ( ! params ) + { + g_warning ( "Line %ld: No options for this kind of layer", line_num ); + continue; + } + + for ( i = 0; i < params_count; i++ ) + if ( strlen(params[i].name) == eq_pos && strncasecmp ( line, params[i].name, eq_pos ) == 0 ) + { + VikLayerParamData x; + line += eq_pos+1; + switch ( params[i].type ) + { + case VIK_LAYER_PARAM_DOUBLE: x.d = strtod(line, NULL); break; + case VIK_LAYER_PARAM_UINT: x.u = strtoul(line, NULL, 10); break; + case VIK_LAYER_PARAM_INT: x.i = strtol(line, NULL, 10); break; + case VIK_LAYER_PARAM_BOOLEAN: x.b = TEST_BOOLEAN(line); break; + case VIK_LAYER_PARAM_COLOR: memset(&(x.c), 0, sizeof(x.c)); /* default: black */ + gdk_color_parse ( line, &(x.c) ); break; + default: x.s = line; + } + vik_layer_set_param ( VIK_LAYER(stack->data), i, x, vp ); + found_match = TRUE; + break; + } + if ( ! found_match ) + g_warning ( "Line %ld: Unknown parameter", line_num ); + } + else + g_warning ( "Line %ld: Invalid parameter or parameter outside of layer.", line_num ); + } +/* could be: +[Layer Type=Bla] +[EndLayer] +[LayerData] +name=this +#comment +*/ + } + + while ( stack ) + { + if ( stack->under && stack->under->data && stack->data ) + { + vik_aggregate_layer_add_layer ( VIK_AGGREGATE_LAYER(stack->under->data), VIK_LAYER(stack->data) ); + vik_layer_post_read ( VIK_LAYER(stack->data), vp ); + } + pop(&stack); + } + + if ( ll.lat != 0.0 || ll.lon != 0.0 ) + vik_viewport_set_center_latlon ( VIK_VIEWPORT(vp), &ll ); + + if ( ( ! VIK_LAYER(top)->visible ) && VIK_LAYER(top)->realized ) + vik_treeview_item_set_visible ( VIK_LAYER(top)->vt, &(VIK_LAYER(top)->iter), FALSE ); +} + +/* +read thru file +if "[Layer Type=" + push(&stack) + new default layer of type (str_to_type) (check interface->name) +if "[EndLayer]" + VikLayer *vl = stack->data; + pop(&stack); + vik_aggregate_layer_add_layer(stack->data, vl); +if "[LayerData]" + vik_layer_data ( VIK_LAYER_DATA(stack->data), f, vp ); + +*/ + +/* ---------------------------------------------------- */ + +static FILE *xfopen ( const char *fn, const char *mode ) +{ + if ( strcmp(fn,"-") == 0 ) + return stdin; + else + return fopen(fn, "r"); +} + +static void xfclose ( FILE *f ) +{ + if ( f != stdin && f != stdout ) + fclose ( f ); +} + +/* 0 on failure, 1 on success (vik file) 2 on success (other file) */ +gshort a_file_load ( VikAggregateLayer *top, gpointer vp, const gchar *filename ) +{ + FILE *f = xfopen ( filename, "r" ); + + g_assert ( vp ); + + if ( ! f ) + return 0; + + if ( is_viking_file ( f ) ) + { + file_read ( top, f, vp ); + if ( f != stdin ) + xfclose(f); + return 1; + } + else + { + VikCoord new_center; + VikLayer *vtl = vik_layer_create ( VIK_LAYER_TRW, vp, NULL, FALSE ); + vik_layer_rename ( vtl, a_file_basename ( filename ) ); + a_gpspoint_read_file ( VIK_TRW_LAYER(vtl), f ); + + vik_layer_post_read ( vtl, vp ); + + vik_aggregate_layer_add_layer ( top, vtl ); + + if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(vtl), &new_center ) ) + vik_viewport_set_center_coord ( VIK_VIEWPORT(vp), &new_center ); + xfclose(f); + return 2; + } +} + +gboolean a_file_save ( VikAggregateLayer *top, gpointer vp, const gchar *filename ) +{ + FILE *f = fopen(filename, "w"); + + if ( ! f ) + return FALSE; + + file_write ( top, f, vp ); + + fclose(f); + + return TRUE; +} + + +const gchar *a_file_basename ( const gchar *filename ) +{ + const gchar *t = filename + strlen(filename) - 1; + while ( --t > filename ) + if ( *(t-1) == FILE_SEP ) + break; + if ( t >= filename ) + return t; + return filename; +} + +gboolean a_file_export ( VikTrwLayer *vtl, const gchar *filename, gshort file_type ) +{ + FILE *f = fopen ( filename, "w" ); + if ( f ) + { + if ( file_type == FILE_TYPE_GPSMAPPER ) + a_gpsmapper_write_file ( vtl, f ); + else + a_gpspoint_write_file ( vtl, f ); + fclose ( f ); + return TRUE; + } + return FALSE; +} diff --git a/src/file.h b/src/file.h new file mode 100644 index 00000000..8d3ba29f --- /dev/null +++ b/src/file.h @@ -0,0 +1,35 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _VIKING_FILE_H +#define _VIKING_FILE_H + +#define FILE_TYPE_GPSPOINT 1 +#define FILE_TYPE_GPSMAPPER 2 + +const gchar *a_file_basename ( const gchar *filename ); + +/* 0 on failure, 1 on success (vik file) 2 on success (other file) */ +gshort a_file_load ( VikAggregateLayer *top, gpointer vp, const gchar *filename ); +gboolean a_file_save ( VikAggregateLayer *top, gpointer vp, const gchar *filename ); +gboolean a_file_export ( VikTrwLayer *vtl, const gchar *filename, gshort file_type ); + +#endif diff --git a/src/globals.h b/src/globals.h new file mode 100644 index 00000000..ab932c36 --- /dev/null +++ b/src/globals.h @@ -0,0 +1,54 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __VIKING_GLOBALS_H +#define __VIKING_GLOBALS_H + +#define PROJECT "Viking" +#define VIKING_VERSION "0.1.0" +#define VIKING_VERSION_NAME "Nuovo Giorno" +#define VIKING_URL "http://viking.sf.net/" + +#ifdef WINDOWS +#define access(a,b) _access(a,b) +#define VIKING_FILE_SEP '\\' +#else +#include +#define VIKING_FILE_SEP '/' +#define UNIX_WEB_BROWSER "galeon" +#endif + +#define ALTI_TO_MPP 1.4017295 +#define MPP_TO_ALTI 0.7134044 + +#define VIK_DEFAULT_ALTITUDE 0.0 + +#define VIK_GTK_WINDOW_FROM_WIDGET(x) GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(x))) +#define VIK_GTK_WINDOW_FROM_LAYER(x) VIK_GTK_WINDOW_FROM_WIDGET(VIK_LAYER(x)->vt) + +#define DEG2RAD 0.017453293 +#define RAD2DEG 57.2957795 + +/* mercator projection, latitude conversion (degrees) */ +#define MERCLAT(x) (RAD2DEG * log(tan((0.25 * M_PI) + (0.5 * DEG2RAD * (x))))) +#define DEMERCLAT(x) (RAD2DEG * atan(sinh(DEG2RAD * (x)))) + +#endif diff --git a/src/google.c b/src/google.c new file mode 100644 index 00000000..50ecfa91 --- /dev/null +++ b/src/google.c @@ -0,0 +1,141 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include "coords.h" +#include "vikcoord.h" +#include "mapcoord.h" +#include "http.h" +#include "globals.h" +#include "google.h" + +/* 1 << (x) is like a 2**(x) */ +#define GZ(x) ((1<mode == VIK_COORD_LATLON ); + + if ( xzoom != yzoom ) + return FALSE; + + dest->scale = google_zoom ( xzoom ); + if ( dest->scale == 255 ) + return FALSE; + + dest->x = (src->east_west + 180) / 360 * GZ(17) / xzoom; + dest->y = (180 - MERCLAT(src->north_south)) / 360 * GZ(17) / xzoom; + dest->z = 0; + + return TRUE; +} + +void google_mapcoord_to_center_coord ( MapCoord *src, VikCoord *dest ) +{ + gdouble socalled_mpp = GZ(src->scale); + dest->mode = VIK_COORD_LATLON; + dest->east_west = ((src->x+0.5) / GZ(17) * socalled_mpp * 360) - 180; + dest->north_south = DEMERCLAT(180 - ((src->y+0.5) / GZ(17) * socalled_mpp * 360)); +} + +static void real_google_download ( MapCoord *src, const gchar *dest_fn, const char *verstr ) +{ + gchar *uri = g_strdup_printf ( "/mt?v=%s&x=%d&y=%d&zoom=%d", verstr, src->x, src->y, src->scale ); + a_http_download_get_url ( "mt.google.com", uri, dest_fn ); + g_free ( uri ); +} + +void google_download ( MapCoord *src, const gchar *dest_fn ) +{ + real_google_download ( src, dest_fn, "w2.5" ); +} + +void google_trans_download ( MapCoord *src, const gchar *dest_fn ) +{ + real_google_download ( src, dest_fn, "w2t.1" ); +} + +static char *kh_encode(guint32 x, guint32 y, guint8 scale) +{ + gchar *buf = g_malloc ( (20-scale)*sizeof(gchar) ); + guint32 ya = 1 << (17 - scale); + gint8 i, j; + + if (y < 0 || (ya-1 < y)) { + strcpy(buf,"tqq"); /* BAD */ + return buf; + } + if (x < 0 || ya-1 < x) { + x %= ya; + if (x < 0) + x += ya; + } + + buf[0] = 't'; + for (j = 1, i = 16; i >= scale; i--, j++) { + ya /= 2; + if (y < ya) { + if (x < ya) + buf[j]='q'; + else { + buf[j]='r'; + x -= ya; + } + } else { + if (x < ya) { + buf[j] = 't'; + y -= ya; + } else { + buf[j] = 's'; + x -= ya; + y -= ya; + } + } + } + buf[j] = '\0'; + return buf; +} + +void google_kh_download ( MapCoord *src, const gchar *dest_fn ) +{ + gchar *khenc = kh_encode( src->x, src->y, src->scale ); + gchar *uri = g_strdup_printf ( "/kh?v=3&t=%s", khenc ); + g_free ( khenc ); + a_http_download_get_url ( "kh.google.com", uri, dest_fn ); + g_free ( uri ); +} diff --git a/src/google.h b/src/google.h new file mode 100644 index 00000000..aa8dc7f6 --- /dev/null +++ b/src/google.h @@ -0,0 +1,38 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __VIKING_GOOGLE_H +#define __VIKING_GOOGLE_H + +guint8 google_zoom ( gdouble mpp ); + +/* a bit misleading, this is the "mpp" (really just set zoom level, very + * roughly equivalent so you can easily switch between maps) of + * google maps 1, the second google maps level (1st is 0). */ +#define GOOGLE_ZOOM_ONE_MPP 2.0 + +gboolean google_coord_to_mapcoord ( const VikCoord *src, gdouble xzoom, gdouble yzoom, MapCoord *dest ); +void google_mapcoord_to_center_coord ( MapCoord *src, VikCoord *dest ); +void google_download ( MapCoord *src, const gchar *dest_fn ); +void google_trans_download ( MapCoord *src, const gchar *dest_fn ); +void google_kh_download ( MapCoord *src, const gchar *dest_fn ); + +#endif diff --git a/src/googlemaps.c b/src/googlemaps.c new file mode 100644 index 00000000..f99142cc --- /dev/null +++ b/src/googlemaps.c @@ -0,0 +1,78 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include "coords.h" +#include "vikcoord.h" +#include "mapcoord.h" +#include "http.h" + +#include "googlemaps.h" + +/* 1 << (x-1) is like a 2**(x-1) */ +#define GZ(x) ((1<<(x-1))*GOOGLEMAPS_ZOOM_ONE_MPP) + +static const gdouble scale_mpps[] = { GZ(1)/2, GZ(1), GZ(2), GZ(3), GZ(4), GZ(5), GZ(6), GZ(7), GZ(8), GZ(9), + GZ(10), GZ(11), GZ(12), GZ(13), GZ(14), GZ(15) }; + +static const gint num_scales = (sizeof(scale_mpps) / sizeof(scale_mpps[0])) - 1; + +#define ERROR_MARGIN 0.01 +guint8 googlemaps_zoom ( gdouble mpp ) { + gint i; + for ( i = 0; i <= num_scales; i++ ) { + if ( ABS(scale_mpps[i] - mpp) < ERROR_MARGIN ) + return i; + } + return 255; +} + +gboolean googlemaps_coord_to_mapcoord ( const VikCoord *src, gdouble xzoom, gdouble yzoom, MapCoord *dest ) +{ + g_assert ( src->mode == VIK_COORD_LATLON ); + + if ( xzoom != yzoom ) + return FALSE; + + dest->scale = googlemaps_zoom ( xzoom ); + if ( dest->scale == 255 ) + return FALSE; + + dest->x = (gint) floor ( floor ((src->east_west + 98.35) * (131072 >> dest->scale) * 0.77162458338772) / 128); + dest->y = (gint) floor ( floor ((39.5 - src->north_south) * (131072 >> dest->scale)) / 128); + dest->z = 0; + return TRUE; +} + +void googlemaps_mapcoord_to_center_coord ( MapCoord *src, VikCoord *dest ) +{ + dest->mode = VIK_COORD_LATLON; + dest->north_south = 39.5 - (1.0/(131072 >> src->scale) * (src->y+0.5) * 128); + dest->east_west = 1.0 / (131072 >> src->scale) * (src->x+0.5) / 0.77162458338772 * 128 - 98.35; +} + +void googlemaps_download ( MapCoord *src, const gchar *dest_fn ) +{ + gchar *uri = g_strdup_printf ( "/mt?v=.3&x=%d&y=%d&zoom=%d", src->x, src->y, src->scale ); + a_http_download_get_url ( "mt.google.com", uri, dest_fn ); + g_free ( uri ); +} diff --git a/src/googlemaps.h b/src/googlemaps.h new file mode 100644 index 00000000..95cffd65 --- /dev/null +++ b/src/googlemaps.h @@ -0,0 +1,36 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __VIKING_GOOGLEMAPS_H +#define __VIKING_GOOGLEMAPS_H + +guint8 googlemaps_zoom ( gdouble mpp ); + +/* a bit misleading, this is the "mpp" (really just set zoom level, very + * roughly equivalent so you can easily switch between maps) of + * google maps 1, the second google maps level (1st is 0). */ +#define GOOGLEMAPS_ZOOM_ONE_MPP 2.0 + +gboolean googlemaps_coord_to_mapcoord ( const VikCoord *src, gdouble xzoom, gdouble yzoom, MapCoord *dest ); +void googlemaps_mapcoord_to_center_coord ( MapCoord *src, VikCoord *dest ); +void googlemaps_download ( MapCoord *src, const gchar *dest_fn ); + +#endif diff --git a/src/gpsmapper.c b/src/gpsmapper.c new file mode 100644 index 00000000..54ffd6e0 --- /dev/null +++ b/src/gpsmapper.c @@ -0,0 +1,141 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include "viking.h" + +#include + +/* Name of layer -> RGN type and Type + format: Name RGN40 0x40 + or: Name RGN10 0x2f06 +*/ +/* returns 0 if invalid/no rgn stuff, else returns len of */ +static guint print_rgn_stuff ( const gchar *nm, FILE *f ) +{ + guint len; + gchar *layers; + gchar *name; + + if (!nm) + return 0; + + name = g_strdup ( nm ); + + len = strlen(name); + + + + /* --------------------------------------------- */ + /* added by oddgeir@oddgeirkvien.com, 2005.02.02 */ + /* Format may also be: Name RGN40 0x40 Layers=1 */ + /* or: Name RGN10 0x2f06 Layers=1 */ + + if ( len > 20 && strncasecmp(name+len-8,"LAYERS=",7) == 0 ) /* Layers is added to the description */ + { + layers=name+len-8; + *(name+len-9)=0; + len = strlen(name); + } + else + { + layers=0; + } + /* --------------------------------------------- */ + + + + if ( len > 11 && strncasecmp(name+len-10,"RGN",3) == 0 && +strncasecmp(name+len-4,"0x",2) == 0 ) + { + fprintf ( f, "[%.5s]\nType=%.4s\nLabel=", name+len-10, name+len-4 ); + fwrite ( name, sizeof(gchar), len - 11, f ); + fprintf ( f, "\n" ); + +/* added by oddgeir@oddgeirkvien.com, 2005.02.02 */ + if (layers) fprintf( f, "%s\n",layers); + + g_free ( name ); + + return len - 11; + } + else if ( len > 13 && strncasecmp(name+len-12,"RGN",3) == 0 && +strncasecmp(name+len-6,"0x",2) == 0 ) + { + fprintf ( f, "[%.5s]\nType=%.6s\nLabel=", name+len-12, name+len-6 ); + fwrite ( name, sizeof(gchar), len - 13, f ); + fprintf ( f, "\n" ); + +/* added by oddgeir@oddgeirkvien.com, 2005.02.02 */ + if (layers) fprintf( f, "%s\n",layers); + + g_free ( name ); + + return len - 13; + } + else { + g_free ( name ); + return 0; + } +} + +static void write_waypoint ( const gchar *name, VikWaypoint *wp, FILE *f ) +{ + static struct LatLon ll; + guint len = print_rgn_stuff ( wp->comment, f ); + if ( len ) + { + vik_coord_to_latlon ( &(wp->coord), &ll ); + fprintf ( f, "Data0=(%f,%f)\n", ll.lat, ll.lon ); + fprintf ( f, "[END-%.5s]\n\n", wp->comment+len+1 ); + } +} + +static void write_trackpoint ( VikTrackpoint *tp, FILE *f ) +{ + static struct LatLon ll; + vik_coord_to_latlon ( &(tp->coord), &ll ); + + fprintf ( f, "(%f,%f),", ll.lat, ll.lon ); +} + +static void write_track ( const gchar *name, VikTrack *t, FILE *f ) +{ + guint len = print_rgn_stuff ( t->comment, f ); + if ( len ) + { + fprintf ( f, "Data0=" ); + g_list_foreach ( t->trackpoints, (GFunc) write_trackpoint, f ); + fprintf ( f, "\n[END-%.5s]\n\n", t->comment+len+1 ); + } +} + +void a_gpsmapper_write_file ( VikTrwLayer *trw, FILE *f ) +{ + GHashTable *tracks = vik_trw_layer_get_tracks ( trw ); + GHashTable *waypoints = vik_trw_layer_get_waypoints ( trw ); + + fprintf ( f, "[IMG ID]\nID=%s\nName=%s\nTreSize=1000\nRgnLimit=700\nLevels=2\nLevel0=22\nLevel1=18\nZoom0=0\nZoom1=1\n[END-IMG ID]\n\n", + VIK_LAYER(trw)->name, VIK_LAYER(trw)->name ); + + g_hash_table_foreach ( waypoints, (GHFunc) write_waypoint, f ); + g_hash_table_foreach ( tracks, (GHFunc) write_track, f ); +} + + diff --git a/src/gpsmapper.h b/src/gpsmapper.h new file mode 100644 index 00000000..57ab477a --- /dev/null +++ b/src/gpsmapper.h @@ -0,0 +1,23 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +void a_gpsmapper_write_file ( VikTrwLayer *trw, FILE *f ); + diff --git a/src/gpspoint.c b/src/gpspoint.c new file mode 100644 index 00000000..13e72d72 --- /dev/null +++ b/src/gpspoint.c @@ -0,0 +1,442 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "viking.h" + +#include +#include + +#include +/* strtod */ + + +static void a_gpspoint_write_track ( const gchar *name, VikTrack *t, FILE *f ); +static void a_gpspoint_write_trackpoint ( VikTrackpoint *tp, FILE *f ); +static void a_gpspoint_write_waypoint ( const gchar *name, VikWaypoint *wp, FILE *f ); + + +/* outline for file gpspoint.c + +reading file: + +take a line. +get first tag, if not type, skip it. +if type, record type. if waypoint list, etc move on. if track, make a new track, make it current track, add it, etc. +if waypoint, read on and store to the waypoint. +if trackpoint, make trackpoint, store to current track (error / skip if none) + +*/ + +/* Thanks to etrex-cache's gpsbabel's gpspoint.c for starting me off! */ + +static char line_buffer[2048]; + +#define GPSPOINT_TYPE_NONE 0 +#define GPSPOINT_TYPE_WAYPOINT 1 +#define GPSPOINT_TYPE_TRACKPOINT 2 +/* #define GPSPOINT_TYPE_ROUTEPOINT 3 */ +#define GPSPOINT_TYPE_TRACK 4 + +/* #define GPSPOINT_TYPE_ROUTE 5 */ + +static VikTrack *current_track; /* pointer to pointer to first GList */ + +static gint line_type = GPSPOINT_TYPE_NONE; +static struct LatLon line_latlon; +static gchar *line_name; +static gchar *line_comment; +static gchar *line_image; +static gboolean line_newsegment = FALSE; +static gboolean line_has_timestamp = FALSE; +static time_t line_timestamp = 0; +static gdouble line_altitude = VIK_DEFAULT_ALTITUDE; +static gboolean line_visible = TRUE; +/* other possible properties go here */ + + +static void gpspoint_process_tag ( const gchar *tag, gint len ); +static void gpspoint_process_key_and_value ( const gchar *key, gint key_len, const gchar *value, gint value_len ); + +static gchar *slashdup(const gchar *str) +{ + guint16 len = strlen(str); + guint16 need_bs_count, i, j; + gchar *rv; + for ( i = 0, need_bs_count = 0; i < len; i++ ) + if ( str[i] == '\\' || str[i] == '"' ) + need_bs_count++; + rv = g_malloc ( (len+need_bs_count+1) * sizeof(gchar) ); + for ( i = 0, j = 0; i < len; i++, j++ ) + { + if ( str[i] == '\\' || str[i] == '"' ) + rv[j++] = '\\'; + rv[j] = str[i]; + } + rv[j] = '\0'; + return rv; +} + +static gchar *deslashndup ( const gchar *str, guint16 len ) +{ + guint16 i,j, bs_count, new_len; + gboolean backslash = FALSE; + gchar *rv; + + if ( len < 1 ) + return NULL; + + for ( i = 0, bs_count = 0; i < len; i++ ) + if ( str[i] == '\\' ) + { + bs_count++; + i++; + } + + if ( str[i-1] == '\\' && (len == 1 || str[i-2] != '\\') ) + bs_count--; + + new_len = len - bs_count; + rv = g_malloc ( (new_len+1) * sizeof(gchar) ); + for ( i = 0, j = 0; i < len && j < new_len; i++ ) + if ( str[i] == '\\' && !backslash ) + backslash = TRUE; + else + { + rv[j++] = str[i]; + backslash = FALSE; + } + + rv[new_len] = '\0'; + return rv; +} + +void a_gpspoint_read_file(VikTrwLayer *trw, FILE *f ) { + VikCoordMode coord_mode = vik_trw_layer_get_coord_mode ( trw ); + gchar *tag_start, *tag_end; + GHashTable *tracks = vik_trw_layer_get_tracks ( trw ); + GHashTable *waypoints = vik_trw_layer_get_waypoints ( trw ); + g_assert ( f != NULL && trw != NULL ); + line_type = 0; + line_timestamp = 0; + line_newsegment = FALSE; + current_track = NULL; + while (fgets(line_buffer, 2048, f)) + { + gboolean inside_quote = 0; + gboolean backslash = 0; + + line_buffer[strlen(line_buffer)-1] = '\0'; /* chop off newline */ + + /* for gpspoint files wrapped inside */ + if ( strlen(line_buffer) >= 13 && strncmp ( line_buffer, "~EndLayerData", 13 ) == 0 ) + break; + +/* each line: nullify stuff, make thing if nes, free name if ness */ + tag_start = line_buffer; + for (;;) + { + /* my addition: find first non-whitespace character. if the null, skip line. */ + while (*tag_start != '\0' && isspace(*tag_start)) + tag_start++; + if (tag_start == '\0') + break; + + if (*tag_start == '#') + break; + + tag_end = tag_start; + if (*tag_end == '"') + inside_quote = !inside_quote; + while (*tag_end != '\0' && (!isspace(*tag_end) || inside_quote)) { + tag_end++; + if (*tag_end == '\\' && !backslash) + backslash = TRUE; + else if (backslash) + backslash = FALSE; + else if (*tag_end == '"') + inside_quote = !inside_quote; + } + + gpspoint_process_tag ( tag_start, tag_end - tag_start ); + + if (*tag_end == '\0' ) + break; + else + tag_start = tag_end+1; + } + if (line_type == GPSPOINT_TYPE_WAYPOINT && line_name) + { + VikWaypoint *wp = vik_waypoint_new(); + gint i = strlen(line_name); + wp->visible = line_visible; + wp->altitude = line_altitude; + while ( i-- ) + line_name[i] = toupper(line_name[i]); /* TODO: check for acceptable chars */ + + vik_coord_load_from_latlon ( &(wp->coord), coord_mode, &line_latlon ); + + g_hash_table_insert ( waypoints, line_name, wp ); + + if ( line_comment ) + { + vik_waypoint_set_comment ( wp, line_comment ); + line_comment = NULL; + } + + if ( line_image ) + { + vik_waypoint_set_image ( wp, line_image ); + line_image = NULL; + } + + line_name = NULL; /* will be freed automatically */ + } + else if (line_type == GPSPOINT_TYPE_TRACK && line_name) + { + VikTrack *pl = vik_track_new(); + gint i = strlen(line_name); + + /* Thanks to Peter Jones for this Fix */ + if (!line_name) line_name = g_strdup("UNK"); + + pl->visible = line_visible; + + while ( i-- ) + line_name[i] = toupper(line_name[i]); + + if ( line_comment ) + { + vik_track_set_comment ( pl, line_comment ); + line_comment = NULL; + } + + pl->trackpoints = NULL; + g_hash_table_insert ( tracks, line_name, pl ); + line_name = NULL; /* will be freed automatically */ + + current_track = pl; + } + else if (line_type == GPSPOINT_TYPE_TRACKPOINT && current_track) + { + VikTrackpoint *tp = g_malloc ( sizeof ( VikTrackpoint ) ); + vik_coord_load_from_latlon ( &(tp->coord), coord_mode, &line_latlon ); + tp->newsegment = line_newsegment; + tp->has_timestamp = line_has_timestamp; + tp->timestamp = line_timestamp; + tp->altitude = line_altitude; + current_track->trackpoints = g_list_append ( current_track->trackpoints, tp ); + } + + if (line_name) + g_free ( line_name ); + line_name = NULL; + if (line_comment) + g_free ( line_comment ); + if (line_image) + g_free ( line_image ); + line_comment = NULL; + line_image = NULL; + line_type = GPSPOINT_TYPE_NONE; + line_newsegment = FALSE; + line_has_timestamp = FALSE; + line_timestamp = 0; + line_altitude = VIK_DEFAULT_ALTITUDE; + line_visible = TRUE; + } +} + +/* Tag will be of a few defined forms: + ^[:alpha:]*=".*"$ + ^[:alpha:]*=.*$ + + + +So we must determine end of tag name, start of value, end of value. +*/ +static void gpspoint_process_tag ( const gchar *tag, gint len ) +{ + const gchar *key_end, *value_start, *value_end; + + /* Searching for key end */ + key_end = tag; + + while (++key_end - tag < len) + if (*key_end == '=') + break; + + if (key_end - tag == len) + return; /* no good */ + + if (key_end - tag == len + 1) + value_start = value_end = 0; /* size = 0 */ + else + { + value_start = key_end + 1; /* equal_sign plus one */ + + if (*value_start == '"') + { + value_start++; + if (*value_start == '"') + value_start = value_end = 0; /* size = 0 */ + else + { + if (*(tag+len-1) == '"') + value_end = tag + len - 1; + else + return; /* bogus */ + } + } + else + value_end = tag + len; /* value start really IS value start. */ + + gpspoint_process_key_and_value(tag, key_end - tag, value_start, value_end - value_start); + } +} + +/* +value = NULL for none +*/ +static void gpspoint_process_key_and_value ( const gchar *key, gint key_len, const gchar *value, gint value_len ) +{ + if (key_len == 4 && strncasecmp( key, "type", key_len ) == 0 ) + { + if (value == NULL) + line_type = GPSPOINT_TYPE_NONE; + else if (value_len == 5 && strncasecmp( value, "track", value_len ) == 0 ) + line_type = GPSPOINT_TYPE_TRACK; + else if (value_len == 10 && strncasecmp( value, "trackpoint", value_len ) == 0 ) + line_type = GPSPOINT_TYPE_TRACKPOINT; + else if (value_len == 8 && strncasecmp( value, "waypoint", value_len ) == 0 ) + line_type = GPSPOINT_TYPE_WAYPOINT; + else + /* all others are ignored */ + line_type = GPSPOINT_TYPE_NONE; + } + else if (key_len == 4 && strncasecmp( key, "name", key_len ) == 0 && value != NULL) + { + if (line_name == NULL) + { + line_name = g_strndup ( value, value_len ); + } + } + else if (key_len == 7 && strncasecmp( key, "comment", key_len ) == 0 && value != NULL) + { + if (line_comment == NULL) + line_comment = deslashndup ( value, value_len ); + } + else if (key_len == 5 && strncasecmp( key, "image", key_len ) == 0 && value != NULL) + { + if (line_image == NULL) + line_image = deslashndup ( value, value_len ); + } + else if (key_len == 8 && strncasecmp( key, "latitude", key_len ) == 0 && value != NULL) + { + line_latlon.lat = strtod(value, NULL); + } + else if (key_len == 9 && strncasecmp( key, "longitude", key_len ) == 0 && value != NULL) + { + line_latlon.lon = strtod(value, NULL); + } + else if (key_len == 8 && strncasecmp( key, "altitude", key_len ) == 0 && value != NULL) + { + line_altitude = strtod(value, NULL); + } + else if (key_len == 7 && strncasecmp( key, "visible", key_len ) == 0 && value[0] != 'y' && value[0] != 'Y' && value[0] != 't' && value[0] != 'T') + { + line_visible = FALSE; + } + else if (key_len == 8 && strncasecmp( key, "unixtime", key_len ) == 0 && value != NULL) + { + line_timestamp = strtod(value, NULL); + if ( line_timestamp != 0x80000000 ) + line_has_timestamp = TRUE; + } + else if (key_len == 10 && strncasecmp( key, "newsegment", key_len ) == 0 && value != NULL) + { + line_newsegment = TRUE; + } +} + +static void a_gpspoint_write_waypoint ( const gchar *name, VikWaypoint *wp, FILE *f ) +{ + static struct LatLon ll; + vik_coord_to_latlon ( &(wp->coord), &ll ); + fprintf ( f, "type=\"waypoint\" latitude=\"%f\" longitude=\"%f\" name=\"%s\"", ll.lat, ll.lon, name ); + if ( wp->altitude != VIK_DEFAULT_ALTITUDE ) + fprintf ( f, " altitude=\"%f\"", wp->altitude ); + if ( wp->comment ) + { + gchar *tmp_comment = slashdup(wp->comment); + fprintf ( f, " comment=\"%s\"", tmp_comment ); + g_free ( tmp_comment ); + } + if ( wp->image ) + { + gchar *tmp_image = slashdup(wp->image); + fprintf ( f, " image=\"%s\"", tmp_image ); + g_free ( tmp_image ); + } + if ( ! wp->visible ) + fprintf ( f, " visible=\"n\"" ); + fprintf ( f, "\n" ); +} + +static void a_gpspoint_write_trackpoint ( VikTrackpoint *tp, FILE *f ) +{ + static struct LatLon ll; + vik_coord_to_latlon ( &(tp->coord), &ll ); + + fprintf ( f, "type=\"trackpoint\" latitude=\"%f\" longitude=\"%f\"", ll.lat, ll.lon ); + + if ( tp->altitude != VIK_DEFAULT_ALTITUDE ) + fprintf ( f, " altitude=\"%f\"", tp->altitude ); + if ( tp->has_timestamp ) + fprintf ( f, " unixtime=\"%ld\"", tp->timestamp ); + if ( tp->newsegment ) + fprintf ( f, " newsegment=\"yes\"" ); + fprintf ( f, "\n" ); +} + + +static void a_gpspoint_write_track ( const gchar *name, VikTrack *t, FILE *f ) +{ + if ( t->comment ) + { + gchar *tmp_comment = slashdup(t->comment); + fprintf ( f, "type=\"track\" name=\"%s\" comment=\"%s\"%s\n", name, tmp_comment, t->visible ? "" : " visible=\"n\"" ); + g_free ( tmp_comment ); + } + else + fprintf ( f, "type=\"track\" name=\"%s\"%s\n", name, t->visible ? "" : " visible=\"n\"" ); + g_list_foreach ( t->trackpoints, (GFunc) a_gpspoint_write_trackpoint, f ); + fprintf ( f, "type=\"trackend\"\n" ); +} + +void a_gpspoint_write_file ( VikTrwLayer *trw, FILE *f ) +{ + GHashTable *tracks = vik_trw_layer_get_tracks ( trw ); + GHashTable *waypoints = vik_trw_layer_get_waypoints ( trw ); + + fprintf ( f, "type=\"waypointlist\"\n" ); + g_hash_table_foreach ( waypoints, (GHFunc) a_gpspoint_write_waypoint, f ); + fprintf ( f, "type=\"waypointlistend\"\n" ); + g_hash_table_foreach ( tracks, (GHFunc) a_gpspoint_write_track, f ); +} diff --git a/src/gpspoint.h b/src/gpspoint.h new file mode 100644 index 00000000..c68c36f5 --- /dev/null +++ b/src/gpspoint.h @@ -0,0 +1,28 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _VIKING_GPSPOINT_H +#define _VIKING_GPSPOINT_H + +void a_gpspoint_read_file ( VikTrwLayer *trw, FILE *f ); +void a_gpspoint_write_file ( VikTrwLayer *trw, FILE *f ); + +#endif diff --git a/src/gtkcellrendererprogress.c b/src/gtkcellrendererprogress.c new file mode 100644 index 00000000..deff3b9a --- /dev/null +++ b/src/gtkcellrendererprogress.c @@ -0,0 +1,297 @@ +/* gtkcellrendererprogress.c + * Taken from Gaim 0.77. + * + * Gaim is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* This is taken largely from GtkCellRenderer[Text|Pixbuf|Toggle] by + * Jonathon Blandford for RedHat, Inc. + */ + +#include "gtkcellrendererprogress.h" + +static void gtk_cell_renderer_progress_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void gtk_cell_renderer_progress_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_cell_renderer_progress_init (GtkCellRendererProgress *cellprogress); +static void gtk_cell_renderer_progress_class_init (GtkCellRendererProgressClass *class); +static void gtk_cell_renderer_progress_get_size (GtkCellRenderer *cell, + GtkWidget *widget, + GdkRectangle *cell_area, + gint *x_offset, + gint *y_offset, + gint *width, + gint *height); +static void gtk_cell_renderer_progress_render (GtkCellRenderer *cell, + GdkWindow *window, + GtkWidget *widget, + GdkRectangle *background_area, + GdkRectangle *cell_area, + GdkRectangle *expose_area, + guint flags); +#if 0 +static gboolean gtk_cell_renderer_progress_activate (GtkCellRenderer *cell, + GdkEvent *event, + GtkWidget *widget, + const gchar *path, + GdkRectangle *background_area, + GdkRectangle *cell_area, + guint flags); +#endif +static void gtk_cell_renderer_progress_finalize (GObject *gobject); + +enum { + LAST_SIGNAL +}; + +enum { + PROP_0, + + PROP_PERCENTAGE, + PROP_TEXT, + PROP_SHOW_TEXT +}; + +static gpointer parent_class; +/* static guint progress_cell_renderer_signals [LAST_SIGNAL]; */ + +GType gtk_cell_renderer_progress_get_type (void) +{ + static GType cell_progress_type = 0; + + if (!cell_progress_type) + { + static const GTypeInfo cell_progress_info = + { + sizeof (GtkCellRendererProgressClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) gtk_cell_renderer_progress_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GtkCellRendererProgress), + 0, /* n_preallocs */ + (GInstanceInitFunc) gtk_cell_renderer_progress_init, + }; + + cell_progress_type = + g_type_register_static (GTK_TYPE_CELL_RENDERER, "GtkCellRendererProgress", + &cell_progress_info, 0); + } + + return cell_progress_type; +} + + +static void gtk_cell_renderer_progress_init (GtkCellRendererProgress *cellprogress) +{ + GTK_CELL_RENDERER(cellprogress)->mode = GTK_CELL_RENDERER_MODE_INERT; + GTK_CELL_RENDERER(cellprogress)->xpad = 2; + GTK_CELL_RENDERER(cellprogress)->ypad = 2; +} + +static void gtk_cell_renderer_progress_class_init (GtkCellRendererProgressClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(class); + GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS(class); + + parent_class = g_type_class_peek_parent (class); + object_class->finalize = gtk_cell_renderer_progress_finalize; + + object_class->get_property = gtk_cell_renderer_progress_get_property; + object_class->set_property = gtk_cell_renderer_progress_set_property; + + cell_class->get_size = gtk_cell_renderer_progress_get_size; + cell_class->render = gtk_cell_renderer_progress_render; + + g_object_class_install_property (object_class, + PROP_PERCENTAGE, + g_param_spec_double ("percentage", + "Percentage", + "The fractional progress to display", + 0, 1, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_TEXT, + g_param_spec_string ("text", + "Text", + "Text to overlay over progress bar", + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property(object_class, + PROP_SHOW_TEXT, + g_param_spec_string("text_set", + "Text set", + "Whether to overlay text on the progress bar", + FALSE, + G_PARAM_READABLE | G_PARAM_WRITABLE)); +} + + + +static void gtk_cell_renderer_progress_finalize (GObject *object) +{ +/* + GtkCellRendererProgress *cellprogress = GTK_CELL_RENDERER_PROGRESS(object); +*/ + + (* G_OBJECT_CLASS (parent_class)->finalize) (object); +} + +static void gtk_cell_renderer_progress_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *psec) +{ + GtkCellRendererProgress *cellprogress = GTK_CELL_RENDERER_PROGRESS(object); + + switch (param_id) + { + case PROP_PERCENTAGE: + g_value_set_double(value, cellprogress->progress); + break; + case PROP_TEXT: + g_value_set_string(value, cellprogress->text); + break; + case PROP_SHOW_TEXT: + g_value_set_boolean(value, cellprogress->text_set); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, psec); + break; + } +} + +static void gtk_cell_renderer_progress_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkCellRendererProgress *cellprogress = GTK_CELL_RENDERER_PROGRESS (object); + + switch (param_id) + { + case PROP_PERCENTAGE: + cellprogress->progress = g_value_get_double(value); + break; + case PROP_TEXT: + if (cellprogress->text) + g_free(cellprogress->text); + cellprogress->text = g_strdup(g_value_get_string(value)); + g_object_notify(object, "text"); + break; + case PROP_SHOW_TEXT: + cellprogress->text_set = g_value_get_boolean(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec); + break; + } +} + +GtkCellRenderer *gtk_cell_renderer_progress_new(void) +{ + return g_object_new(GTK_TYPE_CELL_RENDERER_PROGRESS, NULL); +} + +static void gtk_cell_renderer_progress_get_size (GtkCellRenderer *cell, + GtkWidget *widget, + GdkRectangle *cell_area, + gint *x_offset, + gint *y_offset, + gint *width, + gint *height) +{ + gint calc_width; + gint calc_height; + + calc_width = (gint) cell->xpad * 2 + 50; + calc_height = (gint) cell->ypad * 2 + 12; + + if (width) + *width = calc_width; + + if (height) + *height = calc_height; + + if (cell_area) + { + if (x_offset) + { + *x_offset = cell->xalign * (cell_area->width - calc_width); + *x_offset = MAX (*x_offset, 0); + } + if (y_offset) + { + *y_offset = cell->yalign * (cell_area->height - calc_height); + *y_offset = MAX (*y_offset, 0); + } + } +} + + +static void gtk_cell_renderer_progress_render (GtkCellRenderer *cell, + GdkWindow *window, + GtkWidget *widget, + GdkRectangle *background_area, + GdkRectangle *cell_area, + GdkRectangle *expose_area, + guint flags) +{ + GtkCellRendererProgress *cellprogress = (GtkCellRendererProgress *) cell; + + gint width, height; + GtkStateType state; + + width = cell_area->width; + height = cell_area->height; + + if (GTK_WIDGET_HAS_FOCUS (widget)) + state = GTK_STATE_ACTIVE; + else + state = GTK_STATE_NORMAL; + + + + width -= cell->xpad*2; + height -= cell->ypad*2; + + gtk_paint_box (widget->style, + window, + GTK_STATE_NORMAL, GTK_SHADOW_IN, + NULL, widget, "trough", + cell_area->x + cell->xpad, + cell_area->y + cell->ypad, + width - 1, height - 1); + gtk_paint_box (widget->style, + window, + state, GTK_SHADOW_OUT, + NULL, widget, "bar", + cell_area->x + cell->xpad + 1, + cell_area->y + cell->ypad + 1, + (width - 3) * cellprogress->progress, + height - 3); +} diff --git a/src/gtkcellrendererprogress.h b/src/gtkcellrendererprogress.h new file mode 100644 index 00000000..5915d767 --- /dev/null +++ b/src/gtkcellrendererprogress.h @@ -0,0 +1,64 @@ +/* gtkcellrendererprogress.h + * Taken from Gaim 0.77. + * + * Gaim is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __GTK_CELL_RENDERER_PROGRESS_H__ +#define __GTK_CELL_RENDERER_PROGRESS_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define GTK_TYPE_CELL_RENDERER_PROGRESS (gtk_cell_renderer_progress_get_type()) +#define GTK_CELL_RENDERER_PROGRESS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_CELL_RENDERER_PROGRESS, GtkCellRendererProgress)) +#define GTK_CELL_RENDERER_PROGRESS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CELL_RENDERER_PROGRESS, GtkCellRendererProgressClass)) +#define GTK_IS_CELL_PROGRESS_PROGRESS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_RENDERER_PROGRESS)) +#define GTK_IS_CELL_PROGRESS_PROGRESS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CELL_RENDERER_PROGRESS)) +#define GTK_CELL_RENDERER_PROGRESS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CELL_RENDERER_PROGRESS, GtkCellRendererProgressClass)) + +typedef struct _GtkCellRendererProgress GtkCellRendererProgress; +typedef struct _GtkCellRendererProgressClass GtkCellRendererProgressClass; + + struct _GtkCellRendererProgress { + GtkCellRenderer parent; + + gdouble progress; + gchar *text; + gboolean text_set; + }; + + struct _GtkCellRendererProgressClass { + GtkCellRendererClass parent_class; + }; + + GType gtk_cell_renderer_progress_get_type (void); + GtkCellRenderer *gtk_cell_renderer_progress_new (void); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __GTK_CELL_RENDERER_PROGRESS_H__ */ diff --git a/src/gtkcolorbutton.c b/src/gtkcolorbutton.c new file mode 100644 index 00000000..705b08e8 --- /dev/null +++ b/src/gtkcolorbutton.c @@ -0,0 +1,41 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include + +GtkWidget *gtk_color_button_new_with_color ( const GdkColor *color ) +{ + gchar *tmp = g_strdup_printf("#%.2x%.2x%.2x", color->red/256, color->green/256, color->blue/256 ); + GtkWidget *entry = gtk_entry_new (); + gtk_entry_set_text ( GTK_ENTRY(entry), tmp ); + g_free ( tmp ); + return entry; +} + +void gtk_color_button_get_color ( GtkWidget *widget, GdkColor *dest_color ) +{ + gdk_color_parse ( gtk_entry_get_text(GTK_ENTRY(widget)), dest_color ); +} + +GtkWidget *GTK_COLOR_BUTTON ( GtkWidget *w ) +{ + return w; +} diff --git a/src/http.c b/src/http.c new file mode 100644 index 00000000..99199874 --- /dev/null +++ b/src/http.c @@ -0,0 +1,309 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include + + +#ifdef WINDOWS + +#include +#include +#define access(a,b) _access(a,b) +#define close(a) closesocket(a) + +char *dirname ( char * dir ) +{ + char *tmp = dir + strlen(dir) - 1; + while ( tmp != dir && *tmp != '\\' ) + tmp--; + *tmp = '\0'; + return dir; +} + +#else + +#include +#include +#include +#include +#include +#include + +/* dirname */ +#include + +#endif + +#include "http.h" + +int http_connect(const char *hostname, int port) +{ + int sock; + struct sockaddr_in server; + struct hostent *host_addr; + + /* create a socket of type AF_INET, and SOCK_STREAM (TCP) */ + sock = socket(AF_INET, SOCK_STREAM, 0); + + /* get an IP from a domain name -- essential */ + host_addr = gethostbyname(hostname); + if (host_addr == NULL) + return(-1); + + server.sin_family = AF_INET; + /* 110 is the standard POP port. Host TO Network order. */ + server.sin_port = htons(port); + /* get the IP address. */ + server.sin_addr = *((struct in_addr *) host_addr->h_addr); + /* padding unused in sockaddr_in */ +#ifndef WINDOWS + bzero(&(server.sin_zero), 8); +#endif + + if ((connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr))) == -1) + return(-2); + + return(sock); +} + +int http_get_line(int sock, char *buf, int len) +{ + static char lilbuf; + int size, count; + + count = 1; + size = 1; + lilbuf = 'a'; + while (size != 0 && lilbuf != '\n' && count < len) + { + size = recv(sock, &lilbuf, 1, 0); + if (size == 0 && count == 1 ) + return 0; + + if (size > 0) + *buf++ = lilbuf; + count++; + } + *buf = '\0'; + + return 1; +} + +/* makes directory if neccessary */ +int http_download_get_url ( const char *hostname, const char *uri, const char *fn, int already_redirected, int sendhostname ) +{ + static char input_buffer[1024]; + int sock; + int len; + FILE *f, *tmp_f; + /* int hnlen = strlen ( hostname ); */ + + if ( access ( fn, F_OK ) == 0 ) + { + return -3; + } else { + if ( errno == ENOENT) + { + char *tmp = g_strdup ( fn ); +#ifdef WINDOWS + mkdir( dirname ( dirname ( tmp ) ) ); + g_free ( tmp ); tmp = g_strdup ( fn ); + mkdir( dirname ( tmp ) ); +#else + mkdir( dirname ( dirname ( tmp ) ), 0777 ); + g_free ( tmp ); tmp = g_strdup ( fn ); + mkdir( dirname ( tmp ), 0777 ); +#endif + g_free ( tmp ); + } + if ( ! (f = fopen ( fn, "w+b" )) ) /* immediately open file so other threads won't -- prevents race condition */ + return -4; + } +#ifdef WINDOWS + WSADATA usadata; + WSAStartup ( MAKEWORD(2,2), &usadata ); +#endif + + sock = http_connect ( hostname, 80 ); + if (sock < 0) + { + fclose ( f ); + remove ( fn ); + return -1; + } + + + if ( sendhostname ) { + send ( sock, "GET http://", 11, 0); + send ( sock, hostname, strlen(hostname), 0 ); + send ( sock, uri, strlen ( uri ), 0 ); + send ( sock, " HTTP/1.0\r\n\r\n", 13, 0 ); + } else { + send ( sock, "GET ", 4, 0 ); + send ( sock, uri, strlen ( uri ), 0 ); + send ( sock, "\r\n\r\n", 4, 0 ); + } + + /* next, skip through all headers EXCEPT content length., + that is, if it begins with "Content-Length: " (strncasecmp), + atoi that line from +16 (+17 ?), read that many bytes directly + into file (IF we can open it, else return error) and we're done. + */ + + /* "HTTP/1.x 200 OK" check */ + if ( recv ( sock, input_buffer, 12, 0 ) < 12 || input_buffer[9] != '2' || input_buffer[10] != '0' || input_buffer[11] != '0' ) + { + /* maybe it's a redirect */ + if ( ! already_redirected ) + do + { + if ( http_get_line ( sock, input_buffer, 1024 ) == 0 ) + break; + + /* Location: http://abc.def/bla */ + if ( strncmp(input_buffer, "Location: ", 10) == 0 && strlen(input_buffer) > 17 ) + { + char *uri_start; + + int rv; + uri_start = strchr(input_buffer+17,'/'); + + if ( uri_start ) + { + char *newhost = g_strndup ( input_buffer + 17, uri_start - input_buffer - 17 ); + char *newuri = strdup ( uri_start ); + fclose ( f ); + remove ( fn ); + close ( sock ); + + rv = http_download_get_url ( newhost, newuri, fn, 1, sendhostname ); + + free ( newhost ); + free ( newuri ); + return rv; + } + } + } while (input_buffer[0] != '\r' ); + + fclose ( f ); + remove ( fn ); + return 1; + } + + do + { + if ( http_get_line ( sock, input_buffer, 1024 ) == 0 ) + { + fclose ( f ); + remove ( fn ); + close ( sock ); + return -2; + } + } while (input_buffer[0] != '\r' ); + + tmp_f = tmpfile(); + + do { + len = recv ( sock, input_buffer, 1024, 0 ); + if ( len > 0 ) + fwrite ( input_buffer, 1, len, tmp_f ); + } while ( len > 0 ); + + rewind(tmp_f); + + while ( ! feof(tmp_f) ) + { + len = fread ( input_buffer, 1, 1024, tmp_f ); + fwrite ( input_buffer, 1, len, f); + } + fclose ( tmp_f ); + fclose ( f ); + + close ( sock ); +#ifdef WINDOWS + WSACleanup(); /* they sure make winsock programming easy. */ +#endif + return 0; +} + +/* success = 0, -1 = couldn't connect, -2 HTTP error, -3 file exists, -4 couldn't write to file... */ +/* uri: like "/uri.html?whatever" */ +/* only reason for the "wrapper" is so we can do redirects. */ +int a_http_download_get_url ( const char *hostname, const char *uri, const char *fn ) +{ + return http_download_get_url ( hostname, uri, fn, 0, 1 ); +} + +int a_http_download_get_url_nohostname ( const char *hostname, const char *uri, const char *fn ) +{ + return http_download_get_url ( hostname, uri, fn, 0, 0 ); +} + + +int usgs_hack ( const char *scale_factor, const char *uri, const char *fn ) +{ + static char input_buffer[1024]; + int sock; + /* int hnlen = strlen ( scale_factor ); */ + +#ifdef WINDOWS + WSADATA usadata; + WSAStartup ( MAKEWORD(2,2), &usadata ); +#endif + + sock = http_connect ( scale_factor, 80 ); + if (sock < 0) + return -1; + + send ( sock, "GET /", 5, 0); + send ( sock, uri, strlen ( uri ), 0 ); + send ( sock, " HTTP/1.0\r\n\r\n", 13, 0 ); + + /* next, skip through all headers EXCEPT content length., + that is, if it begins with "Content-Length: " (strncasecmp), + atoi that line from +16 (+17 ?), read that many bytes directly + into file (IF we can open it, else return error) and we're done. + */ + + /* "HTTP/1.x 200 OK" check */ + for (;;) { + if ( http_get_line ( sock, input_buffer, 1024 ) == 0 ) + break; + + if ( strncmp(input_buffer, "\t\t\t 28 ) + { + char *uri = input_buffer + 13; + close(sock); + uri[strlen(uri)-4] = '\0'; + return a_http_download_get_url_nohostname ( scale_factor, uri, fn ); + } + } + close ( sock ); +#ifdef WINDOWS + WSACleanup(); /* they sure make winsock programming easy. */ +#endif + return -10; +} diff --git a/src/http.h b/src/http.h new file mode 100644 index 00000000..212f80f9 --- /dev/null +++ b/src/http.h @@ -0,0 +1,29 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _VIKING_HTTP_H +#define _VIKING_HTTP_H +/* TODO: convert to Glib */ +int a_http_download_get_url ( const char *hostname, const char *uri, const char *fn ); +int a_http_download_get_url_nohostname ( const char *hostname, const char *uri, const char *fn ); +int usgs_hack ( const char *scale_factor, const char *uri, const char *fn ); + +#endif diff --git a/src/khmaps.c b/src/khmaps.c new file mode 100644 index 00000000..6c860c8d --- /dev/null +++ b/src/khmaps.c @@ -0,0 +1,126 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include "coords.h" +#include "vikcoord.h" +#include "mapcoord.h" +#include "http.h" + +#include "khmaps.h" + +/* 1 << (x-1) is like a 2**(x-1) */ +#define KH(x) ((1<<(x-1))) + +static const gdouble scale_mpps[] = { KH(1), KH(2), KH(3), KH(4), KH(5), KH(6), KH(7), KH(8), KH(9), + KH(10), KH(11), KH(12), KH(13), KH(14), KH(15) }; + +static const gint num_scales = (sizeof(scale_mpps) / sizeof(scale_mpps[0])) - 1; + +#define ERROR_MARGIN 0.01 +guint8 khmaps_zoom ( gdouble mpp ) { + gint i; + for ( i = 0; i <= num_scales; i++ ) { + if ( ABS(scale_mpps[i] - mpp) < ERROR_MARGIN ) + return i; + } + return 255; +} + +gboolean khmaps_coord_to_mapcoord ( const VikCoord *src, gdouble xzoom, gdouble yzoom, MapCoord *dest ) +{ + g_assert ( src->mode == VIK_COORD_LATLON ); + + if ( xzoom != yzoom ) + return FALSE; + + dest->scale = khmaps_zoom ( xzoom ); + if ( dest->scale == 255 ) + return FALSE; + + + dest->x = (gint) ((1<<(16 - dest->scale)) + (src->east_west / 180 * (1<<(16-dest->scale)))); + dest->y = (gint) ((1<<(16 - dest->scale)) - (src->north_south / 180 * (1<<(16-dest->scale)))); + dest->z = 0; + return TRUE; +} + +void khmaps_mapcoord_to_center_coord ( MapCoord *src, VikCoord *dest ) +{ + dest->mode = VIK_COORD_LATLON; + dest->east_west = (src->x+0.5 - (1<<(16 - src->scale))) * 180 / (1<<(16-src->scale)); + dest->north_south = -((src->y+0.5 - (1<<(16 - src->scale))) * 180 / (1<<(16-src->scale))); +} + +static char *kh_encode(guint32 x, guint32 y, guint8 scale) +{ + gchar *buf = g_malloc ( (20-scale)*sizeof(gchar) ); + guint32 ya = 1 << (17 - scale); + gint8 i, j; + + if (y < 0 || (ya-1 < y)) { + strcpy(buf,"tqq"); /* BAD */ + return buf; + } + if (x < 0 || ya-1 < x) { + x %= ya; + if (x < 0) + x += ya; + } + + buf[0] = 't'; + for (j = 1, i = 16; i >= scale; i--, j++) { + ya /= 2; + if (y < ya) { + if (x < ya) + buf[j]='q'; + else { + buf[j]='r'; + x -= ya; + } + } else { + if (x < ya) { + buf[j] = 't'; + y -= ya; + } else { + buf[j] = 's'; + x -= ya; + y -= ya; + } + } + } + buf[j] = '\0'; + return buf; +} + +void khmaps_download ( MapCoord *src, const gchar *dest_fn ) +{ + gchar *tmp = kh_encode(src->x, src->y, src->scale); + gchar *uri = g_strdup_printf ( "/kh?v=2&t=%s", tmp ); + g_print("%d %d %d = %s\n", src->x, src->y, src->scale, uri); + g_free ( tmp ); + a_http_download_get_url ( "kh.google.com", uri, dest_fn ); + g_free ( uri ); +} + +/* Popularity has its disadvantages ... */ diff --git a/src/khmaps.h b/src/khmaps.h new file mode 100644 index 00000000..a2bbd4a6 --- /dev/null +++ b/src/khmaps.h @@ -0,0 +1,36 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __VIKING_KHMAPS_H +#define __VIKING_KHMAPS_H + +guint8 khmaps_zoom ( gdouble mpp ); + +/* a bit misleading, this is the "mpp" (really just set zoom level, very + * roughly equivalent so you can easily switch between maps) of + * kh maps 1, the second kh maps level (1st is 0). */ +#define KHMAPS_ZOOM_ONE_MPP 0.00001 + +gboolean khmaps_coord_to_mapcoord ( const VikCoord *src, gdouble xzoom, gdouble yzoom, MapCoord *dest ); +void khmaps_mapcoord_to_center_coord ( MapCoord *src, VikCoord *dest ); +void khmaps_download ( MapCoord *src, const gchar *dest_fn ); + +#endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 00000000..09497d3e --- /dev/null +++ b/src/main.c @@ -0,0 +1,110 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "viking.h" +#include "mapcache.h" +#include "background.h" + +#include + +#define MAX_WINDOWS 1024 + +static guint window_count = 0; + +static VikWindow *new_window (); +static void open_window ( VikWindow *vw, const gchar **files ); +static void destroy( GtkWidget *widget, + gpointer data ); + + +/* Another callback */ +static void destroy( GtkWidget *widget, + gpointer data ) +{ + if ( ! --window_count ) + gtk_main_quit (); +} + +static VikWindow *new_window () +{ + if ( window_count < MAX_WINDOWS ) + { + VikWindow *vw = vik_window_new (); + + g_signal_connect (G_OBJECT (vw), "destroy", + G_CALLBACK (destroy), NULL); + g_signal_connect (G_OBJECT (vw), "newwindow", + G_CALLBACK (new_window), NULL); + g_signal_connect (G_OBJECT (vw), "openwindow", + G_CALLBACK (open_window), NULL); + + gtk_widget_show_all ( GTK_WIDGET(vw) ); + + window_count++; + + return vw; + } + return NULL; +} + +static void open_window ( VikWindow *vw, const gchar **files ) +{ + VikWindow *newvw = new_window(); + gboolean change_fn = (!files[1]); /* only change fn if one file */ + if ( newvw ) + while ( *files ) { + vik_window_open_file ( newvw, *(files++), change_fn ); + files++; + } +} + +int main( int argc, char *argv[] ) +{ + VikWindow *first_window; + gboolean dashdash_already = FALSE; + int i = 0; + + g_thread_init ( NULL ); + gdk_threads_init (); + + gtk_init (&argc, &argv); + + a_mapcache_init (); + a_background_init (); + + first_window = new_window(); + + gdk_threads_enter (); + while ( ++i < argc ) { + if ( strcmp(argv[i],"--") == 0 && !dashdash_already ) + dashdash_already = TRUE; /* hack to open '-' */ + else + vik_window_open_file ( first_window, argv[i], argc == 2 ); + } + + gtk_main (); + gdk_threads_leave (); + + a_clipboard_uninit (); + a_mapcache_uninit (); + + return 0; +} diff --git a/src/mapcache.c b/src/mapcache.c new file mode 100644 index 00000000..5ae0fc89 --- /dev/null +++ b/src/mapcache.c @@ -0,0 +1,172 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include "mapcache.h" + +#include "config.h" + +typedef struct _List { + struct _List *next; + gchar *key; +} List; + +/* a circular linked list, a pointer to the tail, and the tail points to the head */ +/* this is so we can free the last */ +static List *queue_tail = NULL; +static int queue_count = 0; + +#define VIK_CONFIG_MAPCACHE_MAX_SIZE 50331648 /* 48 meggerbytes */ +static guint32 queue_size = 0; + + +static GHashTable *cache = NULL; + +#define HASHKEY_FORMAT_STRING "%d-%d-%d-%d-%d-%d-%.3f-%.3f" +#define HASHKEY_FORMAT_STRING_NOSHRINK "%d-%d-%d-%d-%d-%d-" + +void a_mapcache_init () +{ + cache = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, g_object_unref ); +} + +/* returns key from head, adds on newtailkey to tail. */ +static gchar *list_shift_add_entry ( gchar *newtailkey ) +{ + gchar *oldheadkey = queue_tail->next->key; + queue_tail->next->key = newtailkey; + queue_tail = queue_tail->next; + return oldheadkey; +} + +static gchar *list_shift () +{ + gchar *oldheadkey = queue_tail->next->key; + List *oldhead = queue_tail->next; + queue_tail->next = queue_tail->next->next; + g_free ( oldhead ); + return oldheadkey; +} + +/* adds key to tail */ +static void list_add_entry ( gchar *key ) +{ + List *newlist = g_malloc ( sizeof ( List ) ); + newlist->key = key; + if ( queue_tail ) { + newlist->next = queue_tail->next; + queue_tail->next = newlist; + queue_tail = newlist; + } else { + newlist->next = newlist; + queue_tail = newlist; + } +} + +void a_mapcache_add ( GdkPixbuf *pixbuf, gint x, gint y, gint z, guint8 type, guint zoom, guint8 alpha, gdouble xshrinkfactor, gdouble yshrinkfactor ) +{ + gchar *key = g_strdup_printf ( HASHKEY_FORMAT_STRING, x, y, z, type, zoom, alpha, xshrinkfactor, yshrinkfactor ); + static int tmp = 0; + + /* FIXME: what if already exists? problem! */ + g_hash_table_insert ( cache, key, pixbuf ); + + queue_size += gdk_pixbuf_get_rowstride(pixbuf) * gdk_pixbuf_get_height(pixbuf); + queue_size += 100; + queue_count++; + + if ( queue_size > VIK_CONFIG_MAPCACHE_SIZE ) { + gchar *oldkey = list_shift_add_entry ( key ); + GdkPixbuf *oldbuf = g_hash_table_lookup ( cache, oldkey ); + queue_size -= gdk_pixbuf_get_rowstride(oldbuf) * gdk_pixbuf_get_height(oldbuf); + queue_size -= 100; + queue_count --; + g_hash_table_remove ( cache, oldkey ); + while ( queue_size > VIK_CONFIG_MAPCACHE_SIZE ) { + oldkey = list_shift (); + oldbuf = g_hash_table_lookup ( cache, oldkey ); + queue_size -= gdk_pixbuf_get_rowstride(oldbuf) * gdk_pixbuf_get_height(oldbuf); + queue_size -= 100; + queue_count --; + g_hash_table_remove ( cache, oldkey ); + } + + /* chop off 'start' etc */ + } else { + list_add_entry ( key ); + /* business as usual */ + } + + if ( (++tmp == 100 )) { g_print("DEBUG: queue count=%d %ld\n", queue_count, queue_size ); tmp=0; } +} + +GdkPixbuf *a_mapcache_get ( gint x, gint y, gint z, guint8 type, guint zoom, guint8 alpha, gdouble xshrinkfactor, gdouble yshrinkfactor ) +{ + static char key[48]; + g_snprintf ( key, sizeof(key), HASHKEY_FORMAT_STRING, x, y, z, type, zoom, alpha, xshrinkfactor, yshrinkfactor ); + return g_hash_table_lookup ( cache, key ); +} + +void a_mapcache_remove_all_shrinkfactors ( guint16 x, guint16 y, guint16 z, guint8 type, guint zoom, guint8 alpha ) +{ + char key[40]; + List *loop = queue_tail; + List *tmp; + gint len; + + if ( queue_tail == NULL ) + return; + + g_snprintf ( key, sizeof(key), HASHKEY_FORMAT_STRING_NOSHRINK, x, y, z, type, zoom, alpha ); + len = strlen(key); + + /* TODO: check logic here */ + do { + tmp = loop->next; + if ( strncmp(tmp->key, key, len) == 0 ) + { + g_hash_table_remove ( cache, tmp->key ); + if ( tmp == loop ) + loop = queue_tail = NULL; + else + loop->next = tmp->next; + g_free ( tmp ); + tmp = NULL; + } + else + loop = tmp; + + } while ( loop && (loop != queue_tail || tmp == NULL) ); + + /* loop thru list, looking for the one, compare first whatever chars */ + + g_hash_table_remove ( cache, key ); +} + +void a_mapcache_uninit () +{ + g_hash_table_destroy ( cache ); + /* free list */ + cache = NULL; +} + + diff --git a/src/mapcache.h b/src/mapcache.h new file mode 100644 index 00000000..2cc7ed9a --- /dev/null +++ b/src/mapcache.h @@ -0,0 +1,31 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __VIKING_MAPCACHE_H +#define __VIKING_MAPCACHE_H + +void a_mapcache_init (); +void a_mapcache_add ( GdkPixbuf *pixbuf, gint x, gint y, gint z, guint8 type, guint zoom, guint8 alpha, gdouble xshrinkfactor, gdouble yshrinkfactor ); +GdkPixbuf *a_mapcache_get ( gint x, gint y, gint z, guint8 type, guint zoom, guint8 alpha, gdouble xshrinkfactor, gdouble yshrinkfactor ); +void a_mapcache_remove_all_shrinkfactors ( guint16 x, guint16 y, guint16 z, guint8 type, guint zoom, guint8 alpha ); +void a_mapcache_uninit (); + +#endif diff --git a/src/mapcoord.h b/src/mapcoord.h new file mode 100644 index 00000000..6f22168e --- /dev/null +++ b/src/mapcoord.h @@ -0,0 +1,29 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* common struct to all map types and map layer, to hold info about a + * particular tile */ + +typedef struct { + gint x, y; + gint z; /* zone or anything else */ + guint scale; +} MapCoord; diff --git a/src/terraserver.c b/src/terraserver.c new file mode 100644 index 00000000..9ddcb1af --- /dev/null +++ b/src/terraserver.c @@ -0,0 +1,108 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include "coords.h" +#include "vikcoord.h" +#include "mapcoord.h" +#include "http.h" + +#define TERRASERVER_SITE "terraserver-usa.com" +#define MARGIN_OF_ERROR 0.001 + +static int mpp_to_scale ( gdouble mpp, guint8 type ) +{ + mpp *= 4; + gint t = (gint) mpp; + if ( ABS(mpp - t) > MARGIN_OF_ERROR ) + return FALSE; + + switch ( t ) { + case 1: return (type == 4) ? 8 : 0; + case 2: return (type == 4) ? 9 : 0; + case 4: return (type != 2) ? 10 : 0; + case 8: return 11; + case 16: return 12; + case 32: return 13; + case 64: return 14; + case 128: return 15; + case 256: return 16; + case 512: return 17; + case 1024: return 18; + case 2048: return 19; + default: return 0; + } +} + +static gdouble scale_to_mpp ( gint scale ) +{ + return pow(2,scale - 10); +} + +static gboolean terraserver_coord_to_mapcoord ( const VikCoord *src, gdouble xmpp, gdouble ympp, MapCoord *dest, guint8 type ) +{ + g_assert ( src->mode == VIK_COORD_UTM ); + + if ( xmpp != ympp ) + return FALSE; + + dest->scale = mpp_to_scale ( xmpp, type ); + if ( ! dest->scale ) + return FALSE; + + dest->x = (gint)(((gint)(src->east_west))/(200*xmpp)); + dest->y = (gint)(((gint)(src->north_south))/(200*xmpp)); + dest->z = src->utm_zone; + return TRUE; +} + +gboolean terraserver_topo_coord_to_mapcoord ( const VikCoord *src, gdouble xmpp, gdouble ympp, MapCoord *dest ) +{ return terraserver_coord_to_mapcoord ( src, xmpp, ympp, dest, 2 ); } +gboolean terraserver_aerial_coord_to_mapcoord ( const VikCoord *src, gdouble xmpp, gdouble ympp, MapCoord *dest ) +{ return terraserver_coord_to_mapcoord ( src, xmpp, ympp, dest, 1 ); } +gboolean terraserver_urban_coord_to_mapcoord ( const VikCoord *src, gdouble xmpp, gdouble ympp, MapCoord *dest ) +{ return terraserver_coord_to_mapcoord ( src, xmpp, ympp, dest, 4 ); } + +void terraserver_mapcoord_to_center_coord ( MapCoord *src, VikCoord *dest ) +{ + // FIXME: slowdown here! + gdouble mpp = scale_to_mpp ( src->scale ); + dest->mode = VIK_COORD_UTM; + dest->utm_zone = src->z; + dest->east_west = ((src->x * 200) + 100) * mpp; + dest->north_south = ((src->y * 200) + 100) * mpp; +} + +static void terraserver_download ( MapCoord *src, const gchar *dest_fn, guint8 type ) +{ + gchar *uri = g_strdup_printf ( "/tile.ashx?T=%d&S=%d&X=%d&Y=%d&Z=%d", type, + src->scale, src->x, src->y, src->z ); + a_http_download_get_url_nohostname ( TERRASERVER_SITE, uri, dest_fn ); + g_free ( uri ); +} + +void terraserver_topo_download ( MapCoord *src, const gchar *dest_fn ) +{ terraserver_download ( src, dest_fn, 2 ); } +void terraserver_aerial_download ( MapCoord *src, const gchar *dest_fn ) +{ terraserver_download ( src, dest_fn, 1 ); } +void terraserver_urban_download ( MapCoord *src, const gchar *dest_fn ) +{ terraserver_download ( src, dest_fn, 4 ); } diff --git a/src/terraserver.h b/src/terraserver.h new file mode 100644 index 00000000..cfab1e44 --- /dev/null +++ b/src/terraserver.h @@ -0,0 +1,36 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __VIKING_TERRASERVER_H +#define __VIKING_TERRASERVER_H + +gboolean terraserver_topo_coord_to_mapcoord ( const VikCoord *src, gdouble xmpp, gdouble ympp, MapCoord *dest ); +void terraserver_topo_download ( MapCoord *src, const gchar *dest_fn ); + +gboolean terraserver_aerial_coord_to_mapcoord ( const VikCoord *src, gdouble xmpp, gdouble ympp, MapCoord *dest ); +void terraserver_aerial_download ( MapCoord *src, const gchar *dest_fn ); + +gboolean terraserver_urban_coord_to_mapcoord ( const VikCoord *src, gdouble xmpp, gdouble ympp, MapCoord *dest ); +void terraserver_urban_download ( MapCoord *src, const gchar *dest_fn ); + +void terraserver_mapcoord_to_center_coord ( MapCoord *src, VikCoord *dest ); + +#endif diff --git a/src/thumbnails.c b/src/thumbnails.c new file mode 100644 index 00000000..f132e756 --- /dev/null +++ b/src/thumbnails.c @@ -0,0 +1,544 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * Large (and important) sections of this file were adapted from + * ROX-Filer source code, Copyright (C) 2003, the ROX-Filer team, + * originally licensed under the GPL v2 or greater (as above). + * + */ + +#include +#include +#include +#include +#include "viking.h" +#include "thumbnails.h" +#include "thumbnails_pixbuf.h" + +#ifdef __CYGWIN__ +#ifdef __CYGWIN_USE_BIG_TYPES__ +#define ST_SIZE_FMT "%lld" +#else +#define ST_SIZE_FMT "%ld" +#endif +#else +/* FIXME -- on some systems this may need to me "lld", see ROX-Filer code */ +#define ST_SIZE_FMT "%ld" +#endif + +#undef MIN /* quit yer whining, gcc */ +#undef MAX +#include /* for realpath() */ + +#ifdef WINDOWS +#define HOME_DIR "C:\\VIKING" +#define THUMB_DIR "\\THUMBNAILS\\" /* viking maps default viking\maps */ +#define THUMB_SUB_DIR "normal\\" +#define mkdir(a,b) mkdir(a) +#define MAX(a,b) (((a)>(b))?(a):(b)) +#define realpath(X,Y) _fullpath(Y,X,MAX_PATH) + +#else +#define HOME_DIR g_get_home_dir() +#define THUMB_DIR "/.thumbnails/" +#define THUMB_SUB_DIR "normal/" +#endif + +#define PIXMAP_THUMB_SIZE 128 + +static char *md5_hash(const char *message); +static char *pathdup(const char *path); +static GdkPixbuf *save_thumbnail(const char *pathname, GdkPixbuf *full); +static GdkPixbuf *child_create_thumbnail(const gchar *path); + +gboolean a_thumbnails_exists ( const gchar *filename ) +{ + GdkPixbuf *pixbuf = a_thumbnails_get(filename); + if ( pixbuf ) + { + g_object_unref ( G_OBJECT ( pixbuf ) ); + return TRUE; + } + return FALSE; +} + +GdkPixbuf *a_thumbnails_get_default () +{ + return gdk_pixbuf_from_pixdata ( &tnnyl_pixbuf, FALSE, NULL ); +} + +/* filename must be absolute. you could have a function to make sure it exists and absolutize it */ + +void a_thumbnails_create(const gchar *filename) +{ + GdkPixbuf *pixbuf = a_thumbnails_get(filename); + + if ( ! pixbuf ) + pixbuf = child_create_thumbnail(filename); + + if ( pixbuf ) + g_object_unref ( G_OBJECT ( pixbuf ) ); +} + +GdkPixbuf *a_thumbnails_scale_pixbuf(GdkPixbuf *src, int max_w, int max_h) +{ + int w, h; + + w = gdk_pixbuf_get_width(src); + h = gdk_pixbuf_get_height(src); + + if (w <= max_w && h <= max_h) + { + gdk_pixbuf_ref(src); + return src; + } + else + { + float scale_x = ((float) w) / max_w; + float scale_y = ((float) h) / max_h; + float scale = MAX(scale_x, scale_y); + int dest_w = w / scale; + int dest_h = h / scale; + + return gdk_pixbuf_scale_simple(src, + MAX(dest_w, 1), + MAX(dest_h, 1), + GDK_INTERP_BILINEAR); + } +} + +static GdkPixbuf *child_create_thumbnail(const gchar *path) +{ + GdkPixbuf *image; + + image = gdk_pixbuf_new_from_file(path, NULL); + + if (image) + { + GdkPixbuf *thumb = save_thumbnail(path, image); + gdk_pixbuf_unref ( image ); + return thumb; + } + + return NULL; +} + +static GdkPixbuf *save_thumbnail(const char *pathname, GdkPixbuf *full) +{ + struct stat info; + gchar *path; + int original_width, original_height; + GString *to; + char *md5, *swidth, *sheight, *ssize, *smtime, *uri; + mode_t old_mask; + int name_len; + GdkPixbuf *thumb; + + if (stat(pathname, &info) != 0) + return NULL; + + thumb = a_thumbnails_scale_pixbuf(full, PIXMAP_THUMB_SIZE, PIXMAP_THUMB_SIZE); + + original_width = gdk_pixbuf_get_width(full); + original_height = gdk_pixbuf_get_height(full); + + + swidth = g_strdup_printf("%d", original_width); + sheight = g_strdup_printf("%d", original_height); + ssize = g_strdup_printf(ST_SIZE_FMT, info.st_size); + smtime = g_strdup_printf("%ld", (long) info.st_mtime); + + path = pathdup(pathname); + uri = g_strconcat("file://", path, NULL); + md5 = md5_hash(uri); + g_free(path); + + to = g_string_new(HOME_DIR); +#ifndef WINDOWS + mkdir(to->str, 0700); +#endif + g_string_append(to, THUMB_DIR); + mkdir(to->str, 0700); + g_string_append(to, THUMB_SUB_DIR); + mkdir(to->str, 0700); + g_string_append(to, md5); + name_len = to->len + 4; /* Truncate to this length when renaming */ +#ifdef WINDOWS + g_string_append_printf(to, ".png.Viking"); +#else + g_string_append_printf(to, ".png.Viking-%ld", (long) getpid()); +#endif + + g_free(md5); + + old_mask = umask(0077); + gdk_pixbuf_save(thumb, to->str, "png", NULL, + "tEXt::Thumb::Image::Width", swidth, + "tEXt::Thumb::Image::Height", sheight, + "tEXt::Thumb::Size", ssize, + "tEXt::Thumb::MTime", smtime, + "tEXt::Thumb::URI", uri, + "tEXt::Software", PROJECT, + NULL); + umask(old_mask); + + /* We create the file ###.png.ROX-Filer-PID and rename it to avoid + * a race condition if two programs create the same thumb at + * once. + */ + { + gchar *final; + + final = g_strndup(to->str, name_len); + if (rename(to->str, final)) + { + g_warning("Failed to rename '%s' to '%s': %s", + to->str, final, g_strerror(errno)); + g_object_unref ( G_OBJECT(thumb) ); + thumb = NULL; /* return NULL */ + } + + g_free(final); + } + + g_string_free(to, TRUE); + g_free(swidth); + g_free(sheight); + g_free(ssize); + g_free(smtime); + g_free(uri); + + return thumb; +} + + +GdkPixbuf *a_thumbnails_get(const gchar *pathname) +{ + GdkPixbuf *thumb = NULL; + char *thumb_path, *md5, *uri, *path; + const char *ssize, *smtime; + struct stat info; + + path = pathdup(pathname); + uri = g_strconcat("file://", path, NULL); + md5 = md5_hash(uri); + g_free(uri); + + thumb_path = g_strdup_printf("%s%s%s%s.png", HOME_DIR, THUMB_DIR, THUMB_SUB_DIR, md5); + + g_free(md5); + + thumb = gdk_pixbuf_new_from_file(thumb_path, NULL); + if (!thumb) + goto err; + + /* Note that these don't need freeing... */ + ssize = gdk_pixbuf_get_option(thumb, "tEXt::Thumb::Size"); + if (!ssize) + goto err; + + smtime = gdk_pixbuf_get_option(thumb, "tEXt::Thumb::MTime"); + if (!smtime) + goto err; + + if (stat(path, &info) != 0) + goto err; + + if (info.st_mtime != atol(smtime) || info.st_size != atol(ssize)) + goto err; + + goto out; +err: + if (thumb) + gdk_pixbuf_unref(thumb); + thumb = NULL; +out: + g_free(path); + g_free(thumb_path); + return thumb; +} + +/* pathdup() stuff */ + +static char *pathdup(const char *path) +{ + char real[MAXPATHLEN]; + + g_return_val_if_fail(path != NULL, NULL); + + if (realpath(path, real)) + return g_strdup(real); + + return g_strdup(path); +} + +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. The original code was + * written by Colin Plumb in 1993, and put in the public domain. + * + * Modified to use glib datatypes. Put under GPL to simplify + * licensing for ROX-Filer. Taken from Debian's dpkg package. + * + */ + +#define md5byte unsigned char + +typedef struct _MD5Context MD5Context; + +struct _MD5Context { + guint32 buf[4]; + guint32 bytes[2]; + guint32 in[16]; +}; + +static void MD5Init(MD5Context *ctx); +static void MD5Update(MD5Context *ctx, md5byte const *buf, unsigned len); +static char *MD5Final(MD5Context *ctx); +static void MD5Transform(guint32 buf[4], guint32 const in[16]); + +#if G_BYTE_ORDER == G_BIG_ENDIAN +static void byteSwap(guint32 *buf, unsigned words) +{ + md5byte *p = (md5byte *)buf; + + do { + *buf++ = (guint32)((unsigned)p[3] << 8 | p[2]) << 16 | + ((unsigned)p[1] << 8 | p[0]); + p += 4; + } while (--words); +} +#else +#define byteSwap(buf,words) +#endif + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +static void MD5Init(MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bytes[0] = 0; + ctx->bytes[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +static void MD5Update(MD5Context *ctx, md5byte const *buf, unsigned len) +{ + guint32 t; + + /* Update byte count */ + + t = ctx->bytes[0]; + if ((ctx->bytes[0] = t + len) < t) + ctx->bytes[1]++; /* Carry from low to high */ + + t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */ + if (t > len) { + memcpy((md5byte *)ctx->in + 64 - t, buf, len); + return; + } + /* First chunk is an odd size */ + memcpy((md5byte *)ctx->in + 64 - t, buf, t); + byteSwap(ctx->in, 16); + MD5Transform(ctx->buf, ctx->in); + buf += t; + len -= t; + + /* Process data in 64-byte chunks */ + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteSwap(ctx->in, 16); + MD5Transform(ctx->buf, ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + * Returns the newly allocated string of the hash. + */ +static char *MD5Final(MD5Context *ctx) +{ + char *retval; + int i; + int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */ + md5byte *p = (md5byte *)ctx->in + count; + guint8 *bytes; + + /* Set the first char of padding to 0x80. There is always room. */ + *p++ = 0x80; + + /* Bytes of padding needed to make 56 bytes (-8..55) */ + count = 56 - 1 - count; + + if (count < 0) { /* Padding forces an extra block */ + memset(p, 0, count + 8); + byteSwap(ctx->in, 16); + MD5Transform(ctx->buf, ctx->in); + p = (md5byte *)ctx->in; + count = 56; + } + memset(p, 0, count); + byteSwap(ctx->in, 14); + + /* Append length in bits and transform */ + ctx->in[14] = ctx->bytes[0] << 3; + ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29; + MD5Transform(ctx->buf, ctx->in); + + byteSwap(ctx->buf, 4); + + retval = g_malloc(33); + bytes = (guint8 *) ctx->buf; + for (i = 0; i < 16; i++) + sprintf(retval + (i * 2), "%02x", bytes[i]); + retval[32] = '\0'; + + return retval; +} + +# ifndef ASM_MD5 + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f,w,x,y,z,in,s) \ + (w += f(x,y,z) + in, w = (w<>(32-s)) + x) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void MD5Transform(guint32 buf[4], guint32 const in[16]) +{ + register guint32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +# endif /* ASM_MD5 */ + +static char *md5_hash(const char *message) +{ + MD5Context ctx; + + MD5Init(&ctx); + MD5Update(&ctx, message, strlen(message)); + return MD5Final(&ctx); +} diff --git a/src/thumbnails.h b/src/thumbnails.h new file mode 100644 index 00000000..723b9b14 --- /dev/null +++ b/src/thumbnails.h @@ -0,0 +1,31 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __VIKING_THUMBNAILS_H +#define __VIKING_THUMBNAILS_H + +gboolean a_thumbnails_exists ( const gchar *filename ); +void a_thumbnails_create ( const gchar *filename ); +GdkPixbuf *a_thumbnails_get(const gchar *filename); +GdkPixbuf *a_thumbnails_get_default (); +GdkPixbuf *a_thumbnails_scale_pixbuf(GdkPixbuf *src, int max_w, int max_h); + +#endif diff --git a/src/thumbnails_pixbuf.h b/src/thumbnails_pixbuf.h new file mode 100644 index 00000000..2d024764 --- /dev/null +++ b/src/thumbnails_pixbuf.h @@ -0,0 +1,231 @@ +/* GdkPixbuf RGB C-Source image dump 1-byte-run-length-encoded */ + +static const GdkPixdata tnnyl_pixbuf = { + 0x47646b50, /* Pixbuf magic: 'GdkP' */ + 24 + 4636, /* header length + pixel_data length */ + 0x2010001, /* pixdata_type */ + 384, /* rowstride */ + 128, /* width */ + 128, /* height */ + /* pixel_data: */ + "\377\0\0\0\377\0\0\0\377\0\0\0\206\0\0\0\372\377\377\377\206\0\0\0\372" + "\377\377\377\206\0\0\0\372\377\377\377\206\0\0\0\372\377\377\377\206" + "\0\0\0\372\377\377\377\206\0\0\0\372\377\377\377\206\0\0\0\372\377\377" + "\377\206\0\0\0\372\377\377\377\206\0\0\0\372\377\377\377\206\0\0\0\372" + "\377\377\377\206\0\0\0\372\377\377\377\206\0\0\0\372\377\377\377\206" + "\0\0\0\372\377\377\377\206\0\0\0\372\377\377\377\206\0\0\0\372\377\377" + "\377\206\0\0\0\372\377\377\377\206\0\0\0\372\377\377\377\206\0\0\0\372" + "\377\377\377\206\0\0\0\372\377\377\377\206\0\0\0\372\377\377\377\206" + "\0\0\0\372\377\377\377\206\0\0\0\372\377\377\377\206\0\0\0\372\377\377" + "\377\206\0\0\0\372\377\377\377\206\0\0\0\222\377\377\377\202\0\0\0\260" + "\377\377\377\202\0\0\0\247\377\377\377\202\0\0\0\204\377\377\377\202" + "\0\0\0\205\377\377\377\206\0\0\0\203\377\377\377\216\0\0\0\1\377\377" + "\377\202\0\0\0\260\377\377\377\202\0\0\0\247\377\377\377\202\0\0\0\204" + "\377\377\377\202\0\0\0\205\377\377\377\206\0\0\0\203\377\377\377\216" + "\0\0\0\1\377\377\377\202\0\0\0\260\377\377\377\202\0\0\0\247\377\377" + "\377\202\0\0\0\204\377\377\377\202\0\0\0\205\377\377\377\206\0\0\0\211" + "\377\377\377\202\0\0\0\207\377\377\377\202\0\0\0\260\377\377\377\202" + "\0\0\0\255\377\377\377\202\0\0\0\205\377\377\377\206\0\0\0\211\377\377" + "\377\202\0\0\0\207\377\377\377\202\0\0\0\260\377\377\377\202\0\0\0\255" + "\377\377\377\202\0\0\0\205\377\377\377\206\0\0\0\211\377\377\377\202" + "\0\0\0\207\377\377\377\202\0\0\0\202\377\377\377\204\0\0\0\206\377\377" + "\377\202\0\0\0\206\377\377\377\202\0\0\0\204\377\377\377\202\0\0\0\202" + "\377\377\377\204\0\0\0\204\377\377\377\204\0\0\0\206\377\377\377\202" + "\0\0\0\202\377\377\377\204\0\0\0\206\377\377\377\202\0\0\0\202\377\377" + "\377\204\0\0\0\207\377\377\377\206\0\0\0\206\377\377\377\202\0\0\0\204" + "\377\377\377\202\0\0\0\205\377\377\377\206\0\0\0\211\377\377\377\202" + "\0\0\0\207\377\377\377\211\0\0\0\205\377\377\377\202\0\0\0\206\377\377" + "\377\202\0\0\0\204\377\377\377\211\0\0\0\1\377\377\377\207\0\0\0\205" + "\377\377\377\211\0\0\0\205\377\377\377\211\0\0\0\205\377\377\377\210" + "\0\0\0\205\377\377\377\202\0\0\0\204\377\377\377\202\0\0\0\205\377\377" + "\377\206\0\0\0\211\377\377\377\202\0\0\0\207\377\377\377\203\0\0\0\204" + "\377\377\377\203\0\0\0\204\377\377\377\202\0\0\0\206\377\377\377\202" + "\0\0\0\204\377\377\377\203\0\0\0\204\377\377\377\204\0\0\0\204\377\377" + "\377\203\0\0\0\204\377\377\377\204\0\0\0\203\377\377\377\203\0\0\0\204" + "\377\377\377\203\0\0\0\204\377\377\377\203\0\0\0\204\377\377\377\1\0" + "\0\0\205\377\377\377\203\0\0\0\204\377\377\377\202\0\0\0\204\377\377" + "\377\202\0\0\0\205\377\377\377\206\0\0\0\211\377\377\377\202\0\0\0\207" + "\377\377\377\202\0\0\0\206\377\377\377\202\0\0\0\204\377\377\377\202" + "\0\0\0\206\377\377\377\202\0\0\0\204\377\377\377\202\0\0\0\206\377\377" + "\377\202\0\0\0\206\377\377\377\202\0\0\0\204\377\377\377\203\0\0\0\205" + "\377\377\377\203\0\0\0\203\377\377\377\202\0\0\0\206\377\377\377\202" + "\0\0\0\213\377\377\377\202\0\0\0\204\377\377\377\202\0\0\0\204\377\377" + "\377\202\0\0\0\205\377\377\377\206\0\0\0\211\377\377\377\202\0\0\0\207" + "\377\377\377\202\0\0\0\206\377\377\377\202\0\0\0\204\377\377\377\202" + "\0\0\0\206\377\377\377\202\0\0\0\204\377\377\377\202\0\0\0\206\377\377" + "\377\202\0\0\0\206\377\377\377\202\0\0\0\204\377\377\377\202\0\0\0\207" + "\377\377\377\202\0\0\0\203\377\377\377\202\0\0\0\206\377\377\377\202" + "\0\0\0\205\377\377\377\210\0\0\0\204\377\377\377\202\0\0\0\204\377\377" + "\377\202\0\0\0\205\377\377\377\206\0\0\0\211\377\377\377\202\0\0\0\207" + "\377\377\377\202\0\0\0\206\377\377\377\202\0\0\0\204\377\377\377\202" + "\0\0\0\206\377\377\377\202\0\0\0\204\377\377\377\202\0\0\0\206\377\377" + "\377\202\0\0\0\206\377\377\377\202\0\0\0\204\377\377\377\202\0\0\0\207" + "\377\377\377\202\0\0\0\203\377\377\377\202\0\0\0\206\377\377\377\202" + "\0\0\0\204\377\377\377\211\0\0\0\204\377\377\377\202\0\0\0\204\377\377" + "\377\202\0\0\0\205\377\377\377\206\0\0\0\211\377\377\377\202\0\0\0\207" + "\377\377\377\202\0\0\0\206\377\377\377\202\0\0\0\204\377\377\377\202" + "\0\0\0\206\377\377\377\202\0\0\0\204\377\377\377\202\0\0\0\206\377\377" + "\377\202\0\0\0\206\377\377\377\202\0\0\0\204\377\377\377\202\0\0\0\207" + "\377\377\377\202\0\0\0\203\377\377\377\202\0\0\0\206\377\377\377\202" + "\0\0\0\203\377\377\377\203\0\0\0\205\377\377\377\202\0\0\0\204\377\377" + "\377\202\0\0\0\204\377\377\377\202\0\0\0\205\377\377\377\206\0\0\0\211" + "\377\377\377\202\0\0\0\207\377\377\377\202\0\0\0\206\377\377\377\202" + "\0\0\0\204\377\377\377\202\0\0\0\206\377\377\377\202\0\0\0\204\377\377" + "\377\202\0\0\0\206\377\377\377\202\0\0\0\206\377\377\377\202\0\0\0\204" + "\377\377\377\202\0\0\0\207\377\377\377\202\0\0\0\203\377\377\377\202" + "\0\0\0\206\377\377\377\202\0\0\0\203\377\377\377\202\0\0\0\206\377\377" + "\377\202\0\0\0\204\377\377\377\202\0\0\0\204\377\377\377\202\0\0\0\205" + "\377\377\377\206\0\0\0\211\377\377\377\202\0\0\0\207\377\377\377\202" + "\0\0\0\206\377\377\377\202\0\0\0\204\377\377\377\202\0\0\0\206\377\377" + "\377\202\0\0\0\204\377\377\377\202\0\0\0\206\377\377\377\202\0\0\0\206" + "\377\377\377\202\0\0\0\204\377\377\377\203\0\0\0\205\377\377\377\203" + "\0\0\0\203\377\377\377\202\0\0\0\206\377\377\377\202\0\0\0\203\377\377" + "\377\202\0\0\0\205\377\377\377\203\0\0\0\204\377\377\377\202\0\0\0\204" + "\377\377\377\202\0\0\0\205\377\377\377\206\0\0\0\211\377\377\377\202" + "\0\0\0\207\377\377\377\202\0\0\0\206\377\377\377\202\0\0\0\204\377\377" + "\377\203\0\0\0\204\377\377\377\203\0\0\0\204\377\377\377\202\0\0\0\206" + "\377\377\377\202\0\0\0\206\377\377\377\202\0\0\0\204\377\377\377\204" + "\0\0\0\203\377\377\377\203\0\0\0\204\377\377\377\202\0\0\0\206\377\377" + "\377\202\0\0\0\203\377\377\377\203\0\0\0\203\377\377\377\204\0\0\0\204" + "\377\377\377\202\0\0\0\204\377\377\377\202\0\0\0\205\377\377\377\206" + "\0\0\0\211\377\377\377\202\0\0\0\207\377\377\377\202\0\0\0\206\377\377" + "\377\202\0\0\0\205\377\377\377\211\0\0\0\204\377\377\377\202\0\0\0\206" + "\377\377\377\202\0\0\0\206\377\377\377\202\0\0\0\204\377\377\377\211" + "\0\0\0\205\377\377\377\202\0\0\0\206\377\377\377\202\0\0\0\204\377\377" + "\377\211\0\0\0\204\377\377\377\202\0\0\0\204\377\377\377\202\0\0\0\205" + "\377\377\377\206\0\0\0\211\377\377\377\202\0\0\0\207\377\377\377\202" + "\0\0\0\206\377\377\377\202\0\0\0\206\377\377\377\204\0\0\0\202\377\377" + "\377\202\0\0\0\204\377\377\377\202\0\0\0\206\377\377\377\202\0\0\0\206" + "\377\377\377\202\0\0\0\204\377\377\377\202\0\0\0\202\377\377\377\204" + "\0\0\0\206\377\377\377\202\0\0\0\206\377\377\377\202\0\0\0\205\377\377" + "\377\204\0\0\0\202\377\377\377\202\0\0\0\204\377\377\377\202\0\0\0\204" + "\377\377\377\202\0\0\0\205\377\377\377\206\0\0\0\372\377\377\377\206" + "\0\0\0\372\377\377\377\206\0\0\0\372\377\377\377\206\0\0\0\372\377\377" + "\377\206\0\0\0\372\377\377\377\206\0\0\0\372\377\377\377\206\0\0\0\372" + "\377\377\377\206\0\0\0\372\377\377\377\206\0\0\0\372\377\377\377\206" + "\0\0\0\372\377\377\377\206\0\0\0\372\377\377\377\206\0\0\0\372\377\377" + "\377\206\0\0\0\372\377\377\377\206\0\0\0\231\377\377\377\203\0\0\0\207" + "\377\377\377\202\0\0\0\237\377\377\377\203\0\0\0\210\377\377\377\203" + "\0\0\0\250\377\377\377\206\0\0\0\231\377\377\377\204\0\0\0\206\377\377" + "\377\202\0\0\0\221\377\377\377\202\0\0\0\215\377\377\377\203\0\0\0\206" + "\377\377\377\203\0\0\0\214\377\377\377\202\0\0\0\233\377\377\377\206" + "\0\0\0\231\377\377\377\204\0\0\0\206\377\377\377\202\0\0\0\221\377\377" + "\377\202\0\0\0\216\377\377\377\202\0\0\0\206\377\377\377\202\0\0\0\215" + "\377\377\377\202\0\0\0\233\377\377\377\206\0\0\0\231\377\377\377\205" + "\0\0\0\205\377\377\377\202\0\0\0\221\377\377\377\202\0\0\0\217\377\377" + "\377\202\0\0\0\204\377\377\377\202\0\0\0\216\377\377\377\202\0\0\0\233" + "\377\377\377\206\0\0\0\231\377\377\377\202\0\0\0\1\377\377\377\202\0" + "\0\0\205\377\377\377\202\0\0\0\206\377\377\377\205\0\0\0\205\377\377" + "\377\207\0\0\0\213\377\377\377\203\0\0\0\202\377\377\377\203\0\0\0\203" + "\377\377\377\205\0\0\0\205\377\377\377\207\0\0\0\227\377\377\377\206" + "\0\0\0\231\377\377\377\202\0\0\0\202\377\377\377\202\0\0\0\204\377\377" + "\377\202\0\0\0\205\377\377\377\207\0\0\0\204\377\377\377\207\0\0\0\214" + "\377\377\377\202\0\0\0\202\377\377\377\202\0\0\0\203\377\377\377\210" + "\0\0\0\203\377\377\377\207\0\0\0\227\377\377\377\206\0\0\0\231\377\377" + "\377\202\0\0\0\202\377\377\377\202\0\0\0\204\377\377\377\202\0\0\0\204" + "\377\377\377\203\0\0\0\203\377\377\377\203\0\0\0\204\377\377\377\202" + "\0\0\0\221\377\377\377\204\0\0\0\203\377\377\377\203\0\0\0\204\377\377" + "\377\202\0\0\0\204\377\377\377\202\0\0\0\233\377\377\377\206\0\0\0\231" + "\377\377\377\202\0\0\0\203\377\377\377\202\0\0\0\203\377\377\377\202" + "\0\0\0\203\377\377\377\203\0\0\0\205\377\377\377\203\0\0\0\203\377\377" + "\377\202\0\0\0\221\377\377\377\204\0\0\0\202\377\377\377\203\0\0\0\206" + "\377\377\377\202\0\0\0\203\377\377\377\202\0\0\0\233\377\377\377\206" + "\0\0\0\231\377\377\377\202\0\0\0\203\377\377\377\202\0\0\0\203\377\377" + "\377\202\0\0\0\203\377\377\377\202\0\0\0\207\377\377\377\202\0\0\0\203" + "\377\377\377\202\0\0\0\222\377\377\377\202\0\0\0\203\377\377\377\202" + "\0\0\0\207\377\377\377\202\0\0\0\203\377\377\377\202\0\0\0\233\377\377" + "\377\206\0\0\0\231\377\377\377\202\0\0\0\204\377\377\377\202\0\0\0\202" + "\377\377\377\202\0\0\0\203\377\377\377\202\0\0\0\207\377\377\377\202" + "\0\0\0\203\377\377\377\202\0\0\0\222\377\377\377\202\0\0\0\203\377\377" + "\377\213\0\0\0\203\377\377\377\202\0\0\0\233\377\377\377\206\0\0\0\231" + "\377\377\377\202\0\0\0\204\377\377\377\202\0\0\0\202\377\377\377\202" + "\0\0\0\203\377\377\377\202\0\0\0\207\377\377\377\202\0\0\0\203\377\377" + "\377\202\0\0\0\222\377\377\377\202\0\0\0\203\377\377\377\213\0\0\0\203" + "\377\377\377\202\0\0\0\233\377\377\377\206\0\0\0\231\377\377\377\202" + "\0\0\0\205\377\377\377\202\0\0\0\1\377\377\377\202\0\0\0\203\377\377" + "\377\202\0\0\0\207\377\377\377\202\0\0\0\203\377\377\377\202\0\0\0\222" + "\377\377\377\202\0\0\0\203\377\377\377\202\0\0\0\214\377\377\377\202" + "\0\0\0\233\377\377\377\206\0\0\0\231\377\377\377\202\0\0\0\205\377\377" + "\377\205\0\0\0\203\377\377\377\203\0\0\0\205\377\377\377\203\0\0\0\203" + "\377\377\377\202\0\0\0\222\377\377\377\202\0\0\0\203\377\377\377\203" + "\0\0\0\213\377\377\377\202\0\0\0\233\377\377\377\206\0\0\0\231\377\377" + "\377\202\0\0\0\206\377\377\377\204\0\0\0\204\377\377\377\203\0\0\0\203" + "\377\377\377\203\0\0\0\204\377\377\377\202\0\0\0\222\377\377\377\202" + "\0\0\0\204\377\377\377\203\0\0\0\206\377\377\377\1\0\0\0\203\377\377" + "\377\202\0\0\0\233\377\377\377\206\0\0\0\231\377\377\377\202\0\0\0\206" + "\377\377\377\204\0\0\0\205\377\377\377\207\0\0\0\205\377\377\377\206" + "\0\0\0\216\377\377\377\202\0\0\0\205\377\377\377\211\0\0\0\203\377\377" + "\377\206\0\0\0\227\377\377\377\206\0\0\0\231\377\377\377\202\0\0\0\207" + "\377\377\377\203\0\0\0\206\377\377\377\205\0\0\0\207\377\377\377\205" + "\0\0\0\216\377\377\377\202\0\0\0\206\377\377\377\207\0\0\0\205\377\377" + "\377\205\0\0\0\227\377\377\377\206\0\0\0\372\377\377\377\206\0\0\0\372" + "\377\377\377\206\0\0\0\372\377\377\377\206\0\0\0\372\377\377\377\206" + "\0\0\0\372\377\377\377\206\0\0\0\372\377\377\377\206\0\0\0\372\377\377" + "\377\206\0\0\0\372\377\377\377\206\0\0\0\372\377\377\377\206\0\0\0\372" + "\377\377\377\206\0\0\0\372\377\377\377\206\0\0\0\372\377\377\377\206" + "\0\0\0\306\377\377\377\202\0\0\0\231\377\377\377\202\0\0\0\227\377\377" + "\377\206\0\0\0\230\377\377\377\202\0\0\0\254\377\377\377\202\0\0\0\231" + "\377\377\377\202\0\0\0\227\377\377\377\206\0\0\0\230\377\377\377\202" + "\0\0\0\254\377\377\377\202\0\0\0\231\377\377\377\202\0\0\0\227\377\377" + "\377\206\0\0\0\230\377\377\377\202\0\0\0\254\377\377\377\202\0\0\0\231" + "\377\377\377\202\0\0\0\227\377\377\377\206\0\0\0\230\377\377\377\202" + "\0\0\0\254\377\377\377\202\0\0\0\231\377\377\377\202\0\0\0\227\377\377" + "\377\206\0\0\0\230\377\377\377\202\0\0\0\214\377\377\377\205\0\0\0\207" + "\377\377\377\206\0\0\0\210\377\377\377\204\0\0\0\202\377\377\377\202" + "\0\0\0\206\377\377\377\205\0\0\0\210\377\377\377\204\0\0\0\202\377\377" + "\377\202\0\0\0\227\377\377\377\206\0\0\0\230\377\377\377\202\0\0\0\213" + "\377\377\377\207\0\0\0\205\377\377\377\210\0\0\0\206\377\377\377\211" + "\0\0\0\205\377\377\377\210\0\0\0\205\377\377\377\211\0\0\0\227\377\377" + "\377\206\0\0\0\230\377\377\377\202\0\0\0\212\377\377\377\203\0\0\0\203" + "\377\377\377\203\0\0\0\204\377\377\377\1\0\0\0\205\377\377\377\203\0" + "\0\0\204\377\377\377\203\0\0\0\203\377\377\377\204\0\0\0\204\377\377" + "\377\203\0\0\0\204\377\377\377\202\0\0\0\204\377\377\377\203\0\0\0\203" + "\377\377\377\204\0\0\0\227\377\377\377\206\0\0\0\230\377\377\377\202" + "\0\0\0\211\377\377\377\203\0\0\0\205\377\377\377\203\0\0\0\212\377\377" + "\377\202\0\0\0\203\377\377\377\203\0\0\0\205\377\377\377\203\0\0\0\203" + "\377\377\377\203\0\0\0\206\377\377\377\202\0\0\0\202\377\377\377\203" + "\0\0\0\205\377\377\377\203\0\0\0\227\377\377\377\206\0\0\0\230\377\377" + "\377\202\0\0\0\211\377\377\377\202\0\0\0\207\377\377\377\202\0\0\0\204" + "\377\377\377\210\0\0\0\203\377\377\377\202\0\0\0\207\377\377\377\202" + "\0\0\0\203\377\377\377\202\0\0\0\207\377\377\377\202\0\0\0\202\377\377" + "\377\202\0\0\0\207\377\377\377\202\0\0\0\227\377\377\377\206\0\0\0\230" + "\377\377\377\202\0\0\0\211\377\377\377\202\0\0\0\207\377\377\377\202" + "\0\0\0\203\377\377\377\211\0\0\0\203\377\377\377\202\0\0\0\207\377\377" + "\377\202\0\0\0\203\377\377\377\213\0\0\0\202\377\377\377\202\0\0\0\207" + "\377\377\377\202\0\0\0\227\377\377\377\206\0\0\0\230\377\377\377\202" + "\0\0\0\211\377\377\377\202\0\0\0\207\377\377\377\202\0\0\0\202\377\377" + "\377\203\0\0\0\205\377\377\377\202\0\0\0\203\377\377\377\202\0\0\0\207" + "\377\377\377\202\0\0\0\203\377\377\377\213\0\0\0\202\377\377\377\202" + "\0\0\0\207\377\377\377\202\0\0\0\227\377\377\377\206\0\0\0\230\377\377" + "\377\202\0\0\0\211\377\377\377\202\0\0\0\207\377\377\377\202\0\0\0\202" + "\377\377\377\202\0\0\0\206\377\377\377\202\0\0\0\203\377\377\377\202" + "\0\0\0\207\377\377\377\202\0\0\0\203\377\377\377\202\0\0\0\213\377\377" + "\377\202\0\0\0\207\377\377\377\202\0\0\0\227\377\377\377\206\0\0\0\230" + "\377\377\377\202\0\0\0\211\377\377\377\203\0\0\0\205\377\377\377\203" + "\0\0\0\202\377\377\377\202\0\0\0\205\377\377\377\203\0\0\0\203\377\377" + "\377\203\0\0\0\205\377\377\377\203\0\0\0\203\377\377\377\203\0\0\0\212" + "\377\377\377\203\0\0\0\205\377\377\377\203\0\0\0\227\377\377\377\206" + "\0\0\0\230\377\377\377\202\0\0\0\212\377\377\377\203\0\0\0\203\377\377" + "\377\203\0\0\0\203\377\377\377\203\0\0\0\203\377\377\377\204\0\0\0\204" + "\377\377\377\203\0\0\0\203\377\377\377\204\0\0\0\204\377\377\377\203" + "\0\0\0\206\377\377\377\1\0\0\0\203\377\377\377\203\0\0\0\203\377\377" + "\377\204\0\0\0\227\377\377\377\206\0\0\0\230\377\377\377\212\0\0\0\203" + "\377\377\377\207\0\0\0\205\377\377\377\211\0\0\0\205\377\377\377\211" + "\0\0\0\205\377\377\377\211\0\0\0\204\377\377\377\211\0\0\0\227\377\377" + "\377\206\0\0\0\230\377\377\377\212\0\0\0\204\377\377\377\205\0\0\0\207" + "\377\377\377\204\0\0\0\202\377\377\377\202\0\0\0\206\377\377\377\204" + "\0\0\0\202\377\377\377\202\0\0\0\206\377\377\377\207\0\0\0\206\377\377" + "\377\204\0\0\0\202\377\377\377\202\0\0\0\227\377\377\377\206\0\0\0\372" + "\377\377\377\206\0\0\0\372\377\377\377\206\0\0\0\372\377\377\377\206" + "\0\0\0\372\377\377\377\206\0\0\0\372\377\377\377\206\0\0\0\372\377\377" + "\377\206\0\0\0\372\377\377\377\206\0\0\0\372\377\377\377\206\0\0\0\372" + "\377\377\377\206\0\0\0\372\377\377\377\206\0\0\0\372\377\377\377\206" + "\0\0\0\372\377\377\377\206\0\0\0\372\377\377\377\206\0\0\0\372\377\377" + "\377\206\0\0\0\372\377\377\377\206\0\0\0\372\377\377\377\206\0\0\0\372" + "\377\377\377\206\0\0\0\372\377\377\377\206\0\0\0\372\377\377\377\206" + "\0\0\0\372\377\377\377\206\0\0\0\372\377\377\377\206\0\0\0\372\377\377" + "\377\206\0\0\0\372\377\377\377\377\0\0\0\377\0\0\0\377\0\0\0\206\0\0" + "\0", +}; + + diff --git a/src/usgs.c b/src/usgs.c new file mode 100644 index 00000000..e4bcda09 --- /dev/null +++ b/src/usgs.c @@ -0,0 +1,106 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include "coords.h" +#include "vikcoord.h" +#include "mapcoord.h" +#include "http.h" + +#define MARGIN_OF_ERROR 0.0001 +#define REALLYCLOSE(x,y) (ABS((x)-(y)) < MARGIN_OF_ERROR) + +#define USGS_SCALE_TO_MPP .1016 +#define TZ(x) ((x)*USGS_SCALE_TO_MPP) + +/* defined as "2" = "25k" */ +static gint mpp_to_scale ( gdouble mpp ) +{ + if ( REALLYCLOSE(mpp,TZ(10)) || REALLYCLOSE(mpp,TZ(24)) || REALLYCLOSE(mpp,TZ(25)) || REALLYCLOSE(mpp,TZ(50)) || REALLYCLOSE(mpp,TZ(100)) || REALLYCLOSE(mpp,TZ(200)) || REALLYCLOSE(mpp,TZ(250)) || REALLYCLOSE(mpp,TZ(500)) ) + return (gint) (mpp / .1016); + return 0; +} + +gboolean usgs_coord_to_mapcoord ( const VikCoord *src, gdouble xmpp, gdouble ympp, MapCoord *dest ) +{ + g_assert ( src->mode == VIK_COORD_UTM ); + + if ( xmpp != ympp ) + return FALSE; + + dest->scale = mpp_to_scale ( xmpp ); + if ( ! dest->scale ) + return FALSE; + + dest->x = (gint)(((gint)(src->east_west))/(800*xmpp)); + dest->y = (gint)(((gint)(src->north_south))/(600*xmpp)); + dest->z = src->utm_zone; + return TRUE; +} + +void usgs_mapcoord_to_center_coord ( MapCoord *src, VikCoord *dest ) +{ + gdouble mpp = TZ ( src->scale ); + dest->mode = VIK_COORD_UTM; + dest->utm_zone = src->z; + dest->east_west = ((src->x * 800) + 400) * mpp; + dest->north_south = ((src->y * 600) + 300) * mpp; +} + +gint usgs_scale_to_drg ( gint scale ) +{ + switch ( scale ) { + case 10: + case 25: + case 50: + return 25; + case 100: + case 200: + return 100; + default: + return 250; + } +} + + /* BEGIN GEEK BLOCK */ + static int x(f,s,c,t)char *s,*t;{return f&1?*s?*s-c?x(f,++s,c,t):(1+(int)(M_PI*4))[s] : 0 : f&2 ? x(--f,"9027854631916362545933391722",c,t) : f&4 ? *s ? x(f,s+1,(*t=x(f-2,"^&%!*)",*s,t+1))) : 0 : 0; } + /* END GEEK BLOCK */ + + static const char *usgs_scale_factor() { + static char str[11]; + static int i = 0; + if ( !i ) x(4,"1094382657",65 + i++,str); + return str; + } + + +void usgs_download ( MapCoord *src, const gchar *dest_fn ) +{ + /* find center as above */ + gdouble mpp = TZ ( src->scale ); + gint easting = ((src->x * 800) + 400) * mpp; + gint northing = ((src->y * 600) + 300) * mpp; + gchar *uri = g_strdup_printf ( "/map.asp?z=%d&e=%d&n=%d&datum=NAD83&u=4&size=l&s=%d&chkDRG=DRG%d", + src->z, easting, northing, src->scale, usgs_scale_to_drg(src->scale) ); + usgs_hack ( usgs_scale_factor(), uri, dest_fn ); + g_free ( uri ); +} diff --git a/src/usgs.h b/src/usgs.h new file mode 100644 index 00000000..fb0fcb18 --- /dev/null +++ b/src/usgs.h @@ -0,0 +1,29 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __VIKING_USGS_H +#define __VIKING_USGS_H + +gboolean usgs_coord_to_mapcoord ( const VikCoord *src, gdouble xmpp, gdouble ympp, MapCoord *dest ); +void usgs_mapcoord_to_center_coord ( MapCoord *src, VikCoord *dest ); +void usgs_download ( MapCoord *src, const gchar *dest_fn ); + +#endif diff --git a/src/vikaggregatelayer.c b/src/vikaggregatelayer.c new file mode 100644 index 00000000..712dda4b --- /dev/null +++ b/src/vikaggregatelayer.c @@ -0,0 +1,349 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "viking.h" +#include "vikaggregatelayer_pixmap.h" + +#include + +#define DISCONNECT_UPDATE_SIGNAL(vl, val) g_signal_handlers_disconnect_matched(vl, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, val) + +static VikAggregateLayer *aggregate_layer_copy ( VikAggregateLayer *val, gpointer vp ); +static void aggregate_layer_change_coord_mode ( VikAggregateLayer *val, VikCoordMode mode ); + +VikLayerInterface vik_aggregate_layer_interface = { + "Aggregate", + &aggregatelayer_pixbuf, + + NULL, + 0, + + NULL, + 0, + NULL, + 0, + + (VikLayerFuncCreate) vik_aggregate_layer_create, + (VikLayerFuncRealize) vik_aggregate_layer_realize, + (VikLayerFuncPostRead) NULL, + (VikLayerFuncFree) vik_aggregate_layer_free, + + (VikLayerFuncProperties) NULL, + (VikLayerFuncDraw) vik_aggregate_layer_draw, + (VikLayerFuncChangeCoordMode) aggregate_layer_change_coord_mode, + + (VikLayerFuncAddMenuItems) NULL, + (VikLayerFuncSublayerAddMenuItems) NULL, + + (VikLayerFuncSublayerRenameRequest) NULL, + (VikLayerFuncSublayerToggleVisible) NULL, + + (VikLayerFuncCopy) aggregate_layer_copy, + + (VikLayerFuncSetParam) NULL, + (VikLayerFuncGetParam) NULL, + + (VikLayerFuncReadFileData) NULL, + (VikLayerFuncWriteFileData) NULL, + + (VikLayerFuncCopyItem) NULL, + (VikLayerFuncPasteItem) NULL, + (VikLayerFuncFreeCopiedItem) NULL, +}; + +struct _VikAggregateLayer { + VikLayer vl; + GList *children; +}; + +GType vik_aggregate_layer_get_type () +{ + static GType val_type = 0; + + if (!val_type) + { + static const GTypeInfo val_info = + { + sizeof (VikAggregateLayerClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + NULL, /* class init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (VikAggregateLayer), + 0, + NULL /* instance init */ + }; + val_type = g_type_register_static ( VIK_LAYER_TYPE, "VikAggregateLayer", &val_info, 0 ); + } + + return val_type; +} + +VikAggregateLayer *vik_aggregate_layer_create (VikViewport *vp) +{ + VikAggregateLayer *rv = vik_aggregate_layer_new (); + vik_layer_rename ( VIK_LAYER(rv), vik_aggregate_layer_interface.name ); + return rv; +} + +static VikAggregateLayer *aggregate_layer_copy ( VikAggregateLayer *val, gpointer vp ) +{ + VikAggregateLayer *rv = vik_aggregate_layer_new (); + VikLayer *child_layer; + GList *child = val->children; + while ( child ) + { + child_layer = vik_layer_copy ( VIK_LAYER(child->data), vp ); + if ( child_layer ) + rv->children = g_list_append ( rv->children, child_layer ); + g_signal_connect_swapped ( G_OBJECT(child_layer), "update", G_CALLBACK(vik_layer_emit_update), rv ); + child = child->next; + } + return rv; +} + +VikAggregateLayer *vik_aggregate_layer_new () +{ + VikAggregateLayer *val = VIK_AGGREGATE_LAYER ( g_object_new ( VIK_AGGREGATE_LAYER_TYPE, NULL ) ); + vik_layer_init ( VIK_LAYER(val), VIK_LAYER_AGGREGATE ); + val->children = NULL; + return val; +} + +void vik_aggregate_layer_insert_layer ( VikAggregateLayer *val, VikLayer *l, GtkTreeIter *replace_iter ) +{ + GList *theone = g_list_find ( val->children, vik_treeview_item_get_pointer ( VIK_LAYER(val)->vt, replace_iter ) ); + GtkTreeIter iter; + if ( VIK_LAYER(val)->realized ) + { + vik_treeview_insert_layer ( VIK_LAYER(val)->vt, &(VIK_LAYER(val)->iter), &iter, l->name, val, l, l->type, l->type, replace_iter ); + if ( ! l->visible ) + vik_treeview_item_set_visible ( VIK_LAYER(val)->vt, &iter, FALSE ); + vik_layer_realize ( l, VIK_LAYER(val)->vt, &iter ); + + if ( val->children == NULL ) + vik_treeview_expand ( VIK_LAYER(val)->vt, &(VIK_LAYER(val)->iter) ); + } + val->children = g_list_insert ( val->children, l, g_list_position(val->children,theone)+1 ); + + + g_signal_connect_swapped ( G_OBJECT(l), "update", G_CALLBACK(vik_layer_emit_update), val ); +} + +void vik_aggregate_layer_add_layer ( VikAggregateLayer *val, VikLayer *l ) +{ + GtkTreeIter iter; + + if ( VIK_LAYER(val)->realized ) + { + vik_treeview_add_layer ( VIK_LAYER(val)->vt, &(VIK_LAYER(val)->iter), &iter, l->name, val, l, l->type, l->type); + if ( ! l->visible ) + vik_treeview_item_set_visible ( VIK_LAYER(val)->vt, &iter, FALSE ); + vik_layer_realize ( l, VIK_LAYER(val)->vt, &iter ); + + if ( val->children == NULL ) + vik_treeview_expand ( VIK_LAYER(val)->vt, &(VIK_LAYER(val)->iter) ); + } + + val->children = g_list_append ( val->children, l ); + g_signal_connect_swapped ( G_OBJECT(l), "update", G_CALLBACK(vik_layer_emit_update), val ); +} + +void vik_aggregate_layer_move_layer ( VikAggregateLayer *val, GtkTreeIter *child_iter, gboolean up ) +{ + GList *theone, *first, *second; + vik_treeview_move_item ( VIK_LAYER(val)->vt, child_iter, up ); + + theone = g_list_find ( val->children, vik_treeview_item_get_pointer ( VIK_LAYER(val)->vt, child_iter ) ); + + g_assert ( theone != NULL ); + + /* the old switcheroo */ + if ( up && theone->next ) + { + first = theone; + second = theone->next; + } + else if ( !up && theone->prev ) + { + first = theone->prev; + second = theone; + } + else + return; + + first->next = second->next; + second->prev = first->prev; + first->prev = second; + second->next = first; + + /* second is now first */ + + if ( second->prev ) + second->prev->next = second; + if ( first->next ) + first->next->prev = first; + + if ( second->prev == NULL ) + val->children = second; +} + +void vik_aggregate_layer_draw ( VikAggregateLayer *val, gpointer data ) +{ + g_list_foreach ( val->children, (GFunc)(vik_layer_draw), data ); +} + +static void aggregate_layer_change_coord_mode ( VikAggregateLayer *val, VikCoordMode mode ) +{ + GList *iter = val->children; + while ( iter ) + { + vik_layer_change_coord_mode ( VIK_LAYER(iter->data), mode ); + iter = iter->next; + } +} + +static void disconnect_layer_signal ( VikLayer *vl, VikAggregateLayer *val ) +{ + g_assert(DISCONNECT_UPDATE_SIGNAL(vl,val)==1); +} + +void vik_aggregate_layer_free ( VikAggregateLayer *val ) +{ + g_list_foreach ( val->children, (GFunc)(disconnect_layer_signal), val ); + g_list_foreach ( val->children, (GFunc)(g_object_unref), NULL ); + g_list_free ( val->children ); +} + +static void delete_layer_iter ( VikLayer *vl ) +{ + if ( vl->realized ) + vik_treeview_item_delete ( vl->vt, &(vl->iter) ); +} + +void vik_aggregate_layer_clear ( VikAggregateLayer *val ) +{ + g_list_foreach ( val->children, (GFunc)(disconnect_layer_signal), val ); + g_list_foreach ( val->children, (GFunc)(delete_layer_iter), NULL ); + g_list_foreach ( val->children, (GFunc)(g_object_unref), NULL ); + g_list_free ( val->children ); + val->children = NULL; +} + +gboolean vik_aggregate_layer_delete ( VikAggregateLayer *val, GtkTreeIter *iter ) +{ + VikLayer *l = VIK_LAYER( vik_treeview_item_get_pointer ( VIK_LAYER(val)->vt, iter ) ); + gboolean was_visible = l->visible; + + vik_treeview_item_delete ( VIK_LAYER(val)->vt, iter ); + val->children = g_list_remove ( val->children, l ); + g_assert(DISCONNECT_UPDATE_SIGNAL(l,val)==1); + g_object_unref ( l ); + + return was_visible; +} + +/* returns 0 == we're good, 1 == didn't find any layers, 2 == got rejected */ +guint vik_aggregate_layer_tool ( VikAggregateLayer *val, guint16 layer_type, VikToolInterfaceFunc tool_func, GdkEventButton *event, VikViewport *vvp ) +{ + GList *iter = val->children; + gboolean found_rej = FALSE; + if (!iter) + return FALSE; + while (iter->next) + iter = iter->next; + + while ( iter ) + { + /* if this layer "accepts" the tool call */ + if ( VIK_LAYER(iter->data)->visible && VIK_LAYER(iter->data)->type == layer_type ) + { + if ( tool_func ( VIK_LAYER(iter->data), event, vvp ) ) + return 0; + else + found_rej = TRUE; + } + + /* recursive -- try the same for the child aggregate layer. */ + else if ( VIK_LAYER(iter->data)->visible && VIK_LAYER(iter->data)->type == VIK_LAYER_AGGREGATE ) + { + gint rv = vik_aggregate_layer_tool(VIK_AGGREGATE_LAYER(iter->data), layer_type, tool_func, event, vvp); + if ( rv == 0 ) + return 0; + else if ( rv == 2 ) + found_rej = TRUE; + } + iter = iter->prev; + } + return found_rej ? 2 : 1; /* no one wanted to accept the tool call in this layer */ +} + +VikLayer *vik_aggregate_layer_get_top_visible_layer_of_type ( VikAggregateLayer *val, gint type ) +{ + VikLayer *rv; + GList *ls = val->children; + if (!ls) + return NULL; + while (ls->next) + ls = ls->next; + + while ( ls ) + { + if ( VIK_LAYER(ls->data)->visible && VIK_LAYER(ls->data)->type == type ) + return VIK_LAYER(ls->data); + else if ( VIK_LAYER(ls->data)->visible && VIK_LAYER(ls->data)->type == VIK_LAYER_AGGREGATE ) + { + rv = vik_aggregate_layer_get_top_visible_layer_of_type(VIK_AGGREGATE_LAYER(ls->data), type); + if ( rv ) + return rv; + } + ls = ls->prev; + } + return NULL; +} + +void vik_aggregate_layer_realize ( VikAggregateLayer *val, VikTreeview *vt, GtkTreeIter *layer_iter ) +{ + GList *i = val->children; + GtkTreeIter iter; + while ( i ) + { + vik_treeview_add_layer ( VIK_LAYER(val)->vt, layer_iter, &iter, VIK_LAYER(i->data)->name, val, + VIK_LAYER(i->data), VIK_LAYER(i->data)->type, VIK_LAYER(i->data)->type ); + if ( ! VIK_LAYER(i->data)->visible ) + vik_treeview_item_set_visible ( VIK_LAYER(val)->vt, &iter, FALSE ); + vik_layer_realize ( VIK_LAYER(i->data), VIK_LAYER(val)->vt, &iter ); + i = i->next; + } +} + +const GList *vik_aggregate_layer_get_children ( VikAggregateLayer *val ) +{ + return val->children; +} + +gboolean vik_aggregate_layer_is_empty ( VikAggregateLayer *val ) +{ + if ( val->children ) + return FALSE; + return TRUE; +} diff --git a/src/vikaggregatelayer.h b/src/vikaggregatelayer.h new file mode 100644 index 00000000..8ef9f293 --- /dev/null +++ b/src/vikaggregatelayer.h @@ -0,0 +1,64 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _VIKING_AGGREGATELAYER_H +#define _VIKING_AGGREGATELAYER_H + +#include + +#define VIK_AGGREGATE_LAYER_TYPE (vik_aggregate_layer_get_type ()) +#define VIK_AGGREGATE_LAYER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), VIK_AGGREGATE_LAYER_TYPE, VikAggregateLayer)) +#define VIK_AGGREGATE_LAYER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), VIK_AGGREGATE_LAYER_TYPE, VikAggregateLayerClass)) +#define IS_VIK_AGGREGATE_LAYER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VIK_AGGREGATE_LAYER_TYPE)) +#define IS_VIK_AGGREGATE_LAYER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), VIK_AGGREGATE_LAYER_TYPE)) + +typedef struct _VikAggregateLayerClass VikAggregateLayerClass; +struct _VikAggregateLayerClass +{ + VikLayerClass vik_layer_class; +}; + +GType vik_aggregate_layer_get_type (); + +typedef struct _VikAggregateLayer VikAggregateLayer; + +VikAggregateLayer *vik_aggregate_layer_new (); +void vik_aggregate_layer_add_layer ( VikAggregateLayer *val, VikLayer *l ); +void vik_aggregate_layer_insert_layer ( VikAggregateLayer *val, VikLayer *l, GtkTreeIter *replace_layer ); +void vik_aggregate_layer_move_layer ( VikAggregateLayer *val, GtkTreeIter *child_iter, gboolean up ); +void vik_aggregate_layer_draw ( VikAggregateLayer *val, gpointer data ); +void vik_aggregate_layer_free ( VikAggregateLayer *val ); +void vik_aggregate_layer_clear ( VikAggregateLayer *val ); +gboolean vik_aggregate_layer_delete ( VikAggregateLayer *val, GtkTreeIter *iter ); +VikAggregateLayer *vik_aggregate_layer_create (VikViewport *vp); + +/* returns: 0 = success, 1 = none appl. found, 2 = found but rejected */ +guint vik_aggregate_layer_tool ( VikAggregateLayer *val, guint16 layer_type, VikToolInterfaceFunc tool_func, GdkEventButton *event, VikViewport *vvp); + +VikLayer *vik_aggregate_layer_get_top_visible_layer_of_type ( VikAggregateLayer *val, gint type ); +void vik_aggregate_layer_realize ( VikAggregateLayer *val, VikTreeview *vt, GtkTreeIter *layer_iter ); +gboolean vik_aggregate_layer_load_layers ( VikAggregateLayer *val, FILE *f, gpointer vp ); +gboolean vik_aggregate_layer_is_empty ( VikAggregateLayer *val ); + +const GList *vik_aggregate_layer_get_children ( VikAggregateLayer *val ); + + +#endif diff --git a/src/vikaggregatelayer_pixmap.h b/src/vikaggregatelayer_pixmap.h new file mode 100644 index 00000000..cdbdcd6a --- /dev/null +++ b/src/vikaggregatelayer_pixmap.h @@ -0,0 +1,55 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* GdkPixbuf RGB C-Source image dump 1-byte-run-length-encoded */ + +static const GdkPixdata aggregatelayer_pixbuf = { + 0x47646b50, /* Pixbuf magic: 'GdkP' */ + 24 + 576, /* header length + pixel_data length */ + 0x2010001, /* pixdata_type */ + 48, /* rowstride */ + 16, /* width */ + 16, /* height */ + /* pixel_data: */ + "\206\377\377\377\2\330\332\325\250\254\245\216\377\377\377\3d^Q\214\257" + "z\260\261\256\213\377\377\377\10\337\337\337\224\212\206[g@\200\256g" + "dgJQA\37df5\214\222\203\207\377\377\377\12\202\201q\\n>srBs\220Qr\247" + "X\230\266\200pv:{\246V\210\253]\211\226y\205\377\377\377\14\244\244\241" + "h\224L\206\265e}\240p\\W1{\255X\250\317\220\207\252_qu:yv5~\257Zv\205" + "m\204\377\377\377\15aiVz\260]u\253W~\242Yr\226O\177\257`\200\231^y\234" + "Pn\206D|\242I\217\301n}\247p\317\317\317\203\377\377\377\15\200\211f" + "q\237To\236Xj\244]n\261]\205\276}\207\253\177c\231I\202\272k\251\317" + "\254\234\306\215\230\304\207\247\250\245\203\377\377\377\15\211\213g" + "s\231Zr\231`o\237_l\250Y\211\276\206\214\270\216V\2057k\246R\214\274" + "|\216\271n\214\272h\216\220\212\203\377\377\377\15\205v_lq>q{Gg\205H" + "h\226Kv\247Xo\234Q[\2056d\225\77y\246R\202\256Zm\225N\301\301\300\203" + "\377\377\377\15a\\Wkg8xj9qu>l\177@w\237Up\206Cd\2046k\221\77t\235Ix\247" + "P^\202D\277\277\277\203\377\377\377\15\277\277\277V>%wl:to:rz>u\212F" + "wx>ks0l\2058r\231D|\252Sg\216M\277\277\277\204\377\377\377\14\77&\31" + "ue9s\\3sv\77qy;sp9gt3p\224Gu\240L}\256Uf\211F\277\277\277\204\377\377" + "\377\13eQLrT2rd6q~Co|>q_2kX*m\177:r\236F\177\253OXf<\205\377\377\377" + "\13\213\203\202h9%v\\2tz@uz=qI+j4\36mf/o\234BZ\177;\243\243\241\205\377" + "\377\377\12\357\357\357G\"\34yE)l=&vK,J\32\17G\22\14B\34\17FK2\267\271" + "\265\207\377\377\377\7\320\317\317\217\202\201xie\216\206\204\300\277" + "\277\277\277\277\337\337\337\205\377\377\377", +}; + + diff --git a/src/vikcoord.c b/src/vikcoord.c new file mode 100644 index 00000000..a805e4a7 --- /dev/null +++ b/src/vikcoord.c @@ -0,0 +1,117 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include "coords.h" +#include "vikcoord.h" + +/* all coord operations MUST BE ABSTRACTED!!! */ + +void vik_coord_convert(VikCoord *coord, VikCoordMode dest_mode) +{ + static VikCoord tmp; + if ( coord->mode != dest_mode ) + { + if ( dest_mode == VIK_COORD_LATLON ) { + a_coords_utm_to_latlon ( (struct UTM *)coord, (struct LatLon *)&tmp ); + *((struct LatLon *)coord) = *((struct LatLon *)&tmp); + } else { + a_coords_latlon_to_utm ( (struct LatLon *)coord, (struct UTM *)&tmp ); + *((struct UTM *)coord) = *((struct UTM *)&tmp); + } + coord->mode = dest_mode; + } +} + +void vik_coord_copy_convert(const VikCoord *coord, VikCoordMode dest_mode, VikCoord *dest) +{ + if ( coord->mode == dest_mode ) { + *dest = *coord; + } else { + if ( dest_mode == VIK_COORD_LATLON ) + a_coords_utm_to_latlon ( (struct UTM *)coord, (struct LatLon *)dest ); + else + a_coords_latlon_to_utm ( (struct LatLon *)coord, (struct UTM *)dest ); + dest->mode = dest_mode; + } +} + +static gdouble vik_coord_diff_safe(const VikCoord *c1, const VikCoord *c2) +{ + static struct LatLon a, b; + vik_coord_to_latlon ( c1, &a ); + vik_coord_to_latlon ( c2, &b ); + return a_coords_latlon_diff ( &a, &b ); +} + +gdouble vik_coord_diff(const VikCoord *c1, const VikCoord *c2) +{ + if ( c1->mode == c2->mode ) + return vik_coord_diff_safe ( c1, c2 ); + if ( c1->mode == VIK_COORD_UTM ) + return a_coords_utm_diff ( (const struct UTM *) c1, (const struct UTM *) c2 ); + else + return a_coords_latlon_diff ( (const struct LatLon *) c1, (const struct LatLon *) c2 ); +} + +void vik_coord_load_from_latlon ( VikCoord *coord, VikCoordMode mode, const struct LatLon *ll ) +{ + if ( mode == VIK_COORD_LATLON ) + *((struct LatLon *)coord) = *ll; + else + a_coords_latlon_to_utm ( ll, (struct UTM *) coord ); + coord->mode = mode; +} + +void vik_coord_load_from_utm ( VikCoord *coord, VikCoordMode mode, const struct UTM *utm ) +{ + if ( mode == VIK_COORD_UTM ) + *((struct UTM *)coord) = *utm; + else + a_coords_utm_to_latlon ( utm, (struct LatLon *) coord ); + coord->mode = mode; +} + +void vik_coord_to_latlon ( const VikCoord *coord, struct LatLon *dest ) +{ + if ( coord->mode == VIK_COORD_LATLON ) + *dest = *((const struct LatLon *)coord); + else + a_coords_utm_to_latlon ( (const struct UTM *) coord, dest ); +} + +void vik_coord_to_utm ( const VikCoord *coord, struct UTM *dest ) +{ + if ( coord->mode == VIK_COORD_UTM ) + *dest = *((const struct UTM *)coord); + else + a_coords_latlon_to_utm ( (const struct LatLon *) coord, dest ); +} + +gboolean vik_coord_equals ( const VikCoord *coord1, const VikCoord *coord2 ) +{ + if ( coord1->mode != coord2->mode ) + return FALSE; + if ( coord1->mode == VIK_COORD_LATLON ) + return coord1->north_south == coord2->north_south && coord1->east_west == coord2->east_west; + else /* VIK_COORD_UTM */ + return coord1->utm_zone == coord2->utm_zone && coord1->north_south == coord2->north_south && coord1->east_west == coord2->east_west; +} diff --git a/src/vikcoord.h b/src/vikcoord.h new file mode 100644 index 00000000..735a3678 --- /dev/null +++ b/src/vikcoord.h @@ -0,0 +1,60 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _VIKING_VIKCOORD_H +#define _VIKING_VIKCOORD_H + +typedef gshort VikCoordMode; +#define VIK_COORD_UTM 0 +#define VIK_COORD_LATLON 1 + +#define VIK_UTM(x) ((struct UTM *)(x)) +#define VIK_LATLON(x) ((struct LatLon *)(x)) + +typedef struct { + gdouble north_south; /* northing or lat */ + gdouble east_west; /* easting or lon */ + gchar utm_zone; + gchar utm_letter; + + VikCoordMode mode; +} VikCoord; +/* notice we can cast to either UTM or LatLon */ +/* possible more modes to come? xy? we'll leave that as an option */ + +VikCoord *vik_coord_new(); +void vik_coord_free(VikCoord *coord); + +void vik_coord_convert(VikCoord *coord, VikCoordMode dest_mode); +void vik_coord_copy_convert(const VikCoord *coord, VikCoordMode dest_mode, VikCoord *dest); +gdouble vik_coord_diff(const VikCoord *c1, const VikCoord *c2); + +void vik_coord_load_from_latlon ( VikCoord *coord, VikCoordMode mode, const struct LatLon *ll ); +void vik_coord_load_from_utm ( VikCoord *coord, VikCoordMode mode, const struct UTM *utm ); + +void vik_coord_to_latlon ( const VikCoord *coord, struct LatLon *dest ); +void vik_coord_to_utm ( const VikCoord *coord, struct UTM *dest ); + +gboolean vik_coord_equals ( const VikCoord *coord1, const VikCoord *coord2 ); + +/* all coord operations MUST BE ABSTRACTED!!! */ + +#endif diff --git a/src/vikcoordlayer.c b/src/vikcoordlayer.c new file mode 100644 index 00000000..7e6c96da --- /dev/null +++ b/src/vikcoordlayer.c @@ -0,0 +1,282 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "viking.h" +#include "vikcoordlayer_pixmap.h" + +static VikCoordLayer *coord_layer_copy ( VikCoordLayer *vcl, gpointer vp ); +static gboolean coord_layer_set_param ( VikCoordLayer *vcl, guint16 id, VikLayerParamData data, VikViewport *vp ); +static VikLayerParamData coord_layer_get_param ( VikCoordLayer *vcl, guint16 id ); +static void coord_layer_update_gc ( VikCoordLayer *vcl, VikViewport *vp, const gchar *color ); +static void coord_layer_post_read ( VikCoordLayer *vcl, VikViewport *vp ); + +VikLayerParamScale param_scales[] = { + { 0.05, 60.0, 0.25, 10 }, + { 1, 10, 1, 0 }, +}; + +VikLayerParam coord_layer_params[] = { + { "color", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, "Color:", VIK_LAYER_WIDGET_ENTRY }, + { "min_inc", VIK_LAYER_PARAM_DOUBLE, VIK_LAYER_GROUP_NONE, "Minutes Width:", VIK_LAYER_WIDGET_SPINBUTTON, param_scales + 0 }, + { "line_thickness", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, "Line Thickness:", VIK_LAYER_WIDGET_SPINBUTTON, param_scales + 1 }, +}; + + +enum { PARAM_COLOR = 0, PARAM_MIN_INC, PARAM_LINE_THICKNESS, NUM_PARAMS }; + +VikLayerInterface vik_coord_layer_interface = { + "Coord", + &coordlayer_pixbuf, + + NULL, + 0, + + coord_layer_params, + NUM_PARAMS, + NULL, + 0, + + (VikLayerFuncCreate) vik_coord_layer_create, + (VikLayerFuncRealize) NULL, + (VikLayerFuncPostRead) coord_layer_post_read, + (VikLayerFuncFree) vik_coord_layer_free, + + (VikLayerFuncProperties) NULL, + (VikLayerFuncDraw) vik_coord_layer_draw, + (VikLayerFuncChangeCoordMode) NULL, + + (VikLayerFuncAddMenuItems) NULL, + (VikLayerFuncSublayerAddMenuItems) NULL, + + (VikLayerFuncSublayerRenameRequest) NULL, + (VikLayerFuncSublayerToggleVisible) NULL, + + (VikLayerFuncCopy) coord_layer_copy, + + (VikLayerFuncSetParam) coord_layer_set_param, + (VikLayerFuncGetParam) coord_layer_get_param, + + (VikLayerFuncReadFileData) NULL, + (VikLayerFuncWriteFileData) NULL, + + (VikLayerFuncCopyItem) NULL, + (VikLayerFuncPasteItem) NULL, + (VikLayerFuncFreeCopiedItem) NULL, +}; + +struct _VikCoordLayer { + VikLayer vl; + GdkGC *gc; + gdouble deg_inc; + guint8 line_thickness; + gchar *color; +}; + +GType vik_coord_layer_get_type () +{ + static GType vcl_type = 0; + + if (!vcl_type) + { + static const GTypeInfo vcl_info = + { + sizeof (VikCoordLayerClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + NULL, /* class init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (VikCoordLayer), + 0, + NULL /* instance init */ + }; + vcl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikCoordLayer", &vcl_info, 0 ); + } + + return vcl_type; +} + +static VikCoordLayer *coord_layer_copy ( VikCoordLayer *vcl, gpointer vp ) +{ + VikCoordLayer *rv = vik_coord_layer_new ( ); + + rv->color = g_strdup ( vcl->color ); + rv->deg_inc = vcl->deg_inc; + rv->line_thickness = vcl->line_thickness; + rv->gc = vcl->gc; + g_object_ref ( rv->gc ); + return rv; +} + +gboolean coord_layer_set_param ( VikCoordLayer *vcl, guint16 id, VikLayerParamData data, VikViewport *vp ) +{ + switch ( id ) + { + case PARAM_COLOR: if ( vcl->color ) g_free ( vcl->color ); vcl->color = g_strdup ( data.s ); break; + case PARAM_MIN_INC: vcl->deg_inc = data.d / 60.0; break; + case PARAM_LINE_THICKNESS: if ( data.u >= 1 && data.u <= 15 ) vcl->line_thickness = data.u; break; + } + return TRUE; +} + +static VikLayerParamData coord_layer_get_param ( VikCoordLayer *vcl, guint16 id ) +{ + VikLayerParamData rv; + switch ( id ) + { + case PARAM_COLOR: rv.s = vcl->color ? vcl->color : ""; break; + case PARAM_MIN_INC: rv.d = vcl->deg_inc * 60.0; break; + case PARAM_LINE_THICKNESS: rv.i = vcl->line_thickness; break; + } + return rv; +} + +static void coord_layer_post_read ( VikCoordLayer *vcl, VikViewport *vp ) +{ + if ( vcl->gc ) + g_object_unref ( G_OBJECT(vcl->gc) ); + + vcl->gc = vik_viewport_new_gc ( vp, vcl->color, vcl->line_thickness ); +} + +VikCoordLayer *vik_coord_layer_new ( ) +{ + VikCoordLayer *vcl = VIK_COORD_LAYER ( g_object_new ( VIK_COORD_LAYER_TYPE, NULL ) ); + vik_layer_init ( VIK_LAYER(vcl), VIK_LAYER_COORD ); + + vcl->gc = NULL; + vcl->deg_inc = 1.0/60.0; + vcl->line_thickness = 3; + vcl->color = NULL; + return vcl; +} + +void vik_coord_layer_draw ( VikCoordLayer *vcl, gpointer data ) +{ + VikViewport *vp = (VikViewport *) data; + if ( vik_viewport_get_coord_mode(vp) != VIK_COORD_UTM ) + return; + if ( vcl->gc != NULL) + { + const struct UTM *center = (const struct UTM *)vik_viewport_get_center ( vp ); + gdouble xmpp = vik_viewport_get_xmpp ( vp ), ympp = vik_viewport_get_ympp ( vp ); + guint16 width = vik_viewport_get_width ( vp ), height = vik_viewport_get_height ( vp ); + struct LatLon ll, ll2, min, max; + double lon; + int x1, x2; + struct UTM utm; + + utm = *center; + utm.northing = center->northing - ( ympp * height / 2 ); + + a_coords_utm_to_latlon ( &utm, &ll ); + + utm.northing = center->northing + ( ympp * height / 2 ); + + a_coords_utm_to_latlon ( &utm, &ll2 ); + + { + /* find corner coords in lat/lon. + start at whichever is less: top or bottom left lon. goto whichever more: top or bottom right lon + */ + struct LatLon topleft, topright, bottomleft, bottomright; + struct UTM temp_utm; + temp_utm = *center; + temp_utm.easting -= (width/2)*xmpp; + temp_utm.northing += (height/2)*ympp; + a_coords_utm_to_latlon ( &temp_utm, &topleft ); + temp_utm.easting += (width*xmpp); + a_coords_utm_to_latlon ( &temp_utm, &topright ); + temp_utm.northing -= (height*ympp); + a_coords_utm_to_latlon ( &temp_utm, &bottomright ); + temp_utm.easting -= (width*xmpp); + a_coords_utm_to_latlon ( &temp_utm, &bottomleft ); + min.lon = (topleft.lon < bottomleft.lon) ? topleft.lon : bottomleft.lon; + max.lon = (topright.lon > bottomright.lon) ? topright.lon : bottomright.lon; + min.lat = (bottomleft.lat < bottomright.lat) ? bottomleft.lat : bottomright.lat; + max.lat = (topleft.lat > topright.lat) ? topleft.lat : topright.lat; + } + + lon = ((double) ((long) ((min.lon)/ vcl->deg_inc))) * vcl->deg_inc; + ll.lon = ll2.lon = lon; + + for (; ll.lon <= max.lon; ll.lon+=vcl->deg_inc, ll2.lon+=vcl->deg_inc ) + { + a_coords_latlon_to_utm ( &ll, &utm ); + x1 = ( (utm.easting - center->easting) / xmpp ) + (width / 2); + a_coords_latlon_to_utm ( &ll2, &utm ); + x2 = ( (utm.easting - center->easting) / xmpp ) + (width / 2); + vik_viewport_draw_line (vp, vcl->gc, x1, height, x2, 0); + } + + utm = *center; + utm.easting = center->easting - ( xmpp * width / 2 ); + + a_coords_utm_to_latlon ( &utm, &ll ); + + utm.easting = center->easting + ( xmpp * width / 2 ); + + a_coords_utm_to_latlon ( &utm, &ll2 ); + + /* really lat, just reusing a variable */ + lon = ((double) ((long) ((min.lat)/ vcl->deg_inc))) * vcl->deg_inc; + ll.lat = ll2.lat = lon; + + for (; ll.lat <= max.lat ; ll.lat+=vcl->deg_inc, ll2.lat+=vcl->deg_inc ) + { + a_coords_latlon_to_utm ( &ll, &utm ); + x1 = (height / 2) - ( (utm.northing - center->northing) / ympp ); + a_coords_latlon_to_utm ( &ll2, &utm ); + x2 = (height / 2) - ( (utm.northing - center->northing) / ympp ); + vik_viewport_draw_line (vp, vcl->gc, width, x2, 0, x1); + } + } +} + +void vik_coord_layer_free ( VikCoordLayer *vcl ) +{ + if ( vcl->gc != NULL ) + g_object_unref ( G_OBJECT(vcl->gc) ); + + if ( vcl->color != NULL ) + g_free ( vcl->color ); +} + +static void coord_layer_update_gc ( VikCoordLayer *vcl, VikViewport *vp, const gchar *color ) +{ + if ( vcl->color ) + g_free ( vcl->color ); + + vcl->color = g_strdup ( color ); + + if ( vcl->gc ) + g_object_unref ( G_OBJECT(vcl->gc) ); + + vcl->gc = vik_viewport_new_gc ( vp, vcl->color, vcl->line_thickness ); +} + +VikCoordLayer *vik_coord_layer_create ( VikViewport *vp ) +{ + VikCoordLayer *vcl = vik_coord_layer_new (); + coord_layer_update_gc ( vcl, vp, "red" ); + return vcl; +} + diff --git a/src/vikcoordlayer.h b/src/vikcoordlayer.h new file mode 100644 index 00000000..83ae0ff6 --- /dev/null +++ b/src/vikcoordlayer.h @@ -0,0 +1,51 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _VIKING_COORDLAYER_H +#define _VIKING_COORDLAYER_H + +#define VIK_COORD_LAYER_TYPE (vik_coord_layer_get_type ()) +#define VIK_COORD_LAYER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), VIK_COORD_LAYER_TYPE, VikCoordLayer)) +#define VIK_COORD_LAYER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), VIK_COORD_LAYER_TYPE, VikCoordLayerClass)) +#define IS_VIK_COORD_LAYER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VIK_COORD_LAYER_TYPE)) +#define IS_VIK_COORD_LAYER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), VIK_COORD_LAYER_TYPE)) + +typedef struct _VikCoordLayerClass VikCoordLayerClass; +struct _VikCoordLayerClass +{ + VikLayerClass object_class; +}; + +GType vik_coord_layer_get_type (); + +typedef struct _VikCoordLayer VikCoordLayer; + +/* TODO 0.0.8: yup, everything goes. */ + +VikCoordLayer *vik_coord_layer_new ( ); +void vik_coord_layer_draw ( VikCoordLayer *vcl, gpointer data ); +void vik_coord_layer_free ( VikCoordLayer *vcl ); + +VikCoordLayer *vik_coord_layer_create ( VikViewport *vp ); +gboolean vik_coord_layer_properties ( VikCoordLayer *vcl, gpointer vp ); + + +#endif diff --git a/src/vikcoordlayer_pixmap.h b/src/vikcoordlayer_pixmap.h new file mode 100644 index 00000000..a1d867fb --- /dev/null +++ b/src/vikcoordlayer_pixmap.h @@ -0,0 +1,73 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +static const GdkPixdata coordlayer_pixbuf = { + 0x47646b50, /* Pixbuf magic: 'GdkP' */ + 24 + 787, /* header length + pixel_data length */ + 0x2010002, /* pixdata_type */ + 64, /* rowstride */ + 16, /* width */ + 16, /* height */ + /* pixel_data: */ + "\212\377\377\377\377\1\332\340\364\377\203\377\377\377\377\1\310\322" + "\357\377\206\377\377\377\377\2\324\333\362\377\275\311\354\377\202\377" + "\377\377\377\2\340\345\366\377^{\321\377\202\377\377\377\377\2\370\371" + "\375\377Jk\313\377\203\377\377\377\377\5\361\363\373\377\277\312\354" + "\377\346\352\367\377f\202\323\377\325\334\363\377\202\377\377\377\377" + "\2\201\230\333\377\274\310\354\377\202\377\377\377\377\2\266\303\352" + "\377\213\240\336\377\203\377\377\377\377\15\351\353\366\377\213\240\336" + "\377Xv\317\377\24\77\274\377Ij\313\377d\200\323\377\217\243\337\3771" + "V\304\377\341\346\366\377\376\376\376\377\377\377\377\377k\204\322\377" + "\331\340\364\377\205\377\377\377\377\7\235\257\343\377\237\260\343\377" + "\376\376\376\377\343\350\367\377\200\227\333\377Pp\315\377d\200\323\377" + "\202Ij\313\377\2\31C\276\377\232\254\342\377\202\377\377\377\377\5\362" + "\364\373\377\343\350\367\377\377\377\377\377Hi\313\377\364\366\373\377" + "\202\377\377\377\377\2[y\320\377\342\347\366\377\202\377\377\377\377" + "\3\313\324\360\377h\203\324\377\315\326\360\377\202\377\377\377\377\5" + "\334\342\365\377a~\322\377@c\311\377'N\302\377\255\274\347\377\202\355" + "\360\371\377\1Oo\315\377\203\377\377\377\377\2}\224\332\377\305\317\356" + "\377\205\377\377\377\377\13q\213\326\377\254\273\347\377\227\252\341" + "\377Vu\317\377-S\303\377Kl\314\377\271\305\353\377\365\366\374\377\377" + "\377\377\377Df\312\377\374\374\376\377\204\377\377\377\377\2\370\371" + "\375\377Df\312\377\203\377\377\377\377\15Df\312\377\312\323\360\377\212" + "\237\335\377Nn\315\377Hi\313\3771V\304\377\306\320\356\377\373\373\375" + "\377\377\377\377\377\344\350\367\377\321\331\362\377\251\270\346\377" + "\223\246\340\377\202\377\377\377\377\2\315\326\360\377o\211\326\377\203" + "\377\377\377\377\15\221\245\337\377\203\231\333\377~\225\332\377v\217" + "\330\377\377\377\377\377\343\350\367\377m\207\325\377\17;\273\377v\217" + "\330\377\334\342\365\377\377\377\377\377j\205\324\377\322\332\362\377" + "\203\377\377\377\377\2Mm\314\377\365\366\374\377\204\377\377\377\377" + "\7\337\344\365\377[y\320\377\272\306\353\377c\177\322\377Gh\313\377*" + "Q\302\377\346\352\367\377\202\377\377\377\377\2\357\361\372\377Rq\316" + "\377\205\377\377\377\377\2}\224\332\377\277\312\354\377\202\377\377\377" + "\377\7\242\263\344\377g\203\324\377Yw\320\377Mm\314\377\234\256\343\377" + "\226\251\341\377\232\254\342\377\204\377\377\377\377\3\374\374\376\377" + "Bd\311\377\376\376\376\377\202\377\377\377\377\10Ll\314\377\360\362\372" + "\377\377\377\377\377\362\364\373\377\244\264\345\377\34E\276\377Jk\313" + "\377\350\354\370\377\203\377\377\377\377\2\271\305\353\377\206\234\334" + "\377\202\377\377\377\377\2\334\342\365\377`}\322\377\203\377\377\377" + "\377\4\372\372\375\377Hi\313\377\356\361\372\377\373\373\375\377\203" + "\377\377\377\377\2\350\354\370\377\361\363\373\377\202\377\377\377\377" + "\2\304\316\356\377\316\327\361\377\203\377\377\377\377\2\334\342\365" + "\377\226\251\341\377\205\377\377\377\377", +}; diff --git a/src/vikfileentry.c b/src/vikfileentry.c new file mode 100644 index 00000000..0d949316 --- /dev/null +++ b/src/vikfileentry.c @@ -0,0 +1,98 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include + +#include "vikfileentry.h" + +static void choose_file ( VikFileEntry *vfe ); + +struct _VikFileEntry { + GtkHBox parent; + GtkWidget *entry, *button; + GtkWidget *file_selector; +}; + +GType vik_file_entry_get_type (void) +{ + static GType vs_type = 0; + + if (!vs_type) + { + static const GTypeInfo vs_info = + { + sizeof (VikFileEntryClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + NULL, /* class init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (VikFileEntry), + 0, + NULL /* instance init */ + }; + vs_type = g_type_register_static ( GTK_TYPE_HBOX, "VikFileEntry", &vs_info, 0 ); + } + + return vs_type; +} + +GtkWidget *vik_file_entry_new () +{ + VikFileEntry *vfe = VIK_FILE_ENTRY ( g_object_new ( VIK_FILE_ENTRY_TYPE, NULL ) ); + vfe->entry = gtk_entry_new (); + vfe->button = gtk_button_new_with_label ( "Browse..." ); + g_signal_connect_swapped ( G_OBJECT(vfe->button), "clicked", G_CALLBACK(choose_file), vfe ); + + gtk_box_pack_start ( GTK_BOX(vfe), vfe->entry, TRUE, TRUE, 3 ); + gtk_box_pack_start ( GTK_BOX(vfe), vfe->button, FALSE, FALSE, 3 ); + + vfe->file_selector = NULL; + + return GTK_WIDGET(vfe); +} + +G_CONST_RETURN gchar *vik_file_entry_get_filename ( VikFileEntry *vfe ) +{ + return gtk_entry_get_text ( GTK_ENTRY(vfe->entry) ); +} + +void vik_file_entry_set_filename ( VikFileEntry *vfe, const gchar *filename ) +{ + gtk_entry_set_text ( GTK_ENTRY(vfe->entry), filename ); +} + +static void choose_file ( VikFileEntry *vfe ) +{ + if ( ! vfe->file_selector ) + { + GtkWidget *win; + g_assert ( (win = gtk_widget_get_toplevel(GTK_WIDGET(vfe))) ); + vfe->file_selector = gtk_file_selection_new ("Choose file"); + gtk_window_set_transient_for ( GTK_WINDOW(vfe->file_selector), GTK_WINDOW(win) ); + gtk_window_set_destroy_with_parent ( GTK_WINDOW(vfe->file_selector), TRUE ); + } + + if ( gtk_dialog_run ( GTK_DIALOG(vfe->file_selector) ) == GTK_RESPONSE_OK ) + gtk_entry_set_text ( GTK_ENTRY (vfe->entry), gtk_file_selection_get_filename ( GTK_FILE_SELECTION(vfe->file_selector) ) ); + gtk_widget_hide ( vfe->file_selector ); +} + diff --git a/src/vikfileentry.h b/src/vikfileentry.h new file mode 100644 index 00000000..a9f46c04 --- /dev/null +++ b/src/vikfileentry.h @@ -0,0 +1,51 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _VIKING_FILEENTRY_H +#define _VIKING_FILEENTRY_H + +#include +#include +#include + +G_BEGIN_DECLS + +#define VIK_FILE_ENTRY_TYPE (vik_file_entry_get_type ()) +#define VIK_FILE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), VIK_FILE_ENTRY_TYPE, VikFileEntry)) +#define VIK_FILE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), VIK_FILE_ENTRY_TYPE, VikFileEntryClass)) +#define IS_VIK_FILE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VIK_FILE_ENTRY_TYPE)) +#define IS_VIK_FILE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), VIK_FILE_ENTRY_TYPE)) + +typedef struct _VikFileEntry VikFileEntry; +typedef struct _VikFileEntryClass VikFileEntryClass; + +struct _VikFileEntryClass +{ + GtkHBoxClass hbox_class; +}; + +GType vik_file_entry_get_type (); + +GtkWidget *vik_file_entry_new (); +G_CONST_RETURN gchar *vik_file_entry_get_filename ( VikFileEntry *vfe ); +void vik_file_entry_set_filename ( VikFileEntry *vfe, const gchar *filename ); + +#endif diff --git a/src/vikgeoreflayer.c b/src/vikgeoreflayer.c new file mode 100644 index 00000000..0f4077ee --- /dev/null +++ b/src/vikgeoreflayer.c @@ -0,0 +1,543 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "viking.h" +#include "vikgeoreflayer_pixmap.h" +#include +#include + +VikLayerParam georef_layer_params[] = { + { "image", VIK_LAYER_PARAM_STRING, VIK_LAYER_NOT_IN_PROPERTIES }, + { "corner_easting", VIK_LAYER_PARAM_DOUBLE, VIK_LAYER_NOT_IN_PROPERTIES }, + { "corner_northing", VIK_LAYER_PARAM_DOUBLE, VIK_LAYER_NOT_IN_PROPERTIES }, + { "mpp_easting", VIK_LAYER_PARAM_DOUBLE, VIK_LAYER_NOT_IN_PROPERTIES }, + { "mpp_northing", VIK_LAYER_PARAM_DOUBLE, VIK_LAYER_NOT_IN_PROPERTIES }, +}; + +enum { PARAM_IMAGE = 0, PARAM_CE, PARAM_CN, PARAM_ME, PARAM_MN, NUM_PARAMS }; + +static VikGeorefLayer *georef_layer_copy ( VikGeorefLayer *vgl, gpointer vp ); +static gboolean georef_layer_set_param ( VikGeorefLayer *vgl, guint16 id, VikLayerParamData data, VikViewport *vp ); +static VikLayerParamData georef_layer_get_param ( VikGeorefLayer *vgl, guint16 id ); +VikGeorefLayer *georef_layer_new ( ); +VikGeorefLayer *georef_layer_create ( VikViewport *vp ); +static void georef_layer_free ( VikGeorefLayer *vgl ); +gboolean georef_layer_properties ( VikGeorefLayer *vgl, gpointer vp ); +static void georef_layer_draw ( VikGeorefLayer *vgl, gpointer data ); +static void georef_layer_add_menu_items ( VikGeorefLayer *vgl, GtkMenu *menu, gpointer vlp ); +static void georef_layer_set_image ( VikGeorefLayer *vgl, const gchar *image ); +static gboolean georef_layer_dialog ( VikGeorefLayer **vgl, gpointer vp, GtkWindow *w ); +static void georef_layer_load_image ( VikGeorefLayer *vgl ); +static gboolean georef_layer_move_release ( VikGeorefLayer *vgl, GdkEventButton *event, VikViewport *vvp ); +static gboolean georef_layer_move_press ( VikGeorefLayer *vgl, GdkEventButton *event, VikViewport *vvp ); +static gboolean georef_layer_zoom_press ( VikGeorefLayer *vgl, GdkEventButton *event, VikViewport *vvp ); + +static VikToolInterface georef_tools[] = { + { "Georef Move Map", (VikToolInterfaceFunc) georef_layer_move_press, (VikToolInterfaceFunc) georef_layer_move_release }, + { "Georef Zoom Tool", (VikToolInterfaceFunc) georef_layer_zoom_press, NULL }, +}; + +VikLayerInterface vik_georef_layer_interface = { + "GeoRef Map", + &georeflayer_pixbuf, /*icon */ + + georef_tools, + sizeof(georef_tools) / sizeof(VikToolInterface), + + georef_layer_params, + NUM_PARAMS, + NULL, + 0, + + (VikLayerFuncCreate) georef_layer_create, + (VikLayerFuncRealize) NULL, + (VikLayerFuncPostRead) georef_layer_load_image, + (VikLayerFuncFree) georef_layer_free, + + (VikLayerFuncProperties) georef_layer_properties, + (VikLayerFuncDraw) georef_layer_draw, + (VikLayerFuncChangeCoordMode) NULL, + + (VikLayerFuncAddMenuItems) georef_layer_add_menu_items, + (VikLayerFuncSublayerAddMenuItems) NULL, + + (VikLayerFuncSublayerRenameRequest) NULL, + (VikLayerFuncSublayerToggleVisible) NULL, + + (VikLayerFuncCopy) georef_layer_copy, + + (VikLayerFuncSetParam) georef_layer_set_param, + (VikLayerFuncGetParam) georef_layer_get_param, + + (VikLayerFuncReadFileData) NULL, + (VikLayerFuncWriteFileData) NULL, + + (VikLayerFuncCopyItem) NULL, + (VikLayerFuncPasteItem) NULL, + (VikLayerFuncFreeCopiedItem) NULL, +}; + +struct _VikGeorefLayer { + VikLayer vl; + gchar *image; + GdkPixbuf *pixbuf; + struct UTM corner; + gdouble mpp_easting, mpp_northing; + guint width, height; + + gint click_x, click_y; +}; + + + +GType vik_georef_layer_get_type () +{ + static GType vgl_type = 0; + + if (!vgl_type) + { + static const GTypeInfo vgl_info = + { + sizeof (VikGeorefLayerClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + NULL, /* class init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (VikGeorefLayer), + 0, + NULL /* instance init */ + }; + vgl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikGeorefLayer", &vgl_info, 0 ); + } + + return vgl_type; +} + +static VikGeorefLayer *georef_layer_copy ( VikGeorefLayer *vgl, gpointer vp ) +{ + VikGeorefLayer *rv = georef_layer_new (); + rv->corner = vgl->corner; + rv->mpp_easting = vgl->mpp_easting; + rv->mpp_northing = vgl->mpp_northing; + rv->width = vgl->width; + rv->height = vgl->height; + + if ( vgl->image ) + { + rv->image = g_strdup ( vgl->image ); + georef_layer_load_image ( rv ); + } + return rv; +} + +static gboolean georef_layer_set_param ( VikGeorefLayer *vgl, guint16 id, VikLayerParamData data, VikViewport *vp ) +{ + switch ( id ) + { + case PARAM_IMAGE: georef_layer_set_image ( vgl, data.s ); break; + case PARAM_CN: vgl->corner.northing = data.d; break; + case PARAM_CE: vgl->corner.easting = data.d; break; + case PARAM_MN: vgl->mpp_northing = data.d; break; + case PARAM_ME: vgl->mpp_easting = data.d; break; + } + return TRUE; +} + +static VikLayerParamData georef_layer_get_param ( VikGeorefLayer *vgl, guint16 id ) +{ + VikLayerParamData rv; + switch ( id ) + { + case PARAM_IMAGE: rv.s = vgl->image ? vgl->image : ""; break; + case PARAM_CN: rv.d = vgl->corner.northing; break; + case PARAM_CE: rv.d = vgl->corner.easting; break; + case PARAM_MN: rv.d = vgl->mpp_northing; break; + case PARAM_ME: rv.d = vgl->mpp_easting; break; + } + return rv; +} + +VikGeorefLayer *georef_layer_new ( ) +{ + VikGeorefLayer *vgl = VIK_GEOREF_LAYER ( g_object_new ( VIK_GEOREF_LAYER_TYPE, NULL ) ); + vik_layer_init ( VIK_LAYER(vgl), VIK_LAYER_GEOREF ); + + vgl->image = NULL; + vgl->pixbuf = NULL; + vgl->click_x = -1; + vgl->click_y = -1; + return vgl; +} + +static void georef_layer_draw ( VikGeorefLayer *vgl, gpointer data ) +{ +/* bla, bla */ + if ( vgl->pixbuf ) + { + VikViewport *vp = VIK_VIEWPORT(data); + struct UTM utm_middle; + gdouble xmpp = vik_viewport_get_xmpp(vp), ympp = vik_viewport_get_ympp(vp); + vik_coord_to_utm ( vik_viewport_get_center ( vp ), &utm_middle ); + + if ( xmpp == vgl->mpp_easting && ympp == vgl->mpp_northing ) + { + guint width = vik_viewport_get_width(vp), height = vik_viewport_get_height(vp); + gint32 x, y; + vgl->corner.zone = utm_middle.zone; + vgl->corner.letter = utm_middle.letter; + VikCoord corner_coord; + vik_coord_load_from_utm ( &corner_coord, vik_viewport_get_coord_mode(vp), &(vgl->corner) ); + vik_viewport_coord_to_screen ( vp, &corner_coord, &x, &y ); + if ( (x < 0 || x < width) && (y < 0 || y < height) && x+vgl->width > 0 && y+vgl->height > 0 ) + vik_viewport_draw_pixbuf ( vp, vgl->pixbuf, 0, 0, x, y, vgl->width, vgl->height ); /* todo: draw only what we need to. */ + } + } +} + +static void georef_layer_free ( VikGeorefLayer *vgl ) +{ + if ( vgl->image != NULL ) + g_free ( vgl->image ); +} + +VikGeorefLayer *georef_layer_create ( VikViewport *vp ) +{ + return georef_layer_new (); +} + +gboolean georef_layer_properties ( VikGeorefLayer *vgl, gpointer vp ) +{ + return georef_layer_dialog ( &vgl, vp, VIK_GTK_WINDOW_FROM_WIDGET(vp) ); +} + +static void georef_layer_load_image ( VikGeorefLayer *vgl ) +{ + GError *gx = NULL; + if ( vgl->image == NULL ) + return; + + if ( vgl->pixbuf ) + g_object_unref ( G_OBJECT(vgl->pixbuf) ); + + vgl->pixbuf = gdk_pixbuf_new_from_file ( vgl->image, &gx ); + + if (gx) + { + g_warning ( "Couldn't open image file: %s", gx->message ); + g_error_free ( gx ); + } + else + { + vgl->width = gdk_pixbuf_get_width ( vgl->pixbuf ); + vgl->height = gdk_pixbuf_get_height ( vgl->pixbuf ); + } + + /* should find length and width here too */ +} + +static void georef_layer_set_image ( VikGeorefLayer *vgl, const gchar *image ) +{ + if ( vgl->image ) + g_free ( vgl->image ); + if ( image == NULL ) + vgl->image = NULL; + vgl->image = g_strdup ( image ); +} + +static gboolean world_file_read_line ( gchar *buffer, gint size, FILE *f, GtkWidget *widget, gboolean use_value ) +{ + if (!fgets ( buffer, 1024, f )) + { + a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_WIDGET(widget), "Unexpected end of file reading World file." ); + g_free ( buffer ); + fclose ( f ); + return FALSE; + } + if ( use_value ) + { + gdouble val = strtod ( buffer, NULL ); + gtk_spin_button_set_value ( GTK_SPIN_BUTTON(widget), val > 0 ? val : -val ); + } + return TRUE; +} + +static void georef_layer_dialog_load ( GtkWidget *pass_along[4] ) +{ + GtkWidget *file_selector = gtk_file_selection_new ("Choose World file"); + + if ( gtk_dialog_run ( GTK_DIALOG ( file_selector ) ) == GTK_RESPONSE_OK ) + { + FILE *f = fopen ( gtk_file_selection_get_filename ( GTK_FILE_SELECTION(file_selector) ), "r" ); + gtk_widget_destroy ( file_selector ); + if ( !f ) + { + a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_WIDGET(pass_along[0]), "The World file you requested could not be opened for reading." ); + return; + } + else + { + gchar *buffer = g_malloc ( 1024 * sizeof(gchar) ); + if ( world_file_read_line ( buffer, 1024, f, pass_along[0], TRUE ) && world_file_read_line ( buffer, 1024, f, pass_along[1], TRUE ) + && world_file_read_line ( buffer, 1024, f, pass_along[0], FALSE ) && world_file_read_line ( buffer, 1024, f, pass_along[0], FALSE ) + && world_file_read_line ( buffer, 1024, f, pass_along[2], TRUE ) && world_file_read_line ( buffer, 1024, f, pass_along[3], TRUE ) ) + { + g_free ( buffer ); + fclose ( f ); + } + } + } + else + gtk_widget_destroy ( file_selector ); +/* do your jazz +We need a + file selection dialog + file opener for reading, if NULL, send error_msg ( VIK_GTK_WINDOW_FROM_WIDGET(pass_along[0]) ) + does that catch directories too? + read lines -- if not enough lines, give error. + if anything outside, give error. define range with #define CONSTANTS + put 'em in thar widgets, and that's it. +*/ +} + +static void georef_layer_export_params ( gpointer *pass_along[2] ) +{ + VikGeorefLayer *vgl = VIK_GEOREF_LAYER(pass_along[0]); + GtkWidget *file_selector = gtk_file_selection_new ("Choose World file"); + + if ( gtk_dialog_run ( GTK_DIALOG ( file_selector ) ) == GTK_RESPONSE_OK ) + { + FILE *f = fopen ( gtk_file_selection_get_filename ( GTK_FILE_SELECTION(file_selector) ), "w" ); + gtk_widget_destroy ( file_selector ); + if ( !f ) + { + a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_WIDGET(pass_along[0]), "The file you requested could not be opened for writing." ); + return; + } + else + { + fprintf ( f, "%f\n%f\n%f\n%f\n%f\n%f\n", vgl->mpp_easting, vgl->mpp_northing, 0.0, 0.0, vgl->corner.easting, vgl->corner.northing ); + fclose ( f ); + } + } + else + gtk_widget_destroy ( file_selector ); +} + +/* returns TRUE if OK was pressed. */ +static gboolean georef_layer_dialog ( VikGeorefLayer **vgl, gpointer vp, GtkWindow *w ) +{ + GtkWidget *dialog = gtk_dialog_new_with_buttons ("Layer Properties", + w, + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_CANCEL, + GTK_RESPONSE_REJECT, + GTK_STOCK_OK, + GTK_RESPONSE_ACCEPT, + 0 ); + GtkWidget *table, *wfp_hbox, *wfp_label, *wfp_button, *ce_label, *ce_spin, *cn_label, *cn_spin, *xlabel, *xspin, *ylabel, *yspin, *imagelabel, *imageentry; + + GtkWidget *pass_along[4]; + + table = gtk_table_new ( 6, 2, FALSE ); + gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), table, TRUE, TRUE, 0 ); + + wfp_hbox = gtk_hbox_new ( FALSE, 0 ); + wfp_label = gtk_label_new ( "World File Parameters:" ); + wfp_button = gtk_button_new_with_label ( "Load From File..." ); + + gtk_box_pack_start ( GTK_BOX(wfp_hbox), wfp_label, TRUE, TRUE, 0 ); + gtk_box_pack_start ( GTK_BOX(wfp_hbox), wfp_button, FALSE, FALSE, 3 ); + + ce_label = gtk_label_new ( "Corner pixel easting:" ); + ce_spin = gtk_spin_button_new ( (GtkAdjustment *) gtk_adjustment_new ( 4, 0.0, 1500000.0, 1, 5, 5 ), 1, 4 ); + + cn_label = gtk_label_new ( "Corner pixel northing:" ); + cn_spin = gtk_spin_button_new ( (GtkAdjustment *) gtk_adjustment_new ( 4, 0.0, 9000000.0, 1, 5, 5 ), 1, 4 ); + + xlabel = gtk_label_new ( "X (easting) scale (mpp): "); + ylabel = gtk_label_new ( "Y (northing) scale (mpp): "); + + xspin = gtk_spin_button_new ( (GtkAdjustment *) gtk_adjustment_new ( 4, VIK_VIEWPORT_MIN_ZOOM, VIK_VIEWPORT_MAX_ZOOM, 1, 5, 5 ), 1, 8 ); + yspin = gtk_spin_button_new ( (GtkAdjustment *) gtk_adjustment_new ( 4, VIK_VIEWPORT_MIN_ZOOM, VIK_VIEWPORT_MAX_ZOOM, 1, 5, 5 ), 1, 8 ); + + imagelabel = gtk_label_new ( "Map Image:" ); + imageentry = vik_file_entry_new (); + + if (*vgl) + { + gtk_spin_button_set_value ( GTK_SPIN_BUTTON(ce_spin), (*vgl)->corner.easting ); + gtk_spin_button_set_value ( GTK_SPIN_BUTTON(cn_spin), (*vgl)->corner.northing ); + gtk_spin_button_set_value ( GTK_SPIN_BUTTON(xspin), (*vgl)->mpp_easting ); + gtk_spin_button_set_value ( GTK_SPIN_BUTTON(yspin), (*vgl)->mpp_northing ); + if ( (*vgl)->image ) + vik_file_entry_set_filename ( VIK_FILE_ENTRY(imageentry), (*vgl)->image ); + } + else + { + VikCoord corner_coord; + struct UTM utm; + vik_viewport_screen_to_coord ( VIK_VIEWPORT(vp), 0, 0, &corner_coord ); + vik_coord_to_utm ( &corner_coord, &utm ); + gtk_spin_button_set_value ( GTK_SPIN_BUTTON(ce_spin), utm.easting ); + gtk_spin_button_set_value ( GTK_SPIN_BUTTON(cn_spin), utm.northing ); + gtk_spin_button_set_value ( GTK_SPIN_BUTTON(xspin), vik_viewport_get_xmpp ( VIK_VIEWPORT(vp) ) ); + gtk_spin_button_set_value ( GTK_SPIN_BUTTON(yspin), vik_viewport_get_ympp ( VIK_VIEWPORT(vp) ) ); + } + + gtk_table_attach_defaults ( GTK_TABLE(table), imagelabel, 0, 1, 0, 1 ); + gtk_table_attach_defaults ( GTK_TABLE(table), imageentry, 1, 2, 0, 1 ); + gtk_table_attach_defaults ( GTK_TABLE(table), wfp_hbox, 0, 2, 1, 2 ); + gtk_table_attach_defaults ( GTK_TABLE(table), xlabel, 0, 1, 2, 3 ); + gtk_table_attach_defaults ( GTK_TABLE(table), xspin, 1, 2, 2, 3 ); + gtk_table_attach_defaults ( GTK_TABLE(table), ylabel, 0, 1, 3, 4 ); + gtk_table_attach_defaults ( GTK_TABLE(table), yspin, 1, 2, 3, 4 ); + gtk_table_attach_defaults ( GTK_TABLE(table), ce_label, 0, 1, 4, 5 ); + gtk_table_attach_defaults ( GTK_TABLE(table), ce_spin, 1, 2, 4, 5 ); + gtk_table_attach_defaults ( GTK_TABLE(table), cn_label, 0, 1, 5, 6 ); + gtk_table_attach_defaults ( GTK_TABLE(table), cn_spin, 1, 2, 5, 6 ); + + pass_along[0] = xspin; + pass_along[1] = yspin; + pass_along[2] = ce_spin; + pass_along[3] = cn_spin; + g_signal_connect_swapped ( G_OBJECT(wfp_button), "clicked", G_CALLBACK(georef_layer_dialog_load), pass_along ); + + gtk_widget_show_all ( table ); + + if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) + { + if (! *vgl) + { + *vgl = georef_layer_new (); + vik_layer_rename ( VIK_LAYER(*vgl), vik_georef_layer_interface.name ); + } + (*vgl)->corner.easting = gtk_spin_button_get_value ( GTK_SPIN_BUTTON(ce_spin) ); + (*vgl)->corner.northing = gtk_spin_button_get_value ( GTK_SPIN_BUTTON(cn_spin) ); + (*vgl)->mpp_easting = gtk_spin_button_get_value ( GTK_SPIN_BUTTON(xspin) ); + (*vgl)->mpp_northing = gtk_spin_button_get_value ( GTK_SPIN_BUTTON(yspin) ); + if ( (!(*vgl)->image) || strcmp( (*vgl)->image, vik_file_entry_get_filename(VIK_FILE_ENTRY(imageentry)) ) != 0 ) + { + georef_layer_set_image ( *vgl, vik_file_entry_get_filename(VIK_FILE_ENTRY(imageentry)) ); + georef_layer_load_image ( *vgl ); + } + + gtk_widget_destroy ( GTK_WIDGET(dialog) ); + return TRUE; + } + gtk_widget_destroy ( GTK_WIDGET(dialog) ); + return FALSE; +} + +static void georef_layer_zoom_to_fit ( gpointer vgl_vlp[2] ) +{ + vik_viewport_set_xmpp ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vgl_vlp[1])), VIK_GEOREF_LAYER(vgl_vlp[0])->mpp_easting ); + vik_viewport_set_ympp ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vgl_vlp[1])), VIK_GEOREF_LAYER(vgl_vlp[0])->mpp_northing ); + vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vgl_vlp[1]) ); +} + +static void georef_layer_goto_center ( gpointer vgl_vlp[2] ) +{ + VikGeorefLayer *vgl = VIK_GEOREF_LAYER ( vgl_vlp[0] ); + VikViewport *vp = vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vgl_vlp[1])); + struct UTM utm; + VikCoord coord; + + vik_coord_to_utm ( vik_viewport_get_center ( vp ), &utm ); + + utm.easting = vgl->corner.easting + (vgl->width * vgl->mpp_easting / 2); /* only an approximation */ + utm.northing = vgl->corner.northing - (vgl->height * vgl->mpp_northing / 2); + + vik_coord_load_from_utm ( &coord, vik_viewport_get_coord_mode ( vp ), &utm ); + vik_viewport_set_center_coord ( vp, &coord ); + + vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vgl_vlp[1]) ); +} + +static void georef_layer_add_menu_items ( VikGeorefLayer *vgl, GtkMenu *menu, gpointer vlp ) +{ + static gpointer pass_along[2]; + GtkWidget *item; + pass_along[0] = vgl; + pass_along[1] = vlp; + + item = gtk_menu_item_new(); + gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_widget_show ( item ); + + item = gtk_menu_item_new_with_label ( "Zoom to Fit Map" ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(georef_layer_zoom_to_fit), pass_along ); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show ( item ); + + item = gtk_menu_item_new_with_label ( "Goto Map Center" ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(georef_layer_goto_center), pass_along ); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show ( item ); + + item = gtk_menu_item_new_with_label ( "Export to World File" ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(georef_layer_export_params), pass_along ); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show ( item ); +} + +static gboolean georef_layer_move_release ( VikGeorefLayer *vgl, GdkEventButton *event, VikViewport *vvp ) +{ + if ( vgl->click_x != -1 ) + { + vgl->corner.easting += (event->x - vgl->click_x) * vik_viewport_get_xmpp (vvp); + vgl->corner.northing -= (event->y - vgl->click_y) * vik_viewport_get_ympp (vvp); + vik_layer_emit_update ( VIK_LAYER(vgl) ); + return TRUE; + } + return FALSE; /* I didn't move anything on this layer! */ +} + +static gboolean georef_layer_zoom_press ( VikGeorefLayer *vgl, GdkEventButton *event, VikViewport *vvp ) +{ + if ( event->button == 1 ) + { + if ( vgl->mpp_easting < (VIK_VIEWPORT_MAX_ZOOM / 1.05) && vgl->mpp_northing < (VIK_VIEWPORT_MAX_ZOOM / 1.05) ) + { + vgl->mpp_easting *= 1.01; + vgl->mpp_northing *= 1.01; + } + } + else + { + if ( vgl->mpp_easting > (VIK_VIEWPORT_MIN_ZOOM * 1.05) && vgl->mpp_northing > (VIK_VIEWPORT_MIN_ZOOM * 1.05) ) + { + vgl->mpp_easting /= 1.01; + vgl->mpp_northing /= 1.01; + } + } + vik_viewport_set_xmpp ( vvp, vgl->mpp_easting ); + vik_viewport_set_ympp ( vvp, vgl->mpp_northing ); + vik_layer_emit_update ( VIK_LAYER(vgl) ); + return TRUE; +} + +static gboolean georef_layer_move_press ( VikGeorefLayer *vgl, GdkEventButton *event, VikViewport *vvp ) +{ + vgl->click_x = event->x; + vgl->click_y = event->y; + return TRUE; +} diff --git a/src/vikgeoreflayer.h b/src/vikgeoreflayer.h new file mode 100644 index 00000000..d981fcd0 --- /dev/null +++ b/src/vikgeoreflayer.h @@ -0,0 +1,42 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _VIKING_GEOREFLAYER_H +#define _VIKING_GEOREFLAYER_H + +#define VIK_GEOREF_LAYER_TYPE (vik_georef_layer_get_type ()) +#define VIK_GEOREF_LAYER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), VIK_GEOREF_LAYER_TYPE, VikGeorefLayer)) +#define VIK_GEOREF_LAYER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), VIK_GEOREF_LAYER_TYPE, VikGeorefLayerClass)) +#define IS_VIK_GEOREF_LAYER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VIK_GEOREF_LAYER_TYPE)) +#define IS_VIK_GEOREF_LAYER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), VIK_GEOREF_LAYER_TYPE)) + +typedef struct _VikGeorefLayerClass VikGeorefLayerClass; +struct _VikGeorefLayerClass +{ + VikLayerClass object_class; +}; + +GType vik_georef_layer_get_type (); + + +typedef struct _VikGeorefLayer VikGeorefLayer; + +#endif diff --git a/src/vikgeoreflayer_pixmap.h b/src/vikgeoreflayer_pixmap.h new file mode 100644 index 00000000..2b7ee96d --- /dev/null +++ b/src/vikgeoreflayer_pixmap.h @@ -0,0 +1,60 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* GdkPixbuf RGB C-Source image dump 1-byte-run-length-encoded */ + +static const GdkPixdata georeflayer_pixbuf = { + 0x47646b50, /* Pixbuf magic: 'GdkP' */ + 24 + 579, /* header length + pixel_data length */ + 0x2010001, /* pixdata_type */ + 48, /* rowstride */ + 16, /* width */ + 16, /* height */ + /* pixel_data: */ + "\2\377\377\377*\203&\207\37|\32\11b\243_\377\377\377\377@@\377\217\217" + "\262\262\262\21\21\21JJJ\377\377\3774\210/\207\37|\32\12\273\327\271" + "\377\377\377\377@@\24566\6\6\6\214\214\214\376\376\376\377\377\377\256" + "\317\254>\216:\205\37|\32\6\40}\33\353\363\352\377\377\377\373<\255\355\6\21\271\30B-G\305\202*H\310" + "\4\377\377\377ggg\35\35\35\360\360\360\203\377\377\377\1\237\255\346" + "\203*H\310\2\327\16&z-}\203*H\310\3\377\377\377\35\35\35\203\203\203" + "\204\377\377\377\1z\215\335\203*H\310\2\327\16&z-}\203*H\310\3\334\334" + "\334\2\2\2\320\320\320\204\377\377\377\1\220\237\342\203*H\310\2\327" + "\16&z-}\203*H\310", +}; + + diff --git a/src/viking.h b/src/viking.h new file mode 100644 index 00000000..f793be1d --- /dev/null +++ b/src/viking.h @@ -0,0 +1,62 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __VIKING_VIKING_H +#define __VIKING_VIKING_H + +#include +#include +#include +#include + +#ifdef WINDOWS +#include +#include +#else +#include +#endif + +#include "config.h" + +#include "globals.h" +#include "coords.h" +#include "vikcoord.h" +#include "http.h" +#include "vikwaypoint.h" +#include "viktrack.h" +#include "vikviewport.h" +#include "viktreeview.h" +#include "viklayer.h" +#include "vikaggregatelayer.h" +#include "viklayerspanel.h" +#include "vikcoordlayer.h" +#include "vikgeoreflayer.h" +#include "vikstatus.h" +#include "vikfileentry.h" +#include "viktrwlayer.h" +#include "clipboard.h" +#include "dialog.h" +#include "file.h" +#include "vikwindow.h" +#include "gpspoint.h" +#include "gpsmapper.h" + +#endif diff --git a/src/viklayer.c b/src/viklayer.c new file mode 100644 index 00000000..edd2eef4 --- /dev/null +++ b/src/viklayer.c @@ -0,0 +1,526 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "viking.h" +#include "vikradiogroup.h" + +/* functions common to all layers. */ +/* TODO longone: rename interface free -> finalize */ + +extern VikLayerInterface vik_aggregate_layer_interface; +extern VikLayerInterface vik_trw_layer_interface; +extern VikLayerInterface vik_maps_layer_interface; +extern VikLayerInterface vik_coord_layer_interface; +extern VikLayerInterface vik_georef_layer_interface; + +enum { + VL_UPDATE_SIGNAL, + VL_LAST_SIGNAL +}; +static guint layer_signals[VL_LAST_SIGNAL] = { 0 }; + +static GObjectClass *parent_class; + +static void layer_class_init ( VikLayerClass *klass ); +static void layer_init ( VikLayer *vl ); +static void layer_finalize ( VikLayer *vl ); +static gboolean layer_properties_factory ( VikLayer *vl, gpointer vp ); + + +/* TODO longone: rename vik_layer_init -> set_type */ + +GType vik_layer_get_type () +{ + static GType vl_type = 0; + + if (!vl_type) + { + static const GTypeInfo vl_info = + { + sizeof (VikLayerClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) layer_class_init, /* class init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (VikLayer), + 0, + (GInstanceInitFunc) layer_init /* instance init */ + }; + vl_type = g_type_register_static ( G_TYPE_OBJECT, "VikLayer", &vl_info, 0 ); + } + + return vl_type; +} + +static void layer_class_init (VikLayerClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = (GObjectFinalizeFunc) layer_finalize; + + parent_class = g_type_class_peek_parent (klass); + + layer_signals[VL_UPDATE_SIGNAL] = g_signal_new ( "update", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (VikLayerClass, update), NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); +} + +void vik_layer_emit_update ( VikLayer *vl ) +{ + if ( vl->visible ) + g_signal_emit ( G_OBJECT(vl), layer_signals[VL_UPDATE_SIGNAL], 0 ); +} + +static VikLayerInterface *vik_layer_interfaces[VIK_LAYER_NUM_TYPES] = { + &vik_aggregate_layer_interface, + &vik_trw_layer_interface, + &vik_coord_layer_interface, + &vik_georef_layer_interface, + &vik_maps_layer_interface, +}; + +VikLayerInterface *vik_layer_get_interface ( gint type ) +{ + g_assert ( type < VIK_LAYER_NUM_TYPES ); + return vik_layer_interfaces[type]; +} + +static void layer_init ( VikLayer *vl ) +{ + vl->visible = TRUE; + vl->name = NULL; + vl->realized = FALSE; +} + +void vik_layer_init ( VikLayer *vl, gint type ) +{ + vl->type = type; +} + +/* frees old name */ +void vik_layer_rename ( VikLayer *l, const gchar *new_name ) +{ + g_assert ( l != NULL); + if ( l->name ) + g_free ( l->name ); + l->name = g_strdup ( new_name ); +} + +void vik_layer_rename_no_copy ( VikLayer *l, gchar *new_name ) +{ + g_assert ( l != NULL); + if ( l->name ) + g_free ( l->name ); + l->name = new_name; +} + +VikLayer *vik_layer_create ( gint type, gpointer vp, GtkWindow *w, gboolean interactive ) +{ + VikLayer *new_layer = NULL; + g_assert ( type < VIK_LAYER_NUM_TYPES ); + + new_layer = vik_layer_interfaces[type]->create ( vp ); + + g_assert ( new_layer != NULL ); + + if ( interactive ) + { + if ( vik_layer_properties ( new_layer, vp ) ) + vik_layer_rename ( VIK_LAYER(new_layer), vik_layer_interfaces[type]->name ); + else + { + g_object_unref ( G_OBJECT(new_layer) ); /* cancel that */ + new_layer = NULL; + } + } + return new_layer; +} + +/* returns TRUE if OK was pressed */ +gboolean vik_layer_properties ( VikLayer *layer, gpointer vp ) +{ + if ( vik_layer_interfaces[layer->type]->properties ) + return vik_layer_interfaces[layer->type]->properties ( layer, vp ); + return layer_properties_factory ( layer, vp ); +} + +void vik_layer_draw ( VikLayer *l, gpointer data ) +{ + if ( l->visible ) + if ( vik_layer_interfaces[l->type]->draw ) + vik_layer_interfaces[l->type]->draw ( l, data ); +} + +void vik_layer_change_coord_mode ( VikLayer *l, VikCoordMode mode ) +{ + if ( vik_layer_interfaces[l->type]->change_coord_mode ) + vik_layer_interfaces[l->type]->change_coord_mode ( l, mode ); +} + +VikLayer *vik_layer_copy ( VikLayer *vl, gpointer vp ) +{ + if ( vik_layer_interfaces[vl->type]->copy ) + { + VikLayer *rv = vik_layer_interfaces[vl->type]->copy ( vl, vp ); + if ( rv ) + { + vik_layer_rename ( rv, vl->name ); + rv->visible = vl->visible; + } + return rv; + } + else + return NULL; +} + + +static void layer_finalize ( VikLayer *vl ) +{ + g_assert ( vl != NULL ); + if ( vik_layer_interfaces[vl->type]->free ) + vik_layer_interfaces[vl->type]->free ( vl ); + if ( vl->name ) + g_free ( vl->name ); + G_OBJECT_CLASS(parent_class)->finalize(G_OBJECT(vl)); +} + +/* sublayer switching */ +gboolean vik_layer_sublayer_toggle_visible ( VikLayer *l, gint subtype, gpointer sublayer ) +{ + if ( vik_layer_interfaces[l->type]->sublayer_toggle_visible ) + return vik_layer_interfaces[l->type]->sublayer_toggle_visible ( l, subtype, sublayer ); + return TRUE; /* if unknown, will always be visible */ +} + +void vik_layer_realize ( VikLayer *l, VikTreeview *vt, GtkTreeIter *layer_iter ) +{ + l->vt = vt; + l->iter = *layer_iter; + l->realized = TRUE; + if ( vik_layer_interfaces[l->type]->realize ) + vik_layer_interfaces[l->type]->realize ( l, vt, layer_iter ); +} + +void vik_layer_add_menu_items ( VikLayer *l, GtkMenu *menu, gpointer vlp ) +{ + if ( vik_layer_interfaces[l->type]->add_menu_items ) + vik_layer_interfaces[l->type]->add_menu_items ( l, menu, vlp ); +} + +gboolean vik_layer_sublayer_add_menu_items ( VikLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter ) +{ + if ( vik_layer_interfaces[l->type]->sublayer_add_menu_items ) + return vik_layer_interfaces[l->type]->sublayer_add_menu_items ( l, menu, vlp, subtype, sublayer, iter ); + return FALSE; +} + + +const gchar *vik_layer_sublayer_rename_request ( VikLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter ) +{ + if ( vik_layer_interfaces[l->type]->sublayer_rename_request ) + return vik_layer_interfaces[l->type]->sublayer_rename_request ( l, newname, vlp, subtype, sublayer, iter ); + return NULL; +} + +GdkPixbuf *vik_layer_load_icon ( gint type ) +{ + g_assert ( type < VIK_LAYER_NUM_TYPES ); + if ( vik_layer_interfaces[type]->icon ) + return gdk_pixbuf_from_pixdata ( vik_layer_interfaces[type]->icon, FALSE, NULL ); + return NULL; +} + +gboolean vik_layer_set_param ( VikLayer *layer, guint16 id, VikLayerParamData data, gpointer vp ) +{ + if ( vik_layer_interfaces[layer->type]->set_param ) + return vik_layer_interfaces[layer->type]->set_param ( layer, id, data, vp ); + return FALSE; +} + +void vik_layer_post_read ( VikLayer *layer, gpointer vp ) +{ + if ( vik_layer_interfaces[layer->type]->post_read ) + vik_layer_interfaces[layer->type]->post_read ( layer, vp ); +} + +static GtkWidget *properties_widget_new_widget ( VikLayerParam *param, VikLayerParamData data ) +{ + GtkWidget *rv; + switch ( param->widget_type ) + { + case VIK_LAYER_WIDGET_COLOR: + if ( param->type == VIK_LAYER_PARAM_COLOR ) + rv = gtk_color_button_new_with_color ( &(data.c) ); + break; + case VIK_LAYER_WIDGET_CHECKBUTTON: + if ( param->type == VIK_LAYER_PARAM_BOOLEAN ) + { + //rv = gtk_check_button_new_with_label ( //param->title ); + rv = gtk_check_button_new (); + if ( data.b ) + gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(rv), TRUE ); + } + break; + case VIK_LAYER_WIDGET_COMBOBOX: +#ifndef GTK_2_2 + if ( param->type == VIK_LAYER_PARAM_UINT && param->widget_data ) + { + gchar **pstr = param->widget_data; + rv = gtk_combo_box_new_text (); + while ( *pstr ) + gtk_combo_box_append_text ( GTK_COMBO_BOX ( rv ), *(pstr++) ); + if ( param->extra_widget_data ) /* map of alternate uint values for options */ + { + int i; + for ( i = 0; ((const char **)param->widget_data)[i]; i++ ) + if ( ((guint *)param->extra_widget_data)[i] == data.u ) + { + gtk_combo_box_set_active ( GTK_COMBO_BOX(rv), i ); + break; + } + } + gtk_combo_box_set_active ( GTK_COMBO_BOX ( rv ), data.u ); + } + break; +#endif + case VIK_LAYER_WIDGET_RADIOGROUP: + if ( param->type == VIK_LAYER_PARAM_UINT && param->widget_data ) + { + rv = vik_radio_group_new ( (const gchar **) param->widget_data ); + if ( param->extra_widget_data ) /* map of alternate uint values for options */ + { + int i; + for ( i = 0; ((const char **)param->widget_data)[i]; i++ ) + if ( ((guint *)param->extra_widget_data)[i] == data.u ) + { + vik_radio_group_set_selected ( VIK_RADIO_GROUP(rv), i ); + break; + } + } + else if ( data.u ) /* zero is already default */ + vik_radio_group_set_selected ( VIK_RADIO_GROUP(rv), data.u ); + } + break; + case VIK_LAYER_WIDGET_SPINBUTTON: + if ( (param->type == VIK_LAYER_PARAM_DOUBLE || param->type == VIK_LAYER_PARAM_UINT + || param->type == VIK_LAYER_PARAM_INT) && param->widget_data ) + { + gdouble init_val = (param->type == VIK_LAYER_PARAM_DOUBLE) ? data.d : (param->type == VIK_LAYER_PARAM_UINT ? data.u : data.i); + VikLayerParamScale *scale = (VikLayerParamScale *) param->widget_data; + rv = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new( init_val, scale->min, scale->max, scale->step, scale->step, scale->step )), scale->step, scale->digits ); + } + break; + case VIK_LAYER_WIDGET_ENTRY: + if ( param->type == VIK_LAYER_PARAM_STRING ) + { + rv = gtk_entry_new (); + gtk_entry_set_text ( GTK_ENTRY(rv), data.s ); + } + break; + case VIK_LAYER_WIDGET_FILEENTRY: + if ( param->type == VIK_LAYER_PARAM_STRING ) + { + rv = vik_file_entry_new (); + vik_file_entry_set_filename ( VIK_FILE_ENTRY(rv), data.s ); + } + break; + case VIK_LAYER_WIDGET_HSCALE: + if ( (param->type == VIK_LAYER_PARAM_DOUBLE || param->type == VIK_LAYER_PARAM_UINT + || param->type == VIK_LAYER_PARAM_INT) && param->widget_data ) + { + gdouble init_val = (param->type == VIK_LAYER_PARAM_DOUBLE) ? data.d : (param->type == VIK_LAYER_PARAM_UINT ? data.u : data.i); + VikLayerParamScale *scale = (VikLayerParamScale *) param->widget_data; + rv = gtk_hscale_new_with_range ( scale->min, scale->max, scale->step ); + gtk_scale_set_digits ( GTK_SCALE(rv), scale->digits ); + gtk_range_set_value ( GTK_RANGE(rv), init_val ); + } + } + return rv; +} + +static VikLayerParamData properties_widget_get_value ( GtkWidget *widget, VikLayerParam *param ) +{ + VikLayerParamData rv; + switch ( param->widget_type ) + { + case VIK_LAYER_WIDGET_COLOR: + gtk_color_button_get_color ( GTK_COLOR_BUTTON(widget), &(rv.c) ); + break; + case VIK_LAYER_WIDGET_CHECKBUTTON: + rv.b = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); + break; + case VIK_LAYER_WIDGET_COMBOBOX: +#ifndef GTK_2_2 + rv.i = gtk_combo_box_get_active ( GTK_COMBO_BOX(widget) ); + if ( rv.i == -1 ) rv.i = 0; + rv.u = rv.i; + if ( param->extra_widget_data ) + rv.u = ((guint *)param->extra_widget_data)[rv.u]; + break; +#endif + case VIK_LAYER_WIDGET_RADIOGROUP: + rv.u = vik_radio_group_get_selected(VIK_RADIO_GROUP(widget)); + if ( param->extra_widget_data ) + rv.u = ((guint *)param->extra_widget_data)[rv.u]; + break; + case VIK_LAYER_WIDGET_SPINBUTTON: + if ( param->type == VIK_LAYER_PARAM_UINT ) + rv.u = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(widget) ); + else if ( param->type == VIK_LAYER_PARAM_INT ) + rv.i = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(widget) ); + else + rv.d = gtk_spin_button_get_value ( GTK_SPIN_BUTTON(widget) ); + break; + case VIK_LAYER_WIDGET_ENTRY: + rv.s = gtk_entry_get_text ( GTK_ENTRY(widget) ); + break; + case VIK_LAYER_WIDGET_FILEENTRY: + rv.s = vik_file_entry_get_filename ( VIK_FILE_ENTRY(widget) ); + break; + case VIK_LAYER_WIDGET_HSCALE: + if ( param->type == VIK_LAYER_PARAM_UINT ) + rv.u = (guint32) gtk_range_get_value ( GTK_RANGE(widget) ); + else if ( param->type == VIK_LAYER_PARAM_INT ) + rv.i = (gint32) gtk_range_get_value ( GTK_RANGE(widget) ); + else + rv.d = gtk_range_get_value ( GTK_RANGE(widget) ); + break; + } + return rv; +} + +/* false if cancel, true if OK */ +/* some would claim this wasn't written to be human-readable. */ +static gboolean layer_properties_factory ( VikLayer *vl, gpointer vp ) +{ + VikLayerParam *params = vik_layer_interfaces[vl->type]->params; + guint16 params_count = vik_layer_interfaces[vl->type]->params_count; + guint16 i, j, widget_count = 0; + gboolean must_redraw = FALSE; + + if ( ! params ) + return TRUE; /* no params == no options, so all is good */ + + for ( i = 0; i < params_count; i++ ) + if ( params[i].group != VIK_LAYER_NOT_IN_PROPERTIES ) + widget_count++; + + if ( widget_count == 0) + return FALSE; + else + { + /* create widgets and titles; place in table */ + GtkWidget *dialog = gtk_dialog_new_with_buttons ( "Layer Properties", + VIK_GTK_WINDOW_FROM_WIDGET(vp), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, + GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL ); + gint resp; + + gchar **groups = vik_layer_interfaces[vl->type]->params_groups; + guint8 groups_count = vik_layer_interfaces[vl->type]->params_groups_count; + + GtkWidget *table = NULL; + GtkWidget **tables = NULL; /* for more than one group */ + + GtkWidget *notebook = NULL; + GtkWidget **widgets = g_malloc ( sizeof(GtkWidget *) * widget_count ); + + if ( groups && groups_count ) + { + guint8 current_group; + guint16 tab_widget_count; + notebook = gtk_notebook_new (); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), notebook, FALSE, FALSE, 0); + tables = g_malloc ( sizeof(GtkWidget *) * groups_count ); + for ( current_group = 0; current_group < groups_count; current_group++ ) + { + tab_widget_count = 0; + for ( j = 0; j < params_count; j ++ ) + if ( params[j].group == current_group ) + tab_widget_count++; + + if ( tab_widget_count ) + { + tables[current_group] = gtk_table_new ( tab_widget_count, 1, FALSE ); + gtk_notebook_append_page ( GTK_NOTEBOOK(notebook), tables[current_group], gtk_label_new(groups[current_group]) ); + } + } + } + else + { + table = gtk_table_new( widget_count, 1, FALSE ); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), table, FALSE, FALSE, 0); + } + + for ( i = 0, j = 0; i < params_count; i++ ) + { + if ( params[i].group != VIK_LAYER_NOT_IN_PROPERTIES ) + { + if ( tables ) + table = tables[MAX(0, params[i].group)]; /* round up NOT_IN_GROUP, that's not reasonable here */ + + widgets[j] = properties_widget_new_widget ( &(params[i]), + vik_layer_interfaces[vl->type]->get_param ( vl, i ) ); + + g_assert ( widgets[j] != NULL ); + + gtk_table_attach ( GTK_TABLE(table), gtk_label_new(params[i].title), 0, 1, j, j+1, 0, 0, 0, 0 ); + gtk_table_attach ( GTK_TABLE(table), widgets[j], 1, 2, j, j+1, GTK_EXPAND | GTK_FILL, 0, 2, 2 ); + j++; + } + } + + gtk_widget_show_all ( dialog ); + + resp = gtk_dialog_run (GTK_DIALOG (dialog)); + if ( resp == GTK_RESPONSE_ACCEPT ) + { + for ( i = 0, j = 0; i < params_count; i++ ) + { + if ( params[i].group != VIK_LAYER_NOT_IN_PROPERTIES ) + { + if ( vik_layer_interfaces[vl->type]->set_param ( vl, i, + properties_widget_get_value ( widgets[j], &(params[i]) ), vp ) ) + must_redraw = TRUE; + j++; + } + } + vik_layer_post_read ( vl, vp ); /* update any gc's */ + + gtk_widget_destroy ( dialog ); /* hide before redrawing. */ + g_free ( widgets ); + + if ( must_redraw ) + vik_layer_emit_update ( vl ); /* if this is a new layer, it won't redraw twice because no on'es listening to this signal. */ + return TRUE; /* user clicked OK */ + } + + if ( tables ) + g_free ( tables ); + gtk_widget_destroy ( dialog ); + g_free ( widgets ); + return FALSE; + } +} diff --git a/src/viklayer.h b/src/viklayer.h new file mode 100644 index 00000000..bee04957 --- /dev/null +++ b/src/viklayer.h @@ -0,0 +1,260 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _VIKING_LAYER_H +#define _VIKING_LAYER_H + +#define VIK_LAYER_TYPE (vik_layer_get_type ()) +#define VIK_LAYER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), VIK_LAYER_TYPE, VikLayer)) +#define VIK_LAYER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), VIK_LAYER_TYPE, VikLayerClass)) +#define IS_VIK_LAYER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VIK_LAYER_TYPE)) +#define IS_VIK_LAYER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), VIK_LAYER_TYPE)) + +typedef struct _VikLayer VikLayer; +typedef struct _VikLayerClass VikLayerClass; + +struct _VikLayerClass +{ + GObjectClass object_class; + void (* update) (VikLayer *vl); +}; + +GType vik_layer_get_type (); + +struct _VikLayer { + GObject obj; + gchar *name; + gboolean visible; + + gboolean realized; + VikTreeview *vt; /* simply a refernce */ + GtkTreeIter iter; + + /* for explicit "polymorphism" (function type switching) */ + guint16 type; +}; + + + +enum { +VIK_LAYER_AGGREGATE = 0, +VIK_LAYER_TRW, +VIK_LAYER_COORD, +VIK_LAYER_GEOREF, +VIK_LAYER_MAPS, +VIK_LAYER_NUM_TYPES +}; + +typedef enum { VIK_LAYER_TOOL_IGNORED=0, + VIK_LAYER_TOOL_ACK, + VIK_LAYER_TOOL_ACK_REDRAW_ABOVE, + VIK_LAYER_TOOL_ACK_REDRAW_ALL, + VIK_LAYER_TOOL_ACK_REDRAW_IF_VISIBLE } + VikLayerToolFuncStatus; +typedef VikLayerToolFuncStatus (*VikToolInterfaceFunc) (VikLayer *,GdkEventButton *,gpointer); + +/* gpointer is viewport */ + +typedef struct _VikToolInterface VikToolInterface; +struct _VikToolInterface { + gchar *name; + VikToolInterfaceFunc callback; + VikToolInterfaceFunc callback_release; +}; + +/* Parameters (for I/O and Properties) */ + +typedef union { + gdouble d; + guint32 u; + gint32 i; + gboolean b; + const gchar *s; + GdkColor c; +} VikLayerParamData; + +typedef struct { + const gchar *name; + guint8 type; + gint16 group; + const gchar *title; + guint8 widget_type; + const gpointer widget_data; + const gpointer extra_widget_data; +} VikLayerParam; + +enum { +VIK_LAYER_NOT_IN_PROPERTIES=-2, +VIK_LAYER_GROUP_NONE=-1 +}; + +enum { +VIK_LAYER_WIDGET_CHECKBUTTON=0, +VIK_LAYER_WIDGET_RADIOGROUP, +VIK_LAYER_WIDGET_SPINBUTTON, +VIK_LAYER_WIDGET_ENTRY, +VIK_LAYER_WIDGET_FILEENTRY, +VIK_LAYER_WIDGET_HSCALE, +VIK_LAYER_WIDGET_COLOR, +VIK_LAYER_WIDGET_COMBOBOX, +}; + +typedef struct { + gdouble min; + gdouble max; + gdouble step; + guint8 digits; +} VikLayerParamScale; + +/* id is index */ +enum { +VIK_LAYER_PARAM_DOUBLE=1, +VIK_LAYER_PARAM_UINT, +VIK_LAYER_PARAM_INT, +VIK_LAYER_PARAM_STRING, +VIK_LAYER_PARAM_BOOLEAN, +VIK_LAYER_PARAM_COLOR, +}; + +/* layer interface functions */ + +/* Create a new layer of a certain type. Should be filled with defaults */ +typedef VikLayer * (*VikLayerFuncCreate) (VikViewport *); + +/* normally only needed for layers with sublayers. This is called when they + * are added to the treeview so they can add sublayers to the treeview. */ +typedef void (*VikLayerFuncRealize) (VikLayer *,VikTreeview *,GtkTreeIter *); + +/* rarely used, this is called after a read operation or properties box is run. + * usually used to create GC's that depend on params, + * but GC's can also be created from create() or set_param() */ +typedef void (*VikLayerFuncPostRead) (VikLayer *,gpointer vp); + +typedef void (*VikLayerFuncFree) (VikLayer *); + +/* do _not_ use this unless absolutely neccesary. Use the dynamic properties (see coordlayer for example) + * returns TRUE if OK was pressed */ +typedef gboolean (*VikLayerFuncProperties) (VikLayer *,VikViewport *); /* gpointer is a VikViewport */ + +typedef void (*VikLayerFuncDraw) (VikLayer *,VikViewport *); +typedef void (*VikLayerFuncChangeCoordMode) (VikLayer *,VikCoordMode); + +typedef void (*VikLayerFuncAddMenuItems) (VikLayer *,GtkMenu *,gpointer); /* gpointer is a VikLayersPanel */ +typedef gboolean (*VikLayerFuncSublayerAddMenuItems) (VikLayer *,GtkMenu *,gpointer, /* first gpointer is a VikLayersPanel */ + gint,gpointer,GtkTreeIter *); +typedef const gchar * (*VikLayerFuncSublayerRenameRequest) (VikLayer *,const gchar *,gpointer, + gint,VikViewport *,GtkTreeIter *); /* first gpointer is a VikLayersPanel */ +typedef gboolean (*VikLayerFuncSublayerToggleVisible) (VikLayer *,gint,gpointer); + +typedef VikLayer * (*VikLayerFuncCopy) (VikLayer *,VikViewport *); + +/* returns TRUE if needs to redraw due to changed param */ +typedef gboolean (*VikLayerFuncSetParam) (VikLayer *, guint16, VikLayerParamData, VikViewport *); + +typedef VikLayerParamData + (*VikLayerFuncGetParam) (VikLayer *, guint16); + +typedef void (*VikLayerFuncReadFileData) (VikLayer *, FILE *); +typedef void (*VikLayerFuncWriteFileData) (VikLayer *, FILE *); + +typedef gpointer (*VikLayerFuncCopyItem) (VikLayer *, gint, gpointer); +typedef gboolean (*VikLayerFuncPasteItem) (VikLayer *, gint, gpointer); +typedef void (*VikLayerFuncFreeCopiedItem) (gint, gpointer); + + +typedef struct _VikLayerInterface VikLayerInterface; + +/* See vik_layer_* for function parameter names */ +struct _VikLayerInterface { + const gchar * name; + const GdkPixdata * icon; + + VikToolInterface * tools; + guint16 tools_count; + + /* for I/O reading to and from .vik files -- params like coordline width, color, etc. */ + VikLayerParam * params; + guint16 params_count; + gchar ** params_groups; + guint8 params_groups_count; + + VikLayerFuncCreate create; + VikLayerFuncRealize realize; + VikLayerFuncPostRead post_read; + VikLayerFuncFree free; + + VikLayerFuncProperties properties; + VikLayerFuncDraw draw; + VikLayerFuncChangeCoordMode change_coord_mode; + + VikLayerFuncAddMenuItems add_menu_items; + VikLayerFuncSublayerAddMenuItems sublayer_add_menu_items; + VikLayerFuncSublayerRenameRequest sublayer_rename_request; + VikLayerFuncSublayerToggleVisible sublayer_toggle_visible; + + VikLayerFuncCopy copy; + + /* for I/O */ + VikLayerFuncSetParam set_param; + VikLayerFuncGetParam get_param; + + /* for I/O -- extra non-param data like TrwLayer data */ + VikLayerFuncReadFileData read_file_data; + VikLayerFuncWriteFileData write_file_data; + + VikLayerFuncCopyItem copy_item; + VikLayerFuncPasteItem paste_item; + VikLayerFuncFreeCopiedItem free_copied_item; +}; + +VikLayerInterface *vik_layer_get_interface ( gint type ); + + +void vik_layer_init ( VikLayer *vl, gint type ); +void vik_layer_draw ( VikLayer *l, gpointer data ); +void vik_layer_change_coord_mode ( VikLayer *l, VikCoordMode mode ); +void vik_layer_rename ( VikLayer *l, const gchar *new_name ); +void vik_layer_rename_no_copy ( VikLayer *l, gchar *new_name ); + +gboolean vik_layer_set_param (VikLayer *layer, guint16 id, VikLayerParamData data, gpointer vp); + +void vik_layer_emit_update ( VikLayer *vl ); + +/* GUI */ +void vik_layer_add_menu_items ( VikLayer *l, GtkMenu *menu, gpointer vlp ); +VikLayer *vik_layer_create ( gint type, gpointer vp, GtkWindow *w, gboolean interactive ); +gboolean vik_layer_properties ( VikLayer *layer, gpointer vp ); + +void vik_layer_realize ( VikLayer *l, VikTreeview *vt, GtkTreeIter * layer_iter ); +void vik_layer_post_read ( VikLayer *layer, gpointer vp ); + +gboolean vik_layer_sublayer_add_menu_items ( VikLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter ); + +VikLayer *vik_layer_copy ( VikLayer *vl, gpointer vp ); + +const gchar *vik_layer_sublayer_rename_request ( VikLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter ); + +gboolean vik_layer_sublayer_toggle_visible ( VikLayer *l, gint subtype, gpointer sublayer ); + +/* TODO: put in layerspanel */ +GdkPixbuf *vik_layer_load_icon ( gint type ); + +#endif diff --git a/src/viklayerspanel.c b/src/viklayerspanel.c new file mode 100644 index 00000000..1b2cdd32 --- /dev/null +++ b/src/viklayerspanel.c @@ -0,0 +1,513 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "viking.h" + +#include + +enum { + VLP_UPDATE_SIGNAL, + VLP_LAST_SIGNAL +}; + +static void layers_panel_class_init ( VikLayersPanelClass *klass ); +static void layers_panel_init ( VikLayersPanel *vlp ); +static void layers_item_edited (VikLayersPanel *vlp, GtkTreeIter *iter, const gchar *new_text); +static void layers_item_toggled (VikLayersPanel *vlp, GtkTreeIter *iter); + +static guint layers_panel_signals[VLP_LAST_SIGNAL] = { 0 }; + +static GObjectClass *parent_class; + +struct _VikLayersPanel { + GtkVBox vbox; + + VikAggregateLayer *toplayer; + GtkTreeIter toplayer_iter; + + VikTreeview *vt; + VikViewport *vvp; /* reference */ + + GtkItemFactory *popup_factory; +}; + +static GtkItemFactoryEntry base_entries[] = { + { "/_Delete", NULL, (GtkItemFactoryCallback) vik_layers_panel_delete_selected, -1, "", GTK_STOCK_DELETE }, + { "/New Layer", NULL, NULL, -1, "" }, +}; + +#define NUM_BASE_ENTRIES 2 + +static void layers_item_toggled (VikLayersPanel *vlp, GtkTreeIter *iter); +static void layers_item_edited (VikLayersPanel *vlp, GtkTreeIter *iter, const gchar *new_text); +static void layers_popup_cb (VikLayersPanel *vlp); +static void layers_popup ( VikLayersPanel *vlp, GtkTreeIter *iter, gint mouse_button ); +static gboolean layers_button_press_cb (VikLayersPanel *vlp, GdkEventButton *event); +static void layers_move_item ( VikLayersPanel *vlp, gboolean up ); +static void layers_move_item_up ( VikLayersPanel *vlp ); +static void layers_move_item_down ( VikLayersPanel *vlp ); +static void layers_panel_finalize ( GObject *gob ); + +GType vik_layers_panel_get_type() +{ + static GType vlp_type = 0; + + if (!vlp_type) + { + static const GTypeInfo vlp_info = + { + sizeof (VikLayersPanelClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) layers_panel_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (VikLayersPanel), + 0, + (GInstanceInitFunc) layers_panel_init, + }; + vlp_type = g_type_register_static ( GTK_TYPE_VBOX, "VikLayersPanel", &vlp_info, 0 ); + } + + return vlp_type; +} + +static void layers_panel_class_init ( VikLayersPanelClass *klass ) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = layers_panel_finalize; + + parent_class = g_type_class_peek_parent (klass); + + layers_panel_signals[VLP_UPDATE_SIGNAL] = g_signal_new ( "update", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (VikLayersPanelClass, update), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); +} + +VikLayersPanel *vik_layers_panel_new () +{ + return VIK_LAYERS_PANEL ( g_object_new ( VIK_LAYERS_PANEL_TYPE, NULL ) ); +} + +void vik_layers_panel_set_viewport ( VikLayersPanel *vlp, VikViewport *vvp ) +{ + vlp->vvp = vvp; + /* TODO: also update GCs (?) */ +} + +VikViewport *vik_layers_panel_get_viewport ( VikLayersPanel *vlp ) +{ + return vlp->vvp; +} + +static void layers_panel_init ( VikLayersPanel *vlp ) +{ + GtkWidget *hbox; + GtkWidget *upbutton, *upimage, *downbutton, *downimage; + GtkWidget *scrolledwindow; + GtkItemFactoryEntry entry; + guint i, tmp; + + vlp->vvp = NULL; + + hbox = gtk_hbox_new ( TRUE, 2 ); + vlp->vt = vik_treeview_new ( ); + + vlp->toplayer = vik_aggregate_layer_new (); + vik_layer_rename ( VIK_LAYER(vlp->toplayer), "Top Layer"); + g_signal_connect_swapped ( G_OBJECT(vlp->toplayer), "update", G_CALLBACK(vik_layers_panel_emit_update), vlp ); + + vik_treeview_add_layer ( vlp->vt, NULL, &(vlp->toplayer_iter), VIK_LAYER(vlp->toplayer)->name, NULL, vlp->toplayer, VIK_LAYER_AGGREGATE, VIK_LAYER_AGGREGATE ); + vik_layer_realize ( VIK_LAYER(vlp->toplayer), vlp->vt, &(vlp->toplayer_iter) ); + + g_signal_connect_swapped ( vlp->vt, "popup_menu", G_CALLBACK(layers_popup_cb), vlp); + g_signal_connect_swapped ( vlp->vt, "button_press_event", G_CALLBACK(layers_button_press_cb), vlp); + g_signal_connect_swapped ( vlp->vt, "item_toggled", G_CALLBACK(layers_item_toggled), vlp); + g_signal_connect_swapped ( vlp->vt, "item_edited", G_CALLBACK(layers_item_edited), vlp); + + upimage = gtk_image_new_from_stock ( GTK_STOCK_GO_UP, GTK_ICON_SIZE_BUTTON ); + upbutton = gtk_button_new ( ); + gtk_container_add ( GTK_CONTAINER(upbutton), upimage ); + gtk_box_pack_start ( GTK_BOX(hbox), upbutton, TRUE, TRUE, 0 ); + g_signal_connect_swapped ( G_OBJECT(upbutton), "clicked", G_CALLBACK(layers_move_item_up), vlp ); + downimage = gtk_image_new_from_stock ( GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_BUTTON ); + downbutton = gtk_button_new ( ); + gtk_container_add ( GTK_CONTAINER(downbutton), downimage ); + gtk_box_pack_start ( GTK_BOX(hbox), downbutton, TRUE, TRUE, 0 ); + g_signal_connect_swapped ( G_OBJECT(downbutton), "clicked", G_CALLBACK(layers_move_item_down), vlp ); + + scrolledwindow = gtk_scrolled_window_new ( NULL, NULL ); + gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC ); + gtk_container_add ( GTK_CONTAINER(scrolledwindow), GTK_WIDGET(vlp->vt) ); + + gtk_box_pack_start ( GTK_BOX(vlp), scrolledwindow, TRUE, TRUE, 0 ); + gtk_box_pack_start ( GTK_BOX(vlp), hbox, FALSE, FALSE, 0 ); + + vlp->popup_factory = gtk_item_factory_new ( GTK_TYPE_MENU, "
", NULL ); + gtk_item_factory_create_items ( vlp->popup_factory, NUM_BASE_ENTRIES, base_entries, vlp ); + for ( i = 0; i < VIK_LAYER_NUM_TYPES; i++ ) + { + /* TODO: FIXME: if name has a '/' in it it will get all messed up. why not have an itemfactory field with + name, icon, shortcut, etc.? */ + entry.path = g_strdup_printf("%s/New %s Layer", base_entries[NUM_BASE_ENTRIES-1].path, vik_layer_get_interface(i)->name ); + entry.accelerator = NULL; + entry.callback = (GtkItemFactoryCallback) vik_layers_panel_new_layer; + entry.callback_action = i; + if ( vik_layer_get_interface(i)->icon ) + { + entry.item_type = ""; + entry.extra_data = gdk_pixdata_serialize ( vik_layer_get_interface(i)->icon, &tmp ); + } + else + entry.item_type = ""; + + gtk_item_factory_create_item ( vlp->popup_factory, &entry, vlp, 1 ); + g_free ( (gpointer) entry.extra_data ); + g_free ( entry.path ); + } +} + +void vik_layers_panel_emit_update ( VikLayersPanel *vlp ) +{ + g_signal_emit ( G_OBJECT(vlp), layers_panel_signals[VLP_UPDATE_SIGNAL], 0 ); +} + +static void layers_item_toggled (VikLayersPanel *vlp, GtkTreeIter *iter) +{ + gboolean visible; + gpointer p; + gint type; + + /* get type and data */ + type = vik_treeview_item_get_type ( vlp->vt, iter ); + p = vik_treeview_item_get_pointer ( vlp->vt, iter ); + + switch ( type ) + { + case VIK_TREEVIEW_TYPE_LAYER: visible = (VIK_LAYER(p)->visible ^= 1); break; + case VIK_TREEVIEW_TYPE_SUBLAYER: visible = vik_layer_sublayer_toggle_visible ( VIK_LAYER(vik_treeview_item_get_parent(vlp->vt, iter)), vik_treeview_item_get_data(vlp->vt, iter), p); break; + default: return; + } + + vik_treeview_item_set_visible ( vlp->vt, iter, visible ); + + vik_layers_panel_emit_update ( vlp ); +} + +static void layers_item_edited (VikLayersPanel *vlp, GtkTreeIter *iter, const gchar *new_text) +{ + if ( vik_treeview_item_get_type ( vlp->vt, iter ) == VIK_TREEVIEW_TYPE_LAYER ) + { + VikLayer *l; + + /* get iter and layer */ + l = VIK_LAYER ( vik_treeview_item_get_pointer ( vlp->vt, iter ) ); + + if ( strcmp ( l->name, new_text ) != 0 ) + { + vik_layer_rename ( l, new_text ); + vik_treeview_item_set_name ( vlp->vt, iter, l->name ); + } + } + else + { + const gchar *name = vik_layer_sublayer_rename_request ( vik_treeview_item_get_parent ( vlp->vt, iter ), new_text, vlp, vik_treeview_item_get_data ( vlp->vt, iter ), vik_treeview_item_get_pointer ( vlp->vt, iter ), iter ); + if ( name ) + vik_treeview_item_set_name ( vlp->vt, iter, name); + } +} + +static gboolean layers_button_press_cb ( VikLayersPanel *vlp, GdkEventButton *event ) +{ + if (event->button == 3) + { + GtkTreeIter iter; + if ( vik_treeview_get_iter_at_pos ( vlp->vt, &iter, event->x, event->y ) ) + { + layers_popup ( vlp, &iter, 3 ); + vik_treeview_item_select ( vlp->vt, &iter ); + } + else + layers_popup ( vlp, NULL, 3 ); + return TRUE; + } + return FALSE; +} + +static void layers_popup ( VikLayersPanel *vlp, GtkTreeIter *iter, gint mouse_button ) +{ + GtkMenu *menu = NULL; + + + if ( iter ) + { + if ( vik_treeview_item_get_type ( vlp->vt, iter ) == VIK_TREEVIEW_TYPE_LAYER ) + { + VikLayer *layer = VIK_LAYER(vik_treeview_item_get_pointer ( vlp->vt, iter )); + + if ( layer->type == VIK_LAYER_AGGREGATE ) + menu = GTK_MENU(gtk_item_factory_get_widget ( vlp->popup_factory, "
" )); + else + { + GtkWidget *del, *prop; + + menu = GTK_MENU ( gtk_menu_new () ); + + prop = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL ); + g_signal_connect_swapped ( G_OBJECT(prop), "activate", G_CALLBACK(vik_layers_panel_properties), vlp ); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), prop); + gtk_widget_show ( prop ); + + del = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL ); + g_signal_connect_swapped ( G_OBJECT(del), "activate", G_CALLBACK(vik_layers_panel_delete_selected), vlp ); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), del); + gtk_widget_show ( del ); + + vik_layer_add_menu_items ( layer, menu, vlp ); + } + } + else + { + menu = GTK_MENU ( gtk_menu_new () ); + if ( ! vik_layer_sublayer_add_menu_items ( vik_treeview_item_get_parent ( vlp->vt, iter ), menu, vlp, vik_treeview_item_get_data ( vlp->vt, iter ), vik_treeview_item_get_pointer ( vlp->vt, iter ), iter ) ) + { + gtk_widget_destroy ( GTK_WIDGET(menu) ); + return; + } + /* TODO: copy, paste, specific things for different types */ + } + } + else + menu = GTK_MENU(gtk_item_factory_get_widget ( vlp->popup_factory, base_entries[NUM_BASE_ENTRIES-1].path )); + gtk_menu_popup ( menu, NULL, NULL, NULL, NULL, mouse_button, gtk_get_current_event_time() ); +} + +static void layers_popup_cb ( VikLayersPanel *vlp ) +{ + GtkTreeIter iter; + layers_popup ( vlp, vik_treeview_get_selected_iter ( vlp->vt, &iter ) ? &iter : NULL, 0 ); +} + +gboolean vik_layers_panel_new_layer ( VikLayersPanel *vlp, gint type ) +{ + VikLayer *l; + g_assert ( vlp->vvp ); + l = vik_layer_create ( type, vlp->vvp, VIK_GTK_WINDOW_FROM_WIDGET(vlp), TRUE ); + if ( l ) + { + vik_layers_panel_add_layer ( vlp, l ); + vik_layers_panel_emit_update ( vlp ); + return TRUE; + } + return FALSE; +} + +void vik_layers_panel_add_layer ( VikLayersPanel *vlp, VikLayer *l ) +{ + GtkTreeIter iter; + GtkTreeIter *replace_iter = NULL; + + /* could be something different so we have to do this */ + vik_layer_change_coord_mode ( l, vik_viewport_get_coord_mode(vlp->vvp) ); + + if ( ! vik_treeview_get_selected_iter ( vlp->vt, &iter ) ) + vik_aggregate_layer_add_layer ( vlp->toplayer, l ); + else + { + VikAggregateLayer *addtoagg; + if (vik_treeview_item_get_type ( vlp->vt, &iter ) == VIK_TREEVIEW_TYPE_LAYER ) + { + if ( ! IS_VIK_AGGREGATE_LAYER(vik_treeview_item_get_pointer ( vlp->vt, &iter )) ) { + addtoagg = VIK_AGGREGATE_LAYER(vik_treeview_item_get_parent ( vlp->vt, &iter )); + replace_iter = &iter; + } + else + addtoagg = VIK_AGGREGATE_LAYER(vik_treeview_item_get_pointer ( vlp->vt, &iter )); + } + else + { + /* a sublayer is selected, first get its parent (layer), then find the layer's parent (aggr. layer) */ + VikLayer *vl = VIK_LAYER(vik_treeview_item_get_parent ( vlp->vt, &iter )); + replace_iter = &(vl->iter); + g_assert ( vl->realized ); + addtoagg = VIK_AGGREGATE_LAYER(vik_treeview_item_get_parent ( vlp->vt, &(vl->iter) ) ); + } + if ( replace_iter ) + vik_aggregate_layer_insert_layer ( addtoagg, l, replace_iter ); + else + vik_aggregate_layer_add_layer ( addtoagg, l ); + } +} + +static void layers_move_item ( VikLayersPanel *vlp, gboolean up ) +{ + GtkTreeIter iter; + VikAggregateLayer *parent; + + /* TODO: deactivate the buttons and stuff */ + if ( ! vik_treeview_get_selected_iter ( vlp->vt, &iter ) ) + return; + + vik_treeview_select_iter ( vlp->vt, &iter ); /* cancel any layer-name editing going on... */ + + if ( vik_treeview_item_get_type ( vlp->vt, &iter ) == VIK_TREEVIEW_TYPE_LAYER ) + { + parent = VIK_AGGREGATE_LAYER(vik_treeview_item_get_parent ( vlp->vt, &iter )); + if ( parent ) /* not toplevel */ + { + vik_aggregate_layer_move_layer ( parent, &iter, up ); + vik_layers_panel_emit_update ( vlp ); + } + } +} + +gboolean vik_layers_panel_properties ( VikLayersPanel *vlp ) +{ + GtkTreeIter iter; + g_assert ( vlp->vvp ); + + if ( vik_treeview_get_selected_iter ( vlp->vt, &iter ) && vik_treeview_item_get_type ( vlp->vt, &iter ) == VIK_TREEVIEW_TYPE_LAYER ) + { + if ( vik_treeview_item_get_data ( vlp->vt, &iter ) == VIK_LAYER_AGGREGATE ) + a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_WIDGET(vlp), "Aggregate Layers have no settable properties." ); + vik_layer_properties ( VIK_LAYER( vik_treeview_item_get_pointer ( vlp->vt, &iter ) ), VIK_GTK_WINDOW_FROM_WIDGET(vlp->vt) ); + return TRUE; + } + else + return FALSE; +} + +void vik_layers_panel_draw_all ( VikLayersPanel *vlp ) +{ + if ( vlp->vvp && VIK_LAYER(vlp->toplayer)->visible ) + vik_aggregate_layer_draw ( vlp->toplayer, vlp->vvp ); +} + +void vik_layers_panel_draw_all_using_viewport ( VikLayersPanel *vlp, VikViewport *vvp ) +{ + if ( vlp->vvp && VIK_LAYER(vlp->toplayer)->visible ) + vik_aggregate_layer_draw ( vlp->toplayer, vvp ); +} + +void vik_layers_panel_delete_selected ( VikLayersPanel *vlp ) +{ + gint type; + GtkTreeIter iter; + + g_return_if_fail ( vik_treeview_get_selected_iter ( vlp->vt, &iter ) ); + + type = vik_treeview_item_get_type ( vlp->vt, &iter ); + + if ( type == VIK_TREEVIEW_TYPE_LAYER ) + { + VikAggregateLayer *parent = vik_treeview_item_get_parent ( vlp->vt, &iter ); + if ( parent ) + { + if ( vik_aggregate_layer_delete ( parent, &iter ) ) + vik_layers_panel_emit_update ( vlp ); + } + else + a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_WIDGET(vlp), "You cannot delete the Top Layer." ); + } +} + +VikLayer *vik_layers_panel_get_selected ( VikLayersPanel *vlp ) +{ + GtkTreeIter iter, parent; + gint type; + + if ( ! vik_treeview_get_selected_iter ( vlp->vt, &iter ) ) + return NULL; + + type = vik_treeview_item_get_type ( vlp->vt, &iter ); + + while ( type != VIK_TREEVIEW_TYPE_LAYER ) + { + if ( ! vik_treeview_item_get_parent_iter ( vlp->vt, &iter, &parent ) ) + return NULL; + iter = parent; + type = vik_treeview_item_get_type ( vlp->vt, &iter ); + } + + return VIK_LAYER( vik_treeview_item_get_pointer ( vlp->vt, &iter ) ); +} + +static void layers_move_item_up ( VikLayersPanel *vlp ) +{ + layers_move_item ( vlp, TRUE ); +} + +static void layers_move_item_down ( VikLayersPanel *vlp ) +{ + layers_move_item ( vlp, FALSE ); +} + +gboolean vik_layers_panel_tool ( VikLayersPanel *vlp, guint16 layer_type, VikToolInterfaceFunc tool_func, GdkEventButton *event, VikViewport *vvp ) +{ + VikLayer *vl = vik_layers_panel_get_selected ( vlp ); + if ( vl && vl->type == layer_type ) + { + tool_func ( vl, event, vvp ); + return TRUE; + } + else if ( VIK_LAYER(vlp->toplayer)->visible && + vik_aggregate_layer_tool ( vlp->toplayer, layer_type, tool_func, event, vvp ) != 1 ) /* either accepted or rejected, but a layer was found */ + return TRUE; + return FALSE; +} + +VikLayer *vik_layers_panel_get_layer_of_type ( VikLayersPanel *vlp, gint type ) +{ + VikLayer *rv = vik_layers_panel_get_selected ( vlp ); + if ( rv == NULL || rv->type != type ) + if ( VIK_LAYER(vlp->toplayer)->visible ) + return vik_aggregate_layer_get_top_visible_layer_of_type ( vlp->toplayer, type ); + else + return NULL; + else + return rv; +} + +VikAggregateLayer *vik_layers_panel_get_top_layer ( VikLayersPanel *vlp ) +{ + return vlp->toplayer; +} + +void vik_layers_panel_clear ( VikLayersPanel *vlp ) +{ + if ( (! vik_aggregate_layer_is_empty(vlp->toplayer)) && a_dialog_overwrite ( VIK_GTK_WINDOW_FROM_WIDGET(vlp), "Are you sure you wish to delete all layers?", NULL ) ) + vik_aggregate_layer_clear ( vlp->toplayer ); /* simply deletes all layers */ +} + +void vik_layers_panel_change_coord_mode ( VikLayersPanel *vlp, VikCoordMode mode ) +{ + vik_layer_change_coord_mode ( VIK_LAYER(vlp->toplayer), mode ); +} + +static void layers_panel_finalize ( GObject *gob ) +{ + VikLayersPanel *vlp = VIK_LAYERS_PANEL ( gob ); + g_object_unref ( VIK_LAYER(vlp->toplayer) ); + g_object_unref ( G_OBJECT(vlp->popup_factory) ); + G_OBJECT_CLASS(parent_class)->finalize(gob); +} + diff --git a/src/viklayerspanel.h b/src/viklayerspanel.h new file mode 100644 index 00000000..cd4214c6 --- /dev/null +++ b/src/viklayerspanel.h @@ -0,0 +1,69 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _VIKING_LAYERS_PANEL_H +#define _VIKING_LAYERS_PANEL_H + +#include +#include +#include + +G_BEGIN_DECLS + +#define VIK_LAYERS_PANEL_TYPE (vik_layers_panel_get_type ()) +#define VIK_LAYERS_PANEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), VIK_LAYERS_PANEL_TYPE, VikLayersPanel)) +#define VIK_LAYERS_PANEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), VIK_LAYERS_PANEL_TYPE, VikLayersPanelClass)) +#define IS_VIK_LAYERS_PANEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VIK_LLAYERS_PANEL_TYPE)) +#define IS_VIK_LAYERS_PANEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), VIK_LAYERS_PANEL_TYPE)) + +typedef struct _VikLayersPanel VikLayersPanel; +typedef struct _VikLayersPanelClass VikLayersPanelClass; + +struct _VikLayersPanelClass +{ + GtkVBoxClass vbox_class; + + void (* update) (VikLayersPanel *vlp); +}; + +GType vik_layers_panel_get_type (); +VikLayersPanel *vik_layers_panel_new (); +void vik_layers_panel_free ( VikLayersPanel *vlp ); +void vik_layers_panel_add_layer ( VikLayersPanel *vlp, VikLayer *l ); +void vik_layers_panel_draw_all ( VikLayersPanel *vlp ); +void vik_layers_panel_draw_all_using_viewport ( VikLayersPanel *vlp, VikViewport *vvp ); +VikLayer *vik_layers_panel_get_selected ( VikLayersPanel *vlp ); +void vik_layers_panel_delete_selected ( VikLayersPanel *vlp ); +VikLayer *vik_layers_panel_get_layer_of_type ( VikLayersPanel *vlp, gint type ); +void vik_layers_panel_set_viewport ( VikLayersPanel *vlp, VikViewport *vvp ); +gboolean vik_layers_panel_tool ( VikLayersPanel *vlp, guint16 layer_type, VikToolInterfaceFunc tool_func, GdkEventButton *event, VikViewport *vvp ); +VikViewport *vik_layers_panel_get_viewport ( VikLayersPanel *vlp ); +void vik_layers_panel_emit_update ( VikLayersPanel *vlp ); +VikLayer *vik_layers_panel_get_layer_of_type ( VikLayersPanel *vlp, gint type ); +gboolean vik_layers_panel_properties ( VikLayersPanel *vlp ); +gboolean vik_layers_panel_new_layer ( VikLayersPanel *vlp, gint type ); +void vik_layers_panel_clear ( VikLayersPanel *vlp ); +VikAggregateLayer *vik_layers_panel_get_top_layer ( VikLayersPanel *vlp ); +void vik_layers_panel_change_coord_mode ( VikLayersPanel *vlp, VikCoordMode mode ); + +G_END_DECLS + +#endif diff --git a/src/vikmapslayer.c b/src/vikmapslayer.c new file mode 100644 index 00000000..e7635857 --- /dev/null +++ b/src/vikmapslayer.c @@ -0,0 +1,875 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2005, Evan Battaglia + * UTM multi-zone stuff by Kit Transue + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define MAX_SHRINKFACTOR 8.0000001 /* zoom 1 viewing 8-tiles */ +#define MIN_SHRINKFACTOR 0.0312499 /* zoom 32 viewing 1-tiles */ + +#include +#include +#include +#include +#include +#include "globals.h" +#include "coords.h" +#include "vikcoord.h" +#include "viktreeview.h" +#include "vikviewport.h" +#include "viklayer.h" +#include "vikmapslayer.h" +#include "vikmapslayer_pixmap.h" + +#include +#include +#include + +#include "mapcache.h" +/* only for dialog.h -- ugh */ +#include "vikwaypoint.h" +#include "dialog.h" + +#include "vikstatus.h" +#include "background.h" + +#include "vikaggregatelayer.h" +#include "viklayerspanel.h" + +#include "mapcoord.h" +#include "terraserver.h" +#include "googlemaps.h" +#include "google.h" +#include "khmaps.h" +#include "expedia.h" +#include "usgs.h" + +typedef struct { + guint8 uniq_id; + guint16 tilesize_x; + guint16 tilesize_y; + guint drawmode; + gboolean (*coord_to_mapcoord) ( const VikCoord *src, gdouble xzoom, gdouble yzoom, MapCoord *dest ); + void (*mapcoord_to_center_coord) ( MapCoord *src, VikCoord *dest ); + void (*download) ( MapCoord *src, const gchar *dest_fn ); + /* TODO: constant size (yay!) */ +} VikMapsLayer_MapType; + + +/****** MAP TYPES ******/ + +static const VikMapsLayer_MapType __map_types[] = { + +{ 2, 200, 200, VIK_VIEWPORT_DRAWMODE_UTM, terraserver_topo_coord_to_mapcoord, terraserver_mapcoord_to_center_coord, terraserver_topo_download }, +{ 1, 200, 200, VIK_VIEWPORT_DRAWMODE_UTM, terraserver_aerial_coord_to_mapcoord, terraserver_mapcoord_to_center_coord, terraserver_aerial_download }, +{ 4, 200, 200, VIK_VIEWPORT_DRAWMODE_UTM, terraserver_urban_coord_to_mapcoord, terraserver_mapcoord_to_center_coord, terraserver_urban_download }, +{ 5, 0, 0, VIK_VIEWPORT_DRAWMODE_EXPEDIA, expedia_coord_to_mapcoord, expedia_mapcoord_to_center_coord, expedia_download }, +{ 9, 128, 128, VIK_VIEWPORT_DRAWMODE_GOOGLE, googlemaps_coord_to_mapcoord, googlemaps_mapcoord_to_center_coord, googlemaps_download }, +{ 6, 800, 600, VIK_VIEWPORT_DRAWMODE_UTM, usgs_coord_to_mapcoord, usgs_mapcoord_to_center_coord, usgs_download }, +{ 8, 256, 256, VIK_VIEWPORT_DRAWMODE_KH, khmaps_coord_to_mapcoord, khmaps_mapcoord_to_center_coord, khmaps_download }, +{ 7, 256, 256, VIK_VIEWPORT_DRAWMODE_MERCATOR, google_coord_to_mapcoord, google_mapcoord_to_center_coord, google_download }, +{ 10, 256, 256, VIK_VIEWPORT_DRAWMODE_MERCATOR, google_coord_to_mapcoord, google_mapcoord_to_center_coord, google_trans_download }, +{ 11, 256, 256, VIK_VIEWPORT_DRAWMODE_MERCATOR, google_coord_to_mapcoord, google_mapcoord_to_center_coord, google_kh_download }, +}; + +#define NUM_MAP_TYPES (sizeof(__map_types)/sizeof(__map_types[0])) + +/******** MAPZOOMS *********/ + +static gchar *params_mapzooms[] = { "Use Viking Zoom Level", "0.25", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", "USGS 10k", "USGS 24k", "USGS 25k", "USGS 50k", "USGS 100k", "USGS 200k", "USGS 250k" }; +static gdouble __mapzooms_x[] = { 0.0, 0.25, 1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0, 512.0, 1024.0, 1.016, 2.4384, 2.54, 5.08, 10.16, 20.32, 25.4 }; +static gdouble __mapzooms_y[] = { 0.0, 0.25, 1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0, 512.0, 1024.0, 1.016, 2.4384, 2.54, 5.08, 10.16, 20.32, 25.4 }; + +static gchar *params_maptypes[] = { "Terraserver Topos", "Terraserver Aerials", "Terraserver Urban Areas", "Expedia (Street Maps)", "Google Maps (Street)", "USGS", "KH Maps", "New (Mercator) Google", "Transparent Google", "New (Mercator) KH" }; +static guint params_maptypes_ids[] = { 2, 1, 4, 5, 9, 6, 8, 7, 10, 11 }; +#define NUM_MAPZOOMS (sizeof(params_mapzooms)/sizeof(params_mapzooms[0])) + +/**************************/ + + +static VikMapsLayer *maps_layer_copy ( VikMapsLayer *vml, VikViewport *vvp ); +static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp ); +static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id ); +static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp ); +static VikMapsLayer *maps_layer_new ( VikViewport *vvp ); +static void maps_layer_free ( VikMapsLayer *vml ); +static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp ); +static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp ); +static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir ); +static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload ); +static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp ); + + +static VikLayerParamScale params_scales[] = { + /* min, max, step, digits (decimal places) */ + { 0, 255, 3, 0 }, /* alpha */ +}; + +VikLayerParam maps_layer_params[] = { + { "mode", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, "Map Type:", VIK_LAYER_WIDGET_RADIOGROUP, params_maptypes, params_maptypes_ids }, + { "directory", VIK_LAYER_PARAM_STRING, VIK_LAYER_GROUP_NONE, "Maps Directory (Optional):", VIK_LAYER_WIDGET_FILEENTRY }, + { "alpha", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, "Alpha:", VIK_LAYER_WIDGET_HSCALE, params_scales }, + { "autodownload", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, "Autodownload maps:", VIK_LAYER_WIDGET_CHECKBUTTON }, + { "mapzoom", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, "Zoom Level:", VIK_LAYER_WIDGET_COMBOBOX, params_mapzooms }, +}; + +enum { PARAM_MAPTYPE=0, PARAM_CACHE_DIR, PARAM_ALPHA, PARAM_AUTODOWNLOAD, PARAM_MAPZOOM, NUM_PARAMS }; + +static VikToolInterface maps_tools[] = { + { "Maps Download", (VikToolInterfaceFunc) maps_layer_download_click, (VikToolInterfaceFunc) maps_layer_download_release }, +}; + +VikLayerInterface vik_maps_layer_interface = { + "Map", + &mapslayer_pixbuf, + + maps_tools, + sizeof(maps_tools) / sizeof(maps_tools[0]), + + maps_layer_params, + NUM_PARAMS, + NULL, + 0, + + (VikLayerFuncCreate) maps_layer_new, + (VikLayerFuncRealize) NULL, + (VikLayerFuncPostRead) NULL, + (VikLayerFuncFree) maps_layer_free, + + (VikLayerFuncProperties) NULL, + (VikLayerFuncDraw) maps_layer_draw, + (VikLayerFuncChangeCoordMode) NULL, + + (VikLayerFuncAddMenuItems) maps_layer_add_menu_items, + (VikLayerFuncSublayerAddMenuItems) NULL, + + (VikLayerFuncSublayerRenameRequest) NULL, + (VikLayerFuncSublayerToggleVisible) NULL, + + (VikLayerFuncCopy) maps_layer_copy, + + (VikLayerFuncSetParam) maps_layer_set_param, + (VikLayerFuncGetParam) maps_layer_get_param, + + (VikLayerFuncReadFileData) NULL, + (VikLayerFuncWriteFileData) NULL, + + (VikLayerFuncCopyItem) NULL, + (VikLayerFuncPasteItem) NULL, + (VikLayerFuncFreeCopiedItem) NULL, +}; + +struct _VikMapsLayer { + VikLayer vl; + guint maptype; + gchar *cache_dir; + guint8 alpha; + guint mapzoom_id; + gdouble xmapzoom, ymapzoom; + + gboolean autodownload; + + gint dl_tool_x, dl_tool_y; + + GtkMenu *dl_right_click_menu; + VikCoord redownload_ul, redownload_br; /* right click menu only */ + VikViewport *redownload_vvp; +}; + +enum { REDOWNLOAD_NONE = 0, REDOWNLOAD_BAD, REDOWNLOAD_ALL }; + + +/****************************************/ +/******** CACHE DIR STUFF ***************/ +/****************************************/ + +#ifdef WINDOWS +#define MAPS_CACHE_DIR "C:\\VIKING-MAPS\\" +#define DIRSTRUCTURE "%st%ds%dz%d\\%d\\%d" +#else /* POSIX */ + +#include + +#define MAPS_CACHE_DIR maps_layer_default_dir() +#define GLOBAL_MAPS_DIR "/var/cache/maps/" +#define DIRSTRUCTURE "%st%ds%dz%d/%d/%d" + +static gchar *maps_layer_default_dir () +{ + static gchar defaultdir[512]; + static gboolean already_run = 0; + if ( ! already_run ) + { + /* Thanks to Mike Davison for the $VIKING_MAPS usage */ + gchar *mapdir = getenv("VIKING_MAPS"); + if ( mapdir && strlen(mapdir) < 497 ) { + strcpy ( defaultdir, mapdir ); + } else if ( access ( GLOBAL_MAPS_DIR, W_OK ) == 0 ) { + strcpy ( defaultdir, GLOBAL_MAPS_DIR ); + } else { + gchar *home = getenv("HOME"); + if ( home && strlen(home) < 497 ) + { + strcpy ( defaultdir, home ); + strcat ( defaultdir, "/.viking-maps/" ); + } + else + { + strcpy ( defaultdir, ".viking-maps/" ); + } + } + already_run = 1; + } + return defaultdir; +} + +#endif + +static void maps_layer_mkdir_if_default_dir ( VikMapsLayer *vml ) +{ + if ( vml->cache_dir && strcmp ( vml->cache_dir, MAPS_CACHE_DIR ) == 0 && access ( vml->cache_dir, F_OK ) != 0 ) + { +#ifdef WINDOWS + mkdir ( vml->cache_dir ); +#else + mkdir ( vml->cache_dir, 0777 ); +#endif + } +} + +static void maps_layer_set_cache_dir ( VikMapsLayer *vml, const gchar *dir ) +{ + guint len; + g_assert ( vml != NULL); + if ( vml->cache_dir ) + g_free ( vml->cache_dir ); + + if ( dir == NULL || dir[0] == '\0' ) + vml->cache_dir = g_strdup ( MAPS_CACHE_DIR ); + else + { + len = strlen(dir); + if ( dir[len-1] != VIKING_FILE_SEP ) + { + vml->cache_dir = g_malloc ( len+2 ); + strncpy ( vml->cache_dir, dir, len ); + vml->cache_dir[len] = VIKING_FILE_SEP; + vml->cache_dir[len+1] = '\0'; + } + else + vml->cache_dir = g_strdup ( dir ); + } + maps_layer_mkdir_if_default_dir ( vml ); +} + +/****************************************/ +/******** GOBJECT STUFF *****************/ +/****************************************/ + +GType vik_maps_layer_get_type () +{ + static GType vml_type = 0; + + if (!vml_type) + { + static const GTypeInfo vml_info = + { + sizeof (VikMapsLayerClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + NULL, /* class init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (VikMapsLayer), + 0, + NULL /* instance init */ + }; + vml_type = g_type_register_static ( VIK_LAYER_TYPE, "VikMapsLayer", &vml_info, 0 ); + } + + return vml_type; +} + +/****************************************/ +/************** PARAMETERS **************/ +/****************************************/ + +static guint map_index_to_uniq_id (guint8 index) +{ + g_assert ( index < NUM_MAP_TYPES ); + return __map_types[index].uniq_id; +} + +static guint map_uniq_id_to_index ( guint uniq_id ) +{ + gint i; + for ( i = 0; i < NUM_MAP_TYPES; i++ ) + if ( __map_types[i].uniq_id == uniq_id ) + return i; + return NUM_MAP_TYPES; /* no such thing */ +} + +static gboolean maps_layer_set_param ( VikMapsLayer *vml, guint16 id, VikLayerParamData data, VikViewport *vvp ) +{ + switch ( id ) + { + case PARAM_CACHE_DIR: maps_layer_set_cache_dir ( vml, data.s ); break; + case PARAM_MAPTYPE: { + gint maptype = map_uniq_id_to_index(data.u); + if ( maptype == NUM_MAP_TYPES ) g_warning("Unknown map type"); + else vml->maptype = maptype; + break; + } + case PARAM_ALPHA: if ( data.u <= 255 ) vml->alpha = data.u; break; + case PARAM_AUTODOWNLOAD: vml->autodownload = data.b; break; + case PARAM_MAPZOOM: if ( data.u < NUM_MAPZOOMS ) { + vml->mapzoom_id = data.u; + vml->xmapzoom = __mapzooms_x [data.u]; + vml->ymapzoom = __mapzooms_y [data.u]; + }else g_warning ("Unknown Map Zoom"); break; + } + return TRUE; +} + +static VikLayerParamData maps_layer_get_param ( VikMapsLayer *vml, guint16 id ) +{ + VikLayerParamData rv; + switch ( id ) + { + case PARAM_CACHE_DIR: rv.s = (vml->cache_dir && strcmp(vml->cache_dir, MAPS_CACHE_DIR) != 0) ? vml->cache_dir : ""; break; + case PARAM_MAPTYPE: rv.u = map_index_to_uniq_id ( vml->maptype ); break; + case PARAM_ALPHA: rv.u = vml->alpha; break; + case PARAM_AUTODOWNLOAD: rv.u = vml->autodownload; break; + case PARAM_MAPZOOM: rv.u = vml->mapzoom_id; break; + } + return rv; +} + +/****************************************/ +/****** CREATING, COPYING, FREEING ******/ +/****************************************/ + +static VikMapsLayer *maps_layer_new ( VikViewport *vvp ) +{ + VikMapsLayer *vml = VIK_MAPS_LAYER ( g_object_new ( VIK_MAPS_LAYER_TYPE, NULL ) ); + vik_layer_init ( VIK_LAYER(vml), VIK_LAYER_MAPS ); + vml->maptype = 0; + vml->alpha = 255; + vml->mapzoom_id = 0; + vml->dl_tool_x = vml->dl_tool_y = -1; + maps_layer_set_cache_dir ( vml, NULL ); + vml->autodownload = FALSE; + + vml->dl_right_click_menu = NULL; + + return vml; +} + +static void maps_layer_free ( VikMapsLayer *vml ) +{ + if ( vml->cache_dir ) + g_free ( vml->cache_dir ); + if ( vml->dl_right_click_menu ) + gtk_object_sink ( GTK_OBJECT(vml->dl_right_click_menu) ); +} + +static VikMapsLayer *maps_layer_copy ( VikMapsLayer *vml, VikViewport *vvp ) +{ + VikMapsLayer *rv = maps_layer_new ( vvp ); + *rv = *vml; + rv->cache_dir = g_strdup(rv->cache_dir); + VIK_LAYER(rv)->name = NULL; + return rv; +} + +/*********************/ +/****** DRAWING ******/ +/*********************/ + +static GdkPixbuf *pixbuf_set_alpha ( GdkPixbuf *pixbuf, guint8 alpha ) +{ + guchar *pixels; + gint width, height, iii, jjj; + + if ( ! gdk_pixbuf_get_has_alpha ( pixbuf ) ) + { + GdkPixbuf *tmp = gdk_pixbuf_add_alpha(pixbuf,FALSE,0,0,0); + g_object_unref(G_OBJECT(pixbuf)); + pixbuf = tmp; + } + + pixels = gdk_pixbuf_get_pixels(pixbuf); + width = gdk_pixbuf_get_width(pixbuf); + height = gdk_pixbuf_get_height(pixbuf); + + /* r,g,b,a,r,g,b,a.... */ + for (iii = 0; iii < width; iii++) for (jjj = 0; jjj < height; jjj++) + { + pixels += 3; + *pixels++ = alpha; + } + return pixbuf; +} + +static GdkPixbuf *pixbuf_shrink ( GdkPixbuf *pixbuf, gdouble xshrinkfactor, gdouble yshrinkfactor ) +{ + GdkPixbuf *tmp; + guint16 width = gdk_pixbuf_get_width(pixbuf), height = gdk_pixbuf_get_height(pixbuf); + tmp = gdk_pixbuf_scale_simple(pixbuf, ceil(width * xshrinkfactor), ceil(height * yshrinkfactor), GDK_INTERP_BILINEAR); + g_object_unref ( G_OBJECT(pixbuf) ); + return tmp; +} + +static GdkPixbuf *get_pixbuf( VikMapsLayer *vml, gint mode, MapCoord *mapcoord, gchar *filename_buf, gint buf_len, gdouble xshrinkfactor, gdouble yshrinkfactor ) +{ + GdkPixbuf *pixbuf; + + /* get the thing */ + pixbuf = a_mapcache_get ( mapcoord->x, mapcoord->y, mapcoord->z, + mode, mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor ); + + if ( ! pixbuf ) { + g_snprintf ( filename_buf, buf_len, DIRSTRUCTURE, + vml->cache_dir, mode, + mapcoord->scale, mapcoord->z, mapcoord->x, mapcoord->y ); + if ( access ( filename_buf, R_OK ) == 0) { + { + GError *gx = NULL; + pixbuf = gdk_pixbuf_new_from_file ( filename_buf, &gx ); + + if (gx) + { + if ( gx->domain != GDK_PIXBUF_ERROR || gx->code != GDK_PIXBUF_ERROR_CORRUPT_IMAGE ) + g_warning ( "Couldn't open image file: %s", gx->message ); + + g_error_free ( gx ); + if ( pixbuf ) + g_object_unref ( G_OBJECT(pixbuf) ); + pixbuf = NULL; + } else { + if ( vml->alpha < 255 ) + pixbuf = pixbuf_set_alpha ( pixbuf, vml->alpha ); + if ( xshrinkfactor != 1.0 || yshrinkfactor != 1.0 ) + pixbuf = pixbuf_shrink ( pixbuf, xshrinkfactor, yshrinkfactor ); + + a_mapcache_add ( pixbuf, mapcoord->x, mapcoord->y, + mapcoord->z, __map_types[vml->maptype].uniq_id, + mapcoord->scale, vml->alpha, xshrinkfactor, yshrinkfactor ); + } + } + } + } + return pixbuf; +} + +static void maps_layer_draw_section ( VikMapsLayer *vml, VikViewport *vvp, VikCoord *ul, VikCoord *br ) +{ + MapCoord ulm, brm; + gdouble xzoom = vik_viewport_get_xmpp ( vvp ); + gdouble yzoom = vik_viewport_get_ympp ( vvp ); + gdouble xshrinkfactor = 1.0, yshrinkfactor = 1.0; + + if ( vml->xmapzoom && (vml->xmapzoom != xzoom || vml->ymapzoom != yzoom) ) { + xshrinkfactor = vml->xmapzoom / xzoom; + yshrinkfactor = vml->ymapzoom / yzoom; + if ( xshrinkfactor > MIN_SHRINKFACTOR && xshrinkfactor < MAX_SHRINKFACTOR && + yshrinkfactor > MIN_SHRINKFACTOR && yshrinkfactor < MAX_SHRINKFACTOR ) { + xzoom = vml->xmapzoom; + yzoom = vml->xmapzoom; + } else { + g_warning ( "Cowardly refusing to draw tiles at a shrinkfactor more than %.3f (zoomed out) or less than %.3f (zoomed in).", 1/MIN_SHRINKFACTOR, 1/MAX_SHRINKFACTOR ); + } + } + + /* coord -> ID */ + if ( __map_types[vml->maptype].coord_to_mapcoord ( ul, xzoom, yzoom, &ulm ) && + __map_types[vml->maptype].coord_to_mapcoord ( br, xzoom, yzoom, &brm ) ) { + + /* loop & draw */ + gint x, y; + gint xmin = MIN(ulm.x, brm.x), xmax = MAX(ulm.x, brm.x); + gint ymin = MIN(ulm.y, brm.y), ymax = MAX(ulm.y, brm.y); + gint mode = __map_types[vml->maptype].uniq_id; + + VikCoord coord; + gint xx, yy, width, height; + GdkPixbuf *pixbuf; + + guint max_path_len = strlen(vml->cache_dir) + 40; + gchar *path_buf = g_malloc ( max_path_len * sizeof(char) ); + + if ( vml->autodownload ) + start_download_thread ( vml, vvp, ul, br, REDOWNLOAD_NONE ); + + if ( __map_types[vml->maptype].tilesize_x == 0 ) { + for ( x = xmin; x <= xmax; x++ ) { + for ( y = ymin; y <= ymax; y++ ) { + ulm.x = x; + ulm.y = y; + pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor ); + if ( pixbuf ) { + width = gdk_pixbuf_get_width ( pixbuf ); + height = gdk_pixbuf_get_height ( pixbuf ); + + __map_types[vml->maptype].mapcoord_to_center_coord ( &ulm, &coord ); + vik_viewport_coord_to_screen ( vvp, &coord, &xx, &yy ); + xx -= (width/2); + yy -= (height/2); + + vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, width, height ); + } + } + } + } else { /* tilesize is known, don't have to keep converting coords */ + gdouble tilesize_x = __map_types[vml->maptype].tilesize_x * xshrinkfactor; + gdouble tilesize_y = __map_types[vml->maptype].tilesize_y * yshrinkfactor; + /* ceiled so tiles will be maximum size in the case of funky shrinkfactor */ + gint tilesize_x_ceil = ceil ( tilesize_x ); + gint tilesize_y_ceil = ceil ( tilesize_y ); + gint8 xinc = (ulm.x == xmin) ? 1 : -1; + gint8 yinc = (ulm.y == ymin) ? 1 : -1; + gdouble xx, yy; gint xx_tmp, yy_tmp; + gint base_yy, xend, yend; + xend = (xinc == 1) ? (xmax+1) : (xmin-1); + yend = (yinc == 1) ? (ymax+1) : (ymin-1); + + __map_types[vml->maptype].mapcoord_to_center_coord ( &ulm, &coord ); + vik_viewport_coord_to_screen ( vvp, &coord, &xx_tmp, &yy_tmp ); + xx = xx_tmp; yy = yy_tmp; + /* above trick so xx,yy doubles. this is so shrinkfactors aren't rounded off + * eg if tile size 128, shrinkfactor 0.333 */ + xx -= (tilesize_x/2); + base_yy = yy - (tilesize_y/2); + + for ( x = ((xinc == 1) ? xmin : xmax); x != xend; x+=xinc ) { + yy = base_yy; + for ( y = ((yinc == 1) ? ymin : ymax); y != yend; y+=yinc ) { + ulm.x = x; + ulm.y = y; + pixbuf = get_pixbuf ( vml, mode, &ulm, path_buf, max_path_len, xshrinkfactor, yshrinkfactor ); + if ( pixbuf ) + vik_viewport_draw_pixbuf ( vvp, pixbuf, 0, 0, xx, yy, tilesize_x_ceil, tilesize_y_ceil ); + + yy += tilesize_y; + } + xx += tilesize_x; + } + } + + g_free ( path_buf ); + } +} + +static void maps_layer_draw ( VikMapsLayer *vml, VikViewport *vvp ) +{ + if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) ) + { + VikCoord ul, br; + + /* get corner coords */ + if ( vik_viewport_get_coord_mode ( vvp ) == VIK_COORD_UTM && ! vik_viewport_is_one_zone ( vvp ) ) { + /* UTM multi-zone stuff by Kit Transue */ + gchar leftmost_zone, rightmost_zone, i; + leftmost_zone = vik_viewport_leftmost_zone( vvp ); + rightmost_zone = vik_viewport_rightmost_zone( vvp ); + for ( i = leftmost_zone; i <= rightmost_zone; ++i ) { + vik_viewport_corners_for_zonen ( vvp, i, &ul, &br ); + maps_layer_draw_section ( vml, vvp, &ul, &br ); + } + } + else { + vik_viewport_screen_to_coord ( vvp, 0, 0, &ul ); + vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br ); + + maps_layer_draw_section ( vml, vvp, &ul, &br ); + } + } +} + +/*************************/ +/****** DOWNLOADING ******/ +/*************************/ + +/* pass along data to thread, exists even if layer is deleted. */ +typedef struct { + gchar *cache_dir; + gchar *filename_buf; + gint x0, y0, xf, yf; + MapCoord mapcoord; + gint maptype; + gint maxlen; + gint mapstoget; + gint redownload; +} MapDownloadInfo; + +static void mdi_free ( MapDownloadInfo *mdi ) +{ + g_free ( mdi->cache_dir ); + g_free ( mdi->filename_buf ); + g_free ( mdi ); +} + +static void map_download_thread ( MapDownloadInfo *mdi, gpointer threaddata ) +{ + guint donemaps = 0; + gint x, y; + for ( x = mdi->x0; x <= mdi->xf; x++ ) + { + for ( y = mdi->y0; y <= mdi->yf; y++ ) + { + g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE, + mdi->cache_dir, __map_types[mdi->maptype].uniq_id, + mdi->mapcoord.scale, mdi->mapcoord.z, x, y ); + + if ( mdi->redownload == REDOWNLOAD_ALL) + remove ( mdi->filename_buf ); + else if ( mdi->redownload == REDOWNLOAD_BAD && access ( mdi->filename_buf, F_OK ) == 0 ) + { + /* see if this one is bad or what */ + GError *gx = NULL; + GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( mdi->filename_buf, &gx ); + if (gx || (!pixbuf)) + remove ( mdi->filename_buf ); + if ( pixbuf ) + g_object_unref ( pixbuf ); + if ( gx ) + g_error_free ( gx ); + } + + if ( access ( mdi->filename_buf, F_OK ) != 0 ) + { + mdi->mapcoord.x = x; mdi->mapcoord.y = y; + __map_types[mdi->maptype].download ( &(mdi->mapcoord), mdi->filename_buf ); + mdi->mapcoord.x = mdi->mapcoord.y = 0; /* we're temporarily between downloads */ + donemaps++; + a_background_thread_progress ( threaddata, ((gdouble)donemaps) / mdi->mapstoget ); /* this also calls testcancel */ + } + } + } +} + +static void mdi_cancel_cleanup ( MapDownloadInfo *mdi ) +{ + if ( mdi->mapcoord.x || mdi->mapcoord.y ) + { + g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE, + mdi->cache_dir, __map_types[mdi->maptype].uniq_id, + mdi->mapcoord.scale, mdi->mapcoord.z, mdi->mapcoord.x, mdi->mapcoord.y ); + if ( access ( mdi->filename_buf, F_OK ) == 0) + { + remove ( mdi->filename_buf ); + } + } +} + +static void start_download_thread ( VikMapsLayer *vml, VikViewport *vvp, const VikCoord *ul, const VikCoord *br, gint redownload ) +{ + gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ); + gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ); + MapCoord ulm, brm; + if ( __map_types[vml->maptype].coord_to_mapcoord ( ul, xzoom, yzoom, &ulm ) + && __map_types[vml->maptype].coord_to_mapcoord ( br, xzoom, yzoom, &brm ) ) + { + MapDownloadInfo *mdi = g_malloc ( sizeof(MapDownloadInfo) ); + gint a, b; + + /* cache_dir and buffer for dest filename */ + mdi->cache_dir = g_strdup ( vml->cache_dir ); + mdi->maxlen = strlen ( vml->cache_dir ) + 40; + mdi->filename_buf = g_malloc ( mdi->maxlen * sizeof(gchar) ); + mdi->maptype = vml->maptype; + + mdi->mapcoord = ulm; + + mdi->redownload = redownload; + + mdi->x0 = MIN(ulm.x, brm.x); + mdi->xf = MAX(ulm.x, brm.x); + mdi->y0 = MIN(ulm.y, brm.y); + mdi->yf = MAX(ulm.y, brm.y); + + mdi->mapstoget = 0; + + if ( mdi->redownload ) { + mdi->mapstoget = (mdi->xf - mdi->x0 + 1) * (mdi->yf - mdi->y0 + 1); + } else { + /* calculate how many we need */ + for ( a = mdi->x0; a <= mdi->xf; a++ ) + { + for ( b = mdi->y0; b <= mdi->yf; b++ ) + { + g_snprintf ( mdi->filename_buf, mdi->maxlen, DIRSTRUCTURE, + vml->cache_dir, __map_types[vml->maptype].uniq_id, ulm.scale, + ulm.z, a, b ); + if ( access ( mdi->filename_buf, F_OK ) != 0) + mdi->mapstoget++; + } + } + } + + mdi->mapcoord.x = mdi->mapcoord.y = 0; /* for cleanup -- no current map */ + + if ( mdi->mapstoget ) + { + gchar *tmp = g_strdup_printf ( "%s %s%d %s %s...", redownload ? "Redownloading" : "Downloading", redownload == REDOWNLOAD_BAD ? "up to " : "", mdi->mapstoget, params_maptypes[vml->maptype], (mdi->mapstoget == 1) ? "map" : "maps" ); + + /* launch the thread */ + a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vml), /* parent window */ + tmp, /* description string */ + (vik_thr_func) map_download_thread, /* function to call within thread */ + mdi, /* pass along data */ + (vik_thr_free_func) mdi_free, /* function to free pass along data */ + (vik_thr_free_func) mdi_cancel_cleanup, + mdi->mapstoget ); + g_free ( tmp ); + } + else + mdi_free ( mdi ); + } +} + +static void maps_layer_redownload_bad ( VikMapsLayer *vml ) +{ + start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_BAD ); +} +static void maps_layer_redownload_all ( VikMapsLayer *vml ) +{ + start_download_thread ( vml, vml->redownload_vvp, &(vml->redownload_ul), &(vml->redownload_br), REDOWNLOAD_ALL ); +} + +static gboolean maps_layer_download_release ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp ) +{ + + if ( vml->dl_tool_x != -1 && vml->dl_tool_y != -1 ) + { + if ( event->button == 1 ) + { + VikCoord ul, br; + vik_viewport_screen_to_coord ( vvp, MAX(0, MIN(event->x, vml->dl_tool_x)), MAX(0, MIN(event->y, vml->dl_tool_y)), &ul ); + vik_viewport_screen_to_coord ( vvp, MIN(vik_viewport_get_width(vvp), MAX(event->x, vml->dl_tool_x)), MIN(vik_viewport_get_height(vvp), MAX ( event->y, vml->dl_tool_y ) ), &br ); + start_download_thread ( vml, vvp, &ul, &br, REDOWNLOAD_NONE ); + vml->dl_tool_x = vml->dl_tool_y = -1; + return TRUE; + } + else + { + vik_viewport_screen_to_coord ( vvp, MAX(0, MIN(event->x, vml->dl_tool_x)), MAX(0, MIN(event->y, vml->dl_tool_y)), &(vml->redownload_ul) ); + vik_viewport_screen_to_coord ( vvp, MIN(vik_viewport_get_width(vvp), MAX(event->x, vml->dl_tool_x)), MIN(vik_viewport_get_height(vvp), MAX ( event->y, vml->dl_tool_y ) ), &(vml->redownload_br) ); + + vml->redownload_vvp = vvp; + + vml->dl_tool_x = vml->dl_tool_y = -1; + + if ( ! vml->dl_right_click_menu ) { + GtkWidget *item; + vml->dl_right_click_menu = GTK_MENU ( gtk_menu_new () ); + + item = gtk_menu_item_new_with_label ( "Redownload bad map(s)" ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_bad), vml ); + gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item ); + + item = gtk_menu_item_new_with_label ( "Redownload all map(s)" ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_redownload_all), vml ); + gtk_menu_shell_append ( GTK_MENU_SHELL(vml->dl_right_click_menu), item ); + } + + gtk_menu_popup ( vml->dl_right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time ); + gtk_widget_show_all ( GTK_WIDGET(vml->dl_right_click_menu) ); + } + } + return FALSE; +} + +static gboolean maps_layer_download_click ( VikMapsLayer *vml, GdkEventButton *event, VikViewport *vvp ) +{ + MapCoord tmp; + if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) && + __map_types[vml->maptype].coord_to_mapcoord ( vik_viewport_get_center ( vvp ), + vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ), + vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ), + &tmp ) ) { + vml->dl_tool_x = event->x, vml->dl_tool_y = event->y; + return TRUE; + } + return FALSE; + + +#if 0 + if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) ) + { + VikCoord coord; + MapCoord mapcoord; + vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord ); + if ( __map_types[vml->maptype].coord_to_mapcoord ( &coord, + vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ), + vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ), + &mapcoord ) ) { + gchar *filename_buf = g_strdup_printf ( DIRSTRUCTURE, + vml->cache_dir, __map_types[vml->maptype].uniq_id, + mapcoord.scale, mapcoord.z, mapcoord.x, mapcoord.y ); + + __map_types[vml->maptype].download ( &mapcoord, filename_buf ); + g_free ( filename_buf ); + vik_layer_emit_update ( VIK_LAYER(vml) ); + return TRUE; + } + } + return FALSE; +#endif +} + +static void maps_layer_download_onscreen_maps ( gpointer vml_vvp[2] ) +{ + VikMapsLayer *vml = vml_vvp[0]; + VikViewport *vvp = vml_vvp[1]; + + gdouble xzoom = vml->xmapzoom ? vml->xmapzoom : vik_viewport_get_xmpp ( vvp ); + gdouble yzoom = vml->ymapzoom ? vml->ymapzoom : vik_viewport_get_ympp ( vvp ); + + VikCoord ul, br; + MapCoord ulm, brm; + + vik_viewport_screen_to_coord ( vvp, 0, 0, &ul ); + vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &br ); + + if ( __map_types[vml->maptype].drawmode == vik_viewport_get_drawmode ( vvp ) && + __map_types[vml->maptype].coord_to_mapcoord ( &ul, xzoom, yzoom, &ulm ) && + __map_types[vml->maptype].coord_to_mapcoord ( &br, xzoom, yzoom, &brm ) ) + start_download_thread ( vml, vvp, &ul, &br, REDOWNLOAD_NONE ); + else + a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vml), "Wrong drawmode / zoom level for this map." ); + +} + +static void maps_layer_add_menu_items ( VikMapsLayer *vml, GtkMenu *menu, VikLayersPanel *vlp ) +{ + static gpointer pass_along[2]; + GtkWidget *item; + pass_along[0] = vml; + pass_along[1] = vik_layers_panel_get_viewport( VIK_LAYERS_PANEL(vlp) ); + + item = gtk_menu_item_new(); + gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_widget_show ( item ); + + item = gtk_menu_item_new_with_label ( "Download Onscreen Maps" ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(maps_layer_download_onscreen_maps), pass_along ); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show ( item ); +} diff --git a/src/vikmapslayer.h b/src/vikmapslayer.h new file mode 100644 index 00000000..39f17e4c --- /dev/null +++ b/src/vikmapslayer.h @@ -0,0 +1,41 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _VIKING_MAPSLAYER_H +#define _VIKING_MAPSLAYER_H + +#define VIK_MAPS_LAYER_TYPE (vik_maps_layer_get_type ()) +#define VIK_MAPS_LAYER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), VIK_MAPS_LAYER_TYPE, VikMapsLayer)) +#define VIK_MAPS_LAYER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), VIK_MAPS_LAYER_TYPE, VikMapsLayerClass)) +#define IS_VIK_MAPS_LAYER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VIK_MAPS_LAYER_TYPE)) +#define IS_VIK_MAPS_LAYER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), VIK_MAPS_LAYER_TYPE)) + +typedef struct _VikMapsLayerClass VikMapsLayerClass; +struct _VikMapsLayerClass +{ + VikLayerClass object_class; +}; + +GType vik_maps_layer_get_type (); + +typedef struct _VikMapsLayer VikMapsLayer; + +#endif diff --git a/src/vikmapslayer_pixmap.h b/src/vikmapslayer_pixmap.h new file mode 100644 index 00000000..6c1c9609 --- /dev/null +++ b/src/vikmapslayer_pixmap.h @@ -0,0 +1,44 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* GdkPixbuf RGB C-Source image dump 1-byte-run-length-encoded */ + +static const GdkPixdata mapslayer_pixbuf = { + 0x47646b50, /* Pixbuf magic: 'GdkP' */ + 24 + 318, /* header length + pixel_data length */ + 0x2010001, /* pixdata_type */ + 48, /* rowstride */ + 16, /* width */ + 16, /* height */ + /* pixel_data: */ + "\1\6q6\216\10\202\26\1\5^[\216\10\202\26\3\7}\40\1=\234\7t2\204\10\202" + "\26\1\10\202\27\207\10\202\26\5\7}\40\2E\214\5eN\0""3\257\6r6\203\10" + "\202\26\15\7|!\3M|\1""9\244\2H\206\5fM\10\200\31\10\201\27\5`Y\1=\234" + "\5aU\10\202\26\0""0\266\5cR\203\10\202\26\1\4Ye\204\0/\267\4\1>\232\3" + "M}\3Pw\7}\40\202\10\202\26\1\10\200\31\204\10\202\26\3\10\200\32\3M}" + "\0""2\261\203\0/\267\1\3M}\202\10\202\26\1\10\202\27\210\10\202\26\1" + "\3Qu\203\0/\267\1\2@\226\213\10\202\26\1\5aV\203\0/\267\1\1\77\231\214" + "\10\202\26\1\3Sq\202\0/\267\1\4Zd\207\10\202\26\1\16\177\26\204\10\202" + "\26\4\10~\36\2A\224\2D\216\7{#\206\10\202\26\3$t\26\330\31\30T\\\27\204" + "\10\202\26\1\10\201\30\210\10\202\26\3\14\200\26lP\27+p\26\232\10\202" + "\26\1\10\202\27\217\10\202\26\1\10}\37\214\10\202\26\4\10\201\27\6l@" + "\3M}\0""1\263\213\10\202\26\2\5fL\1""9\244\203\0/\267", +}; diff --git a/src/vikradiogroup.c b/src/vikradiogroup.c new file mode 100644 index 00000000..5b17df5b --- /dev/null +++ b/src/vikradiogroup.c @@ -0,0 +1,128 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include + +#include "vikradiogroup.h" + +static GObjectClass *parent_class; + +static void radio_group_finalize ( GObject *gob ); +static void radio_group_class_init ( VikRadioGroupClass *klass ); + +struct _VikRadioGroup { + GtkVBox parent; + GSList *radios; + guint options_count; +}; + +GType vik_radio_group_get_type (void) +{ + static GType vrg_type = 0; + + if (!vrg_type) + { + static const GTypeInfo vrg_info = + { + sizeof (VikRadioGroupClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) radio_group_class_init, /* class init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (VikRadioGroup), + 0, + NULL /* instance init */ + }; + vrg_type = g_type_register_static ( GTK_TYPE_VBOX, "VikRadioGroup", &vrg_info, 0 ); + } + + return vrg_type; +} + +static void radio_group_class_init ( VikRadioGroupClass *klass ) +{ + /* Destructor */ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = radio_group_finalize; + + parent_class = g_type_class_peek_parent (klass); +} + +GtkWidget *vik_radio_group_new ( const gchar **options ) +{ + VikRadioGroup *vrg; + GtkWidget *t; + + if ( ! *options ) + return NULL; + + vrg = VIK_RADIO_GROUP ( g_object_new ( VIK_RADIO_GROUP_TYPE, NULL ) ); + + t = gtk_radio_button_new_with_label ( NULL, options[0] ); + gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(t), TRUE ); + gtk_box_pack_start ( GTK_BOX(vrg), t, FALSE, FALSE, 0 ); + + vrg->radios = g_slist_append ( NULL, t ); + vrg->options_count = 1; + + for ( options++ ; *options ; options++ ) + { + t = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(vrg->radios->data), *options ); + g_slist_append( vrg->radios, t ); + gtk_box_pack_start ( GTK_BOX(vrg), GTK_WIDGET(t), FALSE, FALSE, 0 ); + vrg->options_count++; + } + + return GTK_WIDGET(vrg); +} + +void vik_radio_group_set_selected ( VikRadioGroup *vrg, guint8 i ) +{ + if ( i < vrg->options_count ) + gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(g_slist_nth_data(vrg->radios,i)), TRUE ); +} + +guint8 vik_radio_group_get_selected ( VikRadioGroup *vrg ) +{ + guint8 i = 0; + GSList *iter = vrg->radios; + while ( iter ) + { + if ( gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(iter->data) ) ) + return i; + iter = iter->next; + i++; + } + return 0; +} + +static void radio_group_finalize ( GObject *gob ) +{ + VikRadioGroup *vrg = VIK_RADIO_GROUP ( gob ); + if ( vrg->radios ) + g_slist_free ( vrg->radios ); + G_OBJECT_CLASS(parent_class)->finalize(gob); +} + diff --git a/src/vikradiogroup.h b/src/vikradiogroup.h new file mode 100644 index 00000000..82940718 --- /dev/null +++ b/src/vikradiogroup.h @@ -0,0 +1,52 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _VIKING_RADIOGROUP_H +#define _VIKING_RADIOGROUP_H + +#include +#include +#include + +G_BEGIN_DECLS + +#define VIK_RADIO_GROUP_TYPE (vik_radio_group_get_type ()) +#define VIK_RADIO_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), VIK_RADIO_GROUP_TYPE, VikRadioGroup)) +#define VIK_RADIO_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), VIK_RADIO_GROUP_TYPE, VikRadioGroupClass)) +#define IS_VIK_RADIO_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VIK_RADIO_GROUP_TYPE)) +#define IS_VIK_RADIO_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), VIK_RADIO_GROUP_TYPE)) + +typedef struct _VikRadioGroup VikRadioGroup; +typedef struct _VikRadioGroupClass VikRadioGroupClass; + +struct _VikRadioGroupClass +{ + GtkVBoxClass vbox_class; +}; + +GType vik_radio_group_get_type (); + +GtkWidget *vik_radio_group_new ( const gchar **options ); +void vik_radio_group_set_selected ( VikRadioGroup *vrg, guint8 i ); +guint8 vik_radio_group_get_selected ( VikRadioGroup *vrg ); + + +#endif diff --git a/src/vikstatus.c b/src/vikstatus.c new file mode 100644 index 00000000..3ea89267 --- /dev/null +++ b/src/vikstatus.c @@ -0,0 +1,109 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* gtk status bars: just plain dumb. this file +shouldn't have to exist. */ +#include + +#include "vikstatus.h" + +#define STATUS_COUNT 5 + +struct _VikStatusbar { + GtkStatusbar parent; + gint num_extra_bars; + GtkWidget *status[STATUS_COUNT-1]; + gboolean empty[STATUS_COUNT]; +}; + +GType vik_statusbar_get_type (void) +{ + static GType vs_type = 0; + + if (!vs_type) + { + static const GTypeInfo vs_info = + { + sizeof (VikStatusbarClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + NULL, /* class init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (VikStatusbar), + 0, + NULL /* instance init */ + }; + vs_type = g_type_register_static ( GTK_TYPE_STATUSBAR, "VikStatusbar", &vs_info, 0 ); + } + + return vs_type; +} + +VikStatusbar *vik_statusbar_new () +{ + VikStatusbar *vs = VIK_STATUSBAR ( g_object_new ( VIK_STATUSBAR_TYPE, NULL ) ); + gint i; + + gtk_statusbar_set_has_resize_grip ( GTK_STATUSBAR(vs), FALSE ); + + vs->status[0] = gtk_statusbar_new(); + gtk_statusbar_set_has_resize_grip ( GTK_STATUSBAR(vs->status[0]), FALSE ); + gtk_box_pack_start ( GTK_BOX(vs), vs->status[0], FALSE, FALSE, 1); + gtk_widget_set_size_request ( vs->status[0], 100, -1 ); + + vs->status[1] = gtk_statusbar_new(); + gtk_statusbar_set_has_resize_grip ( GTK_STATUSBAR(vs->status[1]), FALSE ); + gtk_box_pack_start ( GTK_BOX(vs), vs->status[1], FALSE, FALSE, 1); + gtk_widget_set_size_request ( vs->status[1], 100, -1 ); + + vs->status[2] = gtk_statusbar_new(); + gtk_statusbar_set_has_resize_grip ( GTK_STATUSBAR(vs->status[2]), FALSE ); + gtk_box_pack_end ( GTK_BOX(vs), vs->status[2], TRUE, TRUE, 1); + + vs->status[3] = gtk_statusbar_new(); + gtk_statusbar_set_has_resize_grip ( GTK_STATUSBAR(vs->status[3]), FALSE ); + gtk_box_pack_end ( GTK_BOX(vs), vs->status[3], TRUE, TRUE, 1); + + for ( i = 0; i < STATUS_COUNT; i++ ) + vs->empty[i] = TRUE; + + return vs; +} + +void vik_statusbar_set_message ( VikStatusbar *vs, gint field, const gchar *message ) +{ + if ( field >= 0 && field < STATUS_COUNT ) + { + GtkStatusbar *gsb; + if ( field == 0 ) + gsb = GTK_STATUSBAR(vs); + else + gsb = GTK_STATUSBAR(vs->status[field-1]); + + if ( !vs->empty[field] ) + gtk_statusbar_pop ( gsb, 0 ); + else + vs->empty[field] = FALSE; + + gtk_statusbar_push ( gsb, 0, message ); + } +} diff --git a/src/vikstatus.h b/src/vikstatus.h new file mode 100644 index 00000000..95538b14 --- /dev/null +++ b/src/vikstatus.h @@ -0,0 +1,51 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _VIKING_STATUS_H +#define _VIKING_STATUS_H + +#include +#include +#include + +G_BEGIN_DECLS + +#define VIK_STATUSBAR_TYPE (vik_statusbar_get_type ()) +#define VIK_STATUSBAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), VIK_STATUSBAR_TYPE, VikStatusbar)) +#define VIK_STATUSBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), VIK_STATUSBAR_TYPE, VikStatusbarClass)) +#define IS_VIK_STATUSBAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VIK_STATUSBAR_TYPE)) +#define IS_VIK_STATUSBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), VIK_STATUSBAR_TYPE)) + +typedef struct _VikStatusbar VikStatusbar; +typedef struct _VikStatusbarClass VikStatusbarClass; + +struct _VikStatusbarClass +{ + GtkStatusbarClass statusbar_class; +}; + +GType vik_statusbar_get_type (); + +VikStatusbar *vik_statusbar_new (); +void vik_statusbar_set_message ( VikStatusbar *vs, gint field, const gchar *message ); + + +#endif diff --git a/src/viktrack.c b/src/viktrack.c new file mode 100644 index 00000000..60550b60 --- /dev/null +++ b/src/viktrack.c @@ -0,0 +1,426 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include "coords.h" +#include "vikcoord.h" +#include "viktrack.h" +#include "globals.h" + +VikTrack *vik_track_new() +{ + VikTrack *tr = g_malloc ( sizeof ( VikTrack ) ); + tr->trackpoints = NULL; + tr->comment = NULL; + tr->ref_count = 1; + return tr; +} + +void vik_track_set_comment_no_copy(VikTrack *tr, gchar *comment) +{ + if ( tr->comment ) + g_free ( tr->comment ); + tr->comment = comment; +} + + +void vik_track_set_comment(VikTrack *tr, const gchar *comment) +{ + if ( tr->comment ) + g_free ( tr->comment ); + + if ( comment && comment[0] != '\0' ) + tr->comment = g_strdup(comment); + else + tr->comment = NULL; +} + +void vik_track_ref(VikTrack *tr) +{ + tr->ref_count++; +} + +void vik_track_free(VikTrack *tr) +{ + if ( tr->ref_count-- > 1 ) + return; + + if ( tr->comment ) + g_free ( tr->comment ); + g_list_foreach ( tr->trackpoints, (GFunc) g_free, NULL ); + g_list_free( tr->trackpoints ); + g_free ( tr ); +} + +VikTrack *vik_track_copy ( const VikTrack *tr ) +{ + VikTrack *new_tr = vik_track_new(); + VikTrackpoint *new_tp; + GList *tp_iter = tr->trackpoints; + new_tr->visible = tr->visible; + new_tr->trackpoints = NULL; + while ( tp_iter ) + { + new_tp = g_malloc ( sizeof ( VikTrackpoint ) ); + *new_tp = *((VikTrackpoint *)(tp_iter->data)); + new_tr->trackpoints = g_list_append ( new_tr->trackpoints, new_tp ); + tp_iter = tp_iter->next; + } + vik_track_set_comment(new_tr,tr->comment); + return new_tr; +} + +VikTrackpoint *vik_trackpoint_new() +{ + return g_malloc0(sizeof(VikTrackpoint)); +} + +void vik_trackpoint_free(VikTrackpoint *tp) +{ + g_free(tp); +} + +VikTrackpoint *vik_trackpoint_copy(VikTrackpoint *tp) +{ + VikTrackpoint *rv = vik_trackpoint_new(); + *rv = *tp; + return rv; +} + +gdouble vik_track_get_length(const VikTrack *tr) +{ + gdouble len = 0.0; + if ( tr->trackpoints ) + { + GList *iter = tr->trackpoints->next; + while (iter) + { + if ( ! VIK_TRACKPOINT(iter->data)->newsegment ) + len += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord), + &(VIK_TRACKPOINT(iter->prev->data)->coord) ); + iter = iter->next; + } + } + return len; +} + +gdouble vik_track_get_length_including_gaps(const VikTrack *tr) +{ + gdouble len = 0.0; + if ( tr->trackpoints ) + { + GList *iter = tr->trackpoints->next; + while (iter) + { + len += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord), + &(VIK_TRACKPOINT(iter->prev->data)->coord) ); + iter = iter->next; + } + } + return len; +} + +gulong vik_track_get_tp_count(const VikTrack *tr) +{ + gulong num = 0; + GList *iter = tr->trackpoints; + while ( iter ) + { + num++; + iter = iter->next; + } + return num; +} + +gulong vik_track_get_dup_point_count ( const VikTrack *tr ) +{ + gulong num = 0; + GList *iter = tr->trackpoints; + while ( iter ) + { + if ( iter->next && vik_coord_equals ( &(VIK_TRACKPOINT(iter->data)->coord), + &(VIK_TRACKPOINT(iter->next->data)->coord) ) ) + num++; + iter = iter->next; + } + return num; +} + +void vik_track_remove_dup_points ( VikTrack *tr ) +{ + GList *iter = tr->trackpoints; + while ( iter ) + { + if ( iter->next && vik_coord_equals ( &(VIK_TRACKPOINT(iter->data)->coord), + &(VIK_TRACKPOINT(iter->next->data)->coord) ) ) + { + g_free ( iter->next->data ); + tr->trackpoints = g_list_delete_link ( tr->trackpoints, iter->next ); + } + else + iter = iter->next; + } +} + +guint vik_track_get_segment_count(const VikTrack *tr) +{ + guint num = 1; + GList *iter = tr->trackpoints; + if ( !iter ) + return 0; + while ( (iter = iter->next) ) + { + if ( VIK_TRACKPOINT(iter->data)->newsegment ) + num++; + } + return num; +} + +VikTrack **vik_track_split_into_segments(VikTrack *t, guint *ret_len) +{ + VikTrack **rv; + VikTrack *tr; + guint i; + guint segs = vik_track_get_segment_count(t); + GList *iter; + + if ( segs < 2 ) + { + *ret_len = 0; + return NULL; + } + + rv = g_malloc ( segs * sizeof(VikTrack *) ); + tr = vik_track_copy ( t ); + rv[0] = tr; + iter = tr->trackpoints; + + i = 1; + while ( (iter = iter->next) ) + { + if ( VIK_TRACKPOINT(iter->data)->newsegment ) + { + iter->prev->next = NULL; + iter->prev = NULL; + rv[i] = vik_track_new(); + if ( tr->comment ) + vik_track_set_comment ( rv[i], tr->comment ); + rv[i]->visible = tr->visible; + rv[i]->trackpoints = iter; + i++; + } + } + *ret_len = segs; + return rv; +} + +void vik_track_reverse ( VikTrack *tr ) +{ + GList *iter; + tr->trackpoints = g_list_reverse(tr->trackpoints); + + /* fix 'newsegment' */ + iter = g_list_last ( tr->trackpoints ); + while ( iter ) + { + if ( ! iter->next ) /* last segment, was first, cancel newsegment. */ + VIK_TRACKPOINT(iter->data)->newsegment = FALSE; + if ( ! iter->prev ) /* first segment by convention has newsegment flag. */ + VIK_TRACKPOINT(iter->data)->newsegment = TRUE; + else if ( VIK_TRACKPOINT(iter->data)->newsegment && iter->next ) + { + VIK_TRACKPOINT(iter->next->data)->newsegment = TRUE; + VIK_TRACKPOINT(iter->data)->newsegment = FALSE; + } + iter = iter->prev; + } +} + +gdouble vik_track_get_average_speed(const VikTrack *tr) +{ + gdouble len = 0.0; + guint32 time = 0; + if ( tr->trackpoints ) + { + GList *iter = tr->trackpoints->next; + while (iter) + { + if ( VIK_TRACKPOINT(iter->data)->has_timestamp && + VIK_TRACKPOINT(iter->prev->data)->has_timestamp && + (! VIK_TRACKPOINT(iter->data)->newsegment) ) + { + len += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord), + &(VIK_TRACKPOINT(iter->prev->data)->coord) ); + time += ABS(VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp); + } + iter = iter->next; + } + } + return (time == 0) ? 0 : ABS(len/time); +} + +gdouble vik_track_get_max_speed(const VikTrack *tr) +{ + gdouble maxspeed = 0.0, speed = 0.0; + if ( tr->trackpoints ) + { + GList *iter = tr->trackpoints->next; + while (iter) + { + if ( VIK_TRACKPOINT(iter->data)->has_timestamp && + VIK_TRACKPOINT(iter->prev->data)->has_timestamp && + (! VIK_TRACKPOINT(iter->data)->newsegment) ) + { + speed = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord), &(VIK_TRACKPOINT(iter->prev->data)->coord) ) + / ABS(VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp); + if ( speed > maxspeed ) + maxspeed = speed; + } + iter = iter->next; + } + } + return maxspeed; +} + +void vik_track_convert ( VikTrack *tr, VikCoordMode dest_mode ) +{ + GList *iter = tr->trackpoints; + while (iter) + { + vik_coord_convert ( &(VIK_TRACKPOINT(iter->data)->coord), dest_mode ); + iter = iter->next; + } +} + +/* I understood this when I wrote it ... maybe ... Basically it eats up the + * proper amounts of length on the track and averages elevation over that. */ +gdouble *vik_track_make_elevation_map ( const VikTrack *tr, guint16 num_chunks ) +{ + gdouble *pts; + gdouble total_length, chunk_length, current_dist, current_area_under_curve, current_seg_length, dist_along_seg = 0.0; + gdouble altitude1, altitude2; + guint16 current_chunk; + gboolean ignore_it = FALSE; + + GList *iter = tr->trackpoints; + + g_assert ( num_chunks < 16000 ); + + pts = g_malloc ( sizeof(gdouble) * num_chunks ); + + total_length = vik_track_get_length_including_gaps ( tr ); + chunk_length = total_length / num_chunks; + + current_dist = 0.0; + current_area_under_curve = 0; + current_chunk = 0; + current_seg_length = 0; + + current_seg_length = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord), + &(VIK_TRACKPOINT(iter->next->data)->coord) ); + altitude1 = VIK_TRACKPOINT(iter->data)->altitude; + altitude2 = VIK_TRACKPOINT(iter->next->data)->altitude; + dist_along_seg = 0; + + while ( current_chunk < num_chunks ) { + + /* go along current seg */ + if ( current_seg_length && (current_seg_length - dist_along_seg) > chunk_length ) { + dist_along_seg += chunk_length; + + /* / + * pt2 * + * /x altitude = alt_at_pt_1 + alt_at_pt_2 / 2 = altitude1 + slope * dist_value_of_pt_inbetween_pt1_and_pt2 + * /xx avg altitude = area under curve / chunk len + *pt1 *xxx avg altitude = altitude1 + (altitude2-altitude1)/(current_seg_length)*(dist_along_seg + (chunk_len/2)) + * / xxx + * / xxx + **/ + + if ( ignore_it ) + pts[current_chunk] = VIK_DEFAULT_ALTITUDE; + else + pts[current_chunk] = altitude1 + (altitude2-altitude1)*((dist_along_seg + (chunk_length/2))/current_seg_length); + + current_chunk++; + } else { + /* finish current seg */ + if ( current_seg_length ) { + gdouble altitude_at_dist_along_seg = altitude1 + (altitude2-altitude1)/(current_seg_length)*dist_along_seg; + current_dist = current_seg_length - dist_along_seg; + current_area_under_curve = current_dist*(altitude_at_dist_along_seg + altitude2)*0.5; + } else { current_dist = current_area_under_curve = 0; } /* should only happen if first current_seg_length == 0 */ + + /* get intervening segs */ + iter = iter->next; + while ( iter && iter->next ) { + current_seg_length = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord), + &(VIK_TRACKPOINT(iter->next->data)->coord) ); + altitude1 = VIK_TRACKPOINT(iter->data)->altitude; + altitude2 = VIK_TRACKPOINT(iter->next->data)->altitude; + ignore_it = VIK_TRACKPOINT(iter->next->data)->newsegment; + + if ( chunk_length - current_dist >= current_seg_length ) { + current_dist += current_seg_length; + current_area_under_curve += current_seg_length * (altitude1+altitude2) * 0.5; + iter = iter->next; + } else { + break; + } + } + + /* final seg */ + dist_along_seg = chunk_length - current_dist; + if ( ignore_it ) + pts[current_chunk] = current_area_under_curve / current_dist; + else { + current_area_under_curve += dist_along_seg * (altitude1 + (altitude2 - altitude1)*dist_along_seg/current_seg_length); + pts[current_chunk] = current_area_under_curve / chunk_length; + } + + current_dist = 0; + current_chunk++; + } + } + + return pts; +} + + +void vik_track_get_total_elevation_gain(const VikTrack *tr, gdouble *up, gdouble *down) +{ + gdouble diff; + *up = *down = 0; + if ( tr->trackpoints ) + { + GList *iter = tr->trackpoints->next; + while (iter) + { + diff = VIK_TRACKPOINT(iter->data)->altitude - VIK_TRACKPOINT(iter->prev->data)->altitude; + if ( diff > 0 ) + *up += diff; + else + *down -= diff; + iter = iter->next; + } + } +} diff --git a/src/viktrack.h b/src/viktrack.h new file mode 100644 index 00000000..ce73ec7b --- /dev/null +++ b/src/viktrack.h @@ -0,0 +1,73 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _VIKING_TRACK_H +#define _VIKING_TRACK_H + +/* todo important: put these in their own header file, maybe.probably also rename */ + +#define VIK_TRACK(x) ((VikTrack *)(x)) +#define VIK_TRACKPOINT(x) ((VikTrackpoint *)(x)) + +typedef struct _VikTrackpoint VikTrackpoint; +struct _VikTrackpoint { + VikCoord coord; + gboolean newsegment; + gboolean has_timestamp; + time_t timestamp; + gdouble altitude; +}; + +typedef struct _VikTrack VikTrack; +struct _VikTrack { + GList *trackpoints; + gboolean visible; + gchar *comment; + guint8 ref_count; +}; + +VikTrack *vik_track_new(); +void vik_track_set_comment(VikTrack *wp, const gchar *comment); +void vik_track_ref(VikTrack *tr); +void vik_track_free(VikTrack *tr); +VikTrack *vik_track_copy ( const VikTrack *tr ); +void vik_track_set_comment_no_copy(VikTrack *tr, gchar *comment); +VikTrackpoint *vik_trackpoint_new(); +void vik_trackpoint_free(VikTrackpoint *tp); +VikTrackpoint *vik_trackpoint_copy(VikTrackpoint *tp); +gdouble vik_track_get_length(const VikTrack *tr); +gdouble vik_track_get_length_including_gaps(const VikTrack *tr); +gulong vik_track_get_tp_count(const VikTrack *tr); +guint vik_track_get_segment_count(const VikTrack *tr); +VikTrack **vik_track_split_into_segments(VikTrack *tr, guint *ret_len); +void vik_track_reverse(VikTrack *tr); + +gulong vik_track_get_dup_point_count ( const VikTrack *vt ); +void vik_track_remove_dup_points ( VikTrack *vt ); + +gdouble vik_track_get_max_speed(const VikTrack *tr); +gdouble vik_track_get_average_speed(const VikTrack *tr); + +void vik_track_convert ( VikTrack *tr, VikCoordMode dest_mode ); +gdouble *vik_track_make_elevation_map ( const VikTrack *tr, guint16 num_chunks ); +void vik_track_get_total_elevation_gain(const VikTrack *tr, gdouble *up, gdouble *down); + +#endif diff --git a/src/viktreeview.c b/src/viktreeview.c new file mode 100644 index 00000000..b8935a6f --- /dev/null +++ b/src/viktreeview.c @@ -0,0 +1,455 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include + +#include "viking.h" + +#include "config.h" + +#define TREEVIEW_GET(model,iter,what,dest) gtk_tree_model_get(GTK_TREE_MODEL(model),(iter),(what),(dest),-1) + +enum { + VT_ITEM_EDITED_SIGNAL, + VT_ITEM_TOGGLED_SIGNAL, + VT_LAST_SIGNAL +}; + +static guint treeview_signals[VT_LAST_SIGNAL] = { 0, 0 }; + +static GObjectClass *parent_class; + +enum +{ + NAME_COLUMN = 0, + VISIBLE_COLUMN, + ICON_COLUMN, + /* invisible */ + + TYPE_COLUMN, + ITEM_PARENT_COLUMN, + ITEM_POINTER_COLUMN, + ITEM_DATA_COLUMN, + HAS_VISIBLE_COLUMN, + EDITABLE_COLUMN, + /* properties dialog, delete, rename, etc. */ + NUM_COLUMNS +}; + +struct _VikTreeview { + GtkTreeView treeview; + GtkTreeModel *model; + + GdkPixbuf *layer_type_icons[VIK_LAYER_NUM_TYPES]; +}; + +/* TODO: find, make "static" and put up here all non-"a_" functions */ +static void treeview_class_init ( VikTreeviewClass *klass ); +static void treeview_init ( VikTreeview *vt ); +static void treeview_finalize ( GObject *gob ); +static void treeview_add_columns ( VikTreeview *vt ); + +GType vik_treeview_get_type (void) +{ + static GType vt_type = 0; + + if (!vt_type) + { + static const GTypeInfo vt_info = + { + sizeof (VikTreeviewClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) treeview_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (VikTreeview), + 0, + (GInstanceInitFunc) treeview_init, + }; + vt_type = g_type_register_static ( GTK_TYPE_TREE_VIEW, "VikTreeview", &vt_info, 0 ); + } + + return vt_type; +} + +static void treeview_class_init ( VikTreeviewClass *klass ) +{ + /* Destructor */ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = treeview_finalize; + + parent_class = g_type_class_peek_parent (klass); + + treeview_signals[VT_ITEM_EDITED_SIGNAL] = g_signal_new ( "item_edited", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (VikTreeviewClass, item_edited), NULL, NULL, + g_cclosure_marshal_VOID__UINT_POINTER, G_TYPE_NONE, 2, GTK_TYPE_TREE_ITER, G_TYPE_STRING); + /* VOID__UINT_POINTER: kinda hack-ish, but it works. */ + + treeview_signals[VT_ITEM_TOGGLED_SIGNAL] = g_signal_new ( "item_toggled", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (VikTreeviewClass, item_toggled), NULL, NULL, + g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, GTK_TYPE_TREE_ITER ); +} + +static void treeview_edited_cb (GtkCellRendererText *cell, gchar *path_str, const gchar *new_name, VikTreeview *vt) +{ + GtkTreeIter iter; + + /* get type and data */ + vik_treeview_get_iter_from_path_str ( vt, &iter, path_str ); + + g_signal_emit ( G_OBJECT(vt), treeview_signals[VT_ITEM_EDITED_SIGNAL], 0, &iter, new_name, 0 ); +} + +static void treeview_toggled_cb (GtkCellRendererToggle *cell, gchar *path_str, VikTreeview *vt) +{ + GtkTreeIter iter; + + /* get type and data */ + vik_treeview_get_iter_from_path_str ( vt, &iter, path_str ); + + g_signal_emit ( G_OBJECT(vt), +treeview_signals[VT_ITEM_TOGGLED_SIGNAL], 0, &iter ); +} + +VikTreeview *vik_treeview_new () +{ + return VIK_TREEVIEW ( g_object_new ( VIK_TREEVIEW_TYPE, NULL ) ); +} + +gint vik_treeview_item_get_type ( VikTreeview *vt, GtkTreeIter *iter ) +{ + gint rv; + TREEVIEW_GET ( vt->model, iter, TYPE_COLUMN, &rv ); + return rv; +} + +gint vik_treeview_item_get_data ( VikTreeview *vt, GtkTreeIter *iter ) +{ + gint rv; + TREEVIEW_GET ( vt->model, iter, ITEM_DATA_COLUMN, &rv ); + return rv; +} + +gpointer vik_treeview_item_get_pointer ( VikTreeview *vt, GtkTreeIter *iter ) +{ + gpointer rv; + TREEVIEW_GET ( vt->model, iter, ITEM_POINTER_COLUMN, &rv ); + return rv; +} + +void vik_treeview_item_set_pointer ( VikTreeview *vt, GtkTreeIter *iter, gpointer pointer ) +{ + gtk_tree_store_set ( GTK_TREE_STORE(vt->model), iter, ITEM_POINTER_COLUMN, pointer, -1 ); +} + +gpointer vik_treeview_item_get_parent ( VikTreeview *vt, GtkTreeIter *iter ) +{ + gpointer rv; + TREEVIEW_GET ( vt->model, iter, ITEM_PARENT_COLUMN, &rv ); + return rv; +} + +void vik_treeview_get_iter_from_path_str ( VikTreeview *vt, GtkTreeIter *iter, const gchar *path_str ) +{ + gtk_tree_model_get_iter_from_string ( GTK_TREE_MODEL(vt->model), iter, path_str ); +} + +static void treeview_add_columns ( VikTreeview *vt ) +{ + gint col_offset; + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + + renderer = gtk_cell_renderer_text_new (); + g_signal_connect (renderer, "edited", + G_CALLBACK (treeview_edited_cb), vt); + + g_object_set (G_OBJECT (renderer), "xalign", 0.0, NULL); + + col_offset = gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (vt), + -1, "Layer Name", + renderer, "text", + NAME_COLUMN, + "editable", EDITABLE_COLUMN, + NULL); + + column = gtk_tree_view_get_column (GTK_TREE_VIEW (vt), col_offset - 1); + gtk_tree_view_column_set_sizing (GTK_TREE_VIEW_COLUMN (column), + GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width (GTK_TREE_VIEW_COLUMN (column), 100); + gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE); + gtk_tree_view_column_set_resizable (GTK_TREE_VIEW_COLUMN (column), TRUE); + + renderer = gtk_cell_renderer_pixbuf_new (); + + g_object_set (G_OBJECT (renderer), "xalign", 0.0, NULL); + + col_offset = gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (vt), + -1, "Type", + renderer, "pixbuf", + ICON_COLUMN, + NULL); + + + column = gtk_tree_view_get_column (GTK_TREE_VIEW (vt), col_offset - 1); + gtk_tree_view_column_set_fixed_width (GTK_TREE_VIEW_COLUMN (column), 33); + gtk_tree_view_column_set_sizing (GTK_TREE_VIEW_COLUMN (column), + GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE); + + + renderer = gtk_cell_renderer_toggle_new (); + g_object_set (G_OBJECT (renderer), "xalign", 0.0, NULL); + + g_signal_connect (renderer, "toggled", G_CALLBACK (treeview_toggled_cb), vt); + + col_offset = gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (vt), + -1, "Visible", + renderer, + "active", + VISIBLE_COLUMN, + "visible", + HAS_VISIBLE_COLUMN, + "activatable", + HAS_VISIBLE_COLUMN, + NULL); + + column = gtk_tree_view_get_column (GTK_TREE_VIEW (vt), col_offset - 1); + gtk_tree_view_column_set_fixed_width (GTK_TREE_VIEW_COLUMN (column), 40); + gtk_tree_view_column_set_sizing (GTK_TREE_VIEW_COLUMN (column), + GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_clickable (GTK_TREE_VIEW_COLUMN (column), TRUE); + +} + +void treeview_init ( VikTreeview *vt ) +{ + guint16 i; + + vt->model = GTK_TREE_MODEL(gtk_tree_store_new ( NUM_COLUMNS, G_TYPE_STRING, G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, G_TYPE_INT, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_INT, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN )); + + /* create tree view */ + gtk_tree_view_set_model ( GTK_TREE_VIEW(vt), vt->model ); + treeview_add_columns ( vt ); + g_object_unref (vt->model); + + gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (vt), TRUE); + gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (vt)), + GTK_SELECTION_SINGLE); + + for ( i = 0; i < VIK_LAYER_NUM_TYPES; i++ ) + vt->layer_type_icons[i] = vik_layer_load_icon ( i ); /* if icon can't be loaded, it will be null and simply not be shown. */ + +} + +gboolean vik_treeview_item_get_parent_iter ( VikTreeview *vt, GtkTreeIter *iter, GtkTreeIter *parent ) +{ + return gtk_tree_model_iter_parent ( GTK_TREE_MODEL(vt->model), parent, iter ); +} + +gboolean vik_treeview_move_item ( VikTreeview *vt, GtkTreeIter *iter, gboolean up ) +{ + gint t = vik_treeview_item_get_type ( vt, iter ); + if ( t == VIK_TREEVIEW_TYPE_LAYER ) + { + GtkTreeIter switch_iter; + if (up) + { + /* iter to path to iter */ + GtkTreePath *path = gtk_tree_model_get_path ( vt->model, iter ); + if ( !gtk_tree_path_prev ( path ) || !gtk_tree_model_get_iter ( vt->model, &switch_iter, path ) ) + { + gtk_tree_path_free ( path ); + return FALSE; + } + gtk_tree_path_free ( path ); + } + else + { + switch_iter = *iter; + if ( !gtk_tree_model_iter_next ( vt->model, &switch_iter ) ) + return FALSE; + } + gtk_tree_store_swap ( GTK_TREE_STORE(vt->model), iter, &switch_iter ); + return TRUE; + /* now, the easy part. actually switching them, not the GUI */ + } /* if item is map */ + return FALSE; +} + +gboolean vik_treeview_get_iter_at_pos ( VikTreeview *vt, GtkTreeIter *iter, gint x, gint y ) +{ + GtkTreePath *path; + gtk_tree_view_get_path_at_pos ( GTK_TREE_VIEW(vt), x, y, &path, NULL, NULL, NULL ); + if ( ! path ) + return FALSE; + + gtk_tree_model_get_iter (GTK_TREE_MODEL(vt->model), iter, path); + gtk_tree_path_free ( path ); + return TRUE; +} + +void vik_treeview_select_iter ( VikTreeview *vt, GtkTreeIter *iter ) +{ + gtk_tree_selection_select_iter ( gtk_tree_view_get_selection ( GTK_TREE_VIEW ( vt ) ), iter ); +} + +gboolean vik_treeview_get_selected_iter ( VikTreeview *vt, GtkTreeIter *iter ) +{ + return gtk_tree_selection_get_selected ( gtk_tree_view_get_selection ( GTK_TREE_VIEW ( vt ) ), NULL, iter ); +} + +void vik_treeview_item_delete ( VikTreeview *vt, GtkTreeIter *iter ) +{ + gtk_tree_store_remove ( GTK_TREE_STORE(vt->model), iter ); +} + +/* Treeview Reform Project */ + +void vik_treeview_item_set_name ( VikTreeview *vt, GtkTreeIter *iter, const gchar *to ) +{ + g_return_if_fail ( iter != NULL && to != NULL ); + gtk_tree_store_set ( GTK_TREE_STORE(vt->model), iter, NAME_COLUMN, to, -1); +} + +void vik_treeview_item_set_visible ( VikTreeview *vt, GtkTreeIter *iter, gboolean to ) +{ + g_return_if_fail ( iter != NULL ); + gtk_tree_store_set ( GTK_TREE_STORE(vt->model), iter, VISIBLE_COLUMN, to, -1 ); +} + +void vik_treeview_expand ( VikTreeview *vt, GtkTreeIter *iter ) +{ + GtkTreePath *path; + path = gtk_tree_model_get_path ( vt->model, iter ); + gtk_tree_view_expand_row ( GTK_TREE_VIEW(vt), path, FALSE ); + gtk_tree_path_free ( path ); +} + +void vik_treeview_item_select ( VikTreeview *vt, GtkTreeIter *iter ) +{ + gtk_tree_selection_select_iter ( gtk_tree_view_get_selection ( GTK_TREE_VIEW ( vt ) ), iter ); +} + +void vik_treeview_add_layer ( VikTreeview *vt, GtkTreeIter *parent_iter, GtkTreeIter *iter, const gchar *name, gpointer parent, + gpointer item, gint data, gint icon_type ) +{ + g_assert ( iter != NULL ); + g_assert ( icon_type < VIK_LAYER_NUM_TYPES ); + gtk_tree_store_prepend ( GTK_TREE_STORE(vt->model), iter, parent_iter ); + gtk_tree_store_set ( GTK_TREE_STORE(vt->model), iter, NAME_COLUMN, name, VISIBLE_COLUMN, TRUE, + TYPE_COLUMN, VIK_TREEVIEW_TYPE_LAYER, ITEM_PARENT_COLUMN, parent, ITEM_POINTER_COLUMN, item, + ITEM_DATA_COLUMN, data, HAS_VISIBLE_COLUMN, TRUE, EDITABLE_COLUMN, TRUE, + ICON_COLUMN, icon_type >= 0 ? vt->layer_type_icons[icon_type] : NULL, -1 ); +} + +void vik_treeview_insert_layer ( VikTreeview *vt, GtkTreeIter *parent_iter, GtkTreeIter *iter, const gchar *name, gpointer parent, + gpointer item, gint data, gint icon_type, GtkTreeIter *sibling ) +{ + g_assert ( iter != NULL ); + g_assert ( icon_type < VIK_LAYER_NUM_TYPES ); + gtk_tree_store_insert_before ( GTK_TREE_STORE(vt->model), iter, parent_iter, sibling ); + gtk_tree_store_set ( GTK_TREE_STORE(vt->model), iter, NAME_COLUMN, name, VISIBLE_COLUMN, TRUE, + TYPE_COLUMN, VIK_TREEVIEW_TYPE_LAYER, ITEM_PARENT_COLUMN, parent, ITEM_POINTER_COLUMN, item, + ITEM_DATA_COLUMN, data, HAS_VISIBLE_COLUMN, TRUE, EDITABLE_COLUMN, TRUE, + ICON_COLUMN, icon_type >= 0 ? vt->layer_type_icons[icon_type] : NULL, -1 ); +} + +void vik_treeview_add_sublayer ( VikTreeview *vt, GtkTreeIter *parent_iter, GtkTreeIter *iter, const gchar *name, gpointer parent, gpointer item, + gint data, GdkPixbuf *icon, gboolean has_visible, gboolean editable ) +{ + g_assert ( iter != NULL ); + + gtk_tree_store_prepend ( GTK_TREE_STORE(vt->model), iter, parent_iter ); + gtk_tree_store_set ( GTK_TREE_STORE(vt->model), iter, NAME_COLUMN, name, VISIBLE_COLUMN, TRUE, TYPE_COLUMN, VIK_TREEVIEW_TYPE_SUBLAYER, ITEM_PARENT_COLUMN, parent, ITEM_POINTER_COLUMN, item, ITEM_DATA_COLUMN, data, HAS_VISIBLE_COLUMN, has_visible, EDITABLE_COLUMN, editable, ICON_COLUMN, icon, -1 ); +} + + +#ifdef VIK_CONFIG_ALPHABETIZED_TRW + +void vik_treeview_sublayer_realphabetize ( VikTreeview *vt, GtkTreeIter *iter, const gchar *newname ) +{ + GtkTreeIter search_iter, parent_iter; + gchar *search_name; + g_assert ( iter != NULL ); + + gtk_tree_model_iter_parent ( vt->model, &parent_iter, iter ); + + g_assert ( gtk_tree_model_iter_children ( vt->model, &search_iter, &parent_iter ) ); + + do { + gtk_tree_model_get ( vt->model, &search_iter, NAME_COLUMN, &search_name, -1 ); + if ( strcmp ( search_name, newname ) > 0 ) /* not >= or would trip on itself */ + { + gtk_tree_store_move_before ( GTK_TREE_STORE(vt->model), iter, &search_iter ); + return; + } + } while ( gtk_tree_model_iter_next ( vt->model, &search_iter ) ); + + gtk_tree_store_move_before ( GTK_TREE_STORE(vt->model), iter, NULL ); +} + +void vik_treeview_add_sublayer_alphabetized + ( VikTreeview *vt, GtkTreeIter *parent_iter, GtkTreeIter *iter, const gchar *name, gpointer parent, gpointer item, + gint data, GdkPixbuf *icon, gboolean has_visible, gboolean editable ) +{ + GtkTreeIter search_iter; + gchar *search_name; + g_assert ( iter != NULL ); + + if ( gtk_tree_model_iter_children ( vt->model, &search_iter, parent_iter ) ) + { + gboolean found_greater_string = FALSE; + do { + gtk_tree_model_get ( vt->model, &search_iter, NAME_COLUMN, &search_name, -1 ); + if ( strcmp ( search_name, name ) >= 0 ) + { + gtk_tree_store_insert_before ( GTK_TREE_STORE(vt->model), iter, parent_iter, &search_iter ); + found_greater_string = TRUE; + break; + } + } while ( gtk_tree_model_iter_next ( vt->model, &search_iter ) ); + + if ( ! found_greater_string ) + gtk_tree_store_append ( GTK_TREE_STORE(vt->model), iter, parent_iter ); + } + else + gtk_tree_store_prepend ( GTK_TREE_STORE(vt->model), iter, parent_iter ); + + gtk_tree_store_set ( GTK_TREE_STORE(vt->model), iter, NAME_COLUMN, name, VISIBLE_COLUMN, TRUE, TYPE_COLUMN, VIK_TREEVIEW_TYPE_SUBLAYER, ITEM_PARENT_COLUMN, parent, ITEM_POINTER_COLUMN, item, ITEM_DATA_COLUMN, data, HAS_VISIBLE_COLUMN, has_visible, EDITABLE_COLUMN, editable, ICON_COLUMN, icon, -1 ); +} + +#endif + +static void treeview_finalize ( GObject *gob ) +{ + VikTreeview *vt = VIK_TREEVIEW ( gob ); + guint16 i; + + for ( i = 0; i < VIK_LAYER_NUM_TYPES; i++ ) + if ( vt->layer_type_icons[i] != NULL ) + g_object_unref ( G_OBJECT(vt->layer_type_icons[i]) ); + + G_OBJECT_CLASS(parent_class)->finalize(gob); +} diff --git a/src/viktreeview.h b/src/viktreeview.h new file mode 100644 index 00000000..a700a15c --- /dev/null +++ b/src/viktreeview.h @@ -0,0 +1,103 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _VIKING_TREEVIEW_H +#define _VIKING_TREEVIEW_H + +#include "config.h" + +#include +#include +#include + +G_BEGIN_DECLS + +#define VIK_TREEVIEW_TYPE (vik_treeview_get_type ()) +#define VIK_TREEVIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), VIK_TREEVIEW_TYPE, VikTreeview)) +#define VIK_TREEVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), VIK_TREEVIEW_TYPE, VikTreeviewClass)) +#define IS_VIK_TREEVIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VIK_TREEVIEW_TYPE)) +#define IS_VIK_TREEVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), VIK_TREEVIEW_TYPE)) + +typedef struct _VikTreeview VikTreeview; +typedef struct _VikTreeviewClass VikTreeviewClass; + +struct _VikTreeviewClass +{ + GtkTreeViewClass vbox_class; + void (* item_edited) (VikTreeview *vt, GtkTreeIter *iter, const gchar *new_name); + void (* item_toggled) (VikTreeview *vt,GtkTreeIter *iter); +}; + +enum { + VIK_TREEVIEW_TYPE_LAYER = 0, + VIK_TREEVIEW_TYPE_SUBLAYER +}; + +GType vik_treeview_get_type (); + + +VikTreeview *vik_treeview_new (); + +GtkWidget *vik_treeview_get_widget ( VikTreeview *vt ); + +gint vik_treeview_item_get_data ( VikTreeview *vt, GtkTreeIter *iter ); +gint vik_treeview_item_get_type ( VikTreeview *vt, GtkTreeIter *iter ); +gpointer vik_treeview_item_get_pointer ( VikTreeview *vt, GtkTreeIter *iter ); +void vik_treeview_item_set_pointer ( VikTreeview *vt, GtkTreeIter *iter, gpointer pointer ); + +gpointer vik_treeview_item_get_parent ( VikTreeview *vt, GtkTreeIter *iter ); + +void vik_treeview_select_iter ( VikTreeview *vt, GtkTreeIter *iter ); +gboolean vik_treeview_get_selected_iter ( VikTreeview *vt, GtkTreeIter *iter ); + +void vik_treeview_item_set_name ( VikTreeview *vt, GtkTreeIter *iter, const gchar *to ); +void vik_treeview_item_set_visible ( VikTreeview *vt, GtkTreeIter *iter, gboolean to ); +void vik_treeview_item_delete ( VikTreeview *vt, GtkTreeIter *iter ); + +gboolean vik_treeview_get_iter_at_pos ( VikTreeview *vt, GtkTreeIter *iter, gint x, gint y ); + +void vik_treeview_get_iter_from_path_str ( VikTreeview *vt, GtkTreeIter *iter, const gchar *path_str ); +gboolean vik_treeview_move_item ( VikTreeview *vt, GtkTreeIter *iter, gboolean up ); +void vik_treeview_item_select ( VikTreeview *vt, GtkTreeIter *iter ); + +gboolean vik_treeview_item_get_parent_iter ( VikTreeview *vt, GtkTreeIter *iter, GtkTreeIter *parent ); +void vik_treeview_expand_toplevel ( VikTreeview *vt ); +void vik_treeview_expand ( VikTreeview *vt, GtkTreeIter *iter ); + +void vik_treeview_add_layer ( VikTreeview *vt, GtkTreeIter *parent_iter, GtkTreeIter *iter, const gchar *name, gpointer parent, + gpointer item, gint data, gint icon_type ); /* icon type: type of layer or -1 -> no icon */ +void vik_treeview_insert_layer ( VikTreeview *vt, GtkTreeIter *parent_iter, GtkTreeIter *iter, const gchar *name, gpointer parent, + gpointer item, gint data, gint icon_type, GtkTreeIter *sibling ); +void vik_treeview_add_sublayer ( VikTreeview *vt, GtkTreeIter *parent_iter, GtkTreeIter *iter, const gchar *name, gpointer parent, gpointer item, + gint data, GdkPixbuf *icon, gboolean has_visible, gboolean editable ); + +gboolean vik_treeview_get_iter_with_name ( VikTreeview *vt, GtkTreeIter *iter, GtkTreeIter *parent_iter, const gchar *name ); + +#ifdef VIK_CONFIG_ALPHABETIZED_TRW +void vik_treeview_add_sublayer_alphabetized ( VikTreeview *vt, GtkTreeIter *parent_iter, GtkTreeIter *iter, const gchar *name, gpointer parent, gpointer item, + gint data, GdkPixbuf *icon, gboolean has_visible, gboolean editable ); + +void vik_treeview_sublayer_realphabetize ( VikTreeview *vt, GtkTreeIter *iter, const gchar *newname ); +#endif + +G_END_DECLS + +#endif diff --git a/src/viktrwlayer.c b/src/viktrwlayer.c new file mode 100644 index 00000000..93a94d90 --- /dev/null +++ b/src/viktrwlayer.c @@ -0,0 +1,2431 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define WAYPOINT_FONT "Sans 8" + +/* WARNING: If you go beyond this point, we are NOT responsible for any ill effects on your sanity */ +/* viktrwlayer.c -- 2200 lines can make a difference in the state of things */ + +#include "viking.h" +#include "viktrwlayer_pixmap.h" +#include "viktrwlayer_tpwin.h" +#include "viktrwlayer_propwin.h" +#include "thumbnails.h" +#include "background.h" + +#include +#include +#include +#include + +#define VIK_TRW_LAYER_TRACK_GC 13 +#define VIK_TRW_LAYER_TRACK_GC_RATES 10 +#define VIK_TRW_LAYER_TRACK_GC_MIN 0 +#define VIK_TRW_LAYER_TRACK_GC_MAX 11 +#define VIK_TRW_LAYER_TRACK_GC_BLACK 12 + +#define DRAWMODE_BY_TRACK 0 +#define DRAWMODE_BY_VELOCITY 1 +#define DRAWMODE_ALL_BLACK 2 + +#define POINTS 1 +#define LINES 2 + +/* this is how it knows when you click if you are clicking close to a trackpoint. */ +#define TRACKPOINT_SIZE_APPROX 5 +#define WAYPOINT_SIZE_APPROX 5 + +enum { +VIK_TRW_LAYER_SUBLAYER_TRACKS, +VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, +VIK_TRW_LAYER_SUBLAYER_TRACK, +VIK_TRW_LAYER_SUBLAYER_WAYPOINT +}; + +enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS }; + +struct _VikTrwLayer { + VikLayer vl; + GHashTable *tracks; + GHashTable *tracks_iters; + GHashTable *waypoints_iters; + GHashTable *waypoints; + GtkTreeIter waypoints_iter, tracks_iter; + gboolean tracks_visible, waypoints_visible; + guint8 drawmode; + guint8 drawpoints; + guint8 drawlines; + guint8 line_thickness; + guint8 bg_line_thickness; + + guint8 wp_symbol; + guint8 wp_size; + + gdouble velocity_min, velocity_max; + GArray *track_gc; + guint16 track_gc_iter; + GdkGC *track_bg_gc; + GdkGC *waypoint_gc; + GdkGC *waypoint_text_gc; + GdkGC *waypoint_bg_gc; + GdkFont *waypoint_font; + VikTrack *current_track; + guint16 ct_x1, ct_y1, ct_x2, ct_y2; + + VikCoordMode coord_mode; + + /* wp editing tool */ + VikWaypoint *current_wp; + gchar *current_wp_name; + gboolean moving_wp; + gboolean waypoint_rightclick; + + /* track editing tool */ + GList *current_tpl; + gchar *current_tp_track_name; + VikTrwLayerTpwin *tpwin; + + /* weird hack for joining tracks */ + GList *last_tpl; + gchar *last_tp_track_name; + + /* track editing tool -- more specifically, moving tps */ + gboolean moving_tp; + + gboolean drawlabels; + gboolean drawimages; + guint8 image_alpha; + GQueue *image_cache; + guint8 image_size; + guint16 image_cache_size; + + /* for waypoint text */ + PangoLayout *wplabellayout; + + gboolean has_verified_thumbnails; + + GtkMenu *wp_right_click_menu; + +}; + +/* A caached waypoint image. */ +typedef struct { + GdkPixbuf *pixbuf; + gchar *image; /* filename */ +} CachedPixbuf; + +struct DrawingParams { + VikViewport *vp; + VikTrwLayer *vtl; + gdouble xmpp, ympp; + guint16 width, height; + const VikCoord *center; + gint track_gc_iter; + gboolean one_zone, lat_lon; + gdouble ce1, ce2, cn1, cn2; +}; + +static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] ); +static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] ); + +static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp ); +static void trw_layer_free_track_gcs ( VikTrwLayer *vtl ); + +static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 ); +static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp ); +static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp ); + +static void goto_coord ( VikLayersPanel *vlp, const VikCoord *coord ); +static void trw_layer_goto_track_startpoint ( gpointer pass_along[5] ); +static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] ); +static void trw_layer_centerize ( gpointer layer_and_vlp[2] ); +static void trw_layer_export ( gpointer layer_and_vlp[2], guint file_type ); +static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] ); +static void trw_layer_new_wp ( gpointer lav[2] ); + +/* pop-up items */ +static void trw_layer_properties_item ( gpointer pass_along[5] ); +static void trw_layer_goto_waypoint ( gpointer pass_along[5] ); +static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[5] ); + +static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[4] ); +static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[4] ); +static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp ); + +static gboolean tool_new_waypoint ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ); +static gboolean tool_new_track ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ); + +static VikTrwLayer *trw_layer_copy ( VikTrwLayer *vtl, gpointer vp ); +static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp ); +static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id ); + +static gpointer trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer ); +static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, gpointer item ); +static void trw_layer_free_copied_item ( gint subtype, gpointer item ); + +static void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, const gchar *trk_name ); +static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl ); +static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy ); +static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response ); +static void trw_layer_tpwin_init ( VikTrwLayer *vtl ); +static gboolean tool_edit_trackpoint ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ); +static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ); +static gboolean tool_show_picture ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ); + +static gboolean tool_edit_waypoint ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ); +static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ); + +static gboolean uppercase_exists_in_hash ( GHashTable *hash, const gchar *str ); + +static void cached_pixbuf_free ( CachedPixbuf *cp ); +static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name ); +static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp ); + +static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y ); +static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y ); + +static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode ); + +static VikToolInterface trw_layer_tools[] = { + { "Create Waypoint", (VikToolInterfaceFunc) tool_new_waypoint, NULL }, + { "Create Track", (VikToolInterfaceFunc) tool_new_track, NULL }, + { "Edit Waypoint", (VikToolInterfaceFunc) tool_edit_waypoint, (VikToolInterfaceFunc) tool_edit_waypoint_release }, + { "Edit Trackpoint", (VikToolInterfaceFunc) tool_edit_trackpoint, (VikToolInterfaceFunc) tool_edit_trackpoint_release }, + { "Show Picture", (VikToolInterfaceFunc) tool_show_picture, NULL }, +}; + + +/****** PARAMETERS ******/ + +static gchar *params_groups[] = { "Waypoints", "Tracks", "Waypoint Images" }; +enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES }; + +static gchar *params_drawmodes[] = { "Draw by Track", "Draw by Velocity", "All Tracks Black", 0 }; +static gchar *params_wpsymbols[] = { "Filled Square", "Square", "Circle", "X", 0 }; + +static VikLayerParamScale params_scales[] = { + /* min max step digits */ + { 1, 10, 1, 0 }, /* line_thickness */ + { 0.0, 99.0, 1, 2 }, /* velocity_min */ + { 1.0, 100.0, 1.0, 2 }, /* velocity_max */ + /* 5 * step == how much to turn */ + { 16, 128, 3.2, 0 }, /* image_size */ + { 0, 255, 5, 0 }, /* image alpha */ + { 5, 500, 5, 0 }, /* image cache_size */ + { 0, 8, 1, 0 }, /* image cache_size */ + { 1, 64, 1, 0 }, /* wpsize */ +}; + +VikLayerParam trw_layer_params[] = { + { "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES }, + { "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES }, + + { "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, "Track Drawing Mode:", VIK_LAYER_WIDGET_RADIOGROUP, params_drawmodes }, + { "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, "Draw Track Lines", VIK_LAYER_WIDGET_CHECKBUTTON }, + { "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, "Draw Trackpoints", VIK_LAYER_WIDGET_CHECKBUTTON }, + { "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, "Track Thickness:", VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 0 }, + { "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, "Track BG Thickness:", VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 6 }, + { "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, "Track Background Color", VIK_LAYER_WIDGET_COLOR, 0 }, + { "velocity_min", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, "Min Track Velocity:", VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 1 }, + { "velocity_max", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, "Max Track Velocity:", VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 2 }, + + { "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, "Draw Labels", VIK_LAYER_WIDGET_CHECKBUTTON }, + { "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, "Waypoint Color:", VIK_LAYER_WIDGET_COLOR, 0 }, + { "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, "Waypoint Text:", VIK_LAYER_WIDGET_COLOR, 0 }, + { "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, "Background:", VIK_LAYER_WIDGET_COLOR, 0 }, + { "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, "Fake BG Color Translucency:", VIK_LAYER_WIDGET_CHECKBUTTON, 0 }, + { "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, "Waypoint symbol:", VIK_LAYER_WIDGET_RADIOGROUP, params_wpsymbols }, + { "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, "Waypoint size:", VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 7 }, + + { "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, "Draw Waypoint Images", VIK_LAYER_WIDGET_CHECKBUTTON }, + { "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, "Image Size (pixels):", VIK_LAYER_WIDGET_HSCALE, params_scales + 3 }, + { "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, "Image Alpha:", VIK_LAYER_WIDGET_HSCALE, params_scales + 4 }, + { "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, "Image Memory Cache Size:", VIK_LAYER_WIDGET_HSCALE, params_scales + 5 }, +}; + +enum { PARAM_TV, PARAM_WV, PARAM_DM, PARAM_DL, PARAM_DP, PARAM_LT, PARAM_BLT, PARAM_TBGC, PARAM_VMIN, PARAM_VMAX, PARAM_DLA, PARAM_WPC, PARAM_WPTC, PARAM_WPBC, PARAM_WPBA, PARAM_WPSYM, PARAM_WPSIZE, PARAM_DI, PARAM_IS, PARAM_IA, PARAM_ICS, NUM_PARAMS }; + +/****** END PARAMETERS ******/ + +VikLayerInterface vik_trw_layer_interface = { + "TrackWaypoint", + &trwlayer_pixbuf, + + trw_layer_tools, + sizeof(trw_layer_tools) / sizeof(VikToolInterface), + + trw_layer_params, + NUM_PARAMS, + params_groups, /* params_groups */ + sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */ + + (VikLayerFuncCreate) vik_trw_layer_create, + (VikLayerFuncRealize) vik_trw_layer_realize, + (VikLayerFuncPostRead) trw_layer_verify_thumbnails, + (VikLayerFuncFree) vik_trw_layer_free, + + (VikLayerFuncProperties) NULL, + (VikLayerFuncDraw) vik_trw_layer_draw, + (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode, + + (VikLayerFuncAddMenuItems) vik_trw_layer_add_menu_items, + (VikLayerFuncSublayerAddMenuItems) vik_trw_layer_sublayer_add_menu_items, + + (VikLayerFuncSublayerRenameRequest) vik_trw_layer_sublayer_rename_request, + (VikLayerFuncSublayerToggleVisible) vik_trw_layer_sublayer_toggle_visible, + + (VikLayerFuncCopy) trw_layer_copy, + + (VikLayerFuncSetParam) trw_layer_set_param, + (VikLayerFuncGetParam) trw_layer_get_param, + + (VikLayerFuncReadFileData) a_gpspoint_read_file, + (VikLayerFuncWriteFileData) a_gpspoint_write_file, + + (VikLayerFuncCopyItem) trw_layer_copy_item, + (VikLayerFuncPasteItem) trw_layer_paste_item, + (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item, +}; + +/* for copy & paste (I think?) */ +typedef struct { + gchar *name; + VikWaypoint *wp; +} NamedWaypoint; + +typedef struct { + gchar *name; + VikTrack *tr; +} NamedTrack; + +GType vik_trw_layer_get_type () +{ + static GType vtl_type = 0; + + if (!vtl_type) + { + static const GTypeInfo vtl_info = + { + sizeof (VikTrwLayerClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + NULL, /* class init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (VikTrwLayer), + 0, + NULL /* instance init */ + }; + vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 ); + } + + return vtl_type; +} + + +static gpointer trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer ) +{ + if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT && sublayer ) + { + NamedWaypoint *nw = g_malloc ( sizeof ( NamedWaypoint ) ); + nw->name = g_strdup(sublayer); + nw->wp = vik_waypoint_copy ( g_hash_table_lookup ( vtl->waypoints, sublayer ) ); + return nw; + } + if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && sublayer ) + { + NamedTrack *nt = g_malloc ( sizeof ( NamedTrack ) ); + nt->name = g_strdup(sublayer); + nt->tr = g_hash_table_lookup ( vtl->tracks, sublayer ); + vik_track_ref(nt->tr); + return nt; + } + return NULL; +} + +static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, gpointer item ) +{ + if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT && item ) + { + NamedWaypoint *nw = (NamedWaypoint *) item; + vik_trw_layer_add_waypoint ( vtl, g_strdup(nw->name), vik_waypoint_copy(nw->wp) ); + return TRUE; + } + if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && item ) + { + NamedTrack *nt = (NamedTrack *) item; + vik_trw_layer_add_track ( vtl, g_strdup(nt->name), vik_track_copy(nt->tr) ); + return TRUE; + } + return FALSE; +} + +static void trw_layer_free_copied_item ( gint subtype, gpointer item ) +{ + if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT && item ) + { + NamedWaypoint *nw = (NamedWaypoint *) item; + g_free ( nw->name ); + vik_waypoint_free ( nw->wp ); + g_free ( nw ); + } + if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && item ) + { + NamedTrack *nt = (NamedTrack *) item; + g_free ( nt->name ); + vik_track_free ( nt->tr ); + g_free ( nt ); + } +} + +static void waypoint_copy ( const gchar *name, VikWaypoint *wp, GHashTable *dest ) +{ + g_hash_table_insert ( dest, g_strdup(name), vik_waypoint_copy(wp) ); +} + +static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp ) +{ + switch ( id ) + { + case PARAM_TV: vtl->tracks_visible = data.b; break; + case PARAM_WV: vtl->waypoints_visible = data.b; break; + case PARAM_DM: vtl->drawmode = data.u; break; + case PARAM_DP: vtl->drawpoints = data.b; break; + case PARAM_DL: vtl->drawlines = data.b; break; + case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness ) + { + vtl->line_thickness = data.u; + trw_layer_new_track_gcs ( vtl, vp ); + } + break; + case PARAM_BLT: if ( data.u > 0 && data.u <= 8 && data.u != vtl->bg_line_thickness ) + { + vtl->bg_line_thickness = data.u; + trw_layer_new_track_gcs ( vtl, vp ); + } + break; + case PARAM_VMIN: vtl->velocity_min = data.d; break; + case PARAM_VMAX: vtl->velocity_max = data.d; break; + case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break; + case PARAM_DLA: vtl->drawlabels = data.b; break; + case PARAM_DI: vtl->drawimages = data.b; break; + case PARAM_IS: if ( data.u != vtl->image_size ) + { + vtl->image_size = data.u; + g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL ); + g_queue_free ( vtl->image_cache ); + vtl->image_cache = g_queue_new (); + } + break; + case PARAM_IA: vtl->image_alpha = data.u; break; + case PARAM_ICS: vtl->image_cache_size = data.u; + while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */ + cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) ); + break; + case PARAM_WPC: gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(data.c)); break; + case PARAM_WPTC: gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(data.c)); break; + case PARAM_WPBC: gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(data.c)); break; + case PARAM_WPBA: gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY ); break; + case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break; + case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break; + } + return TRUE; +} + +static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id ) +{ + VikLayerParamData rv; + switch ( id ) + { + case PARAM_TV: rv.b = vtl->tracks_visible; break; + case PARAM_WV: rv.b = vtl->waypoints_visible; break; + case PARAM_DM: rv.u = vtl->drawmode; break; + case PARAM_DP: rv.b = vtl->drawpoints; break; + case PARAM_DL: rv.b = vtl->drawlines; break; + case PARAM_LT: rv.u = vtl->line_thickness; break; + case PARAM_BLT: rv.u = vtl->bg_line_thickness; break; + case PARAM_VMIN: rv.d = vtl->velocity_min; break; + case PARAM_VMAX: rv.d = vtl->velocity_max; break; + case PARAM_DLA: rv.b = vtl->drawlabels; break; + case PARAM_DI: rv.b = vtl->drawimages; break; + case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break; + case PARAM_IS: rv.u = vtl->image_size; break; + case PARAM_IA: rv.u = vtl->image_alpha; break; + case PARAM_ICS: rv.u = vtl->image_cache_size; break; + case PARAM_WPC: vik_gc_get_fg_color(vtl->waypoint_gc, &(rv.c)); break; + case PARAM_WPTC: vik_gc_get_fg_color(vtl->waypoint_text_gc, &(rv.c)); break; + case PARAM_WPBC: vik_gc_get_fg_color(vtl->waypoint_bg_gc, &(rv.c)); break; + case PARAM_WPBA: rv.b = (vik_gc_get_function(vtl->waypoint_bg_gc)==GDK_AND); break; + case PARAM_WPSYM: rv.u = vtl->wp_symbol; break; + case PARAM_WPSIZE: rv.u = vtl->wp_size; break; + } + return rv; +} + +static void track_copy ( const gchar *name, VikTrack *tr, GHashTable *dest ) +{ + g_hash_table_insert ( dest, g_strdup ( name ), vik_track_copy(tr) ); +} + +static VikTrwLayer *trw_layer_copy ( VikTrwLayer *vtl, gpointer vp ) +{ + VikTrwLayer *rv = vik_trw_layer_new ( vtl->drawmode ); + PangoFontDescription *pfd; + rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL); + pfd = pango_font_description_from_string (WAYPOINT_FONT); + pango_layout_set_font_description (rv->wplabellayout, pfd); + /* freeing PangoFontDescription, cause it has been copied by prev. call */ + pango_font_description_free (pfd); + + rv->tracks_visible = vtl->tracks_visible; + rv->waypoints_visible = vtl->waypoints_visible; + rv->drawpoints = vtl->drawpoints; + rv->drawlines = vtl->drawlines; + rv->line_thickness = vtl->line_thickness; + rv->bg_line_thickness = vtl->bg_line_thickness; + rv->velocity_min = vtl->velocity_min; + rv->velocity_max = vtl->velocity_max; + rv->drawlabels = vtl->drawlabels; + rv->drawimages = vtl->drawimages; + rv->image_size = vtl->image_size; + rv->image_alpha = vtl->image_alpha; + rv->image_cache_size = vtl->image_cache_size; + rv->has_verified_thumbnails = TRUE; + rv->coord_mode = vtl->coord_mode; + rv->wp_symbol = vtl->wp_symbol; + rv->wp_size = vtl->wp_size; + + trw_layer_new_track_gcs ( rv, VIK_VIEWPORT(vp) ); + + rv->waypoint_gc = gdk_gc_new ( GTK_WIDGET(vp)->window ); + gdk_gc_set_line_attributes ( rv->waypoint_gc, 2, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND ); + + rv->waypoint_text_gc = gdk_gc_new ( GTK_WIDGET(vp)->window ); + rv->waypoint_bg_gc = gdk_gc_new ( GTK_WIDGET(vp)->window ); + gdk_gc_copy ( rv->waypoint_gc, vtl->waypoint_gc ); + gdk_gc_copy ( rv->waypoint_text_gc, vtl->waypoint_text_gc ); + gdk_gc_copy ( rv->waypoint_bg_gc, vtl->waypoint_bg_gc ); + + rv->waypoint_font = gdk_font_load ( "-*-helvetica-bold-r-normal-*-*-100-*-*-p-*-iso8859-1" ); + + g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_copy, rv->waypoints ); + g_hash_table_foreach ( vtl->tracks, (GHFunc) track_copy, rv->tracks ); + + return rv; +} + +VikTrwLayer *vik_trw_layer_new ( gint drawmode ) +{ + VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) ); + vik_layer_init ( VIK_LAYER(rv), VIK_LAYER_TRW ); + + rv->waypoints = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, (GDestroyNotify) vik_waypoint_free ); + rv->tracks = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, (GDestroyNotify) vik_track_free ); + rv->tracks_iters = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, g_free ); + rv->waypoints_iters = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, g_free ); + + /* TODO: constants at top */ + rv->waypoints_visible = rv->tracks_visible = TRUE; + rv->drawmode = drawmode; + rv->drawpoints = TRUE; + rv->drawlines = TRUE; + rv->wplabellayout = NULL; + rv->wp_right_click_menu = NULL; + rv->waypoint_gc = NULL; + rv->waypoint_text_gc = NULL; + rv->waypoint_bg_gc = NULL; + rv->track_gc = NULL; + rv->velocity_max = 5.0; + rv->velocity_min = 0.0; + rv->line_thickness = 1; + rv->line_thickness = 0; + rv->current_wp = NULL; + rv->current_wp_name = NULL; + rv->current_track = NULL; + rv->current_tpl = NULL; + rv->current_tp_track_name = NULL; + rv->moving_tp = FALSE; + rv->moving_wp = FALSE; + rv->waypoint_rightclick = FALSE; + rv->last_tpl = NULL; + rv->last_tp_track_name = NULL; + rv->tpwin = NULL; + rv->image_cache = g_queue_new(); + rv->image_size = 64; + rv->image_alpha = 255; + rv->image_cache_size = 300; + rv->drawimages = TRUE; + rv->drawlabels = TRUE; + return rv; +} + + +void vik_trw_layer_free ( VikTrwLayer *trwlayer ) +{ + g_hash_table_destroy(trwlayer->waypoints); + g_hash_table_destroy(trwlayer->tracks); + + /* ODC: replace with GArray */ + trw_layer_free_track_gcs ( trwlayer ); + + if ( trwlayer->wp_right_click_menu ) + gtk_object_sink ( GTK_OBJECT(trwlayer->wp_right_click_menu) ); + + if ( trwlayer->wplabellayout != NULL) + g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) ); + + if ( trwlayer->waypoint_gc != NULL ) + g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) ); + + if ( trwlayer->waypoint_text_gc != NULL ) + g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) ); + + if ( trwlayer->waypoint_bg_gc != NULL ) + g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) ); + + if ( trwlayer->waypoint_font != NULL ) + gdk_font_unref ( trwlayer->waypoint_font ); + + if ( trwlayer->tpwin != NULL ) + gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) ); + + g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL ); + g_queue_free ( trwlayer->image_cache ); +} + +static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp ) +{ + dp->vp = vp; + dp->xmpp = vik_viewport_get_xmpp ( vp ); + dp->ympp = vik_viewport_get_ympp ( vp ); + dp->width = vik_viewport_get_width ( vp ); + dp->height = vik_viewport_get_height ( vp ); + dp->center = vik_viewport_get_center ( vp ); + dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */ + dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON; + + if ( dp->one_zone ) + { + gint w2, h2; + w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp; + h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp; + /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */ + + dp->ce1 = dp->center->east_west-w2; + dp->ce2 = dp->center->east_west+w2; + dp->cn1 = dp->center->north_south-h2; + dp->cn2 = dp->center->north_south+h2; + } else if ( dp->lat_lon ) { + VikCoord upperleft, bottomright; + /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */ + /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */ + vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft ); + vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright ); + dp->ce1 = upperleft.east_west; + dp->ce2 = bottomright.east_west; + dp->cn1 = bottomright.north_south; + dp->cn2 = upperleft.north_south; + } + + dp->track_gc_iter = 0; +} + +static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 ) +{ + static gdouble rv = 0; + if ( tp1->has_timestamp && tp2->has_timestamp ) + { + rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) + / (tp1->timestamp - tp2->timestamp) ) - vtl->velocity_min; + + if ( rv < 0 ) + return VIK_TRW_LAYER_TRACK_GC_MIN; + else if ( vtl->velocity_min >= vtl->velocity_max ) + return VIK_TRW_LAYER_TRACK_GC_MAX; + + rv *= (VIK_TRW_LAYER_TRACK_GC_RATES / (vtl->velocity_max - vtl->velocity_min)); + + if ( rv >= VIK_TRW_LAYER_TRACK_GC_MAX ) + return VIK_TRW_LAYER_TRACK_GC_MAX; + return (gint) rv; + } + else + return VIK_TRW_LAYER_TRACK_GC_BLACK; +} + +void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y ) +{ + vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y ); + vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 ); + vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 ); + vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 ); +} + +static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct DrawingParams *dp, gboolean drawing_white_background ) +{ + /* TODO: this function is a mess, get rid of any redundancy */ + GList *list = track->trackpoints; + gboolean useoldvals = TRUE; + + gboolean drawpoints; + + const guint8 tp_size_reg = 2; + const guint8 tp_size_cur = 4; + guint8 tp_size; + + if ( ! track->visible ) + return; + + /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */ + if ( dp->vtl->bg_line_thickness && !drawing_white_background ) + trw_layer_draw_track ( name, track, dp, TRUE ); + + if ( drawing_white_background ) + drawpoints = FALSE; + else + drawpoints = dp->vtl->drawpoints; + + if (list) { + int x, y, oldx, oldy; + VikTrackpoint *tp = VIK_TRACKPOINT(list->data); + + tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg; + + vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y ); + + if ( (drawpoints) && dp->track_gc_iter < VIK_TRW_LAYER_TRACK_GC ) + { + GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} }; + vik_viewport_draw_polygon ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter), TRUE, trian, 3 ); + } + + oldx = x; + oldy = y; + + if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK ) + dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_MAX + 1; + + while ((list = g_list_next(list))) + { + tp = VIK_TRACKPOINT(list->data); + tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg; + + /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */ + if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */ + ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */ + tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */ + tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) ) + { + vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y ); + + if ( drawpoints && ! drawing_white_background ) + { + if ( list->next ) { + vik_viewport_draw_rectangle ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter), TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size ); + + /* stops */ + if ( VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > 60 ) + vik_viewport_draw_arc ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, 11), TRUE, x-(3*tp_size), y-(3*tp_size), 6*tp_size, 6*tp_size, 0, 360*64 ); + } + else + vik_viewport_draw_arc ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter), TRUE, x-(2*tp_size), y-(2*tp_size), 4*tp_size, 4*tp_size, 0, 360*64 ); + } + + if ((!tp->newsegment) && (dp->vtl->drawlines)) + { + VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data); + + /* UTM only: zone check */ + if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone ) + draw_utm_skip_insignia ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter), x, y); + + if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) + dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 ); + + if (!useoldvals) + vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy ); + + if ( drawing_white_background ) + vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y); + else + vik_viewport_draw_line ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter), oldx, oldy, x, y); + } + + oldx = x; + oldy = y; + useoldvals = TRUE; + } + else { + if (useoldvals && dp->vtl->drawlines && (!tp->newsegment)) + { + VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data); + if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone ) + { + vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y ); + if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) + dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 ); + + if ( drawing_white_background ) + vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y); + else + vik_viewport_draw_line ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter), oldx, oldy, x, y); + } + else + { + vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y ); + draw_utm_skip_insignia ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter), x, y ); + } + } + useoldvals = FALSE; + } + } + } + if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK ) + if ( ++(dp->track_gc_iter) >= VIK_TRW_LAYER_TRACK_GC ) + dp->track_gc_iter = 0; +} + +/* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */ +static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp ) +{ + trw_layer_draw_track ( name, track, dp, FALSE ); +} + +static void cached_pixbuf_free ( CachedPixbuf *cp ) +{ + g_object_unref ( G_OBJECT(cp->pixbuf) ); + g_free ( cp->image ); +} + +static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name ) +{ + return strcmp ( cp->image, name ); +} + +static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp ) +{ + if ( wp->visible ) + if ( (!dp->one_zone) || ( wp->coord.utm_zone == dp->center->utm_zone && + wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 && + wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) ) + { + gint x, y; + vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y ); + + /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */ + + if ( wp->image && dp->vtl->drawimages ) + { + GdkPixbuf *pixbuf = NULL; + GList *l; + + if ( dp->vtl->image_alpha == 0) + return; + + l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp ); + if ( l ) + pixbuf = ((CachedPixbuf *) l->data)->pixbuf; + else + { + gchar *image = wp->image; + GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image ); + if ( ! regularthumb ) + { + regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */ + image = "\x12\x00"; /* this shouldn't occur naturally. */ + } + if ( regularthumb ) + { + CachedPixbuf *cp = NULL; + cp = g_malloc ( sizeof ( CachedPixbuf ) ); + if ( dp->vtl->image_size == 128 ) + cp->pixbuf = regularthumb; + else + { + cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size); + g_assert ( cp->pixbuf ); + g_object_unref ( G_OBJECT(regularthumb) ); + } + cp->image = g_strdup ( image ); + + /* needed so 'click picture' tool knows how big the pic is; we don't + * store it in cp because they may have been freed already. */ + wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf ); + wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf ); + + g_queue_push_head ( dp->vtl->image_cache, cp ); + if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size ) + cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) ); + + pixbuf = cp->pixbuf; + } + else + { + pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */ + } + } + if ( pixbuf ) + { + gint w, h; + w = gdk_pixbuf_get_width ( pixbuf ); + h = gdk_pixbuf_get_height ( pixbuf ); + + if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */ + { + if ( dp->vtl->image_alpha == 255 ) + vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h ); + else + vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h ); + } + return; /* if failed to draw picture, default to drawing regular waypoint (below) */ + } + } + + /* DRAW ACTUAL DOT */ + if ( wp == dp->vtl->current_wp ) { + switch ( dp->vtl->wp_symbol ) { + case WP_SYMBOL_FILLED_SQUARE: vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_gc, TRUE, x - (dp->vtl->wp_size), y - (dp->vtl->wp_size), dp->vtl->wp_size*2, dp->vtl->wp_size*2 ); break; + case WP_SYMBOL_SQUARE: vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_gc, FALSE, x - (dp->vtl->wp_size), y - (dp->vtl->wp_size), dp->vtl->wp_size*2, dp->vtl->wp_size*2 ); break; + case WP_SYMBOL_CIRCLE: vik_viewport_draw_arc ( dp->vp, dp->vtl->waypoint_gc, TRUE, x - dp->vtl->wp_size, y - dp->vtl->wp_size, dp->vtl->wp_size, dp->vtl->wp_size, 0, 360*64 ); break; + case WP_SYMBOL_X: vik_viewport_draw_line ( dp->vp, dp->vtl->waypoint_gc, x - dp->vtl->wp_size*2, y - dp->vtl->wp_size*2, x + dp->vtl->wp_size*2, y + dp->vtl->wp_size*2 ); + vik_viewport_draw_line ( dp->vp, dp->vtl->waypoint_gc, x - dp->vtl->wp_size*2, y + dp->vtl->wp_size*2, x + dp->vtl->wp_size*2, y - dp->vtl->wp_size*2 ); + } + } + else { + switch ( dp->vtl->wp_symbol ) { + case WP_SYMBOL_FILLED_SQUARE: vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_gc, TRUE, x - dp->vtl->wp_size/2, y - dp->vtl->wp_size/2, dp->vtl->wp_size, dp->vtl->wp_size ); break; + case WP_SYMBOL_SQUARE: vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_gc, FALSE, x - dp->vtl->wp_size/2, y - dp->vtl->wp_size/2, dp->vtl->wp_size, dp->vtl->wp_size ); break; + case WP_SYMBOL_CIRCLE: vik_viewport_draw_arc ( dp->vp, dp->vtl->waypoint_gc, TRUE, x-dp->vtl->wp_size/2, y-dp->vtl->wp_size/2, dp->vtl->wp_size, dp->vtl->wp_size, 0, 360*64 ); break; + case WP_SYMBOL_X: vik_viewport_draw_line ( dp->vp, dp->vtl->waypoint_gc, x-dp->vtl->wp_size, y-dp->vtl->wp_size, x+dp->vtl->wp_size, y+dp->vtl->wp_size ); + vik_viewport_draw_line ( dp->vp, dp->vtl->waypoint_gc, x-dp->vtl->wp_size, y+dp->vtl->wp_size, x+dp->vtl->wp_size, y-dp->vtl->wp_size ); break; + } + } + + if ( dp->vtl->drawlabels ) + { + /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */ + gint width, height; + pango_layout_set_text ( dp->vtl->wplabellayout, name, -1 ); + pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height ); + vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, x + dp->vtl->wp_size - 1, y-1,width+1,height-1); + vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, x + dp->vtl->wp_size, y, dp->vtl->wplabellayout ); + } + } +} + +void vik_trw_layer_draw ( VikTrwLayer *l, gpointer data ) +{ + static struct DrawingParams dp; + g_assert ( l != NULL ); + + init_drawing_params ( &dp, VIK_VIEWPORT(data) ); + dp.vtl = l; + + if ( l->tracks_visible ) + g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp ); + + if (l->waypoints_visible) + g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp ); +} + +static void trw_layer_free_track_gcs ( VikTrwLayer *vtl ) +{ + int i; + if ( vtl->track_bg_gc ) + { + g_object_unref ( vtl->track_bg_gc ); + vtl->track_bg_gc = NULL; + } + + if ( ! vtl->track_gc ) + return; + for ( i = vtl->track_gc->len - 1; i >= 0; i-- ) + g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) ); + g_array_free ( vtl->track_gc, TRUE ); + vtl->track_gc = NULL; +} + +static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp ) +{ + GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ]; + gint width = vtl->line_thickness; + + if ( vtl->track_gc ) + trw_layer_free_track_gcs ( vtl ); + + if ( vtl->track_bg_gc ) + g_object_unref ( vtl->track_bg_gc ); + vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness ); + + vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC ); + + gc[0] = vik_viewport_new_gc ( vp, "#2d870a", width ); /* below range */ + + gc[1] = vik_viewport_new_gc ( vp, "#0a8742", width ); + gc[2] = vik_viewport_new_gc ( vp, "#0a8783", width ); + gc[3] = vik_viewport_new_gc ( vp, "#0a4d87", width ); + gc[4] = vik_viewport_new_gc ( vp, "#05469f", width ); + gc[5] = vik_viewport_new_gc ( vp, "#1b059f", width ); + gc[6] = vik_viewport_new_gc ( vp, "#2d059f", width ); + gc[7] = vik_viewport_new_gc ( vp, "#4a059f", width ); + gc[8] = vik_viewport_new_gc ( vp, "#84059f", width ); + gc[9] = vik_viewport_new_gc ( vp, "#96059f", width ); + gc[10] = vik_viewport_new_gc ( vp, "#f22ef2", width ); + + gc[11] = vik_viewport_new_gc ( vp, "#ff0000", width ); /* above range */ + + gc[12] = vik_viewport_new_gc ( vp, "#000000", width ); /* black / no speed data */ + + g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC ); +} + +VikTrwLayer *vik_trw_layer_create ( VikViewport *vp ) +{ + VikTrwLayer *rv = vik_trw_layer_new ( 0 ); + PangoFontDescription *pfd; + rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL); + pfd = pango_font_description_from_string (WAYPOINT_FONT); + pango_layout_set_font_description (rv->wplabellayout, pfd); + /* freeing PangoFontDescription, cause it has been copied by prev. call */ + pango_font_description_free (pfd); + + vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name ); + + trw_layer_new_track_gcs ( rv, vp ); + + rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 ); + rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 ); + rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 ); + gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND ); + + rv->waypoint_font = gdk_font_load ( "-*-helvetica-bold-r-normal-*-*-100-*-*-p-*-iso8859-1" ); + + rv->has_verified_thumbnails = FALSE; + rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE; + rv->wp_size = 4; + + rv->coord_mode = vik_viewport_get_coord_mode ( vp ); + + return rv; +} + +static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[4] ) +{ + GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter)); + +#ifdef VIK_CONFIG_ALPHABETIZED_TRW + vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, (gint) pass_along[4], NULL, TRUE, TRUE ); +#else + vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, (gint) pass_along[4], NULL, TRUE, TRUE ); +#endif + + *new_iter = *((GtkTreeIter *) pass_along[1]); + g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, name, new_iter ); + + if ( ! track->visible ) + vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE ); +} + +static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[4] ) +{ + GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter)); +#ifdef VIK_CONFIG_ALPHABETIZED_TRW + vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, (gint) pass_along[4], NULL, TRUE, TRUE ); +#else + vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, (gint) pass_along[4], NULL, TRUE, TRUE ); +#endif + + *new_iter = *((GtkTreeIter *) pass_along[1]); + g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, name, new_iter ); + + if ( ! wp->visible ) + vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE ); +} + + +void vik_trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter ) +{ + GtkTreeIter iter2; + gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, (gpointer) VIK_TRW_LAYER_SUBLAYER_TRACK }; + +#ifdef VIK_CONFIG_ALPHABETIZED_TRW + vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), "Tracks", vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE ); +#else + vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), "Tracks", vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE ); +#endif + if ( ! vtl->tracks_visible ) + vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), FALSE ); + + g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along ); + +#ifdef VIK_CONFIG_ALPHABETIZED_TRW + vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), "Waypoints", vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE ); +#else + vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), "Waypoints", vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE ); +#endif + + if ( ! vtl->waypoints_visible ) + vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), FALSE ); + + pass_along[0] = &(vtl->waypoints_iter); + pass_along[4] = (gpointer) VIK_TRW_LAYER_SUBLAYER_WAYPOINT; + + g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along ); + +} + +gboolean vik_trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer ) +{ + switch ( subtype ) + { + case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1); + case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1); + case VIK_TRW_LAYER_SUBLAYER_TRACK: + { + VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer ); + if (t) + return (t->visible ^= 1); + else + return TRUE; + } + case VIK_TRW_LAYER_SUBLAYER_WAYPOINT: + { + VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer ); + if (t) + return (t->visible ^= 1); + else + return TRUE; + } + } + return TRUE; +} + +GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l ) +{ + return l->tracks; +} + +GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l ) +{ + return l->waypoints; +} + +static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] ) +{ + static VikCoord fixme; + vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme ); + if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 ) + maxmin[0].lat = VIK_LATLON(&fixme)->lat; + if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 ) + maxmin[1].lat = VIK_LATLON(&fixme)->lat; + if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 ) + maxmin[0].lon = VIK_LATLON(&fixme)->lon; + if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 ) + maxmin[1].lon = VIK_LATLON(&fixme)->lon; +} + +static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] ) +{ + GList *tr = *t; + static VikCoord fixme; + + while ( tr ) + { + vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme ); + if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 ) + maxmin[0].lat = VIK_LATLON(&fixme)->lat; + if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 ) + maxmin[1].lat = VIK_LATLON(&fixme)->lat; + if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 ) + maxmin[0].lon = VIK_LATLON(&fixme)->lon; + if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 ) + maxmin[1].lon = VIK_LATLON(&fixme)->lon; + tr = tr->next; + } +} + + +gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest ) +{ + /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. like I don't have more important things to worry about... */ + struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} }; + g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin ); + g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin ); + if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0) + return FALSE; + else + { + struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 }; + vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average ); + return TRUE; + } +} + +static void trw_layer_centerize ( gpointer layer_and_vlp[2] ) +{ + VikCoord coord; + if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) ) + goto_coord ( VIK_LAYERS_PANEL(layer_and_vlp[1]), &coord ); + else + a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), "This layer has no waypoints or trackpoints." ); +} + +static void trw_layer_export ( gpointer layer_and_vlp[2], guint file_type ) +{ + GtkWidget *file_selector; + const gchar *fn; + gboolean failed = FALSE; + file_selector = gtk_file_selection_new ("Export Layer"); + + while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_OK ) + { + fn = gtk_file_selection_get_filename (GTK_FILE_SELECTION(file_selector) ); + if ( access ( fn, F_OK ) != 0 ) + { + gtk_widget_hide ( file_selector ); + failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type ); + break; + } + else + { + if ( a_dialog_overwrite ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), "The file \"%s\" exists, do you wish to overwrite it?", a_file_basename ( fn ) ) ) + { + gtk_widget_hide ( file_selector ); + failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type ); + break; + } + } + } + gtk_widget_destroy ( file_selector ); + if ( failed ) + a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), "The filename you requested could not be opened for writing." ); +} + +static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] ) +{ + trw_layer_export ( layer_and_vlp, FILE_TYPE_GPSPOINT ); +} + +static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] ) +{ + trw_layer_export ( layer_and_vlp, FILE_TYPE_GPSMAPPER ); +} + +static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] ) +{ + GHashTable *wps = vik_trw_layer_get_waypoints ( VIK_TRW_LAYER(layer_and_vlp[0]) ); + GtkWidget *dia = gtk_dialog_new_with_buttons ("Create", + VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_CANCEL, + GTK_RESPONSE_REJECT, + GTK_STOCK_OK, + GTK_RESPONSE_ACCEPT, + NULL); + + GtkWidget *label, *entry; + label = gtk_label_new("Waypoint Name:"); + entry = gtk_entry_new(); + + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0); + gtk_widget_show_all ( label ); + gtk_widget_show_all ( entry ); + + while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT ) + { + VikWaypoint *wp; + gchar *upname = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry))); + int i; + + for ( i = strlen(upname)-1; i >= 0; i-- ) + upname[i] = toupper(upname[i]); + + wp = g_hash_table_lookup ( wps, upname ); + + if (!wp) + a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), "Waypoint not found in this layer." ); + else + { + vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) ); + vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) ); + vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, upname ) ); + break; + } + + g_free ( upname ); + + } + gtk_widget_destroy ( dia ); +} + +gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord ) +{ + gchar *name; + static VikWaypoint st_wp; + st_wp.coord = *def_coord; + st_wp.altitude = VIK_DEFAULT_ALTITUDE; + + if ( a_dialog_new_waypoint ( w, &name, &st_wp, vik_trw_layer_get_waypoints ( vtl ), vtl->coord_mode ) ) + { + VikWaypoint *wp = vik_waypoint_new(); + *wp = st_wp; + vik_trw_layer_add_waypoint ( vtl, name, wp ); + return TRUE; + } + return FALSE; +} + +static void trw_layer_new_wp ( gpointer lav[2] ) +{ + VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]); + VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]); + /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason. + instead return true if you want to update. */ + if ( vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), vik_viewport_get_center(vik_layers_panel_get_viewport(vlp))) && VIK_LAYER(vtl)->visible ) + vik_layers_panel_emit_update ( vlp ); +} + +void vik_trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp ) +{ + static gpointer pass_along[2]; + GtkWidget *item; + pass_along[0] = vtl; + pass_along[1] = vlp; + + item = gtk_menu_item_new(); + gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_widget_show ( item ); + + item = gtk_menu_item_new_with_label ( "Goto Center of Layer" ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along ); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show ( item ); + + item = gtk_menu_item_new_with_label ( "Goto Waypoint" ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along ); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show ( item ); + + item = gtk_menu_item_new_with_label ( "Export Layer as GPSPoint" ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along ); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show ( item ); + + item = gtk_menu_item_new_with_label ( "Export Layer as GPSMapper" ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along ); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show ( item ); + + item = gtk_menu_item_new_with_label ( "New Waypoint" ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along ); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show ( item ); +} + +void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp ) +{ + if ( VIK_LAYER(vtl)->realized ) + { + VikWaypoint *oldwp = VIK_WAYPOINT ( g_hash_table_lookup ( vtl->waypoints, name ) ); + if ( oldwp ) + wp->visible = oldwp->visible; /* same visibility so we don't have to update viktreeview */ + else + { + GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter)); +#ifdef VIK_CONFIG_ALPHABETIZED_TRW + vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE ); +#else + vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE ); +#endif + vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, iter ); + g_hash_table_insert ( vtl->waypoints_iters, name, iter ); + wp->visible = TRUE; + } + } + else + wp->visible = TRUE; + + g_hash_table_insert ( vtl->waypoints, name, wp ); + +} + +void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t ) +{ + if ( VIK_LAYER(vtl)->realized ) + { + VikTrack *oldt = VIK_TRACK ( g_hash_table_lookup ( vtl->tracks, name ) ); + if ( oldt ) + t->visible = oldt->visible; /* same visibility so we don't have to update viktreeview */ + else + { + GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter)); +#ifdef VIK_CONFIG_ALPHABETIZED_TRW + vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE ); +#else + vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE ); +#endif + vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, iter ); + g_hash_table_insert ( vtl->tracks_iters, name, iter ); + t->visible = TRUE; + } + } + else + t->visible = TRUE; + + g_hash_table_insert ( vtl->tracks, name, t ); + +} + +static gboolean uppercase_exists_in_hash ( GHashTable *hash, const gchar *str ) +{ + gchar *upp = g_strdup ( str ); + gboolean rv; + char *tmp = upp; + while ( *tmp ) + { + *tmp = toupper(*tmp); + tmp++; + } + rv = g_hash_table_lookup ( hash, upp ) ? TRUE : FALSE; + g_free (upp); + return rv; +} + +/* to be called whenever a track has been deleted or may have been changed. */ +static void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, const gchar *trk_name ) +{ + if (vtl->current_tp_track_name && g_strcasecmp(trk_name, vtl->current_tp_track_name) == 0) + trw_layer_cancel_current_tp ( vtl, FALSE ); + else if (vtl->last_tp_track_name && g_strcasecmp(trk_name, vtl->last_tp_track_name) == 0) + trw_layer_cancel_last_tp ( vtl ); +} + +static gboolean trw_layer_delete_track ( VikTrwLayer *vtl, const gchar *trk_name ) +{ + VikTrack *t = g_hash_table_lookup ( vtl->tracks, trk_name ); + gboolean was_visible = FALSE; + if ( t ) + { + GtkTreeIter *it; + was_visible = t->visible; + if ( t == vtl->current_track ) + vtl->current_track = NULL; + + /* could be current_tp, so we have to check */ + trw_layer_cancel_tps_of_track ( vtl, trk_name ); + + g_assert ( ( it = g_hash_table_lookup ( vtl->tracks_iters, trk_name ) ) ); + vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it ); + g_hash_table_remove ( vtl->tracks_iters, trk_name ); + + /* do this last because trk_name may be pointing to actual orig key */ + g_hash_table_remove ( vtl->tracks, trk_name ); + } + return was_visible; +} + +static void trw_layer_delete_item ( gpointer pass_along[5] ) +{ + VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]); + gboolean was_visible = FALSE; + if ( (gint) pass_along[2] == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) + { + VikWaypoint *wp; + wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); + if ( wp ) + { + GtkTreeIter *it; + + if ( wp == vtl->current_wp ) { + vtl->current_wp = NULL; + vtl->current_wp_name = NULL; + vtl->moving_wp = FALSE; + } + + was_visible = wp->visible; + g_assert ( ( it = g_hash_table_lookup ( vtl->waypoints_iters, (gchar *) pass_along[3] ) ) ); + vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it ); + g_hash_table_remove ( vtl->waypoints_iters, (gchar *) pass_along[3] ); + g_hash_table_remove ( vtl->waypoints, pass_along[3] ); /* last because this frees name */ + } + } + else + { + was_visible = trw_layer_delete_track ( vtl, (gchar *) pass_along[3] ); + } + + if ( was_visible ) + vik_layer_emit_update ( VIK_LAYER(vtl) ); +} + + +static void trw_layer_properties_item ( gpointer pass_along[5] ) +{ + VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]); + if ( (gint) pass_along[2] == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) + { + VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); + if ( wp ) + { + if ( a_dialog_new_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), NULL, wp, NULL, vtl->coord_mode ) ) + + if ( VIK_LAYER(vtl)->visible ) + vik_layer_emit_update ( VIK_LAYER(vtl) ); + } + } + else + { + VikTrack *tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] ); + if ( tr ) + { + gint resp = vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl), tr ); + if ( resp == VIK_TRW_LAYER_PROPWIN_DEL_DUP ) + { + vik_track_remove_dup_points(tr); + /* above operation could have deleted current_tp or last_tp */ + trw_layer_cancel_tps_of_track ( vtl, (gchar *) pass_along[3] ); + vik_layer_emit_update ( VIK_LAYER(vtl) ); + } + if ( resp == VIK_TRW_LAYER_PROPWIN_REVERSE ) + { + vik_track_reverse(tr); + vik_layer_emit_update ( VIK_LAYER(vtl) ); + } + else if ( resp == VIK_TRW_LAYER_PROPWIN_SPLIT ) + { + /* get new tracks, add them, resolve naming conflicts (free if cancel), and delete old. old can still exist on clipboard. */ + guint ntracks; + VikTrack **tracks = vik_track_split_into_segments(tr, &ntracks); + gchar *new_tr_name; + guint i; + for ( i = 0; i < ntracks; i++ ) + { + g_assert ( tracks[i] ); + new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i+1); + /* if ( (wp_exists) && (! overwrite) ) */ + /* don't need to upper case new_tr_name because old tr name was uppercase */ + if ( g_hash_table_lookup ( vtl->tracks, new_tr_name ) && + ( ! a_dialog_overwrite ( VIK_GTK_WINDOW_FROM_LAYER(vtl), "The track \"%s\" exists, do you wish to overwrite it?", new_tr_name ) ) ) + { + gchar *new_new_tr_name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks ); + g_free ( new_tr_name ); + if (new_new_tr_name) + new_tr_name = new_new_tr_name; + else + { + new_tr_name = NULL; + vik_track_free ( tracks[i] ); + } + } + if ( new_tr_name ) + vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] ); + + } + if ( tracks ) + { + g_free ( tracks ); + trw_layer_delete_track ( vtl, (gchar *) pass_along[3] ); + vik_layer_emit_update ( VIK_LAYER(vtl) ); /* chase thru the hoops */ + } + } + } + } +} + +static void goto_coord ( VikLayersPanel *vlp, const VikCoord *coord ) +{ + vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), coord ); + vik_layers_panel_emit_update ( vlp ); +} + +static void trw_layer_goto_track_startpoint ( gpointer pass_along[5] ) +{ + GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints; + if ( trps && trps->data ) + goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(((VikTrackpoint *) trps->data)->coord)); +} + +static void trw_layer_goto_track_center ( gpointer pass_along[5] ) +{ + GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ); + if ( trps && *trps ) + { + struct LatLon average, maxmin[2] = { {0,0}, {0,0} }; + VikCoord coord; + trw_layer_find_maxmin_tracks ( NULL, trps, maxmin ); + average.lat = (maxmin[0].lat+maxmin[1].lat)/2; + average.lon = (maxmin[0].lon+maxmin[1].lon)/2; + vik_coord_load_from_latlon ( &coord, VIK_TRW_LAYER(pass_along[0])->coord_mode, &average ); + goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &coord); + } +} + +static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] ) +{ + GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints; + if ( !trps ) + return; + while ( trps->next ) + trps = trps->next; + goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(((VikTrackpoint *) trps->data)->coord)); +} + +static void trw_layer_goto_waypoint ( gpointer pass_along[5] ) +{ + VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] ); + if ( wp ) + goto_coord ( VIK_LAYERS_PANEL(pass_along[1]), &(wp->coord) ); +} + +static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[5] ) +{ + gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] ); +#ifdef WINDOWS + ShellExecute(NULL, NULL, (char *) webpage, NULL, ".\\", 0); +#else /* WINDOWS */ + GError *err = NULL; + gchar *cmd = g_strdup_printf ( "%s %s", UNIX_WEB_BROWSER, webpage ); + if ( ! g_spawn_command_line_async ( cmd, &err ) ) + { + a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), "Could not launch web browser." ); + g_error_free ( err ); + } + g_free ( cmd ); +#endif /* WINDOWS */ + g_free ( webpage ); +} + +const gchar *vik_trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter ) +{ + if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) + { + int i; + gchar *rv; + VikWaypoint *wp; + + if ( strcasecmp ( newname, sublayer ) == 0 ) + return NULL; + + if ( uppercase_exists_in_hash ( l->waypoints, newname ) ) + { + a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), "Waypoint Already Exists" ); + return NULL; + } + + wp = vik_waypoint_copy ( VIK_WAYPOINT(g_hash_table_lookup ( l->waypoints, sublayer )) ); + g_hash_table_remove ( l->waypoints, sublayer ); + + iter = g_hash_table_lookup ( l->waypoints_iters, sublayer ); + g_hash_table_steal ( l->waypoints_iters, sublayer ); + + rv = g_strdup(newname); + for ( i = strlen(rv) - 1; i >= 0; i-- ) + rv[i] = toupper(rv[i]); + + vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv ); + + g_hash_table_insert ( l->waypoints, rv, wp ); + g_hash_table_insert ( l->waypoints_iters, rv, iter ); + + /* it hasn't been updated yet so we pass new name */ +#ifdef VIK_CONFIG_ALPHABETIZED_TRW + vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv ); +#endif + + vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) ); + return rv; + } + if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) + { + int i; + gchar *rv; + VikTrack *tr; + GtkTreeIter *iter; + gchar *orig_key; + + if ( strcasecmp ( newname, sublayer ) == 0 ) + return NULL; + + if ( uppercase_exists_in_hash ( l->tracks, newname ) ) + { + a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), "Track Already Exists" ); + return NULL; + } + + g_hash_table_lookup_extended ( l->tracks, sublayer, (gpointer *)&orig_key, (gpointer *)&tr ); + g_hash_table_steal ( l->tracks, sublayer ); + + iter = g_hash_table_lookup ( l->tracks_iters, sublayer ); + g_hash_table_steal ( l->tracks_iters, sublayer ); + + rv = g_strdup(newname); + for ( i = strlen(rv) - 1; i >= 0; i-- ) + rv[i] = toupper(rv[i]); + + vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv ); + + g_hash_table_insert ( l->tracks, rv, tr ); + g_hash_table_insert ( l->tracks_iters, rv, iter ); + + /* don't forget about current_tp_track_name, update that too */ + if ( l->current_tp_track_name && g_strcasecmp(orig_key,l->current_tp_track_name) == 0 ) + { + l->current_tp_track_name = rv; + if ( l->tpwin ) + vik_trw_layer_tpwin_set_track_name ( l->tpwin, rv ); + } + else if ( l->last_tp_track_name && g_strcasecmp(orig_key,l->last_tp_track_name) == 0 ) + l->last_tp_track_name = rv; + + g_free ( orig_key ); + +#ifdef VIK_CONFIG_ALPHABETIZED_TRW + vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv ); +#endif + + vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) ); + return rv; + } + return NULL; +} + +static gboolean is_valid_geocache_name ( gchar *str ) +{ + gint len = strlen ( str ); + return len >= 3 && len <= 6 && str[0] == 'G' && str[1] == 'C' && isalnum(str[2]) && (len < 4 || isalnum(str[3])) && (len < 5 || isalnum(str[4])) && (len < 6 || isalnum(str[5])); +} + +/* vlp can be NULL if necessary - i.e. right-click from a tool -- but be careful, some functions may try to use it */ +gboolean vik_trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter ) +{ + static GtkTreeIter staticiter; + static gpointer pass_along[5]; + GtkWidget *item; + gboolean rv = FALSE; + + pass_along[0] = l; + pass_along[1] = vlp; + pass_along[2] = (gpointer) subtype; + pass_along[3] = sublayer; + staticiter = *iter; /* will exist after function has ended */ + pass_along[4] = &staticiter; + + if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) + { + rv = TRUE; + + item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_widget_show ( item ); + + item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_widget_show ( item ); + + if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) + { + /* could be a right-click using the tool */ + if ( vlp != NULL ) { + item = gtk_menu_item_new_with_label ( "Goto" ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_widget_show ( item ); + } + + if ( is_valid_geocache_name ( (gchar *) sublayer ) ) + { + item = gtk_menu_item_new_with_label ( "Visit Geocache Webpage" ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_widget_show ( item ); + } + + } + } + + if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) + { + item = gtk_menu_item_new (); + gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_widget_show ( item ); + + item = gtk_menu_item_new_with_label ( "Goto Startpoint" ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_widget_show ( item ); + + item = gtk_menu_item_new_with_label ( "Goto \"Center\"" ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_widget_show ( item ); + + item = gtk_menu_item_new_with_label ( "Goto Endpoint" ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along ); + gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_widget_show ( item ); + } + + if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) ) + { + item = gtk_menu_item_new (); + gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item ); + gtk_widget_show ( item ); + + item = gtk_menu_item_new_with_label ( "New Waypoint" ); + g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along ); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show ( item ); + } + + return rv; +} + +/* Params are: vvp, event, last match found or NULL */ +static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[2] ) +{ + if ( wp->image && wp->visible ) + { + gint x, y, slackx, slacky; + GdkEventButton *event = (GdkEventButton *) params[1]; + + vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y ); + slackx = wp->image_width / 2; + slacky = wp->image_height / 2; + if ( x <= event->x + slackx && x >= event->x - slackx + && y <= event->y + slacky && y >= event->y - slacky ) + { + params[2] = wp->image; /* we've found a match. however continue searching + * since we want to find the last match -- that + * is, the match that was drawn last. */ + } + } +} + +static gboolean tool_show_picture ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ) +{ + gpointer params[3] = { vvp, event, NULL }; + g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params ); + if ( params[2] ) + { + /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */ +#ifdef WINDOWS + ShellExecute(NULL, NULL, (char *) params[2], NULL, ".\\", 0); +#else /* WINDOWS */ + GError *err = NULL; + gchar *quoted_file = g_shell_quote ( (gchar *) params[2] ); + gchar *cmd = g_strdup_printf ( "eog %s", quoted_file ); + g_free ( quoted_file ); + if ( ! g_spawn_command_line_async ( cmd, &err ) ) + { + a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), "Could not launch eog to open file." ); + g_error_free ( err ); + } + g_free ( cmd ); +#endif /* WINDOWS */ + return TRUE; /* found a match */ + } + else + return FALSE; /* go through other layers, searching for a match */ +} + +static gboolean tool_new_waypoint ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ) +{ + VikCoord coord; + vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord ); + if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) + vik_layer_emit_update ( VIK_LAYER(vtl) ); + return TRUE; +} + +typedef struct { + gint x, y; + gint closest_x, closest_y; + gchar *closest_track_name; + VikTrackpoint *closest_tp; + VikViewport *vvp; + GList *closest_tpl; +} TPSearchParams; + +static void track_search_closest_tp ( gchar *name, VikTrack *t, TPSearchParams *params ) +{ + GList *tpl = t->trackpoints; + VikTrackpoint *tp; + + if ( !t->visible ) + return; + + while (tpl) + { + gint x, y; + tp = VIK_TRACKPOINT(tpl->data); + + vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y ); + + if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX && + ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */ + abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y))) + { + params->closest_track_name = name; + params->closest_tp = tp; + params->closest_tpl = tpl; + params->closest_x = x; + params->closest_y = y; + } + tpl = tpl->next; + } +} + +/* to be called when last_tpl no long exists. */ +static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl ) +{ + if ( vtl->tpwin ) /* can't join with a non-existant TP. */ + vik_trw_layer_tpwin_disable_join ( vtl->tpwin ); + vtl->last_tpl = NULL; + vtl->last_tp_track_name = NULL; +} + +static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy ) +{ + if ( vtl->tpwin ) + { + if ( destroy) + { + gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) ); + vtl->tpwin = NULL; + } + else + vik_trw_layer_tpwin_set_empty ( vtl->tpwin ); + } + if ( vtl->current_tpl ) + { + vtl->current_tpl = NULL; + vtl->current_tp_track_name = NULL; + vik_layer_emit_update(VIK_LAYER(vtl)); + } +} + +static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response ) +{ + g_assert ( vtl->tpwin != NULL ); + if ( response == VIK_TRW_LAYER_TPWIN_CLOSE ) + trw_layer_cancel_current_tp ( vtl, TRUE ); + else if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev ) + { + gchar *name; + if ( ( name = a_dialog_new_track ( GTK_WINDOW(vtl->tpwin), vtl->tracks ) ) ) + { + VikTrack *tr = vik_track_new (); + GList *newglist = g_list_alloc (); + newglist->prev = NULL; + newglist->next = vtl->current_tpl->next; + newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data)); + tr->trackpoints = newglist; + + vtl->current_tpl->next->prev = newglist; /* end old track here */ + vtl->current_tpl->next = NULL; + + vtl->current_tpl = newglist; /* change tp to first of new track. */ + vtl->current_tp_track_name = name; + + vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name ); + + vik_trw_layer_add_track ( vtl, name, tr ); + vik_layer_emit_update(VIK_LAYER(vtl)); + } + } + else if ( response == VIK_TRW_LAYER_TPWIN_DELETE ) + { + VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name ); + GList *new_tpl; + g_assert(tr != NULL); + + /* can't join with a non-existent trackpoint */ + vtl->last_tpl = NULL; + vtl->last_tp_track_name = NULL; + + if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) + { + if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next ) + VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */ + + tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl ); /* this nulls current_tpl->prev and next */ + + /* at this point the old trackpoint exists, but the list links are correct (new), so it is safe to do this. */ + vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track_name ); + + trw_layer_cancel_last_tp ( vtl ); + + g_free ( vtl->current_tpl->data ); /* TODO: vik_trackpoint_free() */ + g_list_free_1 ( vtl->current_tpl ); + vtl->current_tpl = new_tpl; + vik_layer_emit_update(VIK_LAYER(vtl)); + } + else + { + tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl ); + g_free ( vtl->current_tpl->data ); /* TODO longone: vik_trackpoint_new() and vik_trackpoint_free() */ + g_list_free_1 ( vtl->current_tpl ); + trw_layer_cancel_current_tp ( vtl, FALSE ); + } + } + else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next ) + { + vtl->last_tpl = vtl->current_tpl; + vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track_name ); + vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */ + } + else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev ) + { + vtl->last_tpl = vtl->current_tpl; + vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track_name ); + vik_layer_emit_update(VIK_LAYER(vtl)); + } + else if ( response == VIK_TRW_LAYER_TPWIN_JOIN ) + { + VikTrack *tr1 = g_hash_table_lookup ( vtl->tracks, vtl->last_tp_track_name ); + VikTrack *tr2 = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name ); + + VikTrack *tr_first = tr1, *tr_last = tr2; + + gchar *tmp; + + if ( (!vtl->last_tpl->next) && (!vtl->current_tpl->next) ) /* both endpoints */ + vik_track_reverse ( tr2 ); /* reverse the second, that way second track clicked will be later. */ + else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->prev) ) + vik_track_reverse ( tr1 ); + else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->next) ) /* clicked startpoint, then endpoint -- concat end to start */ + { + tr_first = tr2; + tr_last = tr1; + } + /* default -- clicked endpoint then startpoint -- connect endpoint to startpoint */ + + if ( tr_last->trackpoints ) /* deleting this part here joins them into 1 segmented track. useful but there's no place in the UI for this feature. segments should be deprecated anyway. */ + VIK_TRACKPOINT(tr_last->trackpoints->data)->newsegment = FALSE; + tr1->trackpoints = g_list_concat ( tr_first->trackpoints, tr_last->trackpoints ); + tr2->trackpoints = NULL; + + tmp = vtl->current_tp_track_name; + + vtl->current_tp_track_name = vtl->last_tp_track_name; /* current_tp stays the same (believe it or not!) */ + vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name ); + + /* if we did this before, trw_layer_delete_track would have canceled the current tp because + * it was the current track. canceling the current tp would have set vtl->current_tpl to NULL */ + trw_layer_delete_track ( vtl, tmp ); + + trw_layer_cancel_last_tp ( vtl ); /* same TP, can't join. */ + vik_layer_emit_update(VIK_LAYER(vtl)); + } + else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED ) + vik_layer_emit_update (VIK_LAYER(vtl)); +} + +static void trw_layer_tpwin_init ( VikTrwLayer *vtl ) +{ + if ( ! vtl->tpwin ) + { + vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) ); + g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl ); + /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */ + g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl ); + gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) ); + } + if ( vtl->current_tpl ) + vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name ); + /* set layer name and TP data */ +} + +static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y ) +{ + TPSearchParams params; + params.x = x; + params.y = y; + params.vvp = vvp; + params.closest_track_name = NULL; + params.closest_tp = NULL; + g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms); + return params.closest_tp; +} + +static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ) +{ + if ( vtl->moving_tp ) + { + /* vtl->moving_tp_x, vtl->moving_tp_y, etc. */ + VikCoord new_coord; + vtl->moving_tp = FALSE; + vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord ); + + /* snap to TP */ + if ( event->state & GDK_CONTROL_MASK ) + { + VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y ); + if ( tp && tp != vtl->current_tpl->data ) + new_coord = tp->coord; + } + + VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord; + + /* diff dist is diff from orig */ + vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name ); + /* can't join with itself! */ + trw_layer_cancel_last_tp ( vtl ); + + vik_layer_emit_update ( VIK_LAYER(vtl) ); + return TRUE; + } + return FALSE; +} + +static gboolean tool_edit_trackpoint ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ) +{ + TPSearchParams params; + /* OUTDATED DOCUMENTATION: + find 5 pixel range on each side. then put these UTM, and a pointer + to the winning track name (and maybe the winning track itself), and a + pointer to the winning trackpoint, inside an array or struct. pass + this along, do a foreach on the tracks which will do a foreach on the + trackpoints. */ + params.vvp = vvp; + params.x = event->x; + params.y = event->y; + params.closest_track_name = NULL; + /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */ + params.closest_tp = NULL; + + if ( vtl->current_tpl ) + { + /* first check if it is within range of prev. tp. and if current_tp track is shown. (if it is, we are moving that trackpoint.) */ + VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data); + VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_track_name)); + gint x, y; + g_assert ( current_tr ); + + vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y ); + + if ( current_tr->visible && + abs(x - event->x) < TRACKPOINT_SIZE_APPROX && + abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) + { + vtl->moving_tp = TRUE; + return TRUE; + } + + vtl->last_tpl = vtl->current_tpl; + vtl->last_tp_track_name = vtl->current_tp_track_name; + } + + g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, ¶ms); + + if ( params.closest_tp ) + { + vtl->current_tpl = params.closest_tpl; + vtl->current_tp_track_name = params.closest_track_name; + vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, vtl->current_tp_track_name ) ); + trw_layer_tpwin_init ( vtl ); + vik_layer_emit_update ( VIK_LAYER(vtl) ); + return TRUE; + } + + + /* these aren't the droids you're looking for */ + return FALSE; +} + +typedef struct { + gint x, y; + gint closest_x, closest_y; + gchar *closest_wp_name; + VikWaypoint *closest_wp; + VikViewport *vvp; +} WPSearchParams; + +static void waypoint_search_closest_tp ( gchar *name, VikWaypoint *wp, WPSearchParams *params ) +{ + gint x, y; + if ( !wp->visible ) + return; + + vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y ); + + if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX && + ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */ + abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y))) + { + params->closest_wp_name = name; + params->closest_wp = wp; + params->closest_x = x; + params->closest_y = y; + } +} + +static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ) +{ + if ( vtl->moving_wp ) + { + VikCoord new_coord; + vtl->moving_wp = FALSE; + vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord ); + + /* snap to TP */ + if ( event->state & GDK_CONTROL_MASK ) + { + VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y ); + if ( tp ) + new_coord = tp->coord; + } + + /* snap to WP */ + if ( event->state & GDK_SHIFT_MASK ) + { + VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y ); + if ( wp && wp != vtl->current_wp ) + new_coord = wp->coord; + } + + vtl->current_wp->coord = new_coord; + vik_layer_emit_update ( VIK_LAYER(vtl) ); + return TRUE; + } + /* PUT IN RIGHT PLACE!!! */ + if ( vtl->waypoint_rightclick ) + { + if ( vtl->wp_right_click_menu ) + gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) ); + vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () ); + vik_trw_layer_sublayer_add_menu_items ( vtl, vtl->wp_right_click_menu, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, vtl->current_wp_name, g_hash_table_lookup ( vtl->waypoints_iters, vtl->current_wp_name ) ); + gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() ); + vtl->waypoint_rightclick = FALSE; + } + return FALSE; +} + +static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y ) +{ + WPSearchParams params; + params.x = x; + params.y = y; + params.vvp = vvp; + params.closest_wp = NULL; + params.closest_wp_name = NULL; + g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms); + return params.closest_wp; +} + +static gboolean tool_edit_waypoint ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ) +{ + WPSearchParams params; + + if ( vtl->current_wp && vtl->current_wp->visible ) + { + /* first check if current WP is within area (other may be 'closer', but we want to move the current) */ + gint x, y; + vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y ); + + if ( abs(x - event->x) < WAYPOINT_SIZE_APPROX && + abs(y - event->y) < WAYPOINT_SIZE_APPROX ) + { + if ( event->button == 3 ) + vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */ + else + vtl->moving_wp = TRUE; + return TRUE; + } + } + + params.vvp = vvp; + params.x = event->x; + params.y = event->y; + params.closest_wp_name = NULL; + /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */ + params.closest_wp = NULL; + g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, ¶ms); + if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL ) + { + vtl->moving_wp = TRUE; + } + else if ( params.closest_wp ) + { + if ( event->button == 3 ) + vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */ + else + vtl->waypoint_rightclick = FALSE; + + vtl->current_wp = params.closest_wp; + vtl->current_wp_name = params.closest_wp_name; + vtl->moving_wp = FALSE; + + if ( params.closest_wp ) + vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, vtl->current_wp_name ) ); + + /* could make it so don't update if old WP is off screen and new is null but oh well */ + vik_layer_emit_update ( VIK_LAYER(vtl) ); + return TRUE; + } + + vtl->current_wp = NULL; + vtl->current_wp_name = NULL; + vtl->moving_wp = FALSE; + vtl->waypoint_rightclick = FALSE; + return FALSE; +} + +static gboolean tool_new_track ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ) +{ + VikTrackpoint *tp; + + if ( event->button == 3 && vtl->current_track ) + { + /* undo */ + if ( vtl->current_track->trackpoints ) + { + GList *last = g_list_last(vtl->current_track->trackpoints); + g_free ( last->data ); + vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last ); + } + vik_layer_emit_update ( VIK_LAYER(vtl) ); + return TRUE; + } + + if ( event->type == GDK_2BUTTON_PRESS ) + { + /* subtract last (duplicate from double click) tp then end */ + if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 ) + { + GList *last = g_list_last(vtl->current_track->trackpoints); + g_free ( last->data ); + vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last ); + /* undo last, then end */ + vtl->current_track = NULL; + } + return TRUE; + } + + if ( ! vtl->current_track ) + { + gchar *name; + if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks ) ) ) + { + vtl->current_track = vik_track_new(); + vtl->current_track->visible = TRUE; + vik_trw_layer_add_track ( vtl, name, vtl->current_track ); + } + else + return TRUE; + } + tp = vik_trackpoint_new(); + vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) ); + + /* snap to other TP */ + if ( event->state & GDK_CONTROL_MASK ) + { + VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y ); + if ( other_tp ) + tp->coord = other_tp->coord; + } + + tp->newsegment = FALSE; + tp->has_timestamp = FALSE; + tp->timestamp = 0; + tp->altitude = VIK_DEFAULT_ALTITUDE; + vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp ); + + vtl->ct_x1 = vtl->ct_x2; + vtl->ct_y1 = vtl->ct_y2; + vtl->ct_x2 = event->x; + vtl->ct_y2 = event->y; + + vik_layer_emit_update ( VIK_LAYER(vtl) ); + return TRUE; +} + +static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics ) +{ + if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) ) + *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) ); +} + +static void create_thumbnails_thread ( GSList *pics, gpointer threaddata ) +{ + guint total = g_slist_length(pics), done = 0; + while ( pics ) + { + a_thumbnails_create ( (gchar *) pics->data ); + a_background_thread_progress ( threaddata, ((gdouble) ++done) / total ); + pics = pics->next; + } +} + +static void free_pics_slist ( GSList *pics ) +{ + while ( pics ) + { + g_free ( pics->data ); + pics = g_slist_delete_link ( pics, pics ); + } +} + +static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp ) +{ + if ( ! vtl->has_verified_thumbnails ) + { + GSList *pics = NULL; + g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics ); + if ( pics ) + { + gint len = g_slist_length ( pics ); + gchar *tmp = g_strdup_printf ( "Creating %d Image Thumbnails...", len ); + a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl), tmp, (vik_thr_func) create_thumbnails_thread, pics, (vik_thr_free_func) free_pics_slist, NULL, len ); + g_free ( tmp ); + } + } +} + +VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl ) +{ + return vtl->coord_mode; +} + + + +static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode ) +{ + vik_coord_convert ( &(wp->coord), *dest_mode ); +} + +static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode ) +{ + vik_track_convert ( tr, *dest_mode ); +} + +static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode ) +{ + if ( vtl->coord_mode != dest_mode ) + { + vtl->coord_mode = dest_mode; + g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode ); + g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode ); + } +} diff --git a/src/viktrwlayer.h b/src/viktrwlayer.h new file mode 100644 index 00000000..c95f603f --- /dev/null +++ b/src/viktrwlayer.h @@ -0,0 +1,69 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _VIKING_TRWLAYER_H +#define _VIKING_TRWLAYER_H + +#define VIK_TRW_LAYER_TYPE (vik_trw_layer_get_type ()) +#define VIK_TRW_LAYER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), VIK_TRW_LAYER_TYPE, VikTrwLayer)) +#define VIK_TRW_LAYER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), VIK_TRW_LAYER_TYPE, VikTrwLayerClass)) +#define IS_VIK_TRW_LAYER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VIK_TRW_LAYER_TYPE)) +#define IS_VIK_TRW_LAYER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), VIK_TRW_LAYER_TYPE)) + +typedef struct _VikTrwLayerClass VikTrwLayerClass; +struct _VikTrwLayerClass +{ + VikLayerClass object_class; +}; + + +GType vik_trw_layer_get_type (); + +typedef struct _VikTrwLayer VikTrwLayer; + +/* TODO 0.0.8: _none_ of this should be here... interfaces, remember... */ + +VikTrwLayer *vik_trw_layer_new ( gint drawmode ); +void vik_trw_layer_draw ( VikTrwLayer *l, gpointer data ); +void vik_trw_layer_free ( VikTrwLayer *trwlayer ); + +VikTrwLayer *vik_trw_layer_create ( VikViewport *vp ); +gboolean vik_trw_layer_properties ( VikTrwLayer *vtl, gpointer vp ); + +void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp ); +void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t ); + +const gchar *vik_trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter ); + +gboolean vik_trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer ); +void vik_trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter ); + +gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest ); +GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l ); +GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l ); +void vik_trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp ); +gboolean vik_trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter ); +gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord ); + +VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl ); + + +#endif diff --git a/src/viktrwlayer_pixmap.h b/src/viktrwlayer_pixmap.h new file mode 100644 index 00000000..d578336a --- /dev/null +++ b/src/viktrwlayer_pixmap.h @@ -0,0 +1,57 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* GdkPixbuf RGBA C-Source image dump 1-byte-run-length-encoded */ + +static const GdkPixdata trwlayer_pixbuf = { + 0x47646b50, /* Pixbuf magic: 'GdkP' */ + 24 + 514, /* header length + pixel_data length */ + 0x2010002, /* pixdata_type */ + 64, /* rowstride */ + 16, /* width */ + 16, /* height */ + /* pixel_data: */ + "\3\\\\\\\377\0\0\0\377\202\202\202\377\205\377\377\377\377\2\365\324" + "\324\377\363\312\312\377\206\377\377\377\377\4\366\366\366\377GGG\377" + "\15\15\15\377\357\357\357\377\204\377\377\377\377\3\342yy\377\314\23" + "\23\377\375\370\370\377\206\377\377\377\377\4\374\374\374\377TTT\377" + "^^^\377\375\375\375\377\203\377\377\377\377\2\373\355\355\377\364\317" + "\317\377\210\377\377\377\377\4\361\361\361\377%%%\377OOO\377\354\354" + "\354\377\215\377\377\377\377\4\326\326\326\377\32\32\32\377666\377\365" + "\365\365\377\215\377\377\377\377\3\332\332\332\377\35\35\35\377TTT\377" + "\202\377\377\377\377\2\342||\377\346\215\215\377\203\377\377\377\377" + "\2\376\376\376\377\177\177\177\377\205\377\377\377\377\6\303\303\303" + "\377\6\6\6\377\315\315\315\377\377\377\377\377\356\263\263\377\352\237" + "\237\377\202\377\377\377\377\3\307\307\307\377888\377\36\36\36\377\206" + "\377\377\377\377\3RRR\377333\377\366\366\366\377\202\377\377\377\377" + "\5\356\356\356\377kkk\377\2\2\2\377RRR\377\355\355\355\377\206\377\377" + "\377\377\10\307\307\307\377\3\3\3\377;;;\377\341\341\341\377|||\377\20" + "\20\20\377\12\12\12\377\232\232\232\377\211\377\377\377\377\6\210\210" + "\210\377\0\0\0\377\1\1\1\377\0\0\0\377///\377\325\325\325\377\211\377" + "\377\377\377\6\261\261\261\377***\377\0\0\0\377\1\1\1\377nnn\377\370" + "\370\370\377\210\377\377\377\377\6\375\375\375\377qqq\377\1\1\1\377\0" + "\0\0\377\25\25\25\377\263\263\263\377\212\377\377\377\377\5uuu\377\3" + "\3\3\377\0\0\0\377EEE\377\347\347\347\377\212\377\377\377\377\5\226\226" + "\226\377\3\3\3\377\6\6\6\377\211\211\211\377\375\375\375\377\212\377" + "\377\377\377\4\257\257\257\377\4\4\4\377&&&\377\313\313\313\377\213\377" + "\377\377\377\4\365\365\365\377\14\14\14\377]]]\377\362\362\362\377\213" + "\377\377\377\377", +}; diff --git a/src/viktrwlayer_propwin.c b/src/viktrwlayer_propwin.c new file mode 100644 index 00000000..d5599553 --- /dev/null +++ b/src/viktrwlayer_propwin.c @@ -0,0 +1,172 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include "coords.h" +#include "vikcoord.h" +#include "viktrack.h" +#include "viktrwlayer_propwin.h" +#include "vikwaypoint.h" +#include "dialog.h" +#include "globals.h" + +#define PROFILE_WIDTH 600 +#define PROFILE_HEIGHT 300 + +static void minmax_alt(const gdouble *altitudes, gdouble *min, gdouble *max) +{ + *max = -1000; + *min = 20000; + guint i; + for ( i=0; i < PROFILE_WIDTH; i++ ) { + if ( altitudes[i] != VIK_DEFAULT_ALTITUDE ) { + if ( altitudes[i] > *max ) + *max = altitudes[i]; + if ( altitudes[i] < *min ) + *min = altitudes[i]; + } + } + +} + +GtkWidget *vik_trw_layer_create_profile ( GtkWidget *window, VikTrack *tr, gdouble *min_alt, gdouble *max_alt ) +{ + GdkPixmap *pix = gdk_pixmap_new( window->window, PROFILE_WIDTH, PROFILE_HEIGHT, -1 ); + GtkWidget *image = gtk_image_new_from_pixmap ( pix, NULL ); + gdouble *altitudes = vik_track_make_elevation_map ( tr, PROFILE_WIDTH ); + + guint i; + + GdkGC *no_alt_info = gdk_gc_new ( window->window ); + GdkColor color; + + gdk_color_parse ( "red", &color ); + gdk_gc_set_rgb_fg_color ( no_alt_info, &color); + + minmax_alt(altitudes, min_alt, max_alt); + + for ( i = 0; i < PROFILE_WIDTH; i++ ) + if ( altitudes[i] == VIK_DEFAULT_ALTITUDE ) + gdk_draw_line ( GDK_DRAWABLE(pix), no_alt_info, i, 0, i, PROFILE_HEIGHT ); + else + gdk_draw_line ( GDK_DRAWABLE(pix), window->style->white_gc, i, 0, i, PROFILE_HEIGHT-PROFILE_HEIGHT*(altitudes[i]-*min_alt)/(*max_alt-*min_alt) ); + + g_object_unref ( G_OBJECT(pix) ); + g_free ( altitudes ); + g_object_unref ( G_OBJECT(no_alt_info) ); + return image; +} + +gint vik_trw_layer_propwin_run ( GtkWindow *parent, VikTrack *tr ) +{ + GtkWidget *dialog = gtk_dialog_new_with_buttons ("Track Properties", + parent, + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_CANCEL, + GTK_RESPONSE_REJECT, + "Split Segments", + VIK_TRW_LAYER_PROPWIN_SPLIT, + "Reverse", + VIK_TRW_LAYER_PROPWIN_REVERSE, + "Delete Dupl.", + VIK_TRW_LAYER_PROPWIN_DEL_DUP, + GTK_STOCK_OK, + GTK_RESPONSE_ACCEPT, + NULL); + GtkWidget *left_vbox, *right_vbox, *hbox; + GtkWidget *e_cmt; + GtkWidget *l_len, *l_tps, *l_segs, *l_dups, *l_maxs, *l_avgs, *l_avgd, *l_elev, *l_galo; + gdouble tr_len; + guint32 tp_count, seg_count; + gint resp; + + gdouble min_alt, max_alt; + GtkWidget *profile = vik_trw_layer_create_profile(GTK_WIDGET(parent),tr,&min_alt,&max_alt); + + static gchar *label_texts[] = { "Comment:", "Track Length:", "Trackpoints:", "Segments:", "Duplicate Points", "Max Speed", "Avg. Speed", "Avg. Dist. Between TPs", "Elevation Range", "Total Elevation Gain/Loss:" }; + static gchar tmp_buf[20]; + + left_vbox = a_dialog_create_label_vbox ( label_texts, sizeof(label_texts) / sizeof(label_texts[0]) ); + right_vbox = gtk_vbox_new ( TRUE, 3 ); + + e_cmt = gtk_entry_new (); + if ( tr->comment ) + gtk_entry_set_text ( GTK_ENTRY(e_cmt), tr->comment ); + g_signal_connect_swapped ( e_cmt, "activate", G_CALLBACK(a_dialog_response_accept), GTK_DIALOG(dialog) ); + + tr_len = vik_track_get_length(tr); + g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f m", tr_len ); + l_len = gtk_label_new ( tmp_buf ); + + tp_count = vik_track_get_tp_count(tr); + g_snprintf(tmp_buf, sizeof(tmp_buf), "%u", tp_count ); + l_tps = gtk_label_new ( tmp_buf ); + + seg_count = vik_track_get_segment_count(tr) ; + g_snprintf(tmp_buf, sizeof(tmp_buf), "%u", seg_count ); + l_segs = gtk_label_new ( tmp_buf ); + + g_snprintf(tmp_buf, sizeof(tmp_buf), "%lu", vik_track_get_dup_point_count(tr) ); + l_dups = gtk_label_new ( tmp_buf ); + + g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f m/s", vik_track_get_max_speed(tr) ); + l_maxs = gtk_label_new ( tmp_buf ); + + g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f m/s", vik_track_get_average_speed(tr) ); + l_avgs = gtk_label_new ( tmp_buf ); + + g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f m", (tp_count - seg_count) == 0 ? 0 : tr_len / ( tp_count - seg_count ) ); + l_avgd = gtk_label_new ( tmp_buf ); + + g_snprintf(tmp_buf, sizeof(tmp_buf), "%.0f m - %.0f m", min_alt, max_alt ); + l_elev = gtk_label_new ( tmp_buf ); + + vik_track_get_total_elevation_gain(tr, &max_alt, &min_alt ); + g_snprintf(tmp_buf, sizeof(tmp_buf), "%.0f m / %.0f m", max_alt, min_alt ); + l_galo = gtk_label_new ( tmp_buf ); + + gtk_box_pack_start (GTK_BOX(right_vbox), e_cmt, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(right_vbox), l_len, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(right_vbox), l_tps, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(right_vbox), l_segs, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(right_vbox), l_dups, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(right_vbox), l_maxs, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(right_vbox), l_avgs, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(right_vbox), l_avgd, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(right_vbox), l_elev, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(right_vbox), l_galo, FALSE, FALSE, 0); + + hbox = gtk_hbox_new ( TRUE, 0 ); + gtk_box_pack_start (GTK_BOX(hbox), left_vbox, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(hbox), right_vbox, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), profile, FALSE, FALSE, 0); + + + gtk_widget_show_all ( dialog ); + resp = gtk_dialog_run (GTK_DIALOG (dialog)); + if ( resp == GTK_RESPONSE_ACCEPT ) + vik_track_set_comment ( tr, gtk_entry_get_text ( GTK_ENTRY(e_cmt) ) ); + + gtk_widget_destroy ( dialog ); + return resp; +} diff --git a/src/viktrwlayer_propwin.h b/src/viktrwlayer_propwin.h new file mode 100644 index 00000000..078f40e4 --- /dev/null +++ b/src/viktrwlayer_propwin.h @@ -0,0 +1,32 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef _VIKING_TRWLAYER_PROPWIN_H +#define _VIKING_TRWLAYER_PROPWIN_H + +/* should this file exist? */ + +#define VIK_TRW_LAYER_PROPWIN_SPLIT 1 +#define VIK_TRW_LAYER_PROPWIN_REVERSE 2 +#define VIK_TRW_LAYER_PROPWIN_DEL_DUP 3 + +gint vik_trw_layer_propwin_run (); + +#endif diff --git a/src/viktrwlayer_tpwin.c b/src/viktrwlayer_tpwin.c new file mode 100644 index 00000000..40cf095a --- /dev/null +++ b/src/viktrwlayer_tpwin.c @@ -0,0 +1,275 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include + +#include "coords.h" +#include "vikcoord.h" +#include "viktrack.h" +#include "viktrwlayer_tpwin.h" +#include "vikwaypoint.h" +#include "dialog.h" + +#define SET_BUTTON_SENSITIVE(tpwin,num,sens) gtk_widget_set_sensitive ( GTK_WIDGET(g_list_nth_data((tpwin->buttons),(num))), (sens)); + +struct _VikTrwLayerTpwin { + GtkDialog parent; + GtkSpinButton *lat, *lon, *alt; + GtkLabel *track_name, *ts, *localtime, *diff_dist, *diff_time, *diff_speed; + GList *buttons; + VikTrackpoint *cur_tp; + gboolean cur_tp_is_endpoint; /* for join */ + + gboolean sync_to_tp_block; +}; + +GType vik_trw_layer_tpwin_get_type (void) +{ + static GType tpwin_type = 0; + + if (!tpwin_type) + { + static const GTypeInfo tpwin_info = + { + sizeof (VikTrwLayerTpwinClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + NULL, /* class init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (VikTrwLayerTpwin), + 0, + NULL /* instance init */ + }; + tpwin_type = g_type_register_static ( GTK_TYPE_DIALOG, "VikTrwLayerTpwin", &tpwin_info, 0 ); + } + + return tpwin_type; +} + +static void tpwin_sync_ll_to_tp ( VikTrwLayerTpwin *tpwin ) +{ + if ( tpwin->cur_tp && (!tpwin->sync_to_tp_block) ) + { + struct LatLon ll; + VikCoord coord; + ll.lat = gtk_spin_button_get_value ( tpwin->lat ); + ll.lon = gtk_spin_button_get_value ( tpwin->lon ); + vik_coord_load_from_latlon ( &coord, tpwin->cur_tp->coord.mode, &ll ); + + /* don't redraw unless we really have to */ + if ( vik_coord_diff(&(tpwin->cur_tp->coord), &coord) > 0.05 ) /* may not be exact due to rounding */ + { + tpwin->cur_tp->coord = coord; + gtk_dialog_response ( GTK_DIALOG(tpwin), VIK_TRW_LAYER_TPWIN_DATA_CHANGED ); + } + } +} + +static void tpwin_sync_alt_to_tp ( VikTrwLayerTpwin *tpwin ) +{ + if ( tpwin->cur_tp && (!tpwin->sync_to_tp_block) ) + tpwin->cur_tp->altitude = gtk_spin_button_get_value ( tpwin->alt ); +} + +VikTrwLayerTpwin *vik_trw_layer_tpwin_new ( GtkWindow *parent ) +{ + static gchar *left_label_texts[] = { "Part of Track:", "Latitude:", + "Longitude:", "Altitude:", "Timestamp:", "Time:" }; + static gchar *right_label_texts[] = { "Distance Difference:", + "Time Difference:", "\"Speed\" Between:" }; + + VikTrwLayerTpwin *tpwin = VIK_TRW_LAYER_TPWIN ( g_object_new ( VIK_TRW_LAYER_TPWIN_TYPE, NULL ) ); + GtkWidget *main_hbox, *left_vbox, *right_vbox; + GtkWidget *diff_left_vbox, *diff_right_vbox; + + + gtk_window_set_transient_for ( GTK_WINDOW(tpwin), parent ); + gtk_window_set_title ( GTK_WINDOW(tpwin), "Trackpoint" ); + + gtk_dialog_add_buttons ( GTK_DIALOG(tpwin), + GTK_STOCK_CLOSE, VIK_TRW_LAYER_TPWIN_CLOSE, + GTK_STOCK_DELETE, VIK_TRW_LAYER_TPWIN_DELETE, + "Split Here", VIK_TRW_LAYER_TPWIN_SPLIT, + "Join With Last", VIK_TRW_LAYER_TPWIN_JOIN, + GTK_STOCK_GO_BACK, VIK_TRW_LAYER_TPWIN_BACK, + GTK_STOCK_GO_FORWARD, VIK_TRW_LAYER_TPWIN_FORWARD, + NULL ); + tpwin->buttons = gtk_container_get_children(GTK_CONTAINER(GTK_DIALOG(tpwin)->action_area)); + + /* main track info */ + left_vbox = a_dialog_create_label_vbox ( left_label_texts, sizeof(left_label_texts) / sizeof(left_label_texts[0]) ); + + tpwin->track_name = GTK_LABEL(gtk_label_new(NULL)); + tpwin->ts = GTK_LABEL(gtk_label_new(NULL)); + tpwin->localtime = GTK_LABEL(gtk_label_new(NULL)); + + tpwin->lat = GTK_SPIN_BUTTON(gtk_spin_button_new( GTK_ADJUSTMENT(gtk_adjustment_new ( + 0, -90, 90, 0.00005, 0.01, 0 )), 0.00005, 6)); + tpwin->lon = GTK_SPIN_BUTTON(gtk_spin_button_new( GTK_ADJUSTMENT(gtk_adjustment_new ( + 0, -180, 180, 0.00005, 0.01, 0 )), 0.00005, 6)); + + g_signal_connect_swapped ( G_OBJECT(tpwin->lat), "value-changed", G_CALLBACK(tpwin_sync_ll_to_tp), tpwin ); + g_signal_connect_swapped ( G_OBJECT(tpwin->lon), "value-changed", G_CALLBACK(tpwin_sync_ll_to_tp), tpwin ); + + tpwin->alt = GTK_SPIN_BUTTON(gtk_spin_button_new( GTK_ADJUSTMENT(gtk_adjustment_new ( + 0, -1000, 25000, 10, 100, 0 )), 10, 2)); + + g_signal_connect_swapped ( G_OBJECT(tpwin->alt), "value-changed", G_CALLBACK(tpwin_sync_alt_to_tp), tpwin ); + + right_vbox = gtk_vbox_new( TRUE, 3 ); + gtk_box_pack_start ( GTK_BOX(right_vbox), GTK_WIDGET(tpwin->track_name), FALSE, TRUE, 0 ); + gtk_box_pack_start ( GTK_BOX(right_vbox), GTK_WIDGET(tpwin->lat), FALSE, TRUE, 0 ); + gtk_box_pack_start ( GTK_BOX(right_vbox), GTK_WIDGET(tpwin->lon), FALSE, TRUE, 0 ); + gtk_box_pack_start ( GTK_BOX(right_vbox), GTK_WIDGET(tpwin->alt), FALSE, TRUE, 0 ); + gtk_box_pack_start ( GTK_BOX(right_vbox), GTK_WIDGET(tpwin->ts), FALSE, TRUE, 5 ); + gtk_box_pack_start ( GTK_BOX(right_vbox), GTK_WIDGET(tpwin->localtime), FALSE, TRUE, 5 ); + + /* diff info */ + diff_left_vbox = a_dialog_create_label_vbox ( right_label_texts, sizeof(right_label_texts) / sizeof(right_label_texts[0]) ); + + tpwin->diff_dist = GTK_LABEL(gtk_label_new(NULL)); + tpwin->diff_time = GTK_LABEL(gtk_label_new(NULL)); + tpwin->diff_speed = GTK_LABEL(gtk_label_new(NULL)); + + diff_right_vbox = gtk_vbox_new ( TRUE, 3 ); + gtk_box_pack_start ( GTK_BOX(diff_right_vbox), GTK_WIDGET(tpwin->diff_dist), FALSE, TRUE, 5 ); + gtk_box_pack_start ( GTK_BOX(diff_right_vbox), GTK_WIDGET(tpwin->diff_time), FALSE, TRUE, 5 ); + gtk_box_pack_start ( GTK_BOX(diff_right_vbox), GTK_WIDGET(tpwin->diff_speed), FALSE, TRUE, 5 ); + + main_hbox = gtk_hbox_new( TRUE, 0 ); + gtk_box_pack_start ( GTK_BOX(main_hbox), left_vbox, TRUE, TRUE, 0 ); + gtk_box_pack_start ( GTK_BOX(main_hbox), right_vbox, TRUE, TRUE, 0 ); + gtk_box_pack_start ( GTK_BOX(main_hbox), diff_left_vbox, TRUE, TRUE, 0 ); + gtk_box_pack_start ( GTK_BOX(main_hbox), diff_right_vbox, TRUE, TRUE, 0 ); + + gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(tpwin)->vbox), main_hbox, FALSE, FALSE, 0 ); + + tpwin->cur_tp = NULL; + + return tpwin; +} + +void vik_trw_layer_tpwin_set_empty ( VikTrwLayerTpwin *tpwin ) +{ + gtk_label_set_text ( tpwin->track_name, NULL ); + gtk_label_set_text ( tpwin->ts, NULL ); + gtk_label_set_text ( tpwin->localtime, NULL ); + + gtk_widget_set_sensitive ( GTK_WIDGET(tpwin->lat), FALSE ); + gtk_widget_set_sensitive ( GTK_WIDGET(tpwin->lon), FALSE ); + gtk_widget_set_sensitive ( GTK_WIDGET(tpwin->alt), FALSE ); + + SET_BUTTON_SENSITIVE ( tpwin, VIK_TRW_LAYER_TPWIN_SPLIT, FALSE ); + SET_BUTTON_SENSITIVE ( tpwin, VIK_TRW_LAYER_TPWIN_DELETE, FALSE ); + SET_BUTTON_SENSITIVE ( tpwin, VIK_TRW_LAYER_TPWIN_FORWARD, FALSE ); + SET_BUTTON_SENSITIVE ( tpwin, VIK_TRW_LAYER_TPWIN_BACK, FALSE ); + SET_BUTTON_SENSITIVE ( tpwin, VIK_TRW_LAYER_TPWIN_JOIN, FALSE ); + gtk_label_set_text ( tpwin->diff_dist, NULL ); + gtk_label_set_text ( tpwin->diff_time, NULL ); + gtk_label_set_text ( tpwin->diff_speed, NULL ); + tpwin->cur_tp = NULL; +} + +void vik_trw_layer_tpwin_disable_join ( VikTrwLayerTpwin *tpwin ) +{ + SET_BUTTON_SENSITIVE ( tpwin, VIK_TRW_LAYER_TPWIN_JOIN, FALSE ); +} + +void vik_trw_layer_tpwin_set_tp ( VikTrwLayerTpwin *tpwin, GList *tpl, gchar *track_name ) +{ + static char tmp_str[25]; + static struct LatLon ll; + VikTrackpoint *tp = VIK_TRACKPOINT(tpl->data); + + SET_BUTTON_SENSITIVE ( tpwin, VIK_TRW_LAYER_TPWIN_DELETE, TRUE ); + + /* We can only split up a track if it's not an endpoint. Makes sense to me. */ + SET_BUTTON_SENSITIVE ( tpwin, VIK_TRW_LAYER_TPWIN_SPLIT, tpl->next && tpl->prev ); + + SET_BUTTON_SENSITIVE ( tpwin, VIK_TRW_LAYER_TPWIN_FORWARD, (gboolean) tpl->next ); + SET_BUTTON_SENSITIVE ( tpwin, VIK_TRW_LAYER_TPWIN_BACK, (gboolean) tpl->prev ); + + /* we can only join tracks if there was a last tp, the last tp was an endpoint, _AND_ this tp is an endpoint */ + SET_BUTTON_SENSITIVE ( tpwin, VIK_TRW_LAYER_TPWIN_JOIN, tpwin->cur_tp && tpwin->cur_tp_is_endpoint && (!(tpl->next && tpl->prev)) ); + + gtk_widget_set_sensitive ( GTK_WIDGET(tpwin->lat), TRUE ); + gtk_widget_set_sensitive ( GTK_WIDGET(tpwin->lon), TRUE ); + + gtk_label_set_text ( tpwin->track_name, track_name ); + + tpwin->sync_to_tp_block = TRUE; /* don't update while setting data. */ + + vik_coord_to_latlon ( &(tp->coord), &ll ); + gtk_spin_button_set_value ( tpwin->lat, ll.lat ); + gtk_spin_button_set_value ( tpwin->lon, ll.lon ); + gtk_spin_button_set_value ( tpwin->alt, tp->altitude ); + + tpwin->sync_to_tp_block = FALSE; /* don't update whlie setting data. */ + + + if ( tp->has_timestamp ) + { + g_snprintf ( tmp_str, sizeof(tmp_str), "%ld", tp->timestamp ); + gtk_label_set_text ( tpwin->ts, tmp_str ); + g_snprintf ( tmp_str, MIN(25,sizeof(tmp_str)), "%s", ctime(&(tp->timestamp)) ); + /* max. len of 25 will snip off newline, which is good since it messes stuff up */ + gtk_label_set_text ( tpwin->localtime, tmp_str ); + } + else + { + gtk_label_set_text (tpwin->ts, NULL ); + gtk_label_set_text (tpwin->localtime, NULL ); + } + + if ( tpwin->cur_tp ) + { + g_snprintf ( tmp_str, sizeof(tmp_str), "%.3f m", vik_coord_diff(&(tp->coord), &(tpwin->cur_tp->coord))); + gtk_label_set_text ( tpwin->diff_dist, tmp_str ); + if ( tp->has_timestamp && tpwin->cur_tp->has_timestamp ) + { + g_snprintf ( tmp_str, sizeof(tmp_str), "%ld s", tp->timestamp - tpwin->cur_tp->timestamp); + gtk_label_set_text ( tpwin->diff_time, tmp_str ); + if ( tp->timestamp == tpwin->cur_tp->timestamp ) + gtk_label_set_text ( tpwin->diff_speed, "--" ); + else + { + g_snprintf ( tmp_str, sizeof(tmp_str), "%.2f m/s", vik_coord_diff(&(tp->coord), &(tpwin->cur_tp->coord)) / ABS(tp->timestamp - tpwin->cur_tp->timestamp) ); + gtk_label_set_text ( tpwin->diff_speed, tmp_str ); + } + } + else + { + gtk_label_set_text ( tpwin->diff_time, NULL ); + gtk_label_set_text ( tpwin->diff_speed, NULL ); + } + } + + + tpwin->cur_tp = tp; + tpwin->cur_tp_is_endpoint = ! (tpl->next && tpl->prev); +} + +void vik_trw_layer_tpwin_set_track_name ( VikTrwLayerTpwin *tpwin, const gchar *track_name ) +{ + gtk_label_set_text ( tpwin->track_name, track_name ); +} diff --git a/src/viktrwlayer_tpwin.h b/src/viktrwlayer_tpwin.h new file mode 100644 index 00000000..3645529b --- /dev/null +++ b/src/viktrwlayer_tpwin.h @@ -0,0 +1,63 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _VIKING_TRWLAYER_TPWIN_H +#define _VIKING_TRWLAYER_TPWIN_H + +#include +#include +#include +G_BEGIN_DECLS + +/* response codes */ +#define VIK_TRW_LAYER_TPWIN_CLOSE 5 +#define VIK_TRW_LAYER_TPWIN_DELETE 4 +#define VIK_TRW_LAYER_TPWIN_SPLIT 3 +#define VIK_TRW_LAYER_TPWIN_JOIN 2 +#define VIK_TRW_LAYER_TPWIN_BACK 1 +#define VIK_TRW_LAYER_TPWIN_FORWARD 0 + +#define VIK_TRW_LAYER_TPWIN_DATA_CHANGED 100 + +#define VIK_TRW_LAYER_TPWIN_TYPE (vik_trw_layer_tpwin_get_type ()) +#define VIK_TRW_LAYER_TPWIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), VIK_TRW_LAYER_TPWIN_TYPE, VikTrwLayerTpwin)) +#define VIK_TRW_LAYER_TPWIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), VIK_TRW_LAYER_TPWIN_TYPE, VikTrwLayerTpwinClass)) +#define IS_VIK_TRW_LAYER_TPWIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VIK_TRW_LAYER_TPWIN_TYPE)) +#define IS_VIK_TRW_LAYER_TPWIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), VIK_TRW_LAYER_TPWIN_TYPE)) + +typedef struct _VikTrwLayerTpwin VikTrwLayerTpwin; +typedef struct _VikTrwLayerTpwinClass VikTrwLayerTpwinClass; + +struct _VikTrwLayerTpwinClass +{ + GtkDialogClass vik_trw_layer_class; +}; + +GType vik_trw_layer_tpwin_get_type (); + +VikTrwLayerTpwin *vik_trw_layer_tpwin_new (); +void vik_trw_layer_tpwin_set_empty ( VikTrwLayerTpwin *tpwin ); +void vik_trw_layer_tpwin_disable_join ( VikTrwLayerTpwin *tpwin ); +void vik_trw_layer_tpwin_set_tp ( VikTrwLayerTpwin *tpwin, GList *tpl, gchar *track_name ); +void vik_trw_layer_tpwin_set_track_name ( VikTrwLayerTpwin *tpwin, const gchar *track_name ); + + +#endif diff --git a/src/vikviewport.c b/src/vikviewport.c new file mode 100644 index 00000000..78f9a7cc --- /dev/null +++ b/src/vikviewport.c @@ -0,0 +1,824 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * Lat/Lon plotting functions calcxy* are from GPSDrive + * GPSDrive Copyright (C) 2001-2004 Fritz Ganter + * + * Multiple UTM zone patch by Kit Transue + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define DEFAULT_BACKGROUND_COLOR "#CCCCCC" + +#include +#include + +#include "coords.h" +#include "vikcoord.h" +#include "vikviewport.h" + +#include "mapcoord.h" + +/* for ALTI_TO_MPP */ +#include "globals.h" +#include "googlemaps.h" +#include "khmaps.h" + +static gdouble EASTING_OFFSET = 500000.0; + +static void viewport_class_init ( VikViewportClass *klass ); +static void viewport_init ( VikViewport *vvp ); +static void viewport_finalize ( GObject *gob ); +static void viewport_utm_zone_check ( VikViewport *vvp ); + +static gboolean calcxy(double *x, double *y, double lg, double lt, double zero_long, double zero_lat, double pixelfact_x, double pixelfact_y, gint mapSizeX2, gint mapSizeY2 ); +static gboolean calcxy_rev(double *lg, double *lt, gint x, gint y, double zero_long, double zero_lat, double pixelfact_x, double pixelfact_y, gint mapSizeX2, gint mapSizeY2 ); +double calcR (double lat); + +static double Radius[181]; +static void viewport_init_ra(); + +static GObjectClass *parent_class; + +static void viewport_google_rezoom ( VikViewport *vvp ); + + +struct _VikViewport { + GtkDrawingArea drawing_area; + GdkPixmap *scr_buffer; + gint width, height; + VikCoord center; + VikCoordMode coord_mode; + gdouble xmpp, ympp; + + GdkPixbuf *alpha_pixbuf; + guint8 alpha_pixbuf_width; + guint8 alpha_pixbuf_height; + + gdouble utm_zone_width; + gboolean one_utm_zone; + + GdkGC *background_gc; + GdkColor background_color; + + /* subset of coord types. lat lon can be plotted in 2 ways, google or exp. */ + VikViewportDrawMode drawmode; + + /* handy conversion factors which make google plotting extremely fast */ + gdouble google_calcx_fact; + gdouble google_calcy_fact; + gdouble google_calcx_rev_fact; + gdouble google_calcy_rev_fact; +}; + +static gdouble +viewport_utm_zone_width ( VikViewport *vvp ) +{ + if ( vvp->coord_mode == VIK_COORD_UTM ) { + struct LatLon ll; + + /* get latitude of screen bottom */ + struct UTM utm = *((struct UTM *)(vik_viewport_get_center ( vvp ))); + utm.northing -= vvp -> height * vvp -> ympp / 2; + a_coords_utm_to_latlon ( &utm, &ll ); + + /* boundary */ + ll.lon = (utm.zone - 1) * 6 - 180 ; + a_coords_latlon_to_utm ( &ll, &utm); + return fabs ( utm.easting - EASTING_OFFSET ) * 2; + } else + return 0.0; +} + + +GType vik_viewport_get_type (void) +{ + static GType vvp_type = 0; + + if (!vvp_type) + { + static const GTypeInfo vvp_info = + { + sizeof (VikViewportClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) viewport_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (VikViewport), + 0, + (GInstanceInitFunc) viewport_init, + }; + vvp_type = g_type_register_static ( GTK_TYPE_DRAWING_AREA, "VikViewport", &vvp_info, 0 ); + } + return vvp_type; +} + +static void viewport_class_init ( VikViewportClass *klass ) +{ + /* Destructor */ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = viewport_finalize; + + parent_class = g_type_class_peek_parent (klass); +} + +VikViewport *vik_viewport_new () +{ + return VIK_VIEWPORT ( g_object_new ( VIK_VIEWPORT_TYPE, NULL ) ); +} + +static void viewport_init ( VikViewport *vvp ) +{ + viewport_init_ra(); + + /* TODO: not static */ + vvp->xmpp = 4.0; + vvp->ympp = 4.0; + vvp->coord_mode = VIK_COORD_UTM; + vvp->drawmode = VIK_VIEWPORT_DRAWMODE_UTM; + vvp->center.north_south = 0; + vvp->center.east_west = -166021; + vvp->center.utm_zone = 31; + vvp->center.utm_letter = 'N'; + vvp->scr_buffer = NULL; + vvp->alpha_pixbuf = NULL; + vvp->alpha_pixbuf_width = vvp->alpha_pixbuf_height = 0; + vvp->utm_zone_width = 0.0; + vvp->background_gc = NULL; + g_signal_connect (G_OBJECT(vvp), "configure_event", G_CALLBACK(vik_viewport_configure), NULL); +} + +GdkColor *vik_viewport_get_background_gdkcolor ( VikViewport *vvp ) +{ + GdkColor *rv = g_malloc ( sizeof ( GdkColor ) ); + *rv = vvp->background_color; + return rv; +} + +/* returns pointer to internal static storage, changes next time function called, use quickly */ +const gchar *vik_viewport_get_background_color ( VikViewport *vvp ) +{ + static gchar color[8]; + g_snprintf(color, sizeof(color), "#%.2x%.2x%.2x", (int)(vvp->background_color.red/256),(int)(vvp->background_color.green/256),(int)(vvp->background_color.blue/256)); + return color; +} + +void vik_viewport_set_background_color ( VikViewport *vvp, const gchar *colorname ) +{ + g_assert ( vvp->background_gc ); + gdk_color_parse ( colorname, &(vvp->background_color) ); + gdk_gc_set_rgb_fg_color ( vvp->background_gc, &(vvp->background_color) ); +} + +void vik_viewport_set_background_gdkcolor ( VikViewport *vvp, GdkColor *color ) +{ + g_assert ( vvp->background_gc ); + vvp->background_color = *color; + gdk_gc_set_rgb_fg_color ( vvp->background_gc, color ); +} + + +GdkGC *vik_viewport_new_gc ( VikViewport *vvp, const gchar *colorname, gint thickness ) +{ + GdkGC *rv; + GdkColor color; + + rv = gdk_gc_new ( GTK_WIDGET(vvp)->window ); + gdk_color_parse ( colorname, &color ); + gdk_gc_set_rgb_fg_color ( rv, &color ); + gdk_gc_set_line_attributes ( rv, thickness, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND ); + return rv; +} + +GdkGC *vik_viewport_new_gc_from_color ( VikViewport *vvp, GdkColor *color, gint thickness ) +{ + GdkGC *rv; + + rv = gdk_gc_new ( GTK_WIDGET(vvp)->window ); + gdk_gc_set_rgb_fg_color ( rv, color ); + gdk_gc_set_line_attributes ( rv, thickness, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND ); + return rv; +} + +void vik_viewport_configure_manually ( VikViewport *vvp, gint width, guint height ) +{ + vvp->width = width; + vvp->height = height; + if ( vvp->scr_buffer ) + g_object_unref ( G_OBJECT ( vvp->scr_buffer ) ); + vvp->scr_buffer = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, vvp->width, vvp->height, -1 ); +} + + +GdkPixmap *vik_viewport_get_pixmap ( VikViewport *vvp ) +{ + return vvp->scr_buffer; +} + +gboolean vik_viewport_configure ( VikViewport *vvp ) +{ + g_return_val_if_fail ( vvp != NULL, TRUE ); + + vvp->width = GTK_WIDGET(vvp)->allocation.width; + vvp->height = GTK_WIDGET(vvp)->allocation.height; + + if ( vvp->scr_buffer ) + g_object_unref ( G_OBJECT ( vvp->scr_buffer ) ); + + vvp->scr_buffer = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, vvp->width, vvp->height, -1 ); + + /* this is down here so it can get a GC (necessary?) */ + if ( ! vvp->background_gc ) + { + vvp->background_gc = vik_viewport_new_gc ( vvp, "", 1 ); + vik_viewport_set_background_color ( vvp, DEFAULT_BACKGROUND_COLOR ); /* set to "backup" color in vvp->background_color */ + } + + return FALSE; +} + +static void viewport_finalize ( GObject *gob ) +{ + VikViewport *vvp = VIK_VIEWPORT(gob); + + g_return_if_fail ( vvp != NULL ); + + if ( vvp->scr_buffer ) + g_object_unref ( G_OBJECT ( vvp->scr_buffer ) ); + + if ( vvp->alpha_pixbuf ) + g_object_unref ( G_OBJECT ( vvp->alpha_pixbuf ) ); + + if ( vvp->background_gc ) + g_object_unref ( G_OBJECT ( vvp->background_gc ) ); + + G_OBJECT_CLASS(parent_class)->finalize(gob); +} + +void vik_viewport_clear ( VikViewport *vvp ) +{ + g_return_if_fail ( vvp != NULL ); + gdk_draw_rectangle(GDK_DRAWABLE(vvp->scr_buffer), vvp->background_gc, TRUE, 0, 0, vvp->width, vvp->height); +} + +void vik_viewport_sync ( VikViewport *vvp ) +{ + g_return_if_fail ( vvp != NULL ); + gdk_draw_drawable(GTK_WIDGET(vvp)->window, GTK_WIDGET(vvp)->style->bg_gc[0], GDK_DRAWABLE(vvp->scr_buffer), 0, 0, 0, 0, vvp->width, vvp->height); +} + +void vik_viewport_pan_sync ( VikViewport *vvp, gint x_off, gint y_off ) +{ + g_return_if_fail ( vvp != NULL ); + gdk_draw_drawable(GTK_WIDGET(vvp)->window, GTK_WIDGET(vvp)->style->bg_gc[0], GDK_DRAWABLE(vvp->scr_buffer), 0, 0, x_off, y_off, vvp->width, vvp->height); +} + +void vik_viewport_set_zoom ( VikViewport *vvp, gdouble xympp ) +{ + g_return_if_fail ( vvp != NULL ); + if ( xympp >= VIK_VIEWPORT_MIN_ZOOM && xympp <= VIK_VIEWPORT_MAX_ZOOM ) + vvp->xmpp = vvp->ympp = xympp; + + if ( vvp->drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) + viewport_utm_zone_check(vvp); + else if ( vvp->drawmode == VIK_VIEWPORT_DRAWMODE_GOOGLE ) + viewport_google_rezoom ( vvp ); +} + +/* or could do factor */ +void vik_viewport_zoom_in ( VikViewport *vvp ) +{ + g_return_if_fail ( vvp != NULL ); + if ( vvp->xmpp >= (VIK_VIEWPORT_MIN_ZOOM*2) && vvp->ympp >= (VIK_VIEWPORT_MIN_ZOOM*2) ) + { + vvp->xmpp /= 2; + vvp->ympp /= 2; + + if ( vvp->drawmode == VIK_VIEWPORT_DRAWMODE_GOOGLE ) + viewport_google_rezoom ( vvp ); + + viewport_utm_zone_check(vvp); + } +} + +void vik_viewport_zoom_out ( VikViewport *vvp ) +{ + g_return_if_fail ( vvp != NULL ); + if ( vvp->xmpp <= (VIK_VIEWPORT_MAX_ZOOM/2) && vvp->ympp <= (VIK_VIEWPORT_MAX_ZOOM/2) ) + { + vvp->xmpp *= 2; + vvp->ympp *= 2; + + if ( vvp->drawmode == VIK_VIEWPORT_DRAWMODE_GOOGLE ) + viewport_google_rezoom ( vvp ); + + viewport_utm_zone_check(vvp); + } +} + +gdouble vik_viewport_get_zoom ( VikViewport *vvp ) +{ + if ( vvp->xmpp == vvp->ympp ) + return vvp->xmpp; + return 0.0; +} + +gdouble vik_viewport_get_xmpp ( VikViewport *vvp ) +{ + return vvp->xmpp; +} + +gdouble vik_viewport_get_ympp ( VikViewport *vvp ) +{ + return vvp->ympp; +} + +void vik_viewport_set_xmpp ( VikViewport *vvp, gdouble xmpp ) +{ + if ( xmpp >= VIK_VIEWPORT_MIN_ZOOM && xmpp <= VIK_VIEWPORT_MAX_ZOOM ) { + vvp->xmpp = xmpp; + if ( vvp->drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) + viewport_utm_zone_check(vvp); + if ( vvp->drawmode == VIK_VIEWPORT_DRAWMODE_GOOGLE ) + viewport_google_rezoom ( vvp ); + } +} + +void vik_viewport_set_ympp ( VikViewport *vvp, gdouble ympp ) +{ + if ( ympp >= VIK_VIEWPORT_MIN_ZOOM && ympp <= VIK_VIEWPORT_MAX_ZOOM ) { + vvp->ympp = ympp; + if ( vvp->drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) + viewport_utm_zone_check(vvp); + if ( vvp->drawmode == VIK_VIEWPORT_DRAWMODE_GOOGLE ) + viewport_google_rezoom ( vvp ); + } +} + + +const VikCoord *vik_viewport_get_center ( VikViewport *vvp ) +{ + g_return_val_if_fail ( vvp != NULL, NULL ); + return &(vvp->center); +} + +/* called every time we update coordinates/zoom */ +static void viewport_utm_zone_check ( VikViewport *vvp ) +{ + if ( vvp->coord_mode == VIK_COORD_UTM ) + { + struct UTM utm; + struct LatLon ll; + a_coords_utm_to_latlon ( (struct UTM *) &(vvp->center), &ll ); + a_coords_latlon_to_utm ( &ll, &utm ); + if ( utm.zone != vvp->center.utm_zone ) + *((struct UTM *)(&vvp->center)) = utm; + + /* misc. stuff so we don't have to check later */ + vvp->utm_zone_width = viewport_utm_zone_width ( vvp ); + vvp->one_utm_zone = ( vik_viewport_rightmost_zone(vvp) == vik_viewport_leftmost_zone(vvp) ); + } +} + +void vik_viewport_set_center_latlon ( VikViewport *vvp, const struct LatLon *ll ) +{ + vik_coord_load_from_latlon ( &(vvp->center), vvp->coord_mode, ll ); + if ( vvp->coord_mode == VIK_COORD_UTM ) + viewport_utm_zone_check ( vvp ); +} + +void vik_viewport_set_center_utm ( VikViewport *vvp, const struct UTM *utm ) +{ + vik_coord_load_from_utm ( &(vvp->center), vvp->coord_mode, utm ); + if ( vvp->coord_mode == VIK_COORD_UTM ) + viewport_utm_zone_check ( vvp ); +} + +void vik_viewport_set_center_coord ( VikViewport *vvp, const VikCoord *coord ) +{ + vvp->center = *coord; + if ( vvp->coord_mode == VIK_COORD_UTM ) + viewport_utm_zone_check ( vvp ); +} + +void vik_viewport_corners_for_zonen ( VikViewport *vvp, int zone, VikCoord *ul, VikCoord *br ) +{ + g_return_if_fail ( vvp->coord_mode == VIK_COORD_UTM ); + + /* get center, then just offset */ + vik_viewport_center_for_zonen ( vvp, VIK_UTM(ul), zone ); + ul->mode = VIK_COORD_UTM; + *br = *ul; + + ul->north_south += (vvp->ympp * vvp->height / 2); + ul->east_west -= (vvp->xmpp * vvp->width / 2); + br->north_south -= (vvp->ympp * vvp->height / 2); + br->east_west += (vvp->xmpp * vvp->width / 2); +} + +void vik_viewport_center_for_zonen ( VikViewport *vvp, struct UTM *center, int zone) +{ + if ( vvp->coord_mode == VIK_COORD_UTM ) { + *center = *((struct UTM *)(vik_viewport_get_center ( vvp ))); + center->easting -= ( zone - center->zone ) * vvp->utm_zone_width; + center->zone = zone; + } +} + +gchar vik_viewport_leftmost_zone ( VikViewport *vvp ) +{ + if ( vvp->coord_mode == VIK_COORD_UTM ) { + VikCoord coord; + g_assert ( vvp != NULL ); + vik_viewport_screen_to_coord ( vvp, 0, 0, &coord ); + return coord.utm_zone; + } + return '\0'; +} + +gchar vik_viewport_rightmost_zone ( VikViewport *vvp ) +{ + if ( vvp->coord_mode == VIK_COORD_UTM ) { + VikCoord coord; + g_assert ( vvp != NULL ); + vik_viewport_screen_to_coord ( vvp, vvp->width, 0, &coord ); + return coord.utm_zone; + } + return '\0'; +} + + +void vik_viewport_set_center_screen ( VikViewport *vvp, int x, int y ) +{ + g_return_if_fail ( vvp != NULL ); + if ( vvp->coord_mode == VIK_COORD_UTM ) { + /* slightly optimized */ + vvp->center.east_west += vvp->xmpp * (x - (vvp->width/2)); + vvp->center.north_south += vvp->ympp * ((vvp->height/2) - y); + viewport_utm_zone_check ( vvp ); + } else { + VikCoord tmp; + vik_viewport_screen_to_coord ( vvp, x, y, &tmp ); + vik_viewport_set_center_coord ( vvp, &tmp ); + } +} + +gint vik_viewport_get_width( VikViewport *vvp ) +{ + g_return_val_if_fail ( vvp != NULL, 0 ); + return vvp->width; +} + +gint vik_viewport_get_height( VikViewport *vvp ) +{ + g_return_val_if_fail ( vvp != NULL, 0 ); + return vvp->height; +} + +void vik_viewport_screen_to_coord ( VikViewport *vvp, int x, int y, VikCoord *coord ) +{ + if ( vvp->coord_mode == VIK_COORD_UTM ) { + int zone_delta; + struct UTM *utm = (struct UTM *) coord; + coord->mode = VIK_COORD_UTM; + + g_return_if_fail ( vvp != NULL ); + + utm->zone = vvp->center.utm_zone; + utm->letter = vvp->center.utm_letter; + utm->easting = ( ( x - ( vvp->width / 2) ) * vvp->xmpp ) + vvp->center.east_west; + zone_delta = floor( (utm->easting - EASTING_OFFSET ) / vvp->utm_zone_width + 0.5 ); + utm->zone += zone_delta; + utm->easting -= zone_delta * vvp->utm_zone_width; + utm->northing = ( ( ( vvp->height / 2) - y ) * vvp->ympp ) + vvp->center.north_south; + } else if ( vvp->coord_mode == VIK_COORD_LATLON ) { + coord->mode = VIK_COORD_LATLON; + if ( vvp->drawmode == VIK_VIEWPORT_DRAWMODE_EXPEDIA ) + calcxy_rev(&(coord->east_west), &(coord->north_south), x, y, vvp->center.east_west, vvp->center.north_south, vvp->xmpp * ALTI_TO_MPP, vvp->ympp * ALTI_TO_MPP, vvp->width/2, vvp->height/2); + else if ( vvp->drawmode == VIK_VIEWPORT_DRAWMODE_GOOGLE ) { + /* google */ + coord->east_west = (x - (vvp->width/2)) * vvp->google_calcx_rev_fact + vvp->center.east_west; + coord->north_south = ((vvp->height/2) - y) * vvp->google_calcy_rev_fact + vvp->center.north_south; + } else if ( vvp->drawmode == VIK_VIEWPORT_DRAWMODE_KH ) { + coord->east_west = vvp->center.east_west + (180.0 * vvp->xmpp / 65536 / 256 * (x - vvp->width/2)); + coord->north_south = vvp->center.north_south + (180.0 * vvp->ympp / 65536 / 256 * (vvp->height/2 - y)); + } else if ( vvp->drawmode == VIK_VIEWPORT_DRAWMODE_MERCATOR ) { + /* FIXMERCATOR */ + coord->east_west = vvp->center.east_west + (180.0 * vvp->xmpp / 65536 / 256 * (x - vvp->width/2)); + coord->north_south = DEMERCLAT ( MERCLAT(vvp->center.north_south) + (180.0 * vvp->ympp / 65536 / 256 * (vvp->height/2 - y)) ); + +#if 0 +--> THIS IS JUNK HERE. + *y = vvp->height/2 + (65536.0 / 180 / vvp->ympp * (MERCLAT(center->lat) - MERCLAT(ll->lat)))*256.0; + + (*y - vvp->height/2) / 256 / 65536 * 180 * vvp->ympp = (MERCLAT(center->lat) - MERCLAT(ll->lat); + DML((180.0 * vvp->ympp / 65536 / 256 * (vvp->height/2 - y)) + ML(cl)) = ll +#endif + } + } +} + +void vik_viewport_coord_to_screen ( VikViewport *vvp, const VikCoord *coord, int *x, int *y ) +{ + static VikCoord tmp; + g_return_if_fail ( vvp != NULL ); + + if ( coord->mode != vvp->coord_mode ) + { + g_warning ( "Have to convert in vik_viewport_coord_to_screen! This should never happen!"); + vik_coord_copy_convert ( coord, vvp->coord_mode, &tmp ); + coord = &tmp; + } + + if ( vvp->coord_mode == VIK_COORD_UTM ) { + struct UTM *center = (struct UTM *) &(vvp->center); + struct UTM *utm = (struct UTM *) coord; + if ( center->zone != utm->zone && vvp->one_utm_zone ) + { + *x = *y = VIK_VIEWPORT_UTM_WRONG_ZONE; + return; + } + + *x = ( (utm->easting - center->easting) / vvp->xmpp ) + (vvp->width / 2) - + (center->zone - utm->zone ) * vvp->utm_zone_width / vvp->xmpp; + *y = (vvp->height / 2) - ( (utm->northing - center->northing) / vvp->ympp ); + } else if ( vvp->coord_mode == VIK_COORD_LATLON ) { + struct LatLon *center = (struct LatLon *) &(vvp->center); + struct LatLon *ll = (struct LatLon *) coord; + double xx,yy; + if ( vvp->drawmode == VIK_VIEWPORT_DRAWMODE_EXPEDIA ) { + calcxy ( &xx, &yy, center->lon, center->lat, ll->lon, ll->lat, vvp->xmpp * ALTI_TO_MPP, vvp->ympp * ALTI_TO_MPP, vvp->width / 2, vvp->height / 2 ); + *x = xx; *y = yy; + } else if ( vvp->drawmode == VIK_VIEWPORT_DRAWMODE_GOOGLE ) { + /* google */ + *x = vvp->google_calcx_fact * (ll->lon - center->lon) + (vvp->width/2); + *y = vvp->google_calcy_fact * (center->lat - ll->lat) + (vvp->height/2); + } else if ( vvp->drawmode == VIK_VIEWPORT_DRAWMODE_KH ) { + /* subtract, convert to KH coords; blow it up by 256 */ + *x = vvp->width/2 + (65536.0 / 180 / vvp->xmpp * (ll->lon - center->lon))*256.0; + *y = vvp->height/2 + (65536.0 / 180 / vvp->ympp * (center->lat - ll->lat))*256.0; + } else if ( vvp->drawmode == VIK_VIEWPORT_DRAWMODE_MERCATOR ) { + /* FIXMERCATOR: Optimize */ + *x = vvp->width/2 + (65536.0 / 180 / vvp->xmpp * (ll->lon - center->lon))*256.0; + *y = vvp->height/2 + (65536.0 / 180 / vvp->ympp * (MERCLAT(center->lat) - MERCLAT(ll->lat)))*256.0; + } + } +} + +void vik_viewport_draw_line ( VikViewport *vvp, GdkGC *gc, gint x1, gint y1, gint x2, gint y2 ) +{ + if ( ! ( ( x1 < 0 && x2 < 0 ) || ( y1 < 0 && y2 < 0 ) || + ( x1 > vvp->width && x2 > vvp->width ) || ( y1 > vvp->height && y2 > vvp->height ) ) ) + gdk_draw_line ( vvp->scr_buffer, gc, x1, y1, x2, y2); +} + +void vik_viewport_draw_rectangle ( VikViewport *vvp, GdkGC *gc, gboolean filled, gint x1, gint y1, gint x2, gint y2 ) +{ + if ( x1 > -10 && x1 < vvp->width + 10 && y1 > -10 && y1 < vvp->height + 10 ) + gdk_draw_rectangle ( vvp->scr_buffer, gc, filled, x1, y1, x2, y2); +} + +void vik_viewport_draw_string ( VikViewport *vvp, GdkFont *font, GdkGC *gc, gint x1, gint y1, const gchar *string ) +{ + if ( x1 > -100 && x1 < vvp->width + 100 && y1 > -100 && y1 < vvp->height + 100 ) + gdk_draw_string ( vvp->scr_buffer, font, gc, x1, y1, string ); +} + +/* shouldn't use this -- slow -- change the alpha channel instead. */ +void vik_viewport_draw_pixbuf_with_alpha ( VikViewport *vvp, GdkPixbuf *pixbuf, gint alpha, + gint src_x, gint src_y, gint dest_x, gint dest_y, gint w, gint h ) +{ + gint real_dest_x = MAX(dest_x,0); + gint real_dest_y = MAX(dest_y,0); + + if ( alpha == 0 ) + return; /* don't waste your time */ + + if ( w > vvp->alpha_pixbuf_width || h > vvp->alpha_pixbuf_height ) + { + if ( vvp->alpha_pixbuf ) + g_object_unref ( G_OBJECT ( vvp->alpha_pixbuf ) ); + vvp->alpha_pixbuf_width = MAX(w,vvp->alpha_pixbuf_width); + vvp->alpha_pixbuf_height = MAX(h,vvp->alpha_pixbuf_height); + vvp->alpha_pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, vvp->alpha_pixbuf_width, vvp->alpha_pixbuf_height ); + } + + w = MIN(w,vvp->width - dest_x); + h = MIN(h,vvp->height - dest_y); + + /* check that we are drawing within boundaries. */ + src_x += (real_dest_x - dest_x); + src_y += (real_dest_y - dest_y); + w -= (real_dest_x - dest_x); + h -= (real_dest_y - dest_y); + + gdk_pixbuf_get_from_drawable ( vvp->alpha_pixbuf, vvp->scr_buffer, NULL, + real_dest_x, real_dest_y, 0, 0, w, h ); + + /* do a composite */ + gdk_pixbuf_composite ( pixbuf, vvp->alpha_pixbuf, 0, 0, w, h, -src_x, -src_y, 1, 1, 0, alpha ); + + /* draw pixbuf_tmp */ + vik_viewport_draw_pixbuf ( vvp, vvp->alpha_pixbuf, 0, 0, real_dest_x, real_dest_y, w, h ); +} + +void vik_viewport_draw_pixbuf ( VikViewport *vvp, GdkPixbuf *pixbuf, gint src_x, gint src_y, + gint dest_x, gint dest_y, gint w, gint h ) +{ + gdk_draw_pixbuf ( vvp->scr_buffer, +// GTK_WIDGET(vvp)->style->black_gc, +NULL, + pixbuf, + src_x, src_y, dest_x, dest_y, w, h, + GDK_RGB_DITHER_NONE, 0, 0 ); +} + +void vik_viewport_draw_arc ( VikViewport *vvp, GdkGC *gc, gboolean filled, gint x, gint y, gint width, gint height, gint angle1, gint angle2 ) +{ + gdk_draw_arc ( vvp->scr_buffer, gc, filled, x, y, width, height, angle1, angle2 ); +} + + +void vik_viewport_draw_polygon ( VikViewport *vvp, GdkGC *gc, gboolean filled, GdkPoint *points, gint npoints ) +{ + gdk_draw_polygon ( vvp->scr_buffer, gc, filled, points, npoints ); +} + +VikCoordMode vik_viewport_get_coord_mode ( const VikViewport *vvp ) +{ + g_assert ( vvp ); + return vvp->coord_mode; +} + +static void viewport_set_coord_mode ( VikViewport *vvp, VikCoordMode mode ) +{ + g_return_if_fail ( vvp != NULL ); + vvp->coord_mode = mode; + vik_coord_convert ( &(vvp->center), mode ); +} + +/* Thanks GPSDrive */ +static gboolean calcxy_rev(double *lg, double *lt, gint x, gint y, double zero_long, double zero_lat, double pixelfact_x, double pixelfact_y, gint mapSizeX2, gint mapSizeY2 ) +{ + int px, py; + gdouble dif, lat, lon; + double Ra = Radius[90+(gint)zero_lat]; + + px = (mapSizeX2 - x) * pixelfact_x; + py = (-mapSizeY2 + y) * pixelfact_y; + + lat = zero_lat - py / Ra; + lat = zero_lat - py / Ra; + lon = + zero_long - + px / (Ra * + cos (lat * DEG2RAD)); + + dif = lat * (1 - (cos ((fabs (lon - zero_long)) * DEG2RAD))); + lat = lat - dif / 1.5; + lon = + zero_long - + px / (Ra * + cos (lat * DEG2RAD)); + + *lt = lat; + *lg = lon; + return (TRUE); +} + +/* Thanks GPSDrive */ +static gboolean calcxy(double *x, double *y, double lg, double lt, double zero_long, double zero_lat, double pixelfact_x, double pixelfact_y, gint mapSizeX2, gint mapSizeY2 ) +{ + double dif; + double Ra; + gint mapSizeX = 2 * mapSizeX2; + gint mapSizeY = 2 * mapSizeY2; + + g_assert ( lt >= -90.0 && lt <= 90.0 ); +// lg *= rad2deg; // FIXME, optimize equations +// lt *= rad2deg; + Ra = Radius[90+(gint)lt]; + *x = Ra * + cos (lt*DEG2RAD) * (lg - zero_long); + *y = Ra * (lt - zero_lat); + dif = Ra * RAD2DEG * (1 - (cos ((DEG2RAD * (lg - zero_long))))); + *y = *y + dif / 1.85; + *x = *x / pixelfact_x; + *y = *y / pixelfact_y; + *x = mapSizeX2 - *x; + *y += mapSizeY2; + if ((*x < 0)||(*x >= mapSizeX)||(*y < 0)||(*y >= mapSizeY)) + return (FALSE); + return (TRUE); +} + +static void viewport_init_ra() +{ + static gboolean done_before = FALSE; + if ( !done_before ) + { + gint i; + for ( i = -90; i <= 90; i++) + Radius[i+90] = calcR ( (double)i ) * DEG2RAD; + done_before = TRUE; + } +} + +double calcR (double lat) +{ + double a = 6378.137, r, sc, x, y, z; + double e2 = 0.081082 * 0.081082; + /* + * the radius of curvature of an ellipsoidal Earth in the plane of the + * meridian is given by + * + * R' = a * (1 - e^2) / (1 - e^2 * (sin(lat))^2)^(3/2) + * + * + * where a is the equatorial radius, b is the polar radius, and e is + * the eccentricity of the ellipsoid = sqrt(1 - b^2/a^2) + * + * a = 6378 km (3963 mi) Equatorial radius (surface to center distance) + * b = 6356.752 km (3950 mi) Polar radius (surface to center distance) e + * = 0.081082 Eccentricity + */ + + lat = lat * DEG2RAD; + sc = sin (lat); + x = a * (1.0 - e2); + z = 1.0 - e2 * sc * sc; + y = pow (z, 1.5); + r = x / y; + r = r * 1000.0; + return r; +} + +gboolean vik_viewport_is_one_zone ( VikViewport *vvp ) +{ + return vvp->coord_mode == VIK_COORD_UTM && vvp->one_utm_zone; +} + +void vik_viewport_draw_layout ( VikViewport *vvp, GdkGC *gc, gint x, gint y, PangoLayout *layout ) +{ + if ( x > -100 && x < vvp->width + 100 && y > -100 && y < vvp->height + 100 ) + gdk_draw_layout ( vvp->scr_buffer, gc, x, y, layout ); +} + +void vik_gc_get_fg_color ( GdkGC *gc, GdkColor *dest ) +{ + static GdkGCValues values; + gdk_gc_get_values ( gc, &values ); + gdk_colormap_query_color ( gdk_colormap_get_system(), values.foreground.pixel, dest ); +} + +GdkFunction vik_gc_get_function ( GdkGC *gc ) +{ + static GdkGCValues values; + gdk_gc_get_values ( gc, &values ); + return values.function; +} + +void vik_viewport_set_drawmode ( VikViewport *vvp, VikViewportDrawMode drawmode ) +{ + vvp->drawmode = drawmode; + if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) + viewport_set_coord_mode ( vvp, VIK_COORD_UTM ); + else { + viewport_set_coord_mode ( vvp, VIK_COORD_LATLON ); + if ( drawmode == VIK_VIEWPORT_DRAWMODE_GOOGLE ) + viewport_google_rezoom ( vvp ); + } +} + +VikViewportDrawMode vik_viewport_get_drawmode ( VikViewport *vvp ) +{ + return vvp->drawmode; +} + +static void viewport_google_rezoom ( VikViewport *vvp ) +{ + vvp->google_calcx_fact = (GOOGLEMAPS_ZOOM_ONE_MPP * 65536.0 * 0.7716245833877 / vvp->xmpp); + vvp->google_calcy_fact = (GOOGLEMAPS_ZOOM_ONE_MPP * 65536.0 / vvp->ympp); + vvp->google_calcx_rev_fact = 1 / vvp->google_calcx_fact; + vvp->google_calcy_rev_fact = 1 / vvp->google_calcy_fact; +} diff --git a/src/vikviewport.h b/src/vikviewport.h new file mode 100644 index 00000000..ef8c8248 --- /dev/null +++ b/src/vikviewport.h @@ -0,0 +1,142 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _VIKING_VIEWPORT_H +#define _VIKING_VIEWPORT_H +/* Requires or glib, and coords.h*/ + +#include +#include +#include + +G_BEGIN_DECLS + +#define VIK_VIEWPORT_TYPE (vik_viewport_get_type ()) +#define VIK_VIEWPORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), VIK_VIEWPORT_TYPE, VikViewport)) +#define VIK_VIEWPORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), VIK_VIEWPORT_TYPE, VikViewportClass)) +#define IS_VIK_VIEWPORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VIK_VIEWPORT_TYPE)) +#define IS_VIK_VIEWPORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), VIK_VIEWPORT_TYPE)) + +#define VIK_VIEWPORT_MAX_ZOOM 8192.0 +#define VIK_VIEWPORT_MIN_ZOOM 0.125 + +/* used for coord to screen etc, screen to coord */ +#define VIK_VIEWPORT_UTM_WRONG_ZONE -9999999 +#define VIK_VIEWPORT_OFF_SCREEN_DOUBLE -9999999.9 + +typedef struct _VikViewport VikViewport; +typedef struct _VikViewportClass VikViewportClass; + +struct _VikViewportClass +{ + GtkDrawingAreaClass drawing_area_class; +}; + +GType vik_viewport_get_type (); + +VikViewport *vik_viewport_new (); + +void vik_viewport_configure_manually ( VikViewport *vvp, gint width, guint height ); +/* for off-screen viewports */ +gboolean vik_viewport_configure ( VikViewport *vp ); + + +void vik_viewport_clear ( VikViewport *vvp ); + +GdkPixmap *vik_viewport_get_pixmap ( VikViewport *vvp ); + +void vik_viewport_sync ( VikViewport *vvp ); + +void vik_viewport_set_ympp ( VikViewport *vvp, gdouble ympp ); +void vik_viewport_set_xmpp ( VikViewport *vvp, gdouble xmpp ); +gdouble vik_viewport_get_ympp ( VikViewport *vvp ); +gdouble vik_viewport_get_xmpp ( VikViewport *vvp ); + + +void vik_viewport_set_zoom ( VikViewport *vvp, gdouble mpp ); +gdouble vik_viewport_get_zoom ( VikViewport *vvp ); +void vik_viewport_zoom_in ( VikViewport *vvp ); +void vik_viewport_zoom_out ( VikViewport *vvp ); + +const VikCoord *vik_viewport_get_center ( VikViewport *vvp ); + +void vik_viewport_set_center_coord ( VikViewport *vvp, const VikCoord *coord ); +void vik_viewport_set_center_screen ( VikViewport *vvp, int x, int y ); +void vik_viewport_center_for_zonen ( VikViewport *vvp, struct UTM *center, int zone); +gchar vik_viewport_leftmost_zone ( VikViewport *vvp ); +gchar vik_viewport_rightmost_zone ( VikViewport *vvp ); + +void vik_viewport_screen_to_coord ( VikViewport *vvp, int x, int y, VikCoord *coord ); +void vik_viewport_coord_to_screen ( VikViewport *vvp, const VikCoord *coord, int *x, int *y ); + +void vik_viewport_draw_pixbuf_with_alpha ( VikViewport *vvp, GdkPixbuf *pixbuf, gint alpha, + gint src_x, gint src_y, gint dest_x, gint dest_y, gint w, gint h ); +void vik_viewport_draw_pixbuf ( VikViewport *vvp, GdkPixbuf *pixbuf, gint src_x, gint src_y, + gint dest_x, gint dest_y, gint w, gint h ); + + +gint vik_viewport_get_width ( VikViewport *vvp ); +gint vik_viewport_get_height ( VikViewport *vvp ); + +GdkGC *vik_viewport_new_gc ( VikViewport *vvp, const gchar *colorname, gint thickness ); + +void vik_viewport_draw_line ( VikViewport *vvp, GdkGC *gc, gint x1, gint y1, gint x2, gint y2 ); +void vik_viewport_draw_rectangle ( VikViewport *vvp, GdkGC *gc, gboolean filled, gint x1, gint y1, gint x2, gint y2 ); +void vik_viewport_draw_string ( VikViewport *vvp, GdkFont *font, GdkGC *gc, gint x1, gint y1, const gchar *string ); +GdkGC *vik_viewport_new_gc_from_color ( VikViewport *vvp, GdkColor *color, gint thickness ); +void vik_viewport_draw_arc ( VikViewport *vvp, GdkGC *gc, gboolean filled, gint x, gint y, gint width, gint height, gint angle1, gint angle2 ); +void vik_viewport_draw_polygon ( VikViewport *vvp, GdkGC *gc, gboolean filled, GdkPoint *points, gint npoints ); + +void vik_viewport_set_center_utm ( VikViewport *vvp, const struct UTM *utm ); +void vik_viewport_set_center_latlon ( VikViewport *vvp, const struct LatLon *ll ); + +/* you must set drawmode to set coord_mode */ +VikCoordMode vik_viewport_get_coord_mode ( const VikViewport *vvp ); + +gboolean vik_viewport_is_one_zone ( VikViewport *vvp ); + +void vik_viewport_set_background_color ( VikViewport *vvp, const gchar *color ); +/* pointer to static storage -- may change -- use quickly! */ +const gchar *vik_viewport_get_background_color ( VikViewport *vvp ); +GdkColor *vik_viewport_get_background_gdkcolor ( VikViewport *vvp ); +void vik_viewport_set_background_gdkcolor ( VikViewport *vvp, GdkColor * ); + +void vik_viewport_draw_layout ( VikViewport *vvp, GdkGC *gc, gint x, gint y, PangoLayout *layout ); + +/* warning: could be slow, don't obsessively use following function. */ +void vik_gc_get_fg_color ( GdkGC *gc, GdkColor *dest ); + +GdkFunction vik_gc_get_function ( GdkGC *gc ); + +void vik_viewport_pan_sync ( VikViewport *vvp, gint x_off, gint y_off ); + +typedef enum { VIK_VIEWPORT_DRAWMODE_UTM=0, VIK_VIEWPORT_DRAWMODE_EXPEDIA, + VIK_VIEWPORT_DRAWMODE_GOOGLE, VIK_VIEWPORT_DRAWMODE_KH, VIK_VIEWPORT_DRAWMODE_MERCATOR } VikViewportDrawMode; + + +void vik_viewport_set_drawmode ( VikViewport *vvp, VikViewportDrawMode drawmode ); +VikViewportDrawMode vik_viewport_get_drawmode ( VikViewport *vvp ); +void vik_viewport_corners_for_zonen ( VikViewport *vvp, int zone, VikCoord *ul, VikCoord *br ); + + +G_END_DECLS + +#endif diff --git a/src/vikwaypoint.c b/src/vikwaypoint.c new file mode 100644 index 00000000..d5598f5e --- /dev/null +++ b/src/vikwaypoint.c @@ -0,0 +1,82 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include "coords.h" +#include "vikcoord.h" +#include "vikwaypoint.h" + +VikWaypoint *vik_waypoint_new() +{ + VikWaypoint *wp = g_malloc ( sizeof ( VikWaypoint ) ); + wp->comment = NULL; + wp->image = NULL; + return wp; +} + +void vik_waypoint_set_comment_no_copy(VikWaypoint *wp, gchar *comment) +{ + if ( wp->comment ) + g_free ( wp->comment ); + wp->comment = comment; +} + +void vik_waypoint_set_comment(VikWaypoint *wp, const gchar *comment) +{ + if ( wp->comment ) + g_free ( wp->comment ); + + if ( comment && comment[0] != '\0' ) + wp->comment = g_strdup(comment); + else + wp->comment = NULL; +} + +void vik_waypoint_set_image(VikWaypoint *wp, const gchar *image) +{ + if ( wp->image ) + g_free ( wp->image ); + + if ( image && image[0] != '\0' ) + wp->image = g_strdup(image); + else + wp->image = NULL; +} + +void vik_waypoint_free(VikWaypoint *wp) +{ + if ( wp->comment ) + g_free ( wp->comment ); + if ( wp->image ) + g_free ( wp->image ); + g_free ( wp ); +} + +VikWaypoint *vik_waypoint_copy(const VikWaypoint *wp) +{ + VikWaypoint *new_wp = vik_waypoint_new(); + *new_wp = *wp; + new_wp->comment = NULL; /* if the waypoint had a comment, FOR CRYING OUT LOUD DON'T FREE IT! This lousy bug took me TWO HOURS to figure out... sigh... */ + vik_waypoint_set_comment(new_wp,wp->comment); + new_wp->image = NULL; + vik_waypoint_set_image(new_wp,wp->image); + return new_wp; +} diff --git a/src/vikwaypoint.h b/src/vikwaypoint.h new file mode 100644 index 00000000..965cc651 --- /dev/null +++ b/src/vikwaypoint.h @@ -0,0 +1,52 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _VIKING_WAYPOINT_H +#define _VIKING_WAYPOINT_H + +/* todo important: put these in their own header file, maybe.probably also rename */ + +#define VIK_WAYPOINT(x) ((VikWaypoint *)(x)) + +typedef struct _VikWaypoint VikWaypoint; + +struct _VikWaypoint { + VikCoord coord; + gboolean visible; + gdouble altitude; + gchar *comment; + gchar *image; + /* a rather misleading, ugly hack needed for trwlayer's click image. + * these are the height at which the thumbnail is being drawn, not the + * dimensions of the original image. */ + guint8 image_width; + guint8 image_height; + /* int symbol; */ +}; + +VikWaypoint *vik_waypoint_new(); +void vik_waypoint_set_comment(VikWaypoint *wp, const gchar *comment); +void vik_waypoint_set_image(VikWaypoint *wp, const gchar *image); +void vik_waypoint_free(VikWaypoint * wp); +VikWaypoint *vik_waypoint_copy(const VikWaypoint *wp); +void vik_waypoint_set_comment_no_copy(VikWaypoint *wp, gchar *comment); + +#endif diff --git a/src/vikwindow.c b/src/vikwindow.c new file mode 100644 index 00000000..1af7eaad --- /dev/null +++ b/src/vikwindow.c @@ -0,0 +1,1091 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "viking.h" +#include "background.h" + +#define VIKING_TITLE " - Viking " VIKING_VERSION " " VIKING_VERSION_NAME " " VIKING_URL + +#include +#include +#include + +#ifdef WINDOWS +/* TODO IMPORTANT: mkdir for windows header? is it called 'mkdir' */ +#define make_dir(dir) mkdir(dir) +#else +#include +#include +#define make_dir(dir) mkdir(dir,0777) +#endif + +#define DRAW_IMAGE_DEFAULT_WIDTH 1280 +#define DRAW_IMAGE_DEFAULT_HEIGHT 1024 +#define DRAW_IMAGE_DEFAULT_SAVE_AS_PNG TRUE + +static void window_finalize ( GObject *gob ); +static GObjectClass *parent_class; + +static void window_init ( VikWindow *vw ); +static void window_class_init ( VikWindowClass *klass ); + +static void draw_update ( VikWindow *vw ); + +static void newwindow_cb ( VikWindow *vw ); + +/* Drawing & stuff */ + +static gboolean delete_event( VikWindow *vw ); + +static void draw_sync ( VikWindow *vw ); +static void draw_redraw ( VikWindow *vw ); +static void draw_scroll ( VikWindow *vw, GdkEvent *event ); +static void draw_click ( VikWindow *vw, GdkEventButton *event ); +static void draw_release ( VikWindow *vw, GdkEventButton *event ); +static void draw_mouse_motion ( VikWindow *vw, GdkEventMotion *event ); +static void draw_set_current_tool ( VikWindow *vw, guint mode ); +static void draw_zoom ( VikWindow *vw, gint what ); +static void draw_goto ( VikWindow *vw, gint mode ); + +static void draw_status (); + +/* End Drawing Functions */ + +static void menu_addlayer_cb ( VikWindow *vw, gint type ); +static void menu_properties_cb ( VikWindow *vw ); +static void menu_delete_layer_cb ( VikWindow *vw ); +static void menu_dynamic_tool_cb ( VikWindow *vw, guint id ); + +static GtkWidget *window_create_menubar( VikWindow *window ); + +static void load_file ( VikWindow *vw, gboolean newwindow ); +static gboolean save_file_as ( VikWindow *vw ); +static gboolean save_file ( VikWindow *vw ); +static gboolean window_save ( VikWindow *vw ); + +struct _VikWindow { + GtkWindow gtkwindow; + VikViewport *viking_vvp; + VikLayersPanel *viking_vlp; + VikStatusbar *viking_vs; + + GtkItemFactory *item_factory; + + VikCoord oldcoord; + gboolean has_oldcoord; + guint current_tool; + + guint16 tool_layer_id; + guint16 tool_tool_id; + + gint pan_x, pan_y; + + guint draw_image_width, draw_image_height; + gboolean draw_image_save_as_png; + + gchar *filename; + gboolean modified; + + GtkWidget *open_dia, *save_dia; + + gboolean only_updating_coord_mode_ui; /* hack for a bug in GTK */ +}; + +enum { + TOOL_ZOOM = 0, + TOOL_RULER, + TOOL_LAYER, + NUMBER_OF_TOOLS +}; + +enum { + VW_NEWWINDOW_SIGNAL, + VW_OPENWINDOW_SIGNAL, + VW_LAST_SIGNAL +}; + +static guint window_signals[VW_LAST_SIGNAL] = { 0 }; + +static gchar *tool_names[NUMBER_OF_TOOLS] = { "Zoom", "Ruler", "Pan" }; + +GType vik_window_get_type (void) +{ + static GType vw_type = 0; + + if (!vw_type) + { + static const GTypeInfo vw_info = + { + sizeof (VikWindowClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) window_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (VikWindow), + 0, + (GInstanceInitFunc) window_init, + }; + vw_type = g_type_register_static ( GTK_TYPE_WINDOW, "VikWindow", &vw_info, 0 ); + } + + return vw_type; +} + +static void window_finalize ( GObject *gob ) +{ + VikWindow *vw = VIK_WINDOW(gob); + g_return_if_fail ( vw != NULL ); + + a_background_remove_status ( vw->viking_vs ); + + G_OBJECT_CLASS(parent_class)->finalize(gob); +} + +static void window_class_init ( VikWindowClass *klass ) +{ + /* destructor */ + GObjectClass *object_class; + + window_signals[VW_NEWWINDOW_SIGNAL] = g_signal_new ( "newwindow", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (VikWindowClass, newwindow), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + window_signals[VW_OPENWINDOW_SIGNAL] = g_signal_new ( "openwindow", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (VikWindowClass, openwindow), NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_POINTER); + + object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = window_finalize; + + parent_class = g_type_class_peek_parent (klass); + +} + +static void window_init ( VikWindow *vw ) +{ + GtkWidget *main_vbox; + GtkWidget *hpaned; + + gtk_window_set_title ( GTK_WINDOW(vw), "Untitled" VIKING_TITLE ); + + vw->viking_vvp = vik_viewport_new(); + vw->viking_vlp = vik_layers_panel_new(); + vik_layers_panel_set_viewport ( vw->viking_vlp, vw->viking_vvp ); + vw->viking_vs = vik_statusbar_new(); + + vw->filename = NULL; + + vw->has_oldcoord = FALSE; + vw->modified = FALSE; + vw->only_updating_coord_mode_ui = FALSE; + + vw->pan_x = vw->pan_y = -1; + vw->draw_image_width = DRAW_IMAGE_DEFAULT_WIDTH; + vw->draw_image_height = DRAW_IMAGE_DEFAULT_HEIGHT; + vw->draw_image_save_as_png = DRAW_IMAGE_DEFAULT_SAVE_AS_PNG; + + main_vbox = gtk_vbox_new(FALSE, 1); + gtk_container_add (GTK_CONTAINER (vw), main_vbox); + + gtk_box_pack_start (GTK_BOX(main_vbox), window_create_menubar(vw), FALSE, TRUE, 0); + + g_signal_connect (G_OBJECT (vw), "delete_event", G_CALLBACK (delete_event), NULL); + + g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "expose_event", G_CALLBACK(draw_sync), vw); + g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "configure_event", G_CALLBACK(draw_redraw), vw); + gtk_widget_add_events ( GTK_WIDGET(vw->viking_vvp), GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK ); + g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "scroll_event", G_CALLBACK(draw_scroll), vw); + g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_press_event", G_CALLBACK(draw_click), vw); + g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_release_event", G_CALLBACK(draw_release), vw); + g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "motion_notify_event", G_CALLBACK(draw_mouse_motion), vw); + g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "update", G_CALLBACK(draw_update), vw); + + gtk_window_set_default_size ( GTK_WINDOW(vw), 1000, 800); + + hpaned = gtk_hpaned_new (); + gtk_paned_add1 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vlp) ); + gtk_paned_add2 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vvp) ); + + /* This packs the button into the window (a gtk container). */ + gtk_box_pack_start (GTK_BOX(main_vbox), hpaned, TRUE, TRUE, 0); + + gtk_box_pack_end (GTK_BOX(main_vbox), GTK_WIDGET(vw->viking_vs), FALSE, TRUE, 0); + + a_background_add_status(vw->viking_vs); + + vw->open_dia = NULL; + vw->save_dia = NULL; +} + +VikWindow *vik_window_new () +{ + return VIK_WINDOW ( g_object_new ( VIK_WINDOW_TYPE, NULL ) ); +} + +static gboolean delete_event( VikWindow *vw ) +{ + if ( vw->modified ) + { + GtkDialog *dia; + dia = GTK_DIALOG ( gtk_message_dialog_new ( GTK_WINDOW(vw), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, + "Do you want to save the changes you made to the document \"%s\"?\n\nYour changes will be lost if you don't save them.", + vw->filename ? a_file_basename ( vw->filename ) : "Untitled" ) ); + gtk_dialog_add_buttons ( dia, "Don't Save", GTK_RESPONSE_NO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_YES, NULL ); + switch ( gtk_dialog_run ( dia ) ) + { + case GTK_RESPONSE_NO: gtk_widget_destroy ( GTK_WIDGET(dia) ); return FALSE; + case GTK_RESPONSE_CANCEL: gtk_widget_destroy ( GTK_WIDGET(dia) ); return TRUE; + default: gtk_widget_destroy ( GTK_WIDGET(dia) ); return ! save_file(vw); + } + } + return FALSE; +} + +/* Drawing stuff */ +static void newwindow_cb ( VikWindow *vw ) +{ + g_signal_emit ( G_OBJECT(vw), window_signals[VW_NEWWINDOW_SIGNAL], 0 ); +} + +static void draw_update ( VikWindow *vw ) +{ + draw_redraw (vw); + draw_sync (vw); +} + +static void draw_sync ( VikWindow *vw ) +{ + vik_viewport_sync(vw->viking_vvp); + draw_status ( vw ); + /* other things may be necc here later. */ +} + +static void draw_status ( VikWindow *vw ) +{ + static gchar zoom_level[22]; + g_snprintf ( zoom_level, 22, "%.3f/%.3f %s", vik_viewport_get_xmpp (vw->viking_vvp), vik_viewport_get_ympp(vw->viking_vvp), vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ? "mpp" : "pixelfact" ); + if ( vw->current_tool == TOOL_LAYER ) + vik_statusbar_set_message ( vw->viking_vs, 0, vik_layer_get_interface(vw->tool_layer_id)->tools[vw->tool_tool_id].name ); + else + vik_statusbar_set_message ( vw->viking_vs, 0, tool_names[vw->current_tool] ); + + vik_statusbar_set_message ( vw->viking_vs, 2, zoom_level ); +} + +static void draw_redraw ( VikWindow *vw ) +{ + vik_viewport_clear ( vw->viking_vvp); + + vik_layers_panel_draw_all ( vw->viking_vlp ); +} + +static void draw_mouse_motion (VikWindow *vw, GdkEventMotion *event) +{ + static VikCoord coord; + static struct UTM utm; + static struct LatLon ll; + static char pointer_buf[36]; + + vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord ); + vik_coord_to_utm ( &coord, &utm ); + a_coords_utm_to_latlon ( &utm, &ll ); + + g_snprintf ( pointer_buf, 36, "Cursor: %f %f", ll.lat, ll.lon ); + + if ( vw->pan_x != -1 ) + vik_viewport_pan_sync ( vw->viking_vvp, event->x - vw->pan_x, event->y - vw->pan_y ); + + vik_statusbar_set_message ( vw->viking_vs, 4, pointer_buf ); +} + +static void draw_scroll (VikWindow *vw, GdkEvent *event) +{ + if ( event->scroll.direction == GDK_SCROLL_UP ) + vik_viewport_zoom_in (vw->viking_vvp); + else + vik_viewport_zoom_out(vw->viking_vvp); + draw_update(vw); +} + +static void draw_release ( VikWindow *vw, GdkEventButton *event ) +{ + if ( event->button == 2 ) { /* move / pan */ + if ( ABS(vw->pan_x - event->x) <= 1 && ABS(vw->pan_y - event->y) <= 1 ) + vik_viewport_set_center_screen ( vw->viking_vvp, vw->pan_x, vw->pan_y ); + else + vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x, + vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y ); + draw_update ( vw ); + vw->pan_x = vw->pan_y = -1; + } + if ( vw->current_tool == TOOL_LAYER && ( event->button == 1 || event->button == 3 ) && + vik_layer_get_interface(vw->tool_layer_id)->tools[vw->tool_tool_id].callback_release ) + { + vik_layers_panel_tool ( vw->viking_vlp, vw->tool_layer_id, + vik_layer_get_interface(vw->tool_layer_id)->tools[vw->tool_tool_id].callback_release, + event, vw->viking_vvp ); + } +} + +static void draw_click (VikWindow *vw, GdkEventButton *event) +{ + if ( event->button == 2) { + vw->pan_x = (gint) event->x; + vw->pan_y = (gint) event->y; + } else if ( vw->current_tool == TOOL_ZOOM ) + { + vw->modified = TRUE; + vik_viewport_set_center_screen ( vw->viking_vvp, (gint) event->x, (gint) event->y ); + if ( event->button == 1 ) + vik_viewport_zoom_in (vw->viking_vvp); + else if ( event->button == 3 ) + vik_viewport_zoom_out (vw->viking_vvp); + draw_update ( vw ); + } + else if ( vw->current_tool == TOOL_RULER ) + { + struct LatLon ll; + VikCoord coord; + gchar *temp; + if ( event->button == 1 || event->button == 3 ) + { + vik_viewport_screen_to_coord ( vw->viking_vvp, (gint) event->x, (gint) event->y, &coord ); + vik_coord_to_latlon ( &coord, &ll ); + if ( vw->has_oldcoord ) + temp = g_strdup_printf ( "%f %f DIFF %f meters", ll.lat, ll.lon, vik_coord_diff( &coord, &(vw->oldcoord) ) ); + else + temp = g_strdup_printf ( "%f %f", ll.lat, ll.lon ); + + vik_statusbar_set_message ( vw->viking_vs, 3, temp ); + g_free ( temp ); + + vw->oldcoord = coord; + /* we don't use anything else so far */ + vw->has_oldcoord = TRUE; + } + else + { + vik_viewport_set_center_screen ( vw->viking_vvp, (gint) event->x, (gint) event->y ); + draw_update ( vw ); + } + } + else if ( vw->current_tool == TOOL_LAYER ) + { + vw->modified = TRUE; + if ( ! vik_layers_panel_tool ( vw->viking_vlp, vw->tool_layer_id, + vik_layer_get_interface(vw->tool_layer_id)->tools[vw->tool_tool_id].callback, + event, vw->viking_vvp ) ) + a_dialog_info_msg_extra ( GTK_WINDOW(vw), "A %s Layer must exist and be either selected or visible before you can use this function.", vik_layer_get_interface ( vw->tool_layer_id )->name ); + } +} + +static void draw_set_current_tool ( VikWindow *vw, guint mode ) +{ + vw->current_tool = mode; + draw_status ( vw ); +} + +static void draw_zoom ( VikWindow *vw, gint what ) +{ + switch (what) + { + case -3: vik_viewport_zoom_in ( vw->viking_vvp ); break; + case -4: vik_viewport_zoom_out ( vw->viking_vvp ); break; + case -1: vik_viewport_set_zoom ( vw->viking_vvp, 0.5 ); break; + case -2: vik_viewport_set_zoom ( vw->viking_vvp, 0.25 ); break; + default: vik_viewport_set_zoom ( vw->viking_vvp, what ); + } + draw_update ( vw ); +} + +void draw_goto ( VikWindow *vw, gint mode ) +{ + VikCoord new_center; + if ( mode == 1) + { + struct LatLon ll, llold; + vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &llold ); + if ( a_dialog_goto_latlon ( GTK_WINDOW(vw), &ll, &llold ) ) + vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &ll ); + else + return; + } + else + { + struct UTM utm, utmold; + vik_coord_to_utm ( vik_viewport_get_center ( vw->viking_vvp ), &utmold ); + if ( a_dialog_goto_utm ( GTK_WINDOW(vw), &utm, &utmold ) ) + vik_coord_load_from_utm ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &utm ); + else + return; + } + + vik_viewport_set_center_coord ( vw->viking_vvp, &new_center ); + draw_update ( vw ); +} + +static void menu_addlayer_cb ( VikWindow *vw, gint type ) +{ + if ( vik_layers_panel_new_layer ( vw->viking_vlp, type ) ) + { + draw_update ( vw ); + vw->modified = TRUE; + } +} + +static void menu_copy_layer_cb ( VikWindow *vw ) +{ + a_clipboard_copy ( vw->viking_vlp ); +} + +static void menu_cut_layer_cb ( VikWindow *vw ) +{ + a_clipboard_copy ( vw->viking_vlp ); + menu_delete_layer_cb ( vw ); +} + +static void menu_paste_layer_cb ( VikWindow *vw ) +{ + if ( a_clipboard_paste ( vw->viking_vlp ) ) + { + draw_update ( vw ); + vw->modified = TRUE; + } +} + +static void menu_properties_cb ( VikWindow *vw ) +{ + if ( ! vik_layers_panel_properties ( vw->viking_vlp ) ) + a_dialog_info_msg ( GTK_WINDOW(vw), "You must select a layer to show its properties." ); +} + +static void menu_delete_layer_cb ( VikWindow *vw ) +{ + if ( vik_layers_panel_get_selected ( vw->viking_vlp ) ) + { + vik_layers_panel_delete_selected ( vw->viking_vlp ); + vw->modified = TRUE; + } + else + a_dialog_info_msg ( GTK_WINDOW(vw), "You must select a layer to delete." ); +} + +static void menu_dynamic_tool_cb ( VikWindow *vw, guint id ) +{ + /* White Magic, my friends ... White Magic... */ + vw->current_tool = TOOL_LAYER; + vw->tool_layer_id = id >> 16; + vw->tool_tool_id = id & 0xff; + draw_status ( vw ); +} + +static void window_set_filename ( VikWindow *vw, const gchar *filename ) +{ + gchar *title; + if ( vw->filename ) + g_free ( vw->filename ); + if ( filename == NULL ) + { + vw->filename = NULL; + gtk_window_set_title ( GTK_WINDOW(vw), "Untitled" VIKING_TITLE ); + } + else + { + vw->filename = g_strdup(filename); + title = g_strconcat ( a_file_basename ( filename ), VIKING_TITLE, NULL ); + gtk_window_set_title ( GTK_WINDOW(vw), title ); + g_free ( title ); + } +} + +void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename ) +{ + switch ( a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename ) ) + { + case 0: + a_dialog_error_msg ( GTK_WINDOW(vw), "The file you requested could not be opened." ); + break; + case 1: + { + GtkWidget *mode_button; + gchar *buttonname; + if ( change_filename ) + window_set_filename ( vw, filename ); + switch ( vik_viewport_get_drawmode ( vw->viking_vvp ) ) { + case VIK_VIEWPORT_DRAWMODE_UTM: buttonname = "/View/UTM Mode"; break; + case VIK_VIEWPORT_DRAWMODE_EXPEDIA: buttonname = "/View/Expedia Mode"; break; + case VIK_VIEWPORT_DRAWMODE_GOOGLE: buttonname = "/View/Google Mode"; break; + default: buttonname = "/View/KH\\/Flat LatLon Mode"; + } + mode_button = gtk_item_factory_get_item ( vw->item_factory, buttonname ); + g_assert ( mode_button ); + vw->only_updating_coord_mode_ui = TRUE; /* if we don't set this, it will change the coord to UTM if we click Lat/Lon. I don't know why. */ + gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE ); + vw->only_updating_coord_mode_ui = FALSE; + + vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) ); + } + default: draw_update ( vw ); + } +} + +static void load_file ( VikWindow *vw, gboolean newwindow ) +{ + if ( ! vw->open_dia ) + { + vw->open_dia = gtk_file_selection_new ("Please select a GPS data file to open. " ); + gtk_file_selection_set_select_multiple ( GTK_FILE_SELECTION(vw->open_dia), TRUE ); + gtk_window_set_transient_for ( GTK_WINDOW(vw->open_dia), GTK_WINDOW(vw) ); + gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->open_dia), TRUE ); + } + if ( gtk_dialog_run ( GTK_DIALOG(vw->open_dia) ) == GTK_RESPONSE_OK ) + { + gtk_widget_hide ( vw->open_dia ); + if ( (vw->modified || vw->filename) && newwindow ) + g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_selection_get_selections (GTK_FILE_SELECTION(vw->open_dia) ) ); + else { + gchar **files = gtk_file_selection_get_selections (GTK_FILE_SELECTION(vw->open_dia) ); + gboolean change_fn = newwindow && (!files[1]); /* only change fn if one file */ + while ( *files ) { + vik_window_open_file ( vw, *files, change_fn ); + files++; + } + } + } + else + gtk_widget_hide ( vw->open_dia ); +} + +static gboolean save_file_as ( VikWindow *vw ) +{ + gboolean rv = FALSE; + const gchar *fn; + if ( ! vw->save_dia ) + { + vw->save_dia = gtk_file_selection_new ("Save as Viking File. " ); + gtk_window_set_transient_for ( GTK_WINDOW(vw->save_dia), GTK_WINDOW(vw) ); + gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_dia), TRUE ); + } + + while ( gtk_dialog_run ( GTK_DIALOG(vw->save_dia) ) == GTK_RESPONSE_OK ) + { + fn = gtk_file_selection_get_filename (GTK_FILE_SELECTION(vw->save_dia) ); + if ( access ( fn, F_OK ) != 0 || a_dialog_overwrite ( GTK_WINDOW(vw->save_dia), "The file \"%s\" exists, do you wish to overwrite it?", a_file_basename ( fn ) ) ) + { + window_set_filename ( vw, fn ); + rv = window_save ( vw ); + vw->modified = FALSE; + break; + } + } + gtk_widget_hide ( vw->save_dia ); + return rv; +} + +static gboolean window_save ( VikWindow *vw ) +{ + if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) ) + return TRUE; + else + { + a_dialog_error_msg ( GTK_WINDOW(vw), "The filename you requested could not be opened for writing." ); + return FALSE; + } +} + +static gboolean save_file ( VikWindow *vw ) +{ + if ( ! vw->filename ) + return save_file_as ( vw ); + else + { + vw->modified = FALSE; + return window_save ( vw ); + } +} + +static void clear_cb ( VikWindow *vw ) +{ + vik_layers_panel_clear ( vw->viking_vlp ); + window_set_filename ( vw, NULL ); + draw_update ( vw ); +} + +static void window_close ( VikWindow *vw ) +{ + if ( ! delete_event ( vw ) ) + gtk_widget_destroy ( GTK_WIDGET(vw) ); +} + +static void zoom_to ( VikWindow *vw ) +{ + gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp ); + if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) ) + { + vik_viewport_set_xmpp ( vw->viking_vvp, xmpp ); + vik_viewport_set_ympp ( vw->viking_vvp, ympp ); + draw_update ( vw ); + } +} + +static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png ) +{ + /* more efficient way: stuff draws directly to pixbuf (fork viewport) */ + GdkPixbuf *pixbuf_to_save; + gdouble old_xmpp, old_ympp; + GError *error = NULL; + + /* backup old zoom & set new */ + old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ); + old_ympp = vik_viewport_get_ympp ( vw->viking_vvp ); + vik_viewport_set_zoom ( vw->viking_vvp, zoom ); + + /* reset width and height: */ + vik_viewport_configure_manually ( vw->viking_vvp, w, h ); + + /* draw all layers */ + draw_redraw ( vw ); + + /* save buffer as file. */ + pixbuf_to_save = gdk_pixbuf_get_from_drawable ( NULL, GDK_DRAWABLE(vik_viewport_get_pixmap ( vw->viking_vvp )), NULL, 0, 0, 0, 0, w, h); + gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL ); + if (error) + { + fprintf(stderr, "Unable to write to file %s: %s", fn, error->message ); + g_error_free (error); + } + g_object_unref ( G_OBJECT(pixbuf_to_save) ); + + /* pretend like nothing happened ;) */ + vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp ); + vik_viewport_set_ympp ( vw->viking_vvp, old_ympp ); + vik_viewport_configure ( vw->viking_vvp ); + draw_update ( vw ); +} + +static void save_image_dir ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png, guint tiles_w, guint tiles_h ) +{ + gulong size = sizeof(gchar) * (strlen(fn) + 15); + gchar *name_of_file = g_malloc ( size ); + guint x = 1, y = 1; + struct UTM utm_orig, utm; + + /* *** copied from above *** */ + GdkPixbuf *pixbuf_to_save; + gdouble old_xmpp, old_ympp; + GError *error = NULL; + + /* backup old zoom & set new */ + old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ); + old_ympp = vik_viewport_get_ympp ( vw->viking_vvp ); + vik_viewport_set_zoom ( vw->viking_vvp, zoom ); + + /* reset width and height: do this only once for all images (same size) */ + vik_viewport_configure_manually ( vw->viking_vvp, w, h ); + /* *** end copy from above *** */ + + g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM ); + + make_dir(fn); + + utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp )); + + for ( y = 1; y <= tiles_h; y++ ) + { + for ( x = 1; x <= tiles_w; x++ ) + { + g_snprintf ( name_of_file, size, "%s%cy%d-x%d.%s", fn, VIKING_FILE_SEP, y, x, save_as_png ? "png" : "jpg" ); + utm = utm_orig; + if ( tiles_w & 0x1 ) + utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom); + else + utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom); + if ( tiles_h & 0x1 ) /* odd */ + utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom); + else /* even */ + utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom); + + /* move to correct place. */ + vik_viewport_set_center_utm ( vw->viking_vvp, &utm ); + + draw_redraw ( vw ); + + /* save buffer as file. */ + pixbuf_to_save = gdk_pixbuf_get_from_drawable ( NULL, GDK_DRAWABLE(vik_viewport_get_pixmap ( vw->viking_vvp )), NULL, 0, 0, 0, 0, w, h); + gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL ); + if (error) + { + fprintf(stderr, "Unable to write to file %s: %s", name_of_file, error->message ); + g_error_free (error); + } + + g_object_unref ( G_OBJECT(pixbuf_to_save) ); + } + } + + vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig ); + vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp ); + vik_viewport_set_ympp ( vw->viking_vvp, old_ympp ); + vik_viewport_configure ( vw->viking_vvp ); + draw_update ( vw ); + + g_free ( name_of_file ); +} + +static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along) +{ + VikWindow *vw = VIK_WINDOW(pass_along[0]); + GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]); + GtkSpinButton *zoom_spin = GTK_SPIN_BUTTON(pass_along[3]); + gdouble width_min, width_max, height_min, height_max; + gint width, height; + + gtk_spin_button_get_range ( width_spin, &width_min, &width_max ); + gtk_spin_button_get_range ( height_spin, &height_min, &height_max ); + + /* TODO: support for xzoom and yzoom values */ + width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / gtk_spin_button_get_value ( zoom_spin ); + height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / gtk_spin_button_get_value ( zoom_spin ); + + if ( width > width_max || width < width_min || height > height_max || height < height_min ) + a_dialog_info_msg ( GTK_WINDOW(vw), "Viewable region outside allowable pixel size bounds for image. Clipping width/height values." ); + + gtk_spin_button_set_value ( width_spin, width ); + gtk_spin_button_set_value ( height_spin, height ); +} + +static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along) +{ + GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]); + GtkSpinButton *zoom_spin = GTK_SPIN_BUTTON(pass_along[3]); + gchar *label_text; + gdouble w, h; + w = gtk_spin_button_get_value(width_spin) * gtk_spin_button_get_value(zoom_spin); + h = gtk_spin_button_get_value(height_spin) * gtk_spin_button_get_value(zoom_spin); + if (pass_along[4]) /* save many images; find TOTAL area covered */ + { + w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4])); + h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5])); + } + label_text = g_strdup_printf ( "Total area: %ldm x %ldm (%.3f sq. km)", (glong)w, (glong)h, (w*h/1000000)); + gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text); + g_free ( label_text ); +} + +static void draw_to_image_file ( VikWindow *vw, const gchar *fn, gboolean one_image_only ) +{ + /* todo: default for answers inside VikWindow or static (thruout instance) */ + GtkWidget *dialog = gtk_dialog_new_with_buttons ( "Save to Image File", GTK_WINDOW(vw), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_CANCEL, + GTK_RESPONSE_REJECT, + GTK_STOCK_OK, + GTK_RESPONSE_ACCEPT, + 0 ); + GtkWidget *width_label, *width_spin, *height_label, *height_spin; + GtkWidget *png_radio, *jpeg_radio; + GtkWidget *current_window_button; + gpointer current_window_pass_along[7]; + GtkWidget *zoom_label, *zoom_spin; + GtkWidget *total_size_label; + + /* only used if (!one_image_only) */ + GtkWidget *tiles_width_spin, *tiles_height_spin; + + + width_label = gtk_label_new ( "Width (pixels):" ); + width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 5000, 10, 100, 0 )), 10, 0 ); + height_label = gtk_label_new ( "Height (pixels):" ); + height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 5000, 10, 100, 0 )), 10, 0 ); + + zoom_label = gtk_label_new ( "Zoom (meters per pixel):" ); + /* TODO: separate xzoom and yzoom factors */ + zoom_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vik_viewport_get_xmpp(vw->viking_vvp), VIK_VIEWPORT_MIN_ZOOM, VIK_VIEWPORT_MAX_ZOOM/2.0, 1, 100, 3 )), 16, 3); + + total_size_label = gtk_label_new ( NULL ); + + current_window_button = gtk_button_new_with_label ( "Area in current viewable window" ); + current_window_pass_along [0] = vw; + current_window_pass_along [1] = width_spin; + current_window_pass_along [2] = height_spin; + current_window_pass_along [3] = zoom_spin; + current_window_pass_along [4] = NULL; /* used for one_image_only != 1 */ + current_window_pass_along [5] = NULL; + current_window_pass_along [6] = total_size_label; + g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along ); + + png_radio = gtk_radio_button_new_with_label ( NULL, "Save as PNG" ); + jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), "Save as JPEG" ); + + if ( ! vw->draw_image_save_as_png ) + gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE ); + + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), width_label, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), width_spin, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), height_label, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), height_spin, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), current_window_button, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), png_radio, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), jpeg_radio, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), zoom_label, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), zoom_spin, FALSE, FALSE, 0); + + if ( ! one_image_only ) + { + GtkWidget *tiles_width_label, *tiles_height_label; + + + tiles_width_label = gtk_label_new ( "East-west image tiles:" ); + tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 ); + tiles_height_label = gtk_label_new ( "North-south image tiles:" ); + tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 ); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_width_label, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_width_spin, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_height_label, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), tiles_height_spin, FALSE, FALSE, 0); + + current_window_pass_along [4] = tiles_width_spin; + current_window_pass_along [5] = tiles_height_spin; + g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along ); + g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along ); + } + gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), total_size_label, FALSE, FALSE, 0); + g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along ); + g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along ); + g_signal_connect ( G_OBJECT(zoom_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along ); + + draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */ + + gtk_widget_show_all ( GTK_DIALOG(dialog)->vbox ); + + if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) + { + gtk_widget_hide ( GTK_WIDGET(dialog) ); + if ( one_image_only ) + save_image_file ( vw, fn, + vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ), + vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ), + gtk_spin_button_get_value ( GTK_SPIN_BUTTON(zoom_spin) ), /* do not save this value, default is current zoom */ + vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ) ); + else { + if ( vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ) + save_image_dir ( vw, fn, + vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ), + vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ), + gtk_spin_button_get_value ( GTK_SPIN_BUTTON(zoom_spin) ), /* do not save this value, default is current zoom */ + vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ), + gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ), + gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) ); + else + a_dialog_error_msg ( GTK_WINDOW(vw), "You must be in UTM mode to use this feature" ); + } + } + gtk_widget_destroy ( GTK_WIDGET(dialog) ); +} + + +static void draw_to_image_file_cb ( VikWindow *vw ) +{ + GtkWidget *file_selector; + const gchar *fn; + file_selector = gtk_file_selection_new ("Save Image"); + + while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_OK ) + { + fn = gtk_file_selection_get_filename (GTK_FILE_SELECTION(file_selector) ); + if ( access ( fn, F_OK ) != 0 || a_dialog_overwrite ( GTK_WINDOW(file_selector), "The file \"%s\" exists, do you wish to overwrite it?", a_file_basename ( fn ) ) ) + { + gtk_widget_hide ( file_selector ); + draw_to_image_file ( vw, fn, TRUE ); + break; + } + } + gtk_widget_destroy ( file_selector ); +} + +static void draw_to_image_dir_cb ( VikWindow *vw ) +{ + GtkWidget *file_selector; + const gchar *fn; + file_selector = gtk_file_selection_new ("Choose a name for a new directory to hold images"); + + while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_OK ) + { + fn = gtk_file_selection_get_filename (GTK_FILE_SELECTION(file_selector) ); + if ( access ( fn, F_OK ) == 0 ) + a_dialog_info_msg_extra ( GTK_WINDOW(file_selector), "The file %s exists. Please choose a name for a new directory to hold images in that does not exist.", a_file_basename(fn) ); + else + { + gtk_widget_hide ( file_selector ); + draw_to_image_file ( vw, fn, FALSE ); + break; + } + } + gtk_widget_destroy ( file_selector ); +} + +/* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */ +static void window_change_coord_mode ( VikWindow *vw, VikViewportDrawMode drawmode ) +{ + if ( !vw->only_updating_coord_mode_ui ) + { + VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp ); + if ( olddrawmode != drawmode ) + { + /* this takes care of coord mode too */ + vik_viewport_set_drawmode ( vw->viking_vvp, drawmode ); + if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) { + vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM ); + } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) { + vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON ); + } + draw_update ( vw ); + } + } +} + +static void set_bg_color ( VikWindow *vw ) +{ + GtkWidget *colorsd = gtk_color_selection_dialog_new ("Choose a background color"); + GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp ); + gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color ); + gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color ); + if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK ) + { + gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(GTK_COLOR_SELECTION_DIALOG(colorsd)->colorsel), color ); + vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color ); + draw_update ( vw ); + } + g_free ( color ); + gtk_widget_destroy ( colorsd ); +} + +static GtkItemFactoryEntry menu_items[] = { + { "/_File", NULL, NULL, 0, "" }, + { "/File/_New", "N", newwindow_cb, 0, "", GTK_STOCK_NEW }, + { "/File/_Open", "O", load_file, TRUE, "", GTK_STOCK_OPEN }, + { "/File/A_ppend File", NULL, load_file, FALSE, "" }, + { "/File/_Save", "S", (GtkItemFactoryCallback) save_file, 0, "", GTK_STOCK_SAVE }, + { "/File/Save _As", NULL, (GtkItemFactoryCallback) save_file_as, 0, "", GTK_STOCK_SAVE_AS }, + { "/File/sep1", NULL, NULL, 0, "" }, + { "/File/_Generate Image File", NULL, draw_to_image_file_cb, 0, "" }, + { "/File/Generate Directory of Images", NULL, draw_to_image_dir_cb, 0, "" }, + { "/File/sep1", NULL, NULL, 0, "" }, + { "/File/_Close", "W", window_close, 0, "", GTK_STOCK_QUIT }, + { "/_View", NULL, NULL, 0, "" }, + { "/View/_UTM Mode", "u", (GtkItemFactoryCallback) window_change_coord_mode, VIK_VIEWPORT_DRAWMODE_UTM, "" }, + { "/View/_Expedia Mode", "e", (GtkItemFactoryCallback) window_change_coord_mode, VIK_VIEWPORT_DRAWMODE_EXPEDIA, "/View/UTM Mode" }, + { "/View/_Google Mode", "g", (GtkItemFactoryCallback) window_change_coord_mode, VIK_VIEWPORT_DRAWMODE_GOOGLE, "/View/UTM Mode" }, + { "/View/_KH\\/Flat LatLon Mode", "k", (GtkItemFactoryCallback) window_change_coord_mode, VIK_VIEWPORT_DRAWMODE_KH, "/View/UTM Mode" }, + { "/View/_Mercator (New Google)", "m", (GtkItemFactoryCallback) window_change_coord_mode, VIK_VIEWPORT_DRAWMODE_MERCATOR, "/View/UTM Mode" }, + { "/View/sep1", NULL, NULL, 0, "" }, + { "/View/_Go to Lat\\/Lon...", NULL, draw_goto, 1, "" }, + { "/View/Go to UTM...", NULL, draw_goto, 2, "" }, + { "/View/sep1", NULL, NULL, 0, "" }, + { "/View/Set Background Color...", NULL, set_bg_color, 0, "", GTK_STOCK_SELECT_COLOR }, + { "/View/sep1", NULL, NULL, 0, "" }, + { "/View/Zoom _In", "plus", draw_zoom, -3, "", GTK_STOCK_ZOOM_IN }, + { "/View/Zoom _Out", "minus", draw_zoom, -4, "", GTK_STOCK_ZOOM_OUT }, + { "/View/Zoom To...", "Z", zoom_to, 0, "" }, + { "/View/_Zoom", NULL, NULL, 0, "" }, + { "/View/Zoom/0.25", "1", draw_zoom, -2, "" }, + { "/View/Zoom/0.5", "2", draw_zoom, -1, "" }, + { "/View/Zoom/1", "3", draw_zoom, 1, "" }, + { "/View/Zoom/2", "4", draw_zoom, 2, "" }, + { "/View/Zoom/4", "5", draw_zoom, 4, "" }, + { "/View/Zoom/8", "6", draw_zoom, 8, "" }, + { "/View/Zoom/16", "7", draw_zoom, 16, "" }, + { "/View/Zoom/32", "8", draw_zoom, 32, "" }, + { "/View/Zoom/64", "9", draw_zoom, 64, "" }, + { "/View/Zoom/128", "0", draw_zoom, 64, "" }, + { "/View/sep1", NULL, NULL, 0, "" }, + { "/View/Background _Jobs...", "j", (GtkItemFactoryCallback) a_background_show_window, 0, "" }, + + { "/_Layers", NULL, NULL, 0, "" }, + { "/Layers/Cu_t", NULL, menu_cut_layer_cb, -1, "", GTK_STOCK_CUT }, + { "/Layers/_Copy", NULL, menu_copy_layer_cb, -1, "", GTK_STOCK_COPY }, + { "/Layers/_Paste", NULL, menu_paste_layer_cb, -1, "", GTK_STOCK_PASTE }, + { "/Layers/sep1", NULL, NULL, 0, "" }, + { "/Layers/_Properties", NULL, menu_properties_cb, -1, "", GTK_STOCK_PROPERTIES }, + { "/Layers/_Delete", NULL, menu_delete_layer_cb, -1, "", GTK_STOCK_DELETE }, + { "/Layers/Delete All", NULL, clear_cb, 0, "", GTK_STOCK_CLEAR }, + { "/Layers/sep1", NULL, NULL, 0, "" }, + /* Plus Dynamic */ + + { "/_Tools", NULL, NULL, 0, "" }, + { "/Tools/sep1", NULL, NULL, 0, "" }, + { "/Tools/_Zoom", "Z", draw_set_current_tool, TOOL_ZOOM, "" }, + { "/Tools/_Ruler", "R", draw_set_current_tool, TOOL_RULER, "" }, + /* Plus Dynamic */ +}; + +static gint nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]); +static GtkItemFactoryEntry tool_sep = { "/_Tools/sep1", NULL, NULL, 0, "" }; + +static GtkWidget *window_create_menubar( VikWindow *window ) +{ + GtkItemFactory *item_factory; + GtkAccelGroup *accel_group; + GtkItemFactoryEntry add_layer_item; + guint i, j, tmp; + + g_assert ( sizeof(guint16)*2 <= sizeof(guint) ); /* FIXME: should be a compiler warning */ + + accel_group = gtk_accel_group_new (); + item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "
", + accel_group); + gtk_item_factory_create_items (item_factory, nmenu_items, menu_items, window); + gtk_window_add_accel_group ( GTK_WINDOW(window), accel_group); + + add_layer_item.accelerator = NULL; + + for ( i = 0; i < VIK_LAYER_NUM_TYPES; i++ ) + { + /* TODO: FIXME: if name has a '/' in it it will get all messed up. why not have an itemfactory field with + name, serialized icon, shortcut, etc.? */ + add_layer_item.path = g_strdup_printf("/_Layers/New %s Layer", vik_layer_get_interface(i)->name ); + add_layer_item.callback = menu_addlayer_cb; + add_layer_item.callback_action = i; + if ( vik_layer_get_interface(i)->icon ) + { + add_layer_item.item_type = ""; + add_layer_item.extra_data = gdk_pixdata_serialize ( vik_layer_get_interface(i)->icon, &tmp ); + } + else + add_layer_item.item_type = ""; + gtk_item_factory_create_item ( item_factory, &add_layer_item, window, 1 ); + g_free ( add_layer_item.path ); + g_free ( (gpointer) add_layer_item.extra_data ); + + if ( vik_layer_get_interface(i)->tools_count ) + gtk_item_factory_create_item ( item_factory, &tool_sep, window, 1 ); + + for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) + { + add_layer_item.path = g_strdup_printf("/_Tools/%s", vik_layer_get_interface(i)->tools[j].name); + add_layer_item.callback = menu_dynamic_tool_cb; + add_layer_item.callback_action = ( i << 16 ) | j; + add_layer_item.item_type = ""; + gtk_item_factory_create_item ( item_factory, &add_layer_item, window, 1 ); + g_free ( add_layer_item.path ); + } + } + + window->item_factory = item_factory; + return gtk_item_factory_get_widget (item_factory, "
"); +} diff --git a/src/vikwindow.h b/src/vikwindow.h new file mode 100644 index 00000000..5027ea08 --- /dev/null +++ b/src/vikwindow.h @@ -0,0 +1,55 @@ +/* + * viking -- GPS Data and Topo Analyzer, Explorer, and Manager + * + * Copyright (C) 2003-2005, Evan Battaglia + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _VIKING_VIKWINDOW_H +#define _VIKING_VIKWINDOW_H +/* Requires or glib, and coords.h*/ + +#include +#include +#include + +G_BEGIN_DECLS + +#define VIK_WINDOW_TYPE (vik_window_get_type ()) +#define VIK_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), VIK_WINDOW_TYPE, VikWindow)) +#define VIK_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), VIK_WINDOW_TYPE, VikWindowClass)) +#define IS_VIK_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VIK_WINDOW_TYPE)) +#define IS_VIK_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), VIK_WINDOW_TYPE)) + +typedef struct _VikWindow VikWindow; +typedef struct _VikWindowClass VikWindowClass; + +struct _VikWindowClass +{ + GtkWindowClass window_class; + void (* newwindow) (VikWindow *vw); + void (* openwindow) (VikWindow *vw, const gchar *filename); +}; + +GType vik_window_get_type (); + +VikWindow *vik_window_new (); +void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean changefilename ); + +G_END_DECLS + +#endif diff --git a/viking-remote b/viking-remote new file mode 100755 index 00000000..568357ff --- /dev/null +++ b/viking-remote @@ -0,0 +1,120 @@ +#!/usr/bin/perl + +# +# geo://wpname:cmnt@34.15,-118.15/topo=/somedir,aerial=/someotherdir +# + +die "Usage: viking-remote uri\n" if ( $#ARGV < 0 ); + +if ( $ARGV[0] eq "debug" ) { + $DEBUG = 1; + shift @ARGV; +} else { + $DEBUG = 0; +} + +($ARGV[0] =~ m<(geo:)?/*([^/]*)(/(.*))?>) or die "Bad URI"; +$loc = $2; +$extras = $4; + +if ( $loc =~ /^(GC[0-9A-Z]+)$/ ) { + $gc = $loc; + $loc=`gcfetchtxt http://www.geocaching.com/seek/cache_details.aspx?wp=$loc|head -2|tail +2`; +} + +# have a lot of fun... +($loc =~ /^(([^:@]*)(:([^:@]*))?@)?N?(S?)\s*((([0-9'"`°o\-\.])|(° ))*)[, ]\s*E?(W?)\s*(([0-9'"`o\-\.]|(° ))*)(:([0-9\-\.]*))?$/) + or die "Bad URI"; + +$wp = $2 ? $2 : ( $gc ? $gc : "waypoint" ); +$cmt = $4; $lat = $6; $lon = $11; $alt = $15; + +$latfact = ($5 eq "S") ? -1 : 1; +$lonfact = ($10 eq "W") ? -1 : 1; + +if ( $lat =~ /^(-?)(\d*)[°'"`o] *([\d.]*)$/ ) { + $lat = ($2 + ($3/60)) * ($1 ? -1 : 1); +} +if ( $lon =~ /^(-?)(\d*)[°'"`o] *([\d.]*)$/ ) { + $lon = ($2 + ($3/60)) * ($1 ? -1 : 1); + } + +$lat *= $latfact; +$lon *= $lonfact; + +if ( $extras =~ /^(auto)?street/ ) { + $mode = "latlon"; + $zoom = 4.205; +} else { + $mode = "utm"; + $zoom = 4.0; +} + +if ( $DEBUG ) { + open(PIPE, "|cat"); +} else { + open(PIPE, "|viking -- -"); +} + +print PIPE < 0 ) { + $mapname = $mapanddir[0]; + $mapdir = $mapanddir[1]; + } + print PIPE <