Sun, 20 May 2007 06:19:49 +0000
merge of 'b98e72d4089afb8a1879e5fe9627cfb132ee88de'
and 'b2836a24d81e7a1bd1d21b3aea8794b094391344'
--- a/.cvsignore Mon Apr 16 00:43:53 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ -config.h -libtool -config.status -intl -ABOUT-NLS -compile -config.guess -config.sub -Doxyfile -ltconfig -ltmain.sh -install-sh -missing -aclocal.m4 -configure -config.h.in -stamp-h.in -Makefile.in -config.log -config.cache -Makefile -stamp-h -gaim.spec -depcomp -stamp-h1 -autom4te*.cache -configure.2.1x -confdefs.h -*.swp -win32-install-dir -.temp-gettextize -gaim.pc -*.apspec
--- a/.todo Mon Apr 16 00:43:53 2007 +0000 +++ b/.todo Sun May 20 06:19:49 2007 +0000 @@ -2,24 +2,6 @@ <title> Gaim TODO List </title> - <note priority="low" time="1036030063" done="1092939968"> - i18n/portability stuff - <comment> - makes more sense to put this in with specific items at this point - </comment> - <note priority="medium" time="1036028041" done="0"> - sounds/Makefile needs to use host CC, not target (that's the only part that seems broken for cross-compiling --Disconnect) - </note> - <note priority="low" time="1035996506" done="1057532505"> - i18n: icq i18n - <comment> - I consider this as good as it will get. utf8 works well. - </comment> - </note> - <note priority="low" time="1035996519" done="0"> - i18n: info dialog - </note> - </note> <note priority="high" time="1036029816"> UI stuff <note priority="high" time="1053306672" done="1089237474"> @@ -118,12 +100,6 @@ </note> <note priority="high" time="1036029923"> Prefs stuff - <note priority="high" time="1036027042" done="1063207482"> - check to make sure gaimrc properly unescapes things like a " in a password as the .gaimrc file is read in. (reports indicate this may be a problem, esp. for \ and / ) - <comment> - i think this is fixed by the new prefs - </comment> - </note> <note priority="veryhigh" time="1129562765"> make sure prefs save </note> @@ -142,12 +118,6 @@ </note> <note priority="high" time="1036030165"> Core stuff - <note priority="medium" time="1036038372" done="1129562842"> - remember previous state better. both away, and allow start to invisible. - <comment> - status rewrite - </comment> - </note> <note priority="low" time="1036038499" done="1129562864"> encryption and hash stuff currently in gaim consolidated to an api <comment> @@ -228,12 +198,6 @@ </note> <note priority="medium" time="1036030336"> In General (i don't know where this should be) - <note priority="veryhigh" time="1037658028" done="1092939805"> - internal sounds not working now (is this fixed?) - <comment> - libao fixed this, there is no "internal" option - </comment> - </note> <note priority="medium" time="1036039588" done="1063208189"> person support <comment> @@ -303,24 +267,12 @@ </note> <note priority="verylow" time="1036026433"> DISCUSSION: entries here are things i'm not sure are desirable or doable. - <note priority="medium" time="1043803233" done="1089237940"> - make blist.xml and/or .gaim as configureable as .gaimrc - <comment> - .gaim is now configurable at run time - </comment> - </note> <note priority="low" time="1036026529" done="1089237962"> do we really need some of the preferences? (Ignore TIK messages. Does anyone use that method of retrieving Away messages anymore? no, but it is small and it would be a piece of history lost ;-)) <comment> prefsslash04 </comment> </note> - <note priority="verylow" time="1036026575" done="1063208296"> - timestamp on debug messages? - <comment> - done some time ago - </comment> - </note> <note priority="low" time="1036026508"> "console beep" sound option should use different frequency beeps for different events, or at least give some way to allow this behavior. </note>
--- a/AUTHORS Mon Apr 16 00:43:53 2007 +0000 +++ b/AUTHORS Sun May 20 06:19:49 2007 +0000 @@ -1,8 +1,10 @@ gaim - the Pimpin' Penguin IM Clone that's Good for the Soul ============================================================ -We've got an IRC room now too, #gaim on irc.freenode.net (#wingaim for Windows -users). Come check us out. +For a complete list of all contributors, see the COPYRIGHT file. + +We've got an IRC room now too, #gaim on irc.freenode.net (#wingaim for +Windows users). Come check us out. Current Developers: ------------------ @@ -17,23 +19,24 @@ Daniel 'datallah' Atallah Ethan 'Paco-Paco' Blanton - Developer -Herman Bloggs - Win32 Port +Thomas Butter - Developer +Sadrul Habib Chowdhury - Developer Mark 'KingAnt' Doliner - Developer Christian 'ChipX86' Hammond - Developer & Webmaster Gary 'grim' Kramlich - Developer Richard 'rlaager' Laager Christopher 'siege' O'Brien - Developer +Bartosz Oler - Developer Etan 'deryni' Reisner - Developer Tim 'marv' Ringenbach - Developer Luke 'LSchiere' Schierer - Support +Evan Schoenberg - Developer Stu 'nosnilmot' Tomlinson - Developer Nathan 'faceprint' Walp - Developer Crazy Patch Writers: --------------------- - +------------------- Ka-Hing 'javabsp' Cheung -Sadrul Habib Chowdhury Felipe 'shx' Contreras Decklin Foster Peter 'Bleeter' Lawler @@ -42,7 +45,8 @@ Kevin 'SimGuy' Stange Retired Developers: --------- +------------------ +Herman Bloggs - Win32 Port Jim Duchek <jim@linuxpimps.com> - maintainer Rob Flynn <gaim@robflynn.com> - maintainer Adam Fritzler - libfaim maintainer @@ -50,3 +54,74 @@ Jim Seymour - Jabber developer Mark Spencer <markster@marko.net> - original author Eric Warmenhoven <eric@warmenhoven.org> - lead developer + +Other Contributions: +------------------- +Much thanks to Evan Martin <martine@cs.washington.edu> for writing +GtkSpell <http://gtkspell.sourceforge.net> responsible for the +"Highlight misspelled words" feature and for gtk-nativewin +<http://bunny.darktech.org/cvs/gtk-nativewin/> the default GTK+-2.0 +engine used in our win32 port. + +** LOGO DESIGNED BY: Naru Sundar ** + +Peter Teichiman <peter@helixcode.com> +Larry Ewing +Jeramey A. Crawford + Thanks to these boys. Peter and Larry managed to stomp + out a large list of Mem Leaks. Jeramey found the remaining + onees and pointed me to those. Props to the boys at + Helix Code. Thanks guys. + +Nathan Walp + A healthy amount of patches for the Jabber plugin + +Neil Sanchala + Wrote most of the Zephyr plugin + +Arkadiusz Miskiewicz + Wrote the Gadu-Gadu plugin + +David Prater <IM: dRaven43> draven@tcsx.net + Log and Colour Button Images + +Sébastien Carpe <IM: Seb Carpe> + Base HTTP Proxy Support + +Ari Pollak <IM: Ari Pollak> compwiz.dhs.org + Resize conversation window patch + +Decklin Foster + Many GUI improvements, other nifty additions and fixes + +David <IM: CrazyDavy> + The neato-bigger text box + +S D Erle + Writing a cool perl script to translate WinAIM lists to gaim + +BMiller + A good collection of stuff. %n for away messages, import winaim + lists, pic/text/pic+text for buttons, among others + +Lance Rocker + Improved HTML formatting in logs, plus lots of debugging on *BSD. + +ergofobe: + GNOME Url handler patch + +Justin M. Ward <justin@yossman.net>: + Alphabetical Away Messages patch + +G. Sumner Hayes <IM: SumnerFool> Security Patches + +Brian Ryner for a little make file patch :) + +Ryan C. Gordon - I still think you look like Silent Bob. + +Elliot Tobin <elliot@bha.udel.edu> + +Thanks to Jeroen van der Vegt for the initial smiley plugin and images. + +The OpenQ Team + Wrote the QQ plugin (see AUTHORS in the qq directory)
--- a/COPYRIGHT Mon Apr 16 00:43:53 2007 +0000 +++ b/COPYRIGHT Sun May 20 06:19:49 2007 +0000 @@ -1,27 +1,36 @@ Gaim -Copyright (C) 1998-2005 by the following: +Copyright (C) 1998-2006 by the following: If you have contributed to Gaim, you deserve to be on this list. Contact us (see: AUTHORS) and we'll add you. +Saleem Abdulrasool Dave Ahlswede Manuel Amador Matt Amato +Geoffrey Antos Daniel Atallah Paul Aurich Patrick Aussems +Anibal Avelar +Ali Albazaz Alex Badea John Bailey +Chris Banal Luca Barbato Levi Bard +Ryan Barrett Kevin Barry +Lukas Barth Derek Battams Martin Bayard Curtis Beattie Dave Bell Igor Belyi Brian Bernas +Paul Betts Jonas Birmé +George-Cristian Bîrzan Eric Blade Ethan Blanton Joshua Blanton @@ -31,22 +40,30 @@ Jason Boerner Graham Booker Paolo Borelli +Julien Bossart Craig Boston Chris Boyle Derrick J Brashear Matt Brenneke Jeremy Brooks +Jonathan Brossard Philip Brown Sean Burke Thomas Butter +Trevor Caira Andrea Canciani +Damien Carbery Michael Carlson +Keegan Carruthers-Smith Steve Cavilia Julien Cegarra Cerulean Studios, LLC Jonathan Champ +Christophe Chapuis +Patrick Cheung Ka-Hing Cheung Sadrul Habib Chowdhury +Brian Chu Arturo Cisneros, Jr. Vincas Ciziunas Jonathan Clark @@ -54,6 +71,8 @@ Eoin Coffey Jason Cohen Todd Cohen +Jono Cole +Lorenzo Colitti Nathan Conrad Felipe Contreras Alex Converse @@ -62,7 +81,10 @@ Adam Cowell Palmer Cox Jeramey Crawford +Michael Culbertson Martijn Dekker +Philip Derrin +Taso N. Devetzis Balwinder Singh Dheeman Andrew Dieffenbach Finlay Dobbie @@ -78,6 +100,7 @@ Nelson Elhage Ignacio J. Elia Brian Enigma +Mattias Eriksson Stefan Esser Steffen Eschenbacher Marc Etcheverry @@ -85,6 +108,7 @@ Gábor Farkas Jesse Farmer Gavan Fantom (gavan) +Leonardo Fernandes David Fiander Rob Flynn <gaim@robflynn.com> Rob Foehl (rwf) @@ -97,10 +121,14 @@ Adam Fritzler Max G. François Gagné +Andrew Gaul Evgueni V. Gavrilov Ignacy Gawedzki +Georgi Georgiev +Ike Gingerich Gustavo Giráldez Richard Gobeille +Ian Goldberg Michael Golden Charlie Gordon Ryan C. Gordon @@ -117,22 +145,35 @@ Mike Heffner Benjamin Herrenschmidt Fernando Herrera +hjheins +Hil Casey Ho Iain Holmes Joshua Honeycutt Nigel Horne Juanjo Molinero Horno +Nathanael Hoyle +Greg Hudson Magnus Hult Karsten Huneycutt +Kevin Hunter Rian Hunter Thomas Huriaux +Vitaliy Ischenko +Scott Jackson +Hans Petter Jansson Henry Jen Benjamin Kahn +Anders Kaseorg Praveen Karadakal John Kelm +Jochen Kemnade Akuke Kok +Konstantin Korikov Cole Kowalski Gary Kramlich +Jan Kratochvil +Andrej Krivulčík Patrik Kullman Tuomas Kuosmanen Tero Kuusela @@ -156,6 +197,7 @@ Uli Luckas Matthew Luckie Mike Lundy +Jason Lynch Lucio Maciel Brian Macke Paolo Maggi @@ -170,16 +212,20 @@ Torrey McMahon Robert McQueen Robert Mibus +Lars T. Mikkelsen Benjamin Miller Kevin Miller Paul Miller Arkadiusz Miskiewicz Andrew Molloy +Benjamin Moody Tim Mooney Sergio Moretto Christian Muise Richard Nelson +Dennis Nezic Matthew A. Nicholson +Henning Norén Szilard Novaki Novell Padraig O'Briain @@ -188,6 +234,7 @@ Ruediger Oertel Gudmundur Bjarni Olafsson Bartosz Oler +Shawn Outman Nathan Owens (pianocomp81) John Oyler Matt Pandina @@ -196,24 +243,31 @@ Havoc Pennington Ted Percival Eduardo Pérez +Celso Pinto Joao Luís Marques Pinto Aleksander Piotrowski Julien Pivotto Ari Pollak Robey Pointer +Stephen Pope Nathan Poznick +Jory A. Pratt Brent Priddy Federicco Mena Quintero Yosef Radchenko David Raeman +R. Ramkumar +Mart Raudsepp Etan Reisner Kristian Rietveld Pekka Riikonen Tim Ringenbach Dennis Ristuccia +Lee Roach Rhett Robinson Luciano Miguel Ferreira Rocha Andrew Rodland +Miguel Rodríguez (migrax) Jason Roth Jean-Francois Roy Sam S. @@ -222,12 +276,15 @@ Tom Samstag Neil Sanchala Laurent Sansonetti +Andrew Sayman Alceste Scalas Carsten Schaar +Matteo Settenvini Colin Seymour Luke Schierer Ralph Schmieder David Schmitt +Mark Schneider Evan Schoenberg Federico Schwindt Torrey Searle @@ -241,6 +298,7 @@ John Silvestri Craig Slusher Alex Smith +Brad Smith Malcolm Smith David Smock Phil Snowberger @@ -248,6 +306,7 @@ Sony Computer Entertainment America, Inc. Mark Spencer Lex Spoon +Chris Stafford Kevin Stange Richard Stellingwerff Charlie Stockman @@ -274,16 +333,21 @@ Brad Turcotte Junichi Uekawa István Váradi +Martijn van Beers Philip Van Hoof Kristof Vansant James Vega David Vermeille Sid Vicious Bjoern Voigt +Wan Hing Wah Philip Walford Nathan Walp +Jonty Wareing Eric Warmenhoven Adam J. Warrington +Denis Washington +Zsombor Welker Andrew Wellington Adam Wendt Dave West @@ -293,9 +357,12 @@ Dan Willemsen Jason Willis Matt Wilson +Dan Winship +Scott Wolchok Pui Lam Wong Justin Wood Ximian +Ma Xuan Jared Yanovich Timmy Yee Nickolai Zeldovich
--- a/ChangeLog Mon Apr 16 00:43:53 2007 +0000 +++ b/ChangeLog Sun May 20 06:19:49 2007 +0000 @@ -1,6 +1,17 @@ Gaim: The Pimpin' Penguin IM Client that's good for the soul! version 2.0.0: + Build Changes: + * With the Core/UI split completed, it is now possible to build Gaim + without any UIs, creating a libgaim library upon which other UIs + may be constructed + * A new ncurses-based console UI called gaim-text is now available + (Sadrul Habib Chowdhury, Google Summer of Code) + * Reorganized the source tree to split apart the code for the UI + changes and libgaim targets + * libxml2 is now required. We switched from gmarkup to libxml2 for + more correct XML parsing. + Status System: * The code dealing with buddy and account status, away messages, away states, online/offline, etc has been completely rewritten. @@ -12,6 +23,8 @@ becomes idle, load the "Buddy State Notification" plugin Buddy List: + * Performance when manipulating and displaying the buddy list has + been significantly improved (Aaron Sheldon, Google Summer of Code) * Buddy icons are now shown in tooltips (Felipe Contreras) * Tooltips now contain additional information about a "Person" that contains multiple online buddies @@ -20,6 +33,15 @@ * If Gaim is exited with the buddy list hidden in the docklet, it will remain hidden when Gaim is started again (Scott Shedden) * Improved buddy list searching with CTRL+F + * Ability to set a buddy icon for all of your accounts at once via + the buddy list (You can still set per-account icons via the + account editor) + * The space wasted by the group expanders has been eliminated and + the expander setting in .gtkrc-2.0 is no longer needed + * Authorization requests don't popup new dialogs any more. They are + displayed at the bottom of the buddy list instead. + * New mail notifications don't popup new dialogs any more. The are + displayed at the top of the buddylist instead. Conversations and Chats: * Timestamps honor the locale. To use the traditional style, @@ -44,19 +66,33 @@ IM/Send File/Get info/ignore the user * Added tab management options to the tab right-click menu (Sadrul Habib Chowdhury) - * Brand new message queueing system (Casey Harkins) + * Brand new message queueing system. Sounds are played when a + message is queued rather than when the message is dequeued + (Casey Harkins) * Ability to find the last message from a user in a chat (Levi Bard and Sadrul Habib Chowdhury) + * Formatting is preserved across messages + (There are known issues with pasting formatted text. Either use "Paste + as Plain Text", hit Ctrl-R after pasting, or use the Clear Formatting + button on the toolbar.) + * Performance while joining large chat rooms has been significantly + improved (Aaron Sheldon, Google Summer of Code) Sounds: * Beautiful new default sounds (Brad Turcotte) - * Use libao for playing sounds via NAS instead of accessing NAS directly + * Use GStreamer for playing sounds, instead of libao * A volume control in the preferences (Casey Harkins) Log Viewer: * Log viewer aggregates logs from the same "Person" * When opening the log viewer, show the most recent log by default (Peter McCurdy) + * Logs are now saved with the current timezone, which is displayed + in the log viewer + * Text logs are linkified, so URLs are clickable + * The old logger now caches file offsets, so opening the log viewer + for buddies with old logs should be much faster now if you have large + log files (except the first time for a log, when the cache is built) Plugins: * Plugins are now accessed through a separate dialog from the Tools @@ -85,6 +121,17 @@ * The functionality of the auto-reconnect plugin has been moved into the Gaim core, and the plugin itself has been removed. + * 'Highlight when nick said' option added to Message Notification + plugin. + * The system tray icon is now transparent (Dan Winship) + * New Log Reader plugin that can read and display logs from Adium, + MSN Messenger, and Trillian in the log viewer + * New Contact Availability plugin that attempts to predict the + times when people in your buddylist will most likely respond + to you, based on times in the past when they have responded + (Geoffrey Foster, Google Summer of Code) + * A few new plugins: Autoaccept, Autoreply, Buddy Notes, New Line, Offline + Message Emulation, Conversation Colors and Markerline MSN Features: * Custom smiley receiving support (Irving Cordova & Francesco Fracassi) @@ -104,8 +151,10 @@ AIM/ICQ Features: * ICQ file transfer support with newer ICQ clients (Jonathan Clark, Google Summer of Code) - * Many overall improvements to OSCAR file transfers (Jonathan Clark, - Google Summer of Code) + * Many overall improvements to AIM and ICQ file transfers (Jonathan + Clark, Google Summer of Code) + * Support for pausing and resuming AIM and ICQ file transfers + (Graham Booker) * Ability to set ICQ "require authorization" and "web aware" setting (Ettore Simone) * ICQ encoding fix for offline buddies (Ilya Konstantinov) @@ -116,6 +165,7 @@ channel or change your nick * Added /nickserv, /memoserv, /chanserv and /operserv commands (Joao Luís Marques Pinto) + * Added CTCP VERSION via /version (Andrej Krivulčík) Jabber Features: * Support for SRV lookups @@ -132,9 +182,13 @@ * Bonjour (Rendezvous) protocol support (Juanjo Molinero Horno, Google Summer of Code) * Updated Gadu-Gadu protocol support (Bartosz Oler, Google Summer of - Code) + Code). This requires the libgadu library. See + http://gaim.sourceforge.net/faq2.php#libgadu for more information. * SIP/SIMPLE support (Thomas Butter, Google Summer of Code) * Sametime protocol support + Requires the meanwhile library: http://meanwhile.sourceforge.net + * QQ protocol support (Mark Huetsch, Google Summer of Code) + * Removed support for the Napster and TOC protocols Other Noteworthy Changes: * UPnP and NAT traversal support (Adam J. Warrington, Google Summer of @@ -143,8 +197,6 @@ better at lower resolutions (Sadrul Habib Chowdhury) * New "find buddy" results dialog (Alex Converse) * People using input methods can now use Enter again - * GNOME users can open received files by clicking on "Open" in the - file transfer window * Mouse-over hyperlink coloring is now themeable * Buddy Pounces now have a proper management window. (Kevin Stange) * Buddy icons maintain aspect ratio when resized @@ -157,7 +209,13 @@ The spaces (and now backslashes) must be backslash-escaped. (Sadrul Habib Chowdhury) * New e-mail notices are now grouped into one dialog. - (Sadrul Habib Chowdhury) + (Sadrul Habib Chowdhury, Chris Stafford) + * "Open" in the File Transfer window integrates with GNOME, KDE, and + Windows and falls back to the browser in other environments. + * On Mac OS X, the keyboard/mouse idle time pref now uses system idle + time instead of X11 idle time (Michael Culbertson) + * Autocomplete in the buddy pounce dialog (Sadrul Habib Chowdhury) + * Non-blocking socket I/O is used in most protocol plugins Preference Changes: * Preferences have been substantially reorganized and cleaned up @@ -652,7 +710,7 @@ * Removed "Icons on tabs", default to yes * Removed "Sounds when you log in", default to no * Removed "Seconds before resending autoresponse", default to 600 - seconds + seconds * Removed "Send autoresponse in active conversations", default to no * Removed "Show people joining in window", default to yes * Removed "Show people leaving in window", default to yes @@ -920,7 +978,7 @@ list as well. * Events in a conversation (user logged in, logged out, window closed, etc.) now grey the tab. - * Various bug fixes (larne from irc, Tim Ringenbach, Bjoern + * Various bug fixes (larne from irc, Tim Ringenbach, Bjoern Voigt, Paul A (darkrain)) version 0.66 (07/18/2003): @@ -982,10 +1040,10 @@ * Napster protocol updates (Thanks Auke Kok). version 0.62 (04/23/2003): - * Keyboard shortcuts in the buddy list work again (Thanks Joe + * Keyboard shortcuts in the buddy list work again (Thanks Joe Clarke). * Support for Jabber XHTML messages - * Ability to re-request authorization from ICQ and Jabber users by right + * Ability to re-request authorization from ICQ and Jabber users by right clicking on them in your buddy list. * Improved Zephyr internationalization. * Bug causing 'Hide on Send' windows to be lost forever fixed. @@ -1001,7 +1059,7 @@ * Re-implemented the logout icons. * New icons for "away" and "aol" (Thanks, Moses Lei) -version 0.60 (04/04/2003): +version 0.60 (04/04/2003): Core: * Auto-loading protocol plugins. * Plugins dialog and perl script menu merged into preferences. @@ -1013,7 +1071,7 @@ * Added support for automake 1.6. * aim:// URI's supported with gaim-remote command. * Quit Gaim remotely with gaim-remote. (Thanks, John Silvestri) - * Added rudimentary support for X11R6 session management. (Thanks, + * Added rudimentary support for X11R6 session management. (Thanks, Robert McQueen) * Conversation backend and UI are now separated. (Thanks, Christian Hammond) @@ -1021,11 +1079,11 @@ Lichtmaier) * As a side effect of the above: IPv6 support. Tested only with IRC (you can receive ipv6 chat requests from irssi!). - + Plugins: - * Tray icon plugin--replaces the old GNOME applet. You'll need - the panel Notification Area applet (aka system-tray-applet) - for GNOME 2, or the Kicker for KDE 3.1. (Thanks, Robert + * Tray icon plugin--replaces the old GNOME applet. You'll need + the panel Notification Area applet (aka system-tray-applet) + for GNOME 2, or the Kicker for KDE 3.1. (Thanks, Robert McQueen, Nicolás Lichtmaier, Kristian Rietveld, Ari Pollak & Patrick Aussems) * Added GAIM::remove_event_handler and made set_info short @@ -1040,9 +1098,9 @@ AIM/ICQ: * TOC no longer compiles statically by default--use OSCAR. * ICQ plugin no longer gets built--use OSCAR. - * Server-stored buddy lists for ICQ with full support for + * Server-stored buddy lists for ICQ with full support for authorization (Thanks, Mark Doliner) - * File send/receive support for Aim over Oscar (Thanks, William T. + * File send/receive support for Aim over Oscar (Thanks, William T. Mahan and Mark Doliner) * Non-direct connect typing notification for AIM over OSCAR. (Thanks, Mark Doliner) @@ -1055,19 +1113,19 @@ * Ability to add screenname@mac.com people to AIM buddy lists. (Thanks, Graham Booker) * Ability to change ICQ password. (Thanks, Mark Doliner) - * Option to have AIM notify you if you have + * Option to have AIM notify you if you have unread mail. (Thanks, Mark Doliner) * Parse URL messages, Contact Sending and Pager Messages in ICQ. (Thanks, Mark Doliner) * use snprintf instead of sprintf. (Thanks, William T. Mahan) - * Fixed crashbug on empty rvous requests. (Thanks Brandon Scott + * Fixed crashbug on empty rvous requests. (Thanks Brandon Scott (Xeon) for pointing this out, and Matt Pandina for the patch) * Nice Oscar changes--mostly internal. (Thanks, Mark Doliner) IRC: * Added more IRC slash commands -- /W, /VERSION, /MODE, /CTCP stuff, -- and other cool IRC enhancments. (Thanks, Jonas Birmé) - * IRC's /topic with no argument displays the current topic (Thanks, + * IRC's /topic with no argument displays the current topic (Thanks, Mark Doliner) * DCC File Receive support for IRC. * Optional password on IRC accounts. (Thanks, Christian Hammond) @@ -1079,21 +1137,21 @@ * Jabber roster updated on group renames. * Fixed a possible segfault when signing off Jabber. (Thanks, Craig Boston) - * Improved typing notification support for Jabber and + * Improved typing notification support for Jabber and Yahoo! (Thanks, Nathan Walp) * File receive support for Jabber. (Thanks, Nathan Walp) MSN: * MSN users are notified when the other party closes the conversation window. (Thanks, Christian Hammond) - * File receive support for MSN. (Thanks, Christian Hammond) + * File receive support for MSN. (Thanks, Christian Hammond) Internationalization: - * Now using libiconv for better i18n support (Thanks, Junichi + * Now using libiconv for better i18n support (Thanks, Junichi Uekawa) * Lots of i18n fixes (Thanks Matt Wilson, Ethan Blanton, A Lee) * Correct i18n handling for many parts of AIM/ICQ, including - instant messages, away messages, and profiles (Thanks, + instant messages, away messages, and profiles (Thanks, Ethan Blanton) * Improved MSN internationalization (Thanks, A Lee) @@ -1108,7 +1166,7 @@ * Fix first message in tab not displaying bug (Thanks, Etan Reisner) * Changed some default options * Updated desktop and window icons (Thanks, Robert McQueen) - * Switch the .desktop file to the new KDE/GNOME common vfolder + * Switch the .desktop file to the new KDE/GNOME common vfolder format (Thanks, Robert McQueen) * Removed all deprecated GTK calls. Now 100% GTK 2. (Thanks Nathan Walp, Christian Hammond, Ari Pollak, Ethan Blanton, Robert McQueen) @@ -1117,7 +1175,7 @@ Robert McQueen) * Can get info for ICQ and Jabber users from the "Edit Buddies" tab (Thanks, Brian Bernas) - * Code cleanups and fixes (Thanks, Federico Mena Quintero and + * Code cleanups and fixes (Thanks, Federico Mena Quintero and Ka-Hing Cheung) * Word-wrapping on mail notification text (Thanks, Andrew Molloy) * Generic File Transfer PRPL interface (Thanks, Christian Hammond) @@ -1127,10 +1185,10 @@ version 0.59.9 (03/01/2003): * Updated zh_TW.po file (Thanks breeze833) - * Fix an oscar bug that caused some messages from + * Fix an oscar bug that caused some messages from AOL 8.0 to be dropped (Thanks Mark Doliner) * Changed "openprojects" to "freenode" in irc.c - * Fixed charset conversion on systems which use a BOM for UCS-4 + * Fixed charset conversion on systems which use a BOM for UCS-4 (Thanks, Alfredo Pen~a, Ethan Blanton) * Fixed a typo in the man page (Thanks Eric S. Raymond) @@ -1175,7 +1233,7 @@ * Changed "color" binding to Ctrl-K. * Unaliaising a person in the "Online" tab will show up in the "Edit" tab as well (Thanks, Jason Willis) - * Internationalization fixes, esp. with UTF-8 locales + * Internationalization fixes, esp. with UTF-8 locales (Thanks Matt Wilson and Ethan Blanton) version 0.59.2 (09/09/2002): @@ -1212,9 +1270,9 @@ Robert McQueen) * Now using libiconv for better i18n support (Thanks Junichi Uekawa) - * Will work with Perl 5.8 (thanks, Timothy Lee and Dan + * Will work with Perl 5.8 (thanks, Timothy Lee and Dan Colascione) - * Fix for HTTP proxies (thanks, Ethan Blanton) + * Fix for HTTP proxies (thanks, Ethan Blanton) * Read proxy environment variables. (thanks, Christian Hammond) * Use the pretty gaim.png for our menu entry. * Added support for gettext 0.11.x. @@ -1238,7 +1296,7 @@ * Perl scripts can play Gaim sounds (thanks Andrew Rodland) * Internal sounds can be played by commands (thanks Lex Spoon) * Auto-login item in applet menu (thanks Chris Boyle) - * Fixed MSN "Unkown Error Code", "Already there", and + * Fixed MSN "Unkown Error Code", "Already there", and "Already in opposite list" errors * Changed "Play sound" button to "Mute" button * You can now have "reserved" chars in IM and proxy passwords @@ -1255,7 +1313,7 @@ entirely * Gaim can now handle messages from Mac ICQ and Miranda ICQ (Thanks, Mark Doliner) - * Added Mozilla to browser options and changed KFM to + * Added Mozilla to browser options and changed KFM to Konqueror. * Can now set the server and port for MSN and Napster * MSN Internationalization (Thanks Felipe Contreras and @@ -1266,7 +1324,7 @@ version 0.58 (05/13/2002): * Better applet transparency * Option to raise buddy list on signons/signoffs - * Formatting of incoming MSN messages + * Formatting of incoming MSN messages * Get Info from menu multiple-account-aware (thanks Brian Bernas) * Hide and unhide functions for the filectl plugin. @@ -1316,7 +1374,7 @@ * Fixed MSN privacy settings * Group deletion fix (Thanks Mark Doliner) * Alias/Group syncronization for Jabber (Thanks JSeymour) - * Fixed broken signal handling in gdm-started GNOME sessions + * Fixed broken signal handling in gdm-started GNOME sessions (Thanks Jim Seymour, Vann, Robert McQueen) * Oscar group syncronization (Thanks, Mark Doliner) * ICQ Authorization via Oscar (Thanks, Mark Doliner) @@ -1324,18 +1382,18 @@ version 0.55 (03/29/2002): * Jabber improvements (Thanks Jim Seymour) * Various sound cleanups (Thanks Robert McQueen) - * Login process shown in single window (Thanks Michael + * Login process shown in single window (Thanks Michael Golden) * Can reorder your accounts in the account editor (Thanks Luke Schierer) * Shows "mobile" icon for Oscar buddies using mobile devices (Thanks Mark Doliner) * Fixed bug in MSN smilies that crashed PPC (and other?) platforms - * HTTP Proxy settings now HTTP compliant (Thanks Robert McQueen) + * HTTP Proxy settings now HTTP compliant (Thanks Robert McQueen) * Speling corections (Thanks Tero Kuusela) * Oscar list icon fixes (Thanks Mark Doliner) * Oscar idle times work again (Thanks Mark Doliner) - * Protocol icons on Edit Buddies tab (Thanks Christian Hammond) + * Protocol icons on Edit Buddies tab (Thanks Christian Hammond) version 0.54 (03/14/2002): * Compiles without GdkPixbuf again @@ -1346,8 +1404,8 @@ * Option to globally disable Buddy Icon animation (Thanks Luke Schierer) * Numerous bugfixes - * Yahoo! will tell you when your buddies are playing Yahoo! - games and give you the ability to join them + * Yahoo! will tell you when your buddies are playing Yahoo! + games and give you the ability to join them * Yahoo! can receive offline messages * IRC can do DCC chat. * IRC will convert HTML formatting to mIRC formatting. @@ -1450,7 +1508,7 @@ * Better applet icon drawing (thanks to Ari Pollak) * An extraordinary number of bug fixes * Ability to stop animation on buddy icons, restart animation, - hide certain buddy icons, and save people's buddy icons, all + hide certain buddy icons, and save people's buddy icons, all through a right-click menu * Event handlers in perl passed arguments as elements of an array rather than all concatenated as a string, making @@ -1461,7 +1519,7 @@ * Add buddy dialog now lets you select which protocol to add the buddy to * Pressing 'signon' on the first screen for accounts that - do not require passwords no longer incorrectly displays + do not require passwords no longer incorrectly displays an error message. version 0.45 (10/04/2001): @@ -1690,7 +1748,7 @@ MSN (plugins/msn) version 0.11.0-pre2 (12/04/2000): - * Fixed a segfault with a bad util.c + * Fixed a segfault with a bad util.c version 0.11.0-pre1 (12/03/2000): * Multiple connections @@ -1732,7 +1790,7 @@ * A third conversation window display preference. * Better support for things like Sawfish -version 0.10.0 (09/11/2000): +version 0.10.0 (09/11/2000): * New Smiley Faces and Pixmaps added. * Smiley faces now properly wrap in the conversation windows. * Smiley dialog @@ -1746,7 +1804,7 @@ * Redesigned preferences dialog * Redesigned conversation dialog * Removed the Lag-O-Meter (Lag-O-Meter is now a plugin) - * Socks 4/5 Proxy works + * SOCKS 4a/5 proxy works * Buddy Pounces are now saved in .gaimrc * Buddy Chats are now saved in .gaimrc * Ability to merge gaim, aim2, aim4 buddylists. Thanks again bmiller! @@ -1754,8 +1812,8 @@ as. For example, if your buddy's SN is 'CouldntGetMyName', you can alias him as 'Loser'. * Compile with GNOME bits if available - * Added GNOME Url Handler as an available web-browser - * Added the S html tag. + * Added GNOME Url Handler as an available web-browser + * Added the S html tag. * Optionally Ignore TiK's Automated Messages * Option to beep instead of play sound * New icons for panel (depends on some GNOME pixmaps) @@ -1767,11 +1825,11 @@ * More plugin events, more plugin features * Run-time OSCAR support * Added buddy list ticker (See prefs/Appearance). Clicking on a - name will cause a new or previous IM window to display for - that screenname + name will cause a new or previous IM window to display for + that screenname * "You are sending messages too quickly" error is now fixed when you have a large buddylist. - * Fixed the LC_ALL compile problem on Solaris boxes + * Fixed the LC_ALL compile problem on Solaris boxes * Fixed PPC and ARM compile problem with oscar.c * Smileys work better, and don't cause font attributes to drop * Dialog windows are now prettier @@ -1794,7 +1852,7 @@ * Double error bug when sending message to an offline user is fixed. * Pressing enter once again sends a message in buddy chatrooms (oops) - * More fixes for the change on the AOL sign-on process. + * More fixes for the change on the AOL sign-on process. * Fixed bug where Gaim sometimes doesn't find a font to use. * Per-conversation font and color dialogs (thanks fflewddur) * Chat in oscar works (somewhat) @@ -1809,14 +1867,14 @@ * Paned buddy chat window (Thanks Syd) * Buddy lists (and changes) are cached to ~/.gaim/<sn>.blist where <sn> is your screen name. If for some reason, you log - into the AOL server and the buddy list comes back empty, we + into the AOL server and the buddy list comes back empty, we check for a cache file, and, if we find one, read it in. This - essentially implements recovery from a server crash at AOL + essentially implements recovery from a server crash at AOL (AOL does not back up machines that contain TOC-based buddy lists, unfortunately). (Thanks Syd) - * Font selection dialog + * Font selection dialog * Small changes to the Oscar/libfaim stuff (see libfaim/README.gaim) - * SOCKS v4 Proxy support + * SOCKS 4a proxy support * Better proxy support overall (you can get people's info now! :) ) * Two-way file transfer (you can get and send files, but you still can't initiate either) @@ -1860,13 +1918,13 @@ * GNOME Applet support works better (thanks to Eric Warmenhoven for the patch) * Support for displaying true type fonts - * Lag-O-Meter does not send lag-test if not selected + * Lag-O-Meter does not send lag-test if not selected * Fixed problem with saving away messages which contain spaces and numbers. - * Various GNOME Applet Enhancements (thanks AGAIN to - Eric. Someone needs to stop this boy :-) ) + * Various GNOME Applet Enhancements (thanks AGAIN to + Eric. Someone needs to stop this boy :-) ) * A lot of random, obscure bugs fixed - * All of the major and I believe all of the minor memory leaks are + * All of the major and I believe all of the minor memory leaks are now fixed (Thanks to Peter Teichman, Larry Ewing, Jeramey Crawford, and me) @@ -1876,9 +1934,9 @@ * New .gaimrc format * Better support for multiple screen names * Font Properties - * Saving of buddylist window position + * Saving of buddylist window position * Fixed a problem with Gaim and the Netscape-branded version - of Mozilla + of Mozilla * New Sound Properties * More General Properties * Bigger Text-Entry field (Thanks to CrazyDavy for this one) @@ -1908,8 +1966,8 @@ * Some logging fixes and improvements * configurable host/port selection * Clickable Links in buddy chat - * New Gaim Logo - * Display Signon/Signoff messages in conversation windows + * New Gaim Logo + * Display Signon/Signoff messages in conversation windows * Option to strip HTML from logged messages * GNOME cleanups (It might work now haha) * When viewing user info, URLS are converted to clickable links @@ -1983,7 +2041,7 @@ * `Blocking' on conversation window * Add/Remove buddy from conversation window * Scroll-Wheel Mice work in Conversation Window - * Fixed WindowMaker Appicon + * Fixed WindowMaker Appicon * Version Number in About Box * Gaim Slogan in about box :) - * Created Changelog File :) + * Created Changelog File :)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ChangeLog.API Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,678 @@ +Gaim: The Pimpin' Penguin IM Client that's good for the soul! + +version 2.0.0: + Changed: + * All the status stuff. Yay! + * gaim_prefs_connect_callback(), added handle parameter + * gtk_imhtml_toolbar now descends from GtkHBox making it easier to add your + own widgets to it + * gaim_find_conversation_with_account, added a "type" parameter + * gaim_gtk_prefs_labeled_spin_button, the "key" parameter is now a + const char* instead of just a char* + * gaim_gtk_prefs_labeled_entry, the "key" parameter is now a const char* + instead of just a char* + * the add_buddy perl sub. The sub now takes the account as the first + argument, and buddy and group as the second and third. It also adds + the buddy to the server-side buddy list of the given account. + * gaim_connection_new, gaim_account_connect and gaim_account_register no + longer return a GaimConnection + * keep_alive in GaimConnection is renamed to keepalive + * gaim_mkstemp, added a second argument, a boolean, of whether or not the + file is binary + * gaim_log_logger_new, rewritten + * gaim_conv_window_remove_conversation()'s last argument to be a + GaimConversation. + * A new blocked icon: pixmaps/status/default/blocked.png + * In pixmaps/status/default: extendedaway.png renamed to extended_away.png + * In pixmaps/status/default: na.png renamed to unavailable.png + * gtk_imhtml_toggle_bold(): No longer returns a value + * gtk_imhtml_toggle_italic(): No longer returns a value + * gtk_imhtml_toggle_underline(): No longer returns a value + * gtk_imhtml_toggle_strike(): No longer returns a value + * gtk_imhtml_scroll_to_end(): Added the smooth paramter + * gaim_log_new(), added conv parameter + * gaim_buddy_icon_new(), leaves a reference which the caller owns. Use + gaim_buddy_icon_unref() immediately if you don't want a reference (the + old behavior). + * GAIM_CONV_UNKNOWN to GAIM_CONV_TYPE_UNKNOWN. + * GAIM_CONV_IM to GAIM_CONV_TYPE_IM. + * GAIM_CONV_CHAT to GAIM_CONV_TYPE_CHAT. + * GAIM_CONV_MISC to GAIM_CONV_TYPE_MISC. + * GAIM_CONV_ANY to GAIM_CONV_TYPE_ANY. + * GaimConversationUiOps.write_conv, Replaced const char *who with + const char *name, const char *alias + * gaim_conv_chat_add_users(), added extra_msgs and new_arrivals (pass NULL + and FALSE respectively, to get the same behavior as before) + * chat_add_users in GaimConversationUiOps, added cbuddies and + new_arrivals and removed buddies. + * chat_rename_user in GaimConversationUiOps, added new_alias + * gaim_conv_chat_cb_new(), added alias. (pass NULL to get the same + behavior as before). + * GaimConversation.log became GList * GaimConversation.logs, so that a + conversation can have multiple logs at once + * gaim_conv_chat_add_user, added extra_msgs + * gaim_notify_userinfo, removed primary and secondary parameters + * GaimNotifyUiOps.notify_userinfo: removed title, primary, and + secondary parameters + * Idle timers are now added and removed in gtkidle.c in response + to the signed-on and signed-off signals + * GaimXfer->ops.read, GaimXfer->ops.write, gaim_xfer_set_read_fnc(), + gaim_xfer_set_write_fnc(), gaim_xfer_read(), gaim_xfer_write(): + Changed ssize_t to gssize + * serv_got_im, serv_got_chat_in, serv_send_im and serv_chat_send all use + GaimMessageFlags instead of GaimConvImFlags / GaimConvChatFlags + * All core<->prpl message passing now uses html. This was previously true + for receiving messages, it's now also true for sending them. prpls that + don't support html need to gaim_unescape_html() the message. + * Notify API: GCallback -> GaimNotifyCloseCallback, + void *user_data -> gpointer user_data + * gaim_notify_searchresults_get_rows_count, + gaim_notify_searchresults_get_columns_count: return type now guint + * gaim_account_notify_added: No longer checks if there is a + GaimBuddy for the added user, that's left up to the prpls. See the + documentation for this function and gaim_account_request_add. + * gaim_accounts_reorder: new_index is now a gint instead of a size_t + * displaying-message signals: displaying-[im|chat]-msg and + displayed-[im|chat]-msg signals are emitted for all messages + (ie, for received messages, sent messages, system messages, error + messages etc.), and the signals now have + gaim_gtk_conversations_get_handle() for their handle. + * GAIM_NOTIFY_BUTTON_ADD_BUDDY to GAIM_NOTIFY_BUTTON_ADD + * conversation-switched: This signal has been moved from conversation to + the UI and the signal-handlers only receive the + conversation that has been switched to. + * GaimPluginProtocolInfo: Added offline_message + * GaimPluginProtocolInfo: Added whiteboard_prpl_ops + * GaimPluginProtocolInfo: Added media_prpl_ops + * GaimPluginProtocolInfo: Added "user_info" argument to tooltip_text, + changed the return type to void + * GaimPluginProtocolInfo: Added "full" argument to tooltip_text + * gaim_pounce_new(): Added option argument for pounce options + * gaim_network_listen() and gaim_network_listen_range(): Added + socket_type parameter to allow creation of UDP listening. Modified + to be asynchronous with a callback to allow for UPnP operation. + Returns a data structure that can be used to cancel the listen + attempt using gaim_network_listen_cancel() + * GaimPrefCallback: val is now a gconstpointer instead of a gpointer + * gtk_imhtml_get_current_format(): the arguments are now set to TRUE or + FALSE. Previously they were set to TRUE or left alone. Also, you + may now pass NULL if you're not interested in a specific formatting. + * Smiley Themes: Backslashes must be backslash-escaped. + * Plugins: Depedencies are now honored when unloading plugins. + * gaim_markup_extract_info_field(): Added format_cb parameter. + * gaim_markup_extract_info_field(): Changed GString parameter to a GaimNotifyUserInfo paramter. + * gaim_str_to_time(): Added support for parsing the MM/DD/YYYY format. + * gaim_plugin_action_new(): label is now const char * + * gaim_plugin_pref_new_with_name(): name is now const char * + * gaim_plugin_pref_new_with_label(): label is now const char * + * gaim_plugin_pref_new_with_name_and_label(): name and label are + now const char * + * gaim_plugin_pref_set_name(): name is now const char * + * gaim_plugin_pref_get_name(): return type is now const char * + * gaim_plugin_pref_set_label(): label is now const char * + * gaim_plugin_pref_get_label(): return type is now const char * + * gaim_plugin_pref_add_choice(): label is now const char * + * struct proto_chat_entry: label is now const char * + * struct proto_chat_entry: identifier is now const char * + * All network activity has been updated to use non-blocking sockets. + This means that plugins must be updated to expect such a socket from + gaim_proxy_connect() and gaim_network_listen*(). + * gaim_proxy_connect(): changed to return NULL on error and a pointer + to a GaimProxyConnectInfo object which can be used to cancel + connection attempts using gaim_proxy_connect_cancel(). Also added + a 'handle' parameter that can be used to cancel the connection + attempt using gaim_proxy_connect_cancel_with_handle(). + * gaim_gethostbyname_async(): Renamed to gaim_dnsquery_a() and + changed to return a pointer to a data structure that can be + used to cancel the pending DNS query using gaim_dnsquery_destroy() + * gaim_url_fetch(): Renamed to gaim_util_fetch_url() and changed + to return a pointer to a data structure that can be used to cancel + the pending HTTP request using gaim_util_fetch_url_cancel(). + Corresponding callback has changed to accept this data structure + as its first argument, and to accept an error message as an + additional final argument. + * gaim_gtk_create_imhtml(): Added sw_ret() parameter + * gaim_account_get_log(): Added create parameter + * GAIM_CMD_P_VERYHIGH is now GAIM_CMD_P_VERY_HIGH + * gtk_imhtml_search_find(): Now wraps around to the top instead of + clearing the search at the end. + * gaim_gtkxfer_dialog_show: Can now take NULL to show (and possibly + create) a default gtkxfer dialog. + * CHAT_USERS_BUDDY_COLUMN became CHAT_USERS_WEIGHT_COLUMN, along with + a change in the values stored in the column. + * gaim_find_buddies() returns a list of all buddies in the account if name + is NULL. + * gaim_gtk_set_custom_buddy_icon() sets custom icon for a user. + * Hid the definition of _GaimStringref, which already had a warning to + avoid accessing it directly. + * notify_userinfo() UI op is passed a GaimNotifyUserInfo instead of a char* + for the user information + * gaim_buddy_icon_get_scale_size() and was changed to ALWAYS scale + the icon instead of only when icon_spec->scale_rules contains + GAIM_ICON_SCALE_DISPLAY. Callers should be changed to check the + scale_rules before calling this function. + * gaim_gtk_buddy_icon_get_scale_size() was changed to accept an + additional parameter which is used to determine what kind of + scaling should be done, if any. + + Removed: + * gaim_gtk_sound_{get,set}_mute() (replaced by the /gaim/gtk/sound/mute + preference) + * gaim_escape_html(const char *html) (use g_markup_escape_text(html, -1) + instead) + * gaim_accounts_sync, account changes are now scheduled to be saved + automatically + * gaim_connection_connect + * gaim_connection_disconnect + * gaim_connection_register + * gaim_accounts_auto_login + * gaim_find_conversation, use gaim_find_conversation_with_account instead + * gaim_chat_get_display_name + * gaim_conversation_set_history, gaim_conversation_get_history, and + GaimConversation->history. Use gtk_imhtml_get_markup instead. + * set_gaim_user_dir to gaim_util_set_user_dir + * create_prpl_icon to gaim_gtk_create_prpl_icon + * Window flashing support in the core: gaim_conv_window_flash, and flash UI + operation for conversations. Use signal "received-im-msg" or similar. + * All warning stuff from the core. + * gaim_gtkconv_get_dest_tab_at_xy(), instead use gaim_gtkconv_get_tab_at_xy() + * chat_add_user from GaimConversationUiOps: only chat_add_users is used + * chat_remove_user from GaimConversationUiOps: only chat_remove_users is used + * uc from the GaimBuddy struct + * gaim_sound_get_handle() + * gaim_debug_vargs() + * serv_add_buddy(); use gaim_account_add_buddy() instead + * serv_add_buddies(); use gaim_account_add_buddies() instead + * serv_change_passwd(); use gaim_account_change_password() instead + * serv_close() + * serv_finish_login() + * serv_login() + * serv_remove_buddy(); use gaim_account_remove_buddy() instead + * serv_remove_buddies(); use gaim_account_remove_buddies() instead + * serv_rename_group() + * serv_set_buddyicon(): use gaim_account_set_buddy_icon() instead + * serv_touch_idle(): use gaim_gtk_check_idle() instead + * GaimGtkImPane->a_virgin + * gaim_str_strip_cr(); use gaim_str_strip_char(str, '\r') instead + * gaim_find_buddys_group renamed to gaim_buddy_get_group + * gaim_gtkpounce_menu_build() + * gaim_gtkpounce_dialog_show() + * GaimGtkBuddyList->bpmenu + * GaimConvImFlags and GaimConvChatFlags; use GaimMessageFlags instead + * cb and user_data from the ops in GaimNotifyUiOps: This is now handled + by the notify API in the core. + * GaimConversationUiOps.updated: use the conversation-updated signal + * GAIM_SUBTYPE_CONV_WINDOW: windows are now only represented in the UI, + so GAIM_TYPE_BOXED is used for the signal types + * gaim_gtk_privacy_is_showable(): We do fallback privacy in the core + now, so this would always be TRUE now. + * GaimBlistNodeAction: See GaimMenuAction + * gaim_blist_node_action_new(); use gaim_menu_action_new() instead + * gaim_date() + * gaim_date_full(): See gaim_date_format_full() + * gaim_strftime(): See gaim_utf8_strftime() + * GAIM_MESSAGE_COLORIZE + * user_data from gaim_notify_searchresults_new_rows and from + notify_searchresults in GaimNotifyUiOps. + * gaim_conversation_get_send_history(), and send_history from + GaimConversation + * Removed ui_ops from GaimBuddyList. Use gaim_blist_get_ui_ops() instead + * GaimGtkConversation: dialogs (dialogs.search moved to GaimGtkWindow) + * gaim_show_xfer_dialog: Use gaim_gtk_xfer_dialog_show(NULL) instead. + * GaimGtkRoomlistDialog: Nothing used it outside of the file it was in. + * gaim_gtk_roomlist_dialog_new: use gaim_gtk_roomlist_show + * gaim_gtk_roomlist_dialog_new_with_account: use gaim_gtk_roomlist_show_with_account + + Added: + * gaim_prefs_disconnect_by_handle() + * a password field to GaimConnection, which only persists for the + session (when "remember password" is false, account->password is + NEVER set) Use gaim_connection_get_password(GaimConnection *gc) + * gaim_log_common_writer, gaim_log_common_lister, gaim_log_common_sizer, + and gaim_log_get_log_dir to allow log formats that use standard Gaim + log directory to use Gaim's built-in code for these purposes. + * GaimLogCommonLoggerData struct for a basic logger_data struct to be + used with "common" logger functions. + * gaim_gtk_blist_node_is_contact_expanded, returns TRUE if the given + blist node is a buddy inside an expanded contact, or is itself an + expanded contact + * GaimLogSet struct, get_log_sets function to GaimLogLogger, + gaim_log_get_log_sets, gaim_log_set_compare + * gaim_privacy_check(), to check if a given user is allowed to send + messages to the specified account + * gtk_imhtml_clear_formatting() + * gtk_imhtml_delete to clear out part of a imhtml buffer + * gtk_imhtml_get_protocol_name() + * gaim_buddy_icons_get_full_path(), to get the full path of a buddy + icon setting + * CHAT_USERS_ALIAS_COLUMN, CHAT_USERS_COLOR_COLUMN, + CHAT_USERS_BUDDY_COLUMN to the list of columns for the chat + user list + * gaim_account_add_buddy() + * gaim_account_add_buddies() + * gaim_account_remove_buddy() + * gaim_account_remove_buddies() + * gaim_account_change_password() + * gaim_account_supports_offline_message() + * gaim_conversation_close_logs(), to force a conversation's log(s) to + be closed. New logs will be opened as necessary. + * gaim_plugin_get_id() + * gaim_plugin_get_name() + * gaim_plugin_get_version() + * gaim_plugin_get_summary() + * gaim_plugin_get_description() + * gaim_plugin_get_author() + * gaim_plugin_get_homepage() + * gaim_gtkconv_switch_active_conversation(GaimConversation *) + * gaim_str_strip_char() to strip a given character from + a given string + * gaim_util_chrreplace() to replace a given character with a + different character + * gaim_gtk_blist_toggle_visibility() to intelligently toggle the + visiblity of the buddy list + * gaim_gtk_blist_visibility_manager_add() to indicate the addition of a + visibility manager - see the docs for more information + * gaim_gtk_blist_visibility_manager_remove() to indicate the removal of + a visibility manager - see the docs for more information + * gaim_gtk_conversations_find_unseen_list() to get a list of conversations + with an "unseen" state >= to the specified state and other criteria + * gaim_gtk_conversations_fill_menu() fill a menu from list of conversations + * gaim_gtk_create_prpl_icon() + * gaim_gtk_create_prpl_icon_with_status() + * gaim_gtk_pounces_manager_show() + * gaim_gtk_pounces_manager_hide() + * gaim_gtk_pounce_editor_show() + * GAIM_POUNCE_MESSAGE_RECEIVED + * GaimPounceOption + * gaim_pounce_set_options() + * gaim_pounce_set_options() + * GAIM_STOCK_CONNECT, GAIM_STOCK_DISCONNECT + * GAIM_STOCK_PLUGIN + * gaim_account_request_add: Notifies the user that they were added to + someone's buddy list, and offers them the choice + of adding that person to their buddy list. + * gaim_blist_alias_contact() + * gaim_cipher_http_digest_calculate_session_key() + * gaim_cipher_http_digest_calculate_response() + * gaim_notify_searchresults_labeled() + * GAIM_NOTIFY_BUTTON_LABELED, GAIM_NOTIFY_BUTTON_INFO, + GAIM_NOTIFY_BUTTON_IM, GAIM_NOTIFY_BUTTON_JOIN, + GAIM_NOTIFY_BUTTON_INVITE + * stock buttons GAIM_STOCK_IM, GAIM_STOCK_INFO + * gaim_conversation_present() + * GaimConversationUiOps->present(GaimConversation *) + * GaimPlugin.unloadable + * gaim_plugin_is_unloadable() + * GAIM_PLUGIN_PREF_STRING_FORMAT + * gaim_plugin_pref_get_format_type() + * gaim_plugin_pref_set_format_type() + * GaimStringFormatType + * gaim_log_get_handle() + * gaim_log_uninit() + * GAIM_SUBTYPE_LOG + * gaim_marshal_POINTER__POINTER_POINTER + * gaim_utf8_ncr_encode() + * gaim_gtk_log_init() + * gaim_gtk_log_get_handle() + * gaim_gtk_log_uninit() + * gaim_util_fetch_url_request() + * GaimMenuAction + * gaim_menu_action_new() + * gaim_menu_action_free() + * GaimInfoFieldFormatCallback + * gaim_utf8_strftime() + * gaim_date_format_short() + * gaim_date_format_long() + * gaim_date_format_full() + * gaim_time_format() + * gaim_plugin_action_free() + * GaimRequestType: Added GAIM_REQUEST_FOLDER + * GaimRequestUiOps: Added request_folder + * gaim_request_folder() + * gaim_gtk_setup_screenname_autocomplete() + * gaim_gtk_set_cursor() + * gaim_gtk_clear_cursor() + * GAIM_MESSAGE_ACTIVE_ONLY + * gaim_proxy_get_setup() + * GaimNotifySearchResultsCallback: Added user_data. + * gaim_notify_searchresults: Added user_data. + * gaim_network_listen_cancel(): Can be used to cancel a previous + call to gaim_network_listen() or gaim_network_listen_range() + * gaim_proxy_connect_cancel(): Can be used to cancel a pending + gaim_proxy_connect() request + * gaim_proxy_connect_cancel_with_handle(): Can be used to cancel + a previous gaim_proxy_connect() request using a specified handle + * gaim_dnsquery_destroy(): Can be used to cancel a pending DNS + query. + * gaim_util_fetch_url_cancel(): Can be used to cancel a pending + call to gaim_util_fetch_url() or gaim_util_fetch_url_request(). + * GaimGtkWindow: dialogs.search (previously in GaimGtkConversation) + * gaim_buddy_get_server_alias() + * gaim_conv_send_confirm() + * GaimConversationUiOps.send_confirm + * gaim_gtk_roomlist_dialog_show_with_account + * gaim_gtk_tree_view_search_equal_func to be used with + gtk_tree_view_set_search_equal_func + * gaim_xfer_set_bytes_sent(). Sets the offset in the file to + read from or write to. + * gaim_privacy_deny and gaim_privacy_allow + * gaim_gtk_blist_set_headline + * gaim_gtk_set_urgent + * GtkGaimScrollBook and its functions. + + Signals - Changed: (See the Doxygen docs for details on all signals.) + * Signal propagation now stops after a handler returns a non-NULL value. + This value is now returned. Previously, all registered handlers were + called and the value from the last handler was used. + * "buddy-typing" and "buddy-typing-stopped": replaced the GaimConversation* + with GaimAccount*, const char *name. Also, the signal is now emitted + regardless of whether a conversation exists and regardless of whether + the user is on the buddy list. + * "chat-buddy-joined": added the new_arrival argument + * "chat-invited" handlers can now return a value to control what happens + to the invite (accept, reject, prompt the user). + * "chat-left": Emitted *after* setting chat->left to TRUE. + * "drawing-tooltip": the second argument is now a GString* instead of + a char** + * "drawing-tooltip": added the "full" argument + * "received-im-msg" and "received-chat-msg" to match, both now pass a + conversation pointer and flags + * "receiving-im-msg" and "receving-chat-msg" to match, both now pass a + conversation pointer and a pointer to the flags. + * "writing-im-msg", "wrote-im-msg", "writing-chat-msg", "wrote-chat-msg": + Now emitted from a difference place in the message handling code. + The arguments also changed. + * "displaying-im-msg", "displayed-im-msg", "displaying-chat-msg", + "displayed-chat-msg": Added "who" argument, which changes the order + of the existing arguments. + + Signals - Added: (See the Doxygen docs for details on all signals.) + * "account-disabled" + * "account-status-changed" + * "account-alias-changed" + * "cipher-added" + * "cipher-removed" + * "conversation-dragging" + * "dbus-method-called" + * "dbus-introspect" + * "file-recv-accept" + * "file-recv-start" + * "file-recv-cancel" + * "file-recv-complete" + * "file-recv-request" + * "file-send-accept" + * "file-send-start" + * "file-send-cancel" + * "file-send-complete" + * "buddy-added" + * "buddy-removed" + * "blist-node-aliased" + * "buddy-status-changed" + * "buddy-idle-changed": A buddy's idle status changed. + * "buddy-icon-changed" + * "buddy-got-login-time": The login time for a buddy is now known + * "displaying-userinfo" + * "gtkblist-hiding" + * "gtkblist-unhiding" + * "log-displaying" + * "savedstatus-changed" + * "sendto-extended-menu" + + Signals - Removed: + * "account-away": replaced by account-status-changed + * "account-warned" + * "buddy-away": replaced by buddy-status-changed + * "buddy-back": replaced by buddy-status-changed + * "buddy-idle": replaced by buddy-idle-changed + * "buddy-unidle": replaced by buddy-idle-changed + * "buddy-icon-cached": replaced by buddy-icon-changed + * "conversation-drag-end": replaced by conversation-dragging + * "conversation-switching" + +version 1.5.0 (8/11/2005): + * Added: gaim_xfer_conversation_write + Writes a messages to a conversation window with the use + of the associated file transfer. + +version 1.4.0 (7/7/2005): + * Added: gaim_buddy_icon_uncache() + Deletes a cached buddy icon for a specified buddy + * Added: gaim_buddy_icon_get_type + Attempts to determine the type of a given buddy icon. + * Added: buddy-icon-cached signal + Emitted when a new buddy icon is cached. + +version 1.3.1 (6/9/2005): + * No changes + +version 1.3.0 (5/10/2005): + * Added: gaim_blist_schedule_save() + This should be used instead of gaim_blist_sync when you + want the blist.xml file to be written to disk. There + should not be many occasions when you want to do this, + as the functions in the blist API that modify the buddy + list will normally call it for you. + * Added: OPT_PROTO_NO_NORMALIZE_CONV + Tells the conversation API to not normalize screen names + in conversations. This is used by the Jabber PRPL. + +version 1.2.1 (4/3/2005): + * No changes + +version 1.2.0 (3/17/2005): + * You can use gaim_signal_connect_priority() and + gaim_signal_connect_priority_vargs() to connect to + Gaim signals with a given priority (Will Gorman) + * Added: gaim_conversation_set_features + gaim_conversation_get_features + These allow plugins (notable prpls) to change the + formatting capabilities of an existing conversation. + This comes with a new "features" field in + GaimConversation (Christopher O'Brien) + * Added: GAIM_CONNECTION_NO_IMAGES to GaimConectionFlags + (Christopher O'Brien) + * Added: GAIM_CBFLAGS_TYPING to GaimConvChatBuddyFlags + (Christopher O'Brien) + * Added: gaim_account_request_add which takes the same arguments as + * gaim_account_notify_added but always asks the user if they want to add + * the buddy to the buddy list + * Added: An accompanying request_add GaimAccountUiOp + +version 1.1.4 (2/24/2005): + * No changes + +version 1.1.3 (2/17/2005): + * No changes + +version 1.1.2 (1/20/2005): + * No changes + +version 1.1.1 (12/28/2004): + * No changes + +version 1.1.0 (12/02/2004): + * Added: gaim_utf8_salvage + * Added: binary relocation support in prefix.h + WARNING: If your plugin uses anything inside the + #ifdef ENABLE_BINRELOC from prefix.h, it won't be + loadable on a copy of Gaim compiled without binreloc + support. In particular, watch out for the autoconf-like + macros, and accidently including them through internal.h, + which you probably shouldn't be including anyway. + +version 1.0.0 (09/17/2004): + * Added: get_chat_name to the GaimPluginProtocolInfo struct + * Changed: gaim_blist_update_buddy_presence(), presence changed to + type gboolean + * Changed: the versioning scheme, and all the plugin structs + +version 0.82 (08/26/2004): + Gaim API: + * Removed: gaim_gtk_get_dispstyle(), gaim_gtk_change_text() + * Removed: multi.h + * Renamed: ui.h to gtkdialogs.h + * Renamed: gtkinternal.h to gtkgaim.h + * Renamed: show_info_dialog to gaim_gtkdialogs_info + * Renamed: show_log_dialog to gaim_gtkdialogs_log + * Renamed: show_warn_dialog to gaim_gtkdialogs_warn + * Renamed: show_im_dialog to gaim_gtkdialogs_im + * Renamed: gaim_gtkdialogs_new_im to gaim_gtkdialogs_im_with_user + * Renamed: destroy_all_dialogs to gaim_gtkdialogs_destroy_all + * Renamed: alias_dialog_bud to gaim_gtkdialogs_alias_buddy + * Renamed: alias_dialog_contact to gaim_gtkdialogs_alias_contact + * Renamed: alias_dialog_blist_chat to gaim_gtkdialogs_alias_chat + * Renamed: show_confirm_del to gaim_gtkdialogs_remove_buddy + * Renamed: show_confirm_del_group to gaim_gtkdialogs_remove_group + * Renamed: show_confirm_del_blist_chat to gaim_gtkdialogs_remove_chat + * Renamed: show_confirm_del_contact to gaim_gtkdialogs_remove_contact + * Renamed: show_about to gaim_gtkdialogs_about + * Added: gaim_notify_userinfo() and the associated notify_userinfo() UI op + which pass account and contact information associated with the + userinfo + + Buddy List API: + * Changed: gaim_blist_request_add_chat(), added name parameter + * Added: gaim_contact_on_account() + * Added: flags parameter to the GaimBlistNode struct + + Conversation API: + * Added: gaim_gtkconv_button_new() + + Protocol Plugin API: v7 + * Added: chat_info_defaults to the GaimPluginProtocolInfo struct + + Signals: + * Added: conversation-updated for any update to the data associated + with the conversation (topic, icon, adding to buddy list, etc.) + + Conversation API: + * Changed: gaim_conv_chat_add_user() (added new_arrival parameter) + +version 0.81 (08/05/2004): + Commands API: + * Most functions now have a void *data argument. + + Blist API: + * Added: gaim_buddy_get_contact_alias + * Renamed: gaim_get_buddy_alias to gaim_buddy_get_alias + * Renamed: gaim_get_buddy_alias_only to gaim_buddy_get_alias_only + + Conversation API: + * Changed: gaim_conv_chat_add_user(), added flags parameter + * Changed: gaim_conv_chat_add_users(), added GList of flags parameter + * Changed: gaim_conv_chat_get_users(), now returns a GList of + GaimConvChatBuddy's + * Changed: gaim_conv_chat_set_users() now expects a GList of + GaimConvChatBuddy's + * Added: gaim_conv_chat_set_user_flags() + * Added: gaim_conv_chat_get_user_flags() + * Added: gaim_conv_chat_find_user() + * Added: gaim_conv_chat_cb_new() + * Added: gaim_conv_chat_cb_find() + * Added: gaim_conv_chat_cb_destroy() + * Added: gaim_conv_chat_cb_get_name() + + Conversation UI ops: + * Added: chat_update_user() + + Signals: + * Changed: chat-buddy-joining & chat-buddy-joined now include the user's flags + * Changed: chat-buddy-joining & chat-buddy-leaving are now booleans, return + TRUE if you don't want the join/leave to be displayed in the UI. + * Added: chat-buddy-flags for when user's flags change + gaim_marshal_VOID__POINTER_POINTER_POINTER_UINT_UINT (required for the new + chat-buddy-flags signal) + * Added: account-modified for when account settings have been changed. + +version 0.80 (07/15/2004): + Gaim API: + * Removed: PRPL numbers : gaim_account_set_protocol(), + gaim_account_get_protocol(), gaim_accounts_find_with_prpl_num, + gaim_prpl_num_to_id(), gaim_prpl_id_to_num(), GaimProtocol + + Protocol Plugin API: v6 + * Added: can_receive_file & send_file to the GaimPluginProtocolInfo struct + + Signals: + * Changed "chat-invited" to also include the components hash table so + plugins can use serv_join_chat when the signal is emitted. + * Added "chat-topic-changed" signal plugins know when a topic is changed. + +version 0.79 (06/24/2004): + Gaim API: + * gaim_url_parse() now takes two additional parameters, which are used + for returning the username and password from the URL, if they exist. + * Added: has_focus UI op to GaimConversationUiOps and + GaimConvWindowUiOps. + * Added: gaim_conversation_has_focus() and gaim_conv_window_has_focus(). + * Removed: gaim_blist_save() + + Protocol Plugin API: v5 + * Changed: add_buddy, add_buddies, remove_buddy, remove_buddies, + rename_group and remove_group to take GaimBuddy's and + GaimGroup's consistently. + * Removed: OPT_PROTO_BUDDY_ICON (replaced by icon_spec) + * Added: icon_spec to the GaimPluginProtocolInfo struct + +version 0.78 (05/30/2004): + Plugin API: v4 + * Added: actions - for plugins to add to the new Plugin Actions menu + + Loader Plugin API: v2 (no changes) + + Protocol Plugin API: v4 + * Removed: set_dir, get_dir and dir_search (not used, AIM-centric) + * Removed: actions (replaced by generic plugin actions) + + Perl Plugin API: v2 (no changes) + TCL Plugin API: (no changes) + + Signals: + * Added: "blist-node-extended-menu" for extending Buddy, Chat and + Group right-click menus + * Added: "drawing-tooltip" for plugins to allow plugins to change text + appearing in tooltips + * Added: "gtkblist-created" + * Added: "receiving-im-msg" and "receiving-chat-msg" (these behave + exactly like received-*-msg used to) + * Added: "buddy-idle-updated" signal, for when the idle time changes. + * Changed: "received-im-msg" and "received-chat-msg" no longer pass + pointers to who, message and flags, and are now void. + * Removed: "drawing-menu" - it was UI sepecific and + "blist-node-extended-menu" is superior + +version 0.77 (04/22/2004): + Loader & Protocol Plugins independantly versioned + Plugin loading now checks versioning on plugins (Standard, Loader & + Protocol) + new GAIM_{PLUGIN,PRPL,LOADER}_API_VERSION constants + + Plugin API: v3 + * Added: prefs_info for UI independant plugin prefs + + Loader Plugin API: v2 + * Added: api_version at top of GaimPluginLoaderInfo struct + + Protocol Plugin API: v2 + * Added: api_version at top of GaimPluginProtocolInfo struct + * Added: chat_menu for protocol specific extensions to the chat menu + * Removed: get_away "Nada used it. Pink elephants on parade." + * Removed: protocol_prefs (replaced by generic plugin prefs_info) + + Perl Plugin API: v2 (no changes) + TCL API: (no changes) + + Signals: + * Added: "conversation-drag-ended" + +version 0.76 (04/01/2004): + Plugin API: v2 + Perl Plugin API: v2 + Loader Plugin API: (not versioned) + Protocol Plugin API: (not versioned) + * Added: protocol_prefs for protocol specific preferences + * Added: reject_chat so protocols can act on chat invite rejection + + TCL Plugin API: (not versioned) + * Changes to plugin registration to show descriptions +
--- a/Doxyfile.in Mon Apr 16 00:43:53 2007 +0000 +++ b/Doxyfile.in Sun May 20 06:19:49 2007 +0000 @@ -431,7 +431,9 @@ # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = src \ +INPUT = libgaim \ + gtk \ + console \ doc # If the value of the INPUT tag contains directories, you can use the
--- a/HACKING Mon Apr 16 00:43:53 2007 +0000 +++ b/HACKING Sun May 20 06:19:49 2007 +0000 @@ -28,26 +28,24 @@ you don't know GTK+ you should go learn that first. If you're going to hack gaim, PLEASE, PLEASE PLEASE PLEASE send patches -against the absolute latest CVS. I get really annoyed when I get patches +against the absolute latest SVN. I get really annoyed when I get patches against the last released version, especially since I don't usually have a copy of it on my computer, and gaim tends to change a lot between -versions. (I sometimes get annoyed when they're against CVS from 3 days +versions. (I sometimes get annoyed when they're against SVN from 3 days ago, but can't complain because it's usually my fault that I haven't -looked at the patch yet.) To get gaim from CVS (if you haven't already), +looked at the patch yet.) To get gaim from SVN (if you haven't already), run the following commands: -$ export CVSROOT=:pserver:anonymous@cvs.sourceforge.net:/cvsroot/gaim -$ cvs login (hit enter as the password) -$ cvs co gaim (you'll see it getting all of the files) +$ svn co https://svn.sourceforge.net/svnroot/gaim/trunk gaim $ cd gaim $ ./autogen.sh You'll now have your normal gaim tree with ./configure and all (which ./autogen.sh takes the liberty of running for you). (If you want to make -your life really simple, learn how CVS works. CVS is your friend.) To make +your life really simple, learn how SVN works. SVN is your friend.) To make a patch, just edit the files right there in that tree (don't bother with two trees, or even two copies of the same file). Then when you're ready to -make your patch, simply run 'cvs diff -u >my.patch' and post it on +make your patch, simply run 'svn diff > my.patch' and post it on sf.net/projects/gaim in the patches section. Some Documentation is available on the Gaim api if you run the command
--- a/Makefile.am Mon Apr 16 00:43:53 2007 +0000 +++ b/Makefile.am Sun May 20 06:19:49 2007 +0000 @@ -1,44 +1,58 @@ EXTRA_DIST = \ COPYRIGHT \ + ChangeLog.API \ + ChangeLog.win32 \ Doxyfile.in \ + HACKING \ + Makefile.mingw \ + PLUGIN_HOWTO \ + PROGRAMMING_NOTES \ + README.SVN \ + README.dbus \ + README.mingw \ gaim.pc.in \ gaim.spec.in \ gaim.apspec.in \ - gaim.desktop \ + gaim.desktop.in \ + gaim.service.in \ gaim-installer.nsi \ - HACKING \ - PROGRAMMING_NOTES \ - setup-gettext \ - ChangeLog.win32 \ + intltool-extract.in \ + intltool-merge.in \ + intltool-update.in \ config.h.mingw \ - Makefile.mingw \ - README.mingw \ - VERSION \ - VERSION.in \ - plugins/win32/transparency/Makefile.mingw \ - plugins/win32/transparency/win2ktrans.c \ - plugins/win32/winprefs/gtkappbar.c \ - plugins/win32/winprefs/gtkappbar.h \ - plugins/win32/winprefs/Makefile.mingw \ - plugins/win32/winprefs/winprefs.c \ po/Makefile.mingw -gaimincludedir=$(includedir)/gaim -gaiminclude_HEADERS = config.h +noinst_HEADERS = config.h pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = gaim.pc +if ENABLE_DBUS +dbus_servicedir=$(DBUS_SERVICES_DIR) +dbus_service_DATA=gaim.service +endif + dist-hook: gaim.spec cp gaim.spec $(distdir) + rm $(distdir)/config.h -distcheck-hook: plugins/perl/common/Gaim.pm -# cp plugins/perl/common/Gaim.pm $(distdir)/plugins/perl/common +distcheck-hook: libgaim/plugins/perl/common/Gaim.pm gtk/plugins/perl/common/GtkUI.pm +# cp libgaim/plugins/perl/common/Gaim.pm $(distdir)/libgaim/plugins/perl/common appsdir = $(datadir)/applications -apps_DATA = gaim.desktop +apps_in_files = gaim.desktop.in +apps_DATA = $(apps_in_files:.desktop.in=.desktop) +@INTLTOOL_DESKTOP_RULE@ -SUBDIRS = doc intl m4macros pixmaps plugins po sounds src +if ENABLE_GTK +GTK_DIR=gtk +endif + +if ENABLE_GNT +GNT_DIR=console +endif + +SUBDIRS = libgaim doc $(GNT_DIR) $(GTK_DIR) m4macros po docs: Doxyfile if HAVE_DOXYGEN @@ -55,5 +69,5 @@ distuninstallcheck_listfiles = \ find . -type f -print | grep -v perl | grep -v Gaim.3pm -ACLOCAL_AMFLAGS = -I m4 - +DISTCLEANFILES= gaim.desktop libgaim/gconf/gaim.schemas intltool-extract \ + intltool-merge intltool-update
--- a/Makefile.mingw Mon Apr 16 00:43:53 2007 +0000 +++ b/Makefile.mingw Sun May 20 06:19:49 2007 +0000 @@ -5,125 +5,93 @@ # Description: Top Makefile for win32 (mingw) port of Gaim # -GAIM_SRC = ./src -GAIM_PROTOS = $(GAIM_SRC)/protocols -GAIM_PLUGINS = ./plugins -GAIM_PIXMAPS = ./pixmaps -GAIM_SOUNDS = ./sounds -GAIM_INSTALL_DIR = ./win32-install-dir -GTKSPELL_TOP = ../win32-dev/gtkspell-2.0.6/gtkspell -IDLETRACK_TOP = $(GAIM_SRC)/win32/IdleTracker -GTKRC_TOP = ../win32-dev/gtkrc -OSCAR = $(GAIM_PROTOS)/oscar -YAHOO = $(GAIM_PROTOS)/yahoo -MSN = $(GAIM_PROTOS)/msn -TOC = $(GAIM_PROTOS)/toc -IRC = $(GAIM_PROTOS)/irc -JABBER = $(GAIM_PROTOS)/jabber -NAPSTER = $(GAIM_PROTOS)/napster -GG = $(GAIM_PROTOS)/gg -NOVELL = $(GAIM_PROTOS)/novell -SILC = $(GAIM_PROTOS)/silc -SIMPLE = $(GAIM_PROTOS)/simple -SAMETIME = $(GAIM_PROTOS)/sametime -PO = ./po +GAIM_TOP := . +include $(GAIM_TOP)/libgaim/win32/global.mak -MAKENSIS := makensis.exe - -VERSION := $(shell cat ./VERSION) - -NEEDED_DLLS = $(GTKSPELL_TOP)/libgtkspell.dll \ - $(IDLETRACK_TOP)/idletrack.dll +# Generate a X.X.X.X version for the installer file versioning header +# The last digit will be 99 for a final release, 0 for dev or unknown, or the beta number +GAIM_PRODUCT_VERSION = $(shell \ +awk 'BEGIN {FS="."} { \ + if (int($$3) == $$3) { \ + $$4 = "99"; \ + } else { \ + $$5 = $$3; \ + sub(int($$3), "", $$5); \ + if ($$5 == "dev") { \ + $$4 = "0"; \ + } else { \ + if (sub("beta", "", $$5) > 0) { \ + $$4 = $$5; \ + } else { \ + $$4 = "0"; \ + } \ + } \ + } \ + printf("%s.%s.%s.%s", $$1, $$2, int($$3), $$4); \ + exit; \ +}' VERSION) -SOUNDS = $(GAIM_SOUNDS)/alert.wav \ - $(GAIM_SOUNDS)/login.wav \ - $(GAIM_SOUNDS)/logout.wav \ - $(GAIM_SOUNDS)/receive.wav \ - $(GAIM_SOUNDS)/send.wav - - -## -## Don't forget to change STATIC_PROTO_INIT, in config.h.mingw if you -## change the status of a protocol (static/plugin) -## +GTK_INSTALL_VERSION = $(shell \ + source ../gtk_installer/version.sh; \ + echo $$gtk_version \ +) -OSCAR_TYPE = PLUGIN -YAHOO_TYPE = PLUGIN -MSN_TYPE = PLUGIN -TOC_TYPE = PLUGIN -IRC_TYPE = PLUGIN -JABBER_TYPE = PLUGIN -NAPSTER_TYPE = PLUGIN -GG_TYPE = PLUGIN -NOVELL_TYPE = PLUGIN -SILC_TYPE = PLUGIN -SIMPLE_TYPE = PLUGIN -SAMETIME_TYPE = PLUGIN +# Any *.dll or *.exe files included in win32-install-dir that we don't compile +# should be included in this list so they don't get stripped +NON_GAIM_DLLS = \ + freebl3.dll \ + libgtkspell.dll \ + libmeanwhile-1.dll \ + libxml2.dll \ + nspr4.dll \ + nss3.dll \ + nssckbi.dll \ + plc4.dll \ + plds4.dll \ + silc.dll \ + silcclient.dll \ + softokn3.dll \ + ssl3.dll -all: - cp config.h.mingw config.h - $(MAKE) TYPE='$(OSCAR_TYPE)' -C $(OSCAR) -f Makefile.mingw - $(MAKE) TYPE='$(YAHOO_TYPE)' -C $(YAHOO) -f Makefile.mingw - $(MAKE) TYPE='$(MSN_TYPE)' -C $(MSN) -f Makefile.mingw - $(MAKE) TYPE='$(IRC_TYPE)' -C $(IRC) -f Makefile.mingw - $(MAKE) TYPE='$(JABBER_TYPE)' -C $(JABBER) -f Makefile.mingw - $(MAKE) TYPE='$(NAPSTER_TYPE)' -C $(NAPSTER) -f Makefile.mingw - $(MAKE) TYPE='$(GG_TYPE)' -C $(GG) -f Makefile.mingw - $(MAKE) TYPE='$(NOVELL_TYPE)' -C $(NOVELL) -f Makefile.mingw - $(MAKE) TYPE='$(SILC_TYPE)' -C $(SILC) -f Makefile.mingw - $(MAKE) TYPE='$(SIMPLE_TYPE)' -C $(SIMPLE) -f Makefile.mingw - $(MAKE) TYPE='$(SAMETIME_TYPE)' -C $(SAMETIME) -f Makefile.mingw - $(MAKE) -C $(GAIM_SRC) -f Makefile.mingw - $(MAKE) -C $(GAIM_PLUGINS) -f Makefile.mingw +#build an expression for `find` to use to ignore the above files +NON_GAIM_DLLS_FIND_EXP = $(patsubst %,-o -name %,$(NON_GAIM_DLLS)) +.PHONY: all install installer installer_nogtk installer_debug installers clean uninstall create_release_install_dir -install: all - mkdir -p $(GAIM_INSTALL_DIR)/plugins - mkdir -p $(GAIM_INSTALL_DIR)/sounds/gaim - $(MAKE) -C $(GAIM_PIXMAPS) -f Makefile.mingw install - $(MAKE) -C $(PO) -f Makefile.mingw install - $(MAKE) -C $(GAIM_SRC) -f Makefile.mingw install - $(MAKE) -C $(GAIM_PLUGINS) -f Makefile.mingw install - $(MAKE) TYPE='$(OSCAR_TYPE)' -C $(OSCAR) -f Makefile.mingw install - $(MAKE) TYPE='$(YAHOO_TYPE)' -C $(YAHOO) -f Makefile.mingw install - $(MAKE) TYPE='$(MSN_TYPE)' -C $(MSN) -f Makefile.mingw install - $(MAKE) TYPE='$(IRC_TYPE)' -C $(IRC) -f Makefile.mingw install - $(MAKE) TYPE='$(JABBER_TYPE)' -C $(JABBER) -f Makefile.mingw install - $(MAKE) TYPE='$(NAPSTER_TYPE)' -C $(NAPSTER) -f Makefile.mingw install - $(MAKE) TYPE='$(GG_TYPE)' -C $(GG) -f Makefile.mingw install - $(MAKE) TYPE='$(NOVELL_TYPE)' -C $(NOVELL) -f Makefile.mingw install - $(MAKE) TYPE='$(SILC_TYPE)' -C $(SILC) -f Makefile.mingw install - $(MAKE) TYPE='$(SIMPLE_TYPE)' -C $(SIMPLE) -f Makefile.mingw install - $(MAKE) TYPE='$(SAMETIME_TYPE)' -C $(SAMETIME) -f Makefile.mingw install - cp $(NEEDED_DLLS) $(GAIM_INSTALL_DIR) - cp $(SOUNDS) $(GAIM_INSTALL_DIR)/sounds/gaim +all: $(GAIM_CONFIG_H) + $(MAKE) -C $(GAIM_LIB_TOP) -f $(GAIM_WIN32_MAKEFILE) + $(MAKE) -C $(GAIM_GTK_TOP) -f $(GAIM_WIN32_MAKEFILE) + $(MAKE) -C $(GAIM_PO_TOP) -f $(GAIM_WIN32_MAKEFILE) + +install: all $(GAIM_INSTALL_DIR) + $(MAKE) -C $(GAIM_LIB_TOP) -f $(GAIM_WIN32_MAKEFILE) install + $(MAKE) -C $(GAIM_GTK_TOP) -f $(GAIM_WIN32_MAKEFILE) install + $(MAKE) -C $(GAIM_PO_TOP) -f $(GAIM_WIN32_MAKEFILE) install -installer: - $(MAKENSIS) /DGAIM_VERSION="$(VERSION)" /DWITH_GTK gaim-installer.nsi - -installer_nogtk: - $(MAKENSIS) /DGAIM_VERSION="$(VERSION)" gaim-installer.nsi +create_release_install_dir: install + rm -rf $(GAIM_INSTALL_DIR).release + cp -R $(GAIM_INSTALL_DIR) $(GAIM_INSTALL_DIR).release + find $(GAIM_INSTALL_DIR).release \( -name '*.dll' -o -name '*.exe' \) \ + -not \( -false $(NON_GAIM_DLLS_FIND_EXP) \) -exec strip --strip-unneeded {} ';' -installer_debug: - $(MAKENSIS) /DGAIM_VERSION="$(VERSION)" /DDEBUG gaim-installer.nsi +installer: create_release_install_dir + $(MAKENSIS) /V3 /DGAIM_VERSION="$(GAIM_VERSION)" /DGAIM_PRODUCT_VERSION="$(GAIM_PRODUCT_VERSION)" /DWITH_GTK /DGAIM_INSTALL_DIR="$(GAIM_INSTALL_DIR).release" /DGTK_INSTALL_VERSION="$(GTK_INSTALL_VERSION)" gaim-installer.nsi -installers: installer installer_nogtk +installer_nogtk: create_release_install_dir + $(MAKENSIS) /V3 /DGAIM_VERSION="$(GAIM_VERSION)" /DGAIM_PRODUCT_VERSION="$(GAIM_PRODUCT_VERSION)" /DGAIM_INSTALL_DIR="$(GAIM_INSTALL_DIR).release" /DGTK_INSTALL_VERSION="$(GTK_INSTALL_VERSION)" gaim-installer.nsi +installer_debug: install + $(MAKENSIS) /V3 /DGAIM_VERSION="$(GAIM_VERSION)" /DGAIM_PRODUCT_VERSION="$(GAIM_PRODUCT_VERSION)" /DGAIM_INSTALL_DIR="$(GAIM_INSTALL_DIR)" /DDEBUG /DGTK_INSTALL_VERSION="$(GTK_INSTALL_VERSION)" gaim-installer.nsi + +installers: installer installer_nogtk installer_debug clean: - $(MAKE) -C $(PO) -f Makefile.mingw clean - $(MAKE) -C $(OSCAR) -f Makefile.mingw clean - $(MAKE) -C $(YAHOO) -f Makefile.mingw clean - $(MAKE) -C $(MSN) -f Makefile.mingw clean - $(MAKE) -C $(IRC) -f Makefile.mingw clean - $(MAKE) -C $(JABBER) -f Makefile.mingw clean - $(MAKE) -C $(NAPSTER) -f Makefile.mingw clean - $(MAKE) -C $(GG) -f Makefile.mingw clean - $(MAKE) -C $(NOVELL) -f Makefile.mingw clean - $(MAKE) -C $(SILC) -f Makefile.mingw clean - $(MAKE) -C $(SIMPLE) -f Makefile.mingw clean - $(MAKE) -C $(SAMETIME) -f Makefile.mingw clean - $(MAKE) -C $(GAIM_SRC) -f Makefile.mingw clean - $(MAKE) -C $(GAIM_PLUGINS) -f Makefile.mingw clean - rm -rf config.h $(GAIM_INSTALL_DIR) - rm -rf gaim*.exe + $(MAKE) -C $(GAIM_PO_TOP) -f $(GAIM_WIN32_MAKEFILE) clean + $(MAKE) -C $(GAIM_GTK_TOP) -f $(GAIM_WIN32_MAKEFILE) clean + $(MAKE) -C $(GAIM_LIB_TOP) -f $(GAIM_WIN32_MAKEFILE) clean + rm -f $(GAIM_CONFIG_H) gaim*.exe + +uninstall: + rm -rf $(GAIM_INSTALL_PERLMOD_DIR) $(GAIM_INSTALL_PLUGINS_DIR) $(GAIM_INSTALL_PO_DIR) $(GAIM_INSTALL_DIR) $(GAIM_INSTALL_DIR).release + +include $(GAIM_COMMON_TARGETS)
--- a/NEWS Mon Apr 16 00:43:53 2007 +0000 +++ b/NEWS Sun May 20 06:19:49 2007 +0000 @@ -1,5 +1,44 @@ -=[ Gaim ]=- The Pimpin' Penguin IM Client That's Good For The Soul! +2.0.0beta6 (1/17/2006): + Sean: Barring any seriously major new issues, we expect this to be + the final beta release before 2.0.0. This has a bunch of cool UI + changes, some Google Talk features, a bunch new plugins, and other + goodness. + + Nathan: Beta6 rocks. That is all. + + Gary: Long time no news. My silence will end soon ;) + + Evan: My first news! I knocked out a nice collection of crashes, + thanks in part to my ever-patient Adium beta testers. Gaim 2.0.0 + is going to be delicious. :) + +2.0.0beta5 (11/9/2006): + Sean: Another release in our endless stream in betas. This one's + pretty awesome; and it fixes major bugs introduced in previous + ones. + +2.0.0beta4 (10/17/2006) + Sean: Still beta. Maybe the next one should be a gamma.. :) + + Daniel: I'm super chuffed to announce that this will work with newer + (i.e. >= 2.8.0) versions of GTK+ on Windows. + + Luke: Several significant changes in this one, including no longer + using libao for sound! There are no doubt bugs here, but hopefully + nothing major. + + Nathan: I don't have much to say, but yay for another beta! + + Etan: I did a bunch of perl work for this beta again, there is now + some support for perl scripts to call functions in the gtk ui, it + still needs work. + +2.0.0beta3 (03/25/2006): + Mark: Yeah, I know, another beta. Don't worry, we'll get this + puppy out the door eventually. + 2.0.0beta2 (01/24/2006): Mark: So this is the new year, and I don't feel any different, but Gaim is getting better. We hope this will be our last beta before
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PLUGIN_HOWTO Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,20 @@ +For information on writing a plugin for Gaim, go +http://gaim.sourceforge.net/api/ and see the HOWTOs in the +"Related Pages" section. + +You can also generate this documentation locally by installing +doxygen and graphviz dot, then running "make docs" in the Gaim +source tree. The documentation will be in the docs/html directory. + +This next paragraph is old and possibly out of date: +Compilation of the plugins is fairly straight-forward; there is a +Makefile in this directory that has a rule for making the .so file +from a .c file. No modification of the Makefile should be necessary, +unless if you simply want to type 'make' to have it made; otherwise, +'make filename.so' will take filename.c and make the .so plugin from +it. If you need to link in with extra libraries, you can set the +environment variable PLUGIN_LIBS to be the libraries you want to link +with. + +It should be possible to compile plugins outside of the Gaim source +tree, which is a much cleaner solution.
--- a/README Mon Apr 16 00:43:53 2007 +0000 +++ b/README Sun May 20 06:19:49 2007 +0000 @@ -24,12 +24,10 @@ well as the development files!). The configure script will fail if you don't. You can get it from http://www.gtk.org/. -For sound support, you also need libao -(http://freshmeat.net/projects/libao/) and libaudiofile -(http://www.68k.org/~michael/audiofile/). For spellchecking support, you -need libgtkspell (http://gtkspell.sf.net/). Your distro of choice -probably already includes these, just be sure to install the development -packages. +For sound support, you also need gstreamer 0.10 or higher. For +spellchecking support, you need libgtkspell (http://gtkspell.sf.net/). +Your distro of choice probably already includes these, just be sure to +install the development packages. RUN === @@ -73,7 +71,7 @@ If you come across a bug, please report it to http://gaim.sf.net/bug.php. -See README.CVS for information on the bleeding edge CVS version of Gaim. +See README.SVN for information on the bleeding edge SVN version of Gaim. You probably shouldn't use it, as it may eat your children, as well as your settings.
--- a/README.CVS Mon Apr 16 00:43:53 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ -If you plan to use gaim CVS, PLEASE read this message in its entirety! - -Gaim is a fast-moving project with a somewhat regular release schedule. -Due to the rate of gaim development, CVS undergoes frequent bursts of -massive changes, often leaving behind brokenness and partial -functionality while the responsible developers rewrite some portion of -code or seek to add new features. - -What this all boils down to is that CVS _WILL_ sometimes be broken. -Because of this, we ask that users who are not interested in -personally tracking down bugs and fixing them (without a lot of -assistance from the developers!) avoid CVS and use releases. Since -releases will be made often, this should not prevent anyone from using -the newest, shiniest features -- but it will prevent users from having -to deal with ugly development bugs that we already know about but -haven't gotten around to fixing. - -If you are interested in hacking on gaim, please read README and -HACKING, and take note of the issues in PROGRAMMING_NOTES. (Note that -they may be somewhat out of date at times.) Win32 developers, please -read README.mingw. - -By far the best documentation, however, is the documented code. Not -all parts of gaim have yet been documented, but the major subsystems -are falling fast. If you have doxygen, you can use the Doxyfile in -the toplevel directory to generate pretty documentation. Otherwise -(or even if you do!), the header files for each subsystem contain -documentation for the functions they contain. For instance, -conversation.h contains documentation for the entire -gaim_conversation_* API, and account.h contains documentation for the -gaim_account_* API. - -If you have questions, please feel free to contact the gaim developers -by email at gaim-devel@lists.sourceforge.net, on IRC at -irc.freenode.net in #gaim, or via the sourceforge forums at -http://www.sourceforge.net/projects/gaim. Please do as much homework -as you can before contacting us; the more you know about your -question, the faster and more effectively we can help you! - -Send patches to gaim-devel@lists.sourceforge.net or post them in the -Sourceforge forums at http://www.sourceforge.net/projects/gaim.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README.SVN Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,41 @@ +If you plan to use gaim SVN, PLEASE read this message in its entirety! + +Gaim is a fast-moving project with a somewhat regular release schedule. +Due to the rate of gaim development, SVN undergoes frequent bursts of +massive changes, often leaving behind brokenness and partial +functionality while the responsible developers rewrite some portion of +code or seek to add new features. + +What this all boils down to is that SVN _WILL_ sometimes be broken. +Because of this, we ask that users who are not interested in +personally tracking down bugs and fixing them (without a lot of +assistance from the developers!) avoid SVN and use releases. Since +releases will be made often, this should not prevent anyone from using +the newest, shiniest features -- but it will prevent users from having +to deal with ugly development bugs that we already know about but +haven't gotten around to fixing. + +If you are interested in hacking on gaim, please read README and +HACKING, and take note of the issues in PROGRAMMING_NOTES. (Note that +they may be somewhat out of date at times.) Win32 developers, please +read README.mingw. + +By far the best documentation, however, is the documented code. Not +all parts of gaim have yet been documented, but the major subsystems +are falling fast. If you have doxygen, you can use the Doxyfile in +the toplevel directory to generate pretty documentation. Otherwise +(or even if you do!), the header files for each subsystem contain +documentation for the functions they contain. For instance, +conversation.h contains documentation for the entire +gaim_conversation_* API, and account.h contains documentation for the +gaim_account_* API. + +If you have questions, please feel free to contact the gaim developers +by email at gaim-devel@lists.sourceforge.net, on IRC at +irc.freenode.net in #gaim, or via the sourceforge forums at +http://www.sourceforge.net/projects/gaim. Please do as much homework +as you can before contacting us; the more you know about your +question, the faster and more effectively we can help you! + +Send patches to gaim-devel@lists.sourceforge.net or post them in the +Sourceforge forums at http://www.sourceforge.net/projects/gaim.
--- a/README.mingw Mon Apr 16 00:43:53 2007 +0000 +++ b/README.mingw Sun May 20 06:19:49 2007 +0000 @@ -1,10 +1,6 @@ -How to build Gaim using MinGw +How to build Gaim using MinGW ============================= -Since these instructions are constantly changing and in order for me to -avoid maintaining two versions of these instructions please refer to: +Since these instructions are constantly changing, please refer to: -http://gaim.sourceforge.net/win32 - -- Herman - +http://gaim.sourceforge.net/win32/build.php
--- a/VERSION Mon Apr 16 00:43:53 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -2.0.0cvs
--- a/VERSION.in Mon Apr 16 00:43:53 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -@VERSION@
--- a/acinclude.m4 Mon Apr 16 00:43:53 2007 +0000 +++ b/acinclude.m4 Sun May 20 06:19:49 2007 +0000 @@ -411,305 +411,6 @@ rm -f conf.gtktest ]) -dnl This is XIPH_PATH_AO renamed to GAIM_PATH_AO to prevent conflicts. -dnl It's a long story. --elb - -# ao.m4 -# Configure paths for libao -# Jack Moffitt <jack@icecast.org> 10-21-2000 -# Shamelessly stolen from Owen Taylor and Manish Singh - -dnl GAIM_PATH_AO([ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]) -dnl Test for libao, and define AO_CFLAGS and AO_LIBS -dnl -AC_DEFUN([GAIM_PATH_AO], -[dnl -dnl Get the cflags and libraries -dnl -AC_ARG_WITH(ao,[ --with-ao=PFX Prefix where libao is installed (optional)], ao_prefix="$withval", ao_prefix="") -AC_ARG_WITH(ao-libraries,[ --with-ao-libraries=DIR Directory where libao library is installed (optional)], ao_libraries="$withval", ao_libraries="") -AC_ARG_WITH(ao-includes,[ --with-ao-includes=DIR Directory where libao header files are installed (optional)], ao_includes="$withval", ao_includes="") -AC_ARG_ENABLE(aotest, [ --disable-aotest Do not try to compile and run a test ao program],, enable_aotest=yes) - - - if test "x$ao_libraries" != "x" ; then - AO_LIBS="-L$ao_libraries" - elif test "x$ao_prefix" != "x"; then - AO_LIBS="-L$ao_prefix/lib" - elif test "x$prefix" != "xNONE"; then - AO_LIBS="-L$prefix/lib" - fi - - if test "x$ao_includes" != "x" ; then - AO_CFLAGS="-I$ao_includes" - elif test "x$ao_prefix" != "x"; then - AO_CFLAGS="-I$ao_prefix/include" - elif test "x$prefix" != "xNONE"; then - AO_CFLAGS="-I$prefix/include" - fi - - # see where dl* and friends live - AC_CHECK_FUNCS(dlopen, [AO_DL_LIBS=""], [ - AC_CHECK_LIB(dl, dlopen, [AO_DL_LIBS="-ldl"], [ - AC_MSG_WARN([could not find dlopen() needed by libao sound drivers - your system may not be supported.]) - ]) - ]) - - AO_LIBS="$AO_LIBS -lao $AO_DL_LIBS" - - AC_MSG_CHECKING(for ao) - no_ao="" - - - if test "x$enable_aotest" = "xyes" ; then - ac_save_CFLAGS="$CFLAGS" - ac_save_LIBS="$LIBS" - CFLAGS="$CFLAGS $AO_CFLAGS" - LIBS="$LIBS $AO_LIBS" -dnl -dnl Now check if the installed ao is sufficiently new. -dnl - rm -f conf.aotest - AC_TRY_RUN([ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <ao/ao.h> - -int main () -{ - system("touch conf.aotest"); - return 0; -} - -],, no_ao=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) - CFLAGS="$ac_save_CFLAGS" - LIBS="$ac_save_LIBS" - fi - - if test "x$no_ao" = "x" ; then - AC_MSG_RESULT(yes) - ifelse([$1], , :, [$1]) - else - AC_MSG_RESULT(no) - if test -f conf.aotest ; then - : - else - echo "*** Could not run ao test program, checking why..." - CFLAGS="$CFLAGS $AO_CFLAGS" - LIBS="$LIBS $AO_LIBS" - AC_TRY_LINK([ -#include <stdio.h> -#include <ao/ao.h> -], [ return 0; ], - [ echo "*** The test program compiled, but did not run. This usually means" - echo "*** that the run-time linker is not finding ao or finding the wrong" - echo "*** version of ao. If it is not finding ao, you'll need to set your" - echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" - echo "*** to the installed location Also, make sure you have run ldconfig if that" - echo "*** is required on your system" - echo "***" - echo "*** If you have an old version installed, it is best to remove it, although" - echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH"], - [ echo "*** The test program failed to compile or link. See the file config.log for the" - echo "*** exact error that occurred. This usually means ao was incorrectly installed" - echo "*** or that you have moved ao since it was installed." ]) - CFLAGS="$ac_save_CFLAGS" - LIBS="$ac_save_LIBS" - fi - AO_CFLAGS="" - AO_LIBS="" - ifelse([$2], , :, [$2]) - fi - AC_SUBST(AO_CFLAGS) - AC_SUBST(AO_LIBS) - rm -f conf.aotest -]) - -dnl audiofile.m4, included here for those people who don't have audiofile -dnl installed but would like to build CVS. - -# Configure paths for the Audio File Library -# Bertrand Guiheneuf 98-10-21 -# stolen from esd.m4 in esound : -# Manish Singh 98-9-30 -# stolen back from Frank Belew -# stolen from Manish Singh -# Shamelessly stolen from Owen Taylor - -dnl AM_PATH_AUDIOFILE([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) -dnl Test for Audio File Library, and define AUDIOFILE_CFLAGS and AUDIOFILE_LIBS. -dnl -AC_DEFUN([AM_PATH_AUDIOFILE], -[dnl -dnl Get compiler flags and libraries from the audiofile-config script. -dnl -AC_ARG_WITH(audiofile-prefix,[ --with-audiofile-prefix=PFX Prefix where Audio File Library is installed (optional)], - audiofile_prefix="$withval", audiofile_prefix="") -AC_ARG_WITH(audiofile-exec-prefix,[ --with-audiofile-exec-prefix=PFX Exec prefix where Audio File Library is installed (optional)], - audiofile_exec_prefix="$withval", audiofile_exec_prefix="") -AC_ARG_ENABLE(audiofiletest, [ --disable-audiofiletest Do not try to compile and run a test Audio File Library program], , enable_audiofiletest=yes) - - if test x$audiofile_exec_prefix != x ; then - audiofile_args="$audiofile_args --exec-prefix=$audiofile_exec_prefix" - if test x${AUDIOFILE_CONFIG+set} != xset ; then - AUDIOFILE_CONFIG=$audiofile_exec_prefix/bin/audiofile-config - fi - fi - if test x$audiofile_prefix != x ; then - audiofile_args="$audiofile_args --prefix=$audiofile_prefix" - if test x${AUDIOFILE_CONFIG+set} != xset ; then - AUDIOFILE_CONFIG=$audiofile_prefix/bin/audiofile-config - fi - fi - - AC_PATH_PROG(AUDIOFILE_CONFIG, audiofile-config, no) - min_audiofile_version=ifelse([$1], ,0.2.5,$1) - AC_MSG_CHECKING(for Audio File Library - version >= $min_audiofile_version) - no_audiofile="" - if test "$AUDIOFILE_CONFIG" = "no" ; then - no_audiofile=yes - else - AUDIOFILE_LIBS=`$AUDIOFILE_CONFIG $audiofileconf_args --libs` - AUDIOFILE_CFLAGS=`$AUDIOFILE_CONFIG $audiofileconf_args --cflags` - audiofile_major_version=`$AUDIOFILE_CONFIG $audiofile_args --version | \ - sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` - audiofile_minor_version=`$AUDIOFILE_CONFIG $audiofile_args --version | \ - sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` - audiofile_micro_version=`$AUDIOFILE_CONFIG $audiofile_config_args --version | \ - sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` - if test "x$enable_audiofiletest" = "xyes" ; then - AC_LANG_SAVE - AC_LANG_C - ac_save_CFLAGS="$CFLAGS" - ac_save_LIBS="$LIBS" - CFLAGS="$CFLAGS $AUDIOFILE_CFLAGS" - LIBS="$LIBS $AUDIOFILE_LIBS" -dnl -dnl Now check if the installed Audio File Library is sufficiently new. -dnl (Also checks the sanity of the results of audiofile-config to some extent.) -dnl - rm -f conf.audiofiletest - AC_TRY_RUN([ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <audiofile.h> - -char* -my_strdup (char *str) -{ - char *new_str; - - if (str) - { - new_str = malloc ((strlen (str) + 1) * sizeof(char)); - strcpy (new_str, str); - } - else - new_str = NULL; - - return new_str; -} - -int main () -{ - int major, minor, micro; - char *tmp_version; - - system ("touch conf.audiofiletest"); - - /* HP/UX 9 (%@#!) writes to sscanf strings */ - tmp_version = my_strdup("$min_audiofile_version"); - if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { - printf("%s, bad version string\n", "$min_audiofile_version"); - exit(1); - } - - if (($audiofile_major_version > major) || - (($audiofile_major_version == major) && ($audiofile_minor_version > minor)) || - (($audiofile_major_version == major) && ($audiofile_minor_version == minor) && ($audiofile_micro_version >= micro))) - { - return 0; - } - else - { - printf("\n*** 'audiofile-config --version' returned %d.%d.%d, but the minimum version\n", $audiofile_major_version, $audiofile_minor_version, $audiofile_micro_version); - printf("*** of the Audio File Library required is %d.%d.%d. If audiofile-config is correct, then it is\n", major, minor, micro); - printf("*** best to upgrade to the required version.\n"); - printf("*** If audiofile-config was wrong, set the environment variable AUDIOFILE_CONFIG\n"); - printf("*** to point to the correct copy of audiofile-config, and remove the file\n"); - printf("*** config.cache before re-running configure\n"); - return 1; - } -} - -],, no_audiofile=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) - CFLAGS="$ac_save_CFLAGS" - LIBS="$ac_save_LIBS" - AC_LANG_RESTORE - fi - fi - if test "x$no_audiofile" = x ; then - AC_MSG_RESULT(yes) - ifelse([$2], , :, [$2]) - else - AC_MSG_RESULT(no) - if test "$AUDIOFILE_CONFIG" = "no" ; then - cat <<END -*** The audiofile-config script installed by the Audio File Library could -*** not be found. If the Audio File Library was installed in PREFIX, make -*** sure PREFIX/bin is in your path, or set the AUDIOFILE_CONFIG -*** environment variable to the full path to audiofile-config. -END - else - if test -f conf.audiofiletest ; then - : - else - echo "*** Could not run Audio File Library test program; checking why..." - AC_LANG_SAVE - AC_LANG_C - CFLAGS="$CFLAGS $AUDIOFILE_CFLAGS" - LIBS="$LIBS $AUDIOFILE_LIBS" - AC_TRY_LINK([ -#include <stdio.h> -#include <audiofile.h> -], [ return 0; ], - [ cat <<END -*** The test program compiled, but did not run. This usually means that -*** the run-time linker is not finding Audio File Library or finding the -*** wrong version of Audio File Library. -*** -*** If it is not finding Audio File Library, you'll need to set your -*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point -*** to the installed location. Also, make sure you have run ldconfig if -*** that is required on your system. -*** -*** If you have an old version installed, it is best to remove it, although -*** you may also be able to get things to work by modifying -*** LD_LIBRARY_PATH. -END - ], - [ echo "*** The test program failed to compile or link. See the file config.log" - echo "*** for the exact error that occurred. This usually means the Audio File" - echo "*** Library was incorrectly installed or that you have moved the Audio" - echo "*** File Library since it was installed. In the latter case, you may want" - echo "*** to edit the audiofile-config script: $AUDIOFILE_CONFIG" ]) - CFLAGS="$ac_save_CFLAGS" - LIBS="$ac_save_LIBS" - AC_LANG_RESTORE - fi - fi - AUDIOFILE_CFLAGS="" - AUDIOFILE_LIBS="" - ifelse([$3], , :, [$3]) - fi - AC_SUBST(AUDIOFILE_CFLAGS) - AC_SUBST(AUDIOFILE_LIBS) - rm -f conf.audiofiletest -]) - dnl ac_var_timeszone_externals.m4 # Define 'timezone', 'altzone' and 'daylight' @@ -836,3 +537,42 @@ AC_SUBST(BINRELOC_CFLAGS) AC_SUBST(BINRELOC_LIBS) ]) +dnl AM_GCONF_SOURCE_2 +dnl Defines GCONF_SCHEMA_CONFIG_SOURCE which is where you should install schemas +dnl (i.e. pass to gconftool-2 +dnl Defines GCONF_SCHEMA_FILE_DIR which is a filesystem directory where +dnl you should install foo.schemas files +dnl + +AC_DEFUN([AM_GCONF_SOURCE_2], +[ + if test "x$GCONF_SCHEMA_INSTALL_SOURCE" = "x"; then + GCONF_SCHEMA_CONFIG_SOURCE=`gconftool-2 --get-default-source` + else + GCONF_SCHEMA_CONFIG_SOURCE=$GCONF_SCHEMA_INSTALL_SOURCE + fi + + AC_ARG_WITH(gconf-source, + [ --with-gconf-source=sourceaddress Config database for installing schema files.],GCONF_SCHEMA_CONFIG_SOURCE="$withval",) + + AC_SUBST(GCONF_SCHEMA_CONFIG_SOURCE) + AC_MSG_RESULT([Using config source $GCONF_SCHEMA_CONFIG_SOURCE for schema installation]) + + if test "x$GCONF_SCHEMA_FILE_DIR" = "x"; then + GCONF_SCHEMA_FILE_DIR='$(sysconfdir)/gconf/schemas' + fi + + AC_ARG_WITH(gconf-schema-file-dir, + [ --with-gconf-schema-file-dir=dir Directory for installing schema files.],GCONF_SCHEMA_FILE_DIR="$withval",) + + AC_SUBST(GCONF_SCHEMA_FILE_DIR) + AC_MSG_RESULT([Using $GCONF_SCHEMA_FILE_DIR as install directory for schema files]) + + AC_ARG_ENABLE(schemas-install, + [ --disable-schemas-install Disable the schemas installation], + [case ${enableval} in + yes|no) ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-schemas-install) ;; + esac]) + AM_CONDITIONAL([GCONF_SCHEMAS_INSTALL], [test "$enable_schemas_install" != no]) +])
--- a/autogen.sh Mon Apr 16 00:43:53 2007 +0000 +++ b/autogen.sh Sun May 20 06:19:49 2007 +0000 @@ -1,31 +1,41 @@ #!/bin/sh -SETUP_GETTEXT=./setup-gettext +CONFIGURE_ARGS="" +if [ -f configure.args ] ; then + CONFIGURE_ARGS="${CONFIGURE_ARGS} `cat configure.args`" +fi -($SETUP_GETTEXT --gettext-tool) < /dev/null > /dev/null 2>&1 || { +(glib-gettextize --version) < /dev/null > /dev/null 2>&1 || { + echo; + echo "You must have glib-gettextize installed to compile Gaim."; echo; - echo "You must have gettext installed to compile Gaim"; + exit; +} + +(intltoolize --version) < /dev/null > /dev/null 2>&1 || { + echo; + echo "You must have intltool installed to compile Gaim."; echo; exit; } (libtoolize --version) < /dev/null > /dev/null 2>&1 || { echo; - echo "You must have libtool installed to compile Gaim"; + echo "You must have libtool installed to compile Gaim."; echo; exit; } (automake --version) < /dev/null > /dev/null 2>&1 || { echo; - echo "You must have automake installed to compile Gaim"; + echo "You must have automake installed to compile Gaim."; echo; exit; } (autoconf --version) < /dev/null > /dev/null 2>&1 || { echo; - echo "You must have autoconf installed to compile Gaim"; + echo "You must have autoconf installed to compile Gaim."; echo; exit; } @@ -33,17 +43,6 @@ echo "Generating configuration files for Gaim, please wait...." echo; -# Backup the po/ChangeLog. This should prevent the annoying -# gettext ChangeLog modifications. - -cp -p po/ChangeLog po/ChangeLog.save - -echo "Running gettextize, please ignore non-fatal messages...." -$SETUP_GETTEXT - -# Restore the po/ChangeLog file. -mv po/ChangeLog.save po/ChangeLog - echo "Running libtoolize, please ignore non-fatal messages...." echo n | libtoolize --copy --force || exit; @@ -58,10 +57,17 @@ fi done -aclocal $ACLOCAL_FLAGS -I ./m4 || exit; +libtoolize -c -f --automake +glib-gettextize --force --copy +intltoolize --force --copy +aclocal $ACLOCAL_FLAGS || exit; autoheader || exit; automake --add-missing --copy; autoconf || exit; automake || exit; -./configure $@ +echo; +echo "Running ./configure ${CONFIGURE_ARGS} $@" +echo; +./configure ${CONFIGURE_ARGS} $@ +
--- a/config.h.mingw Mon Apr 16 00:43:53 2007 +0000 +++ b/config.h.mingw Sun May 20 06:19:49 2007 +0000 @@ -183,6 +183,9 @@ declares uintmax_t. */ #define HAVE_INTTYPES_H_WITH_UINTMAX 1 +/* Define if we have IOKit */ +/* #undef HAVE_IOKIT */ + /* Define to 1 if you have the `krb_get_err_text' function. */ /* #undef HAVE_KRB_GET_ERR_TEXT */ @@ -204,6 +207,9 @@ /* Define if your <locale.h> file defines LC_MESSAGES. */ /* #define HAVE_LC_MESSAGES 1 */ +/* Define to 1 if you have libgadu. */ +#define HAVE_LIBGADU 1 + /* Define to 1 if you have the `nsl' library (-lnsl). */ /* #define HAVE_LIBNSL 1 */ @@ -315,7 +321,7 @@ /* Define to 1 if you have the <signal.h> header file. */ /* #define HAVE_SIGNAL_H 1 */ -/* define if we have silcmime.h */ +/* Define if we have silcmime.h */ /* #undef HAVE_SILCMIME_H */ /* Define to 1 if you have the <smime.h> header file. */ @@ -373,6 +379,9 @@ /* Define to 1 if you have the `strftime' function. */ #define HAVE_STRFTIME 1 +/* Define to 1 if you have a strftime() that supports the %z format string. */ +/* #undef HAVE_STRFTIME_Z_FORMAT */ + /* Define to 1 if you have the <strings.h> header file. */ #define HAVE_STRINGS_H 1 @@ -419,7 +428,7 @@ #define HAVE_SYS_STAT_H 1 /* Define to 1 if you have the <sys/time.h> header file. */ -#define HAVE_SYS_TIME_H 1 +/* #undef HAVE_SYS_TIME_H */ /* Define to 1 if you have the <sys/types.h> header file. */ #define HAVE_SYS_TYPES_H 1 @@ -520,13 +529,13 @@ #define PACKAGE_NAME "gaim" /* Define to the full name and version of this package. */ -/* #define PACKAGE_STRING "gaim 2.0.0cvs" */ +/* #define PACKAGE_STRING "gaim 2.0.0dev" */ /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "gaim" /* Define to the version of this package. */ -/* #define PACKAGE_VERSION "2.0.0cvs" */ +/* #define PACKAGE_VERSION "2.0.0dev" */ /* Define if <inttypes.h> exists and defines unusable PRI* macros. */ /* #undef PRI_MACROS_BROKEN */ @@ -541,9 +550,9 @@ /* If using the C implementation of alloca, define if you know the direction of stack growth for your system; otherwise it will be automatically deduced at run-time. - STACK_DIRECTION > 0 => grows toward higher addresses - STACK_DIRECTION < 0 => grows toward lower addresses - STACK_DIRECTION = 0 => direction of growth unknown */ + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown */ /* #undef STACK_DIRECTION */ /* Loads static protocol plugin module initialization functions. */ @@ -570,7 +579,7 @@ /* #define USE_SM 1 */ /* Version number of package */ -/* #define VERSION "2.0.0cvs" */ +/* #define VERSION "2.0.0dev" */ /* Define to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel and VAX). */ @@ -620,13 +629,17 @@ /* #undef size_t */ /* socklen_t size */ -#define socklen_t int +/* #define socklen_t int */ /* Define to unsigned long or unsigned long long if <stdint.h> and <inttypes.h> don't define. */ /* #undef uintmax_t */ +#define HAVE_LIBXML 1 + /* * Following are added for Win32 version of Gaim */ #define HAVE_VSNPRINTF 1 + +#define SIZEOF_TIME_T 4
--- a/configure.ac Mon Apr 16 00:43:53 2007 +0000 +++ b/configure.ac Sun May 20 06:19:49 2007 +0000 @@ -1,8 +1,9 @@ dnl Process this file with autoconf to produce a configure script. -AC_INIT([gaim], [2.0.0cvs], [gaim-devel@lists.sourceforge.net]) +AC_INIT([gaim], [2.0.0beta6], [gaim-devel@lists.sourceforge.net]) AC_CANONICAL_SYSTEM AM_CONFIG_HEADER(config.h) AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION) +#AM_INIT_AUTOMAKE([foreign dist-bzip2]) AC_PREREQ([2.50]) @@ -17,11 +18,24 @@ AM_PROG_LIBTOOL LIBTOOL="$LIBTOOL --silent" AC_PROG_INSTALL +AC_PROG_INTLTOOL +PKG_PROG_PKG_CONFIG + +GETTEXT_PACKAGE=gaim +AC_SUBST(GETTEXT_PACKAGE) # before gettexting, in case iconv matters case "$host_os" in darwin*) AC_CHECK_LIB(resolv, res_query) + + AC_CHECK_HEADER(CoreFoundation/CoreFoundation.h, [ + AC_CHECK_HEADER(IOKit/IOKitLib.h, [ + AC_DEFINE(HAVE_IOKIT, 1, [Define if we have IOKit]) + LIBS="$LIBS -framework IOKit -framework CoreFoundation" + ], []) + ], []) + AC_MSG_CHECKING([for fink]) if test -d /sw; then AC_MSG_RESULT([found, adding /sw to search paths]) @@ -35,9 +49,8 @@ ;; esac -ALL_LINGUAS="am az bg bn bs ca cs da de el en_AU en_CA en_GB es et fi fr gl gu he hi hu it ja ka ko ku lt mk my_MM nb nl nn pa pl pt_BR pt ro ru sk sl sq sr sr@Latn sv ta te tr uk vi xh zh_CN zh_TW" -AM_GNU_GETTEXT_VERSION(0.10.40) -AM_GNU_GETTEXT +ALL_LINGUAS="am ar az bg bn bs ca ca@valencia cs da de dz el en_AU en_CA en_GB eo es et eu fa fi fr gl gu he hi hu it ja ka ko ku lt mk my_MM nb ne nl nn pa pl pt_BR pt ro ru sk sl sq sr sr@Latn sv ta te th tr uk vi xh zh_CN zh_TW" +AM_GLIB_GNU_GETTEXT dnl we don't use autobreak on cygwin!! dnl AC_CYGWIN @@ -50,13 +63,16 @@ dnl Checks for typedefs, structures, and compiler characteristics. AC_C_CONST AC_STRUCT_TM +AC_CHECK_SIZEOF(time_t, ,[ +#include <stdio.h> +#include <time.h>]) AC_C_BIGENDIAN dnl Checks for library functions. AC_TYPE_SIGNAL AC_FUNC_STRFTIME -AC_CHECK_FUNCS(strdup strstr atexit) +AC_CHECK_FUNCS(strdup strstr atexit setlocale) dnl Checks for getopt in standard library AC_CHECK_FUNCS(getopt_long,, [ @@ -65,18 +81,19 @@ ]) dnl Check for inet_aton -AC_CHECK_FUNC(inet_aton, , [AC_CHECK_LIB(resolv, inet_aton, , +AC_CHECK_FUNC(inet_aton, , [AC_CHECK_LIB(resolv, inet_aton, , [AC_ERROR(inet_aton not found)])]) AC_CHECK_LIB(resolv, __res_query) AC_CHECK_LIB(nsl, gethostent) -AC_CHECK_FUNC(socket, , - [AC_CHECK_LIB(socket, socket, , [AC_ERROR([socket not found])])]) +AC_CHECK_FUNC(socket, , + [AC_CHECK_LIB(socket, socket, , [AC_ERROR([socket not found])])]) dnl If all goes well, by this point the previous two checks will have dnl pulled in -lsocket and -lnsl if we need them. -AC_CHECK_FUNC(getaddrinfo, [AC_DEFINE([HAVE_GETADDRINFO], [1], - [Define to 1 if you have the getaddrinfo function.])], - [AC_CHECK_LIB(socket, getaddrinfo, - [AC_DEFINE([HAVE_GETADDRINFO]) LIBS="-lsocket -lsnl $LIBS"], , , -lnsl)]) +AC_CHECK_FUNC(getaddrinfo, + [AC_DEFINE([HAVE_GETADDRINFO], [1], + [Define to 1 if you have the getaddrinfo function.])], + [AC_CHECK_LIB(socket, getaddrinfo, + [AC_DEFINE([HAVE_GETADDRINFO]) LIBS="-lsocket -lsnl $LIBS"], , , -lnsl)]) dnl Check for socklen_t (in Unix98) AC_MSG_CHECKING(for socklen_t) @@ -108,32 +125,401 @@ dnl FreeBSD doesn't have libdl, dlopen is provided by libc AC_CHECK_FUNC(dlopen, LIBDL="", [AC_CHECK_LIB(dl, dlopen, LIBDL="-ldl")]) +AC_MSG_CHECKING(for the %z format string in strftime()) +AC_TRY_RUN([ +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#include <time.h> +#include <stdio.h> +int main() +{ + char buf[6]; + time_t t = time(NULL); + + if (strftime(buf, sizeof(buf), "%z", localtime(&t)) != 5) + return 1; + + fprintf(stderr, "strftime(\"%%z\") yields: \"%s\"\n", buf); + + return !((buf[0] == '-' || buf[0] == '+') && + (buf[1] >= '0' && buf[1] <= '9') && + (buf[2] >= '0' && buf[2] <= '9') && + (buf[3] >= '0' && buf[3] <= '9') && + (buf[4] >= '0' && buf[4] <= '9') + ); +} +], [ + AC_MSG_RESULT(yes) + AC_DEFINE([HAVE_STRFTIME_Z_FORMAT], [1], + [Define to 1 if you have a strftime() that supports the %z format string.]) +], [ + AC_MSG_RESULT(no) +], [ + # Fallback for Cross Compiling... + # This will enable the compatibility code. + AC_MSG_RESULT(no) +] +) + +dnl ####################################################################### +dnl # Check for GLib 2.0 (required) +dnl ####################################################################### +PKG_CHECK_MODULES(GLIB, [glib-2.0 >= 2.0.0 gobject-2.0 gmodule-2.0 gthread-2.0], , [ + AC_MSG_RESULT(no) + AC_MSG_ERROR([ + +You must have the GLib 2.0 development headers installed to build Gaim. +])]) +AC_SUBST(GLIB_CFLAGS) +AC_SUBST(GLIB_LIBS) + +AC_ARG_ENABLE(gtkui, [AC_HELP_STRING([--disable-gtkui], + [compile without GTK+ user interface])], + enable_gtkui="$enableval", enable_gtkui="yes") +AC_ARG_ENABLE(consoleui, [AC_HELP_STRING([--disable-consoleui], + [compile without console user interface])], + enable_consoleui=$enableval, enable_consoleui=yes) + +dnl ####################################################################### +dnl # Check for GTK+ 2.0 and other things used by the GTK UI +dnl ####################################################################### +AC_ARG_ENABLE(screensaver, + [AC_HELP_STRING([--disable-screensaver], + [compile without X screensaver extension (used to detect idleness)])], + enable_screensaver="$enableval", enable_screensaver="yes") +AC_ARG_ENABLE(sm, + [AC_HELP_STRING([--disable-sm], + [compile without X session management support])], + enable_sm="$enableval", enable_sm="yes") +AC_ARG_ENABLE(startup-notification, + [AC_HELP_STRING([--disable-startup-notification], + [compile without startup notification support])], + enable_startup_notification="$enableval", enable_startup_notification="yes") +AC_ARG_ENABLE(gtkspell, + [AC_HELP_STRING([--disable-gtkspell], + [compile without GtkSpell automatic spell checking])], + enable_gtkspell="$enableval", enable_gtkspell="yes") +AC_ARG_ENABLE(gevolution, + [AC_HELP_STRING([--disable-gevolution], + [compile without the Gaim Evolution plugin])], + enable_gevolution="$enableval", enable_gevolution="yes") +AC_ARG_ENABLE(cap, + [AC_HELP_STRING([--disable-cap], + [compile without Contact Availability Prediction plugin])], + enable_cap="$enableval", enable_cap="yes") + + +AC_PATH_XTRA +# We can't assume that $x_libraries will be set, because autoconf does not +# set it in the case when the X libraries are in a standard place. +# Ditto for $x_includes +if test X"$x_libraries" = X"" || test X"$x_libraries" = XNONE; then + x_libpath_add= +else + x_libpath_add="-L$x_libraries" +fi +if test X"$x_includes" = X"" || test X"$x_includes" = XNONE; then + x_incpath_add= +else + x_incpath_add="-I$x_includes" +fi + +if test "x$enable_gtkui" = "xyes" ; then + PKG_CHECK_MODULES(GTK, [gtk+-2.0 >= 2.0.0], , [ + AC_MSG_RESULT(no) + AC_MSG_ERROR([ + +You must have the GTK+ 2.0 development headers installed to compile Gaim's +GTK+ interface. If you only want to build the console interface then +specify --disable-gtkui when running configure. +])]) + + AC_SUBST(GTK_CFLAGS) + AC_SUBST(GTK_LIBS) + + dnl ####################################################################### + dnl # Check for XScreenSaver + dnl ####################################################################### + if test "x$enable_screensaver" = "xyes" ; then + old_LIBS="$LIBS" + LIBS="$LIBS $GTK_LIBS $x_libpath_add" + XSS_LIBS="" + XSS_HEADERS="" + AC_CHECK_LIB(Xext, XScreenSaverRegister,[XSS_LIBS="$X_LIBS $X_PRE_LIBS -lX11 -lXext $X_EXTRA_LIBS"],[],[-lX11 -lXext -lm]) + AC_CHECK_LIB(Xss, XScreenSaverRegister,[XSS_LIBS="$X_LIBS $X_PRE_LIBS -lX11 -lXext $X_LIBS $X_EXTRA_LIBS -lXss"],[],[-lX11 -lXext -lm]) + if test "x$XSS_LIBS" != "x"; then + oldCPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $x_incpath_add" + AC_TRY_COMPILE([ + #include <X11/Xlib.h> + #include <X11/extensions/scrnsaver.h> + ], [], [], [enable_screensaver=no]) + CPPFLAGS="$oldCPPFLAGS" + else + enable_screensaver=no + fi + LIBS="$old_LIBS" + + if test "x$enable_screensaver" = "xyes" ; then + AC_DEFINE(USE_SCREENSAVER, 1, [Define if we're using XScreenSaver.]) + AC_SUBST(XSS_LIBS) + fi + fi + + dnl ####################################################################### + dnl # Check for X session management libs + dnl ####################################################################### + if test "x$enable_sm" = "xyes"; then + enable_sm=no + AC_CHECK_LIB(SM, SmcSaveYourselfDone, found_sm_lib=true, , [$x_libpath_add -lICE]) + if test "x$found_sm_lib" = "xtrue"; then + oldCPPFLAGS="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $x_incpath_add" + AC_CHECK_HEADERS(X11/SM/SMlib.h, SM_LIBS="$x_libpath_add -lSM -lICE" enable_sm=yes) + CPPFLAGS="$oldCPPFLAGS" + fi + + if test "x$enable_sm" = "xyes"; then + AC_DEFINE(USE_SM, 1, [Define if we're using X Session Management.]) + AC_SUBST(SM_LIBS) + fi + fi + + dnl ####################################################################### + dnl # Check for startup notification + dnl ####################################################################### + if test "x$enable_startup_notification" = "xyes"; then + PKG_CHECK_MODULES(STARTUP_NOTIFICATION, [libstartup-notification-1.0 >= 0.5], , [ + AC_MSG_RESULT(no) + enable_startup_notification=no + ]) + + if test "x$enable_startup_notification" = "xyes"; then + AC_DEFINE(HAVE_STARTUP_NOTIFICATION, 1, [Define if we're using libstartup-notification.]) + AC_SUBST(STARTUP_NOTIFICATION_CFLAGS) + AC_SUBST(STARTUP_NOTIFICATION_LIBS) + fi + fi + + dnl ####################################################################### + dnl # Check for GtkSpell + dnl ####################################################################### + if test "x$enable_gtkspell" = "xyes" ; then + PKG_CHECK_MODULES(GTKSPELL, gtkspell-2.0 >= 2.0.2, , [ + AC_MSG_RESULT(no) + enable_gtkspell=no + ]) + if test "x$enable_gtkspell" = "xyes" ; then + AC_DEFINE(USE_GTKSPELL, 1, [Define if we're using GtkSpell]) + AC_SUBST(GTKSPELL_CFLAGS) + AC_SUBST(GTKSPELL_LIBS) + fi + fi + + dnl ####################################################################### + dnl # Check for stuff needed by the Evolution integration plugin. + dnl ####################################################################### + if test "x$enable_gevolution" = "xyes"; then + evo_deps="libebook-1.2 libedata-book-1.2" + PKG_CHECK_MODULES(EVOLUTION_ADDRESSBOOK, $evo_deps, , [ + AC_MSG_RESULT(yes) + enable_gevolution="no" + ]) + if test "x$enable_gevolution" = "xno"; then + evo_deps="libebook-1.0 libedata-book-1.0" + PKG_CHECK_MODULES(EVOLUTION_ADDRESSBOOK, $evo_deps, [ + enable_gevolution="yes" + ], [ + AC_MSG_RESULT(yes) + ]) + fi + if test "x$enable_gevolution" = "xyes"; then + AC_DEFINE(HAVE_EVOLUTION_ADDRESSBOOK, 1, [Define if we're using evolution addressbook.]) + AC_SUBST(EVOLUTION_ADDRESSBOOK_CFLAGS) + AC_SUBST(EVOLUTION_ADDRESSBOOK_LIBS) + fi + fi + + dnl ####################################################################### + dnl # Check for libsqlite3 (for the Contact Availability Prediction plugin) + dnl ####################################################################### + if test "x$enable_cap" = "xyes"; then + PKG_CHECK_MODULES(SQLITE3, sqlite3 >= 3.3,,[ + AC_MSG_RESULT(no) + enable_cap="no" + ]) + fi + +else # GTK + enable_cap=no + enable_gevolution=no + enable_gtkspell=no + enable_screensaver=no + enable_sm=no + enable_startup_notification=no +fi # GTK + +AM_CONDITIONAL(ENABLE_GTK, test "x$enable_gtkui" = "xyes") +AM_CONDITIONAL(BUILD_GEVOLUTION, test "x$enable_gevolution" = "xyes") +AM_CONDITIONAL(ENABLE_CAP, test "x$enable_cap" = "xyes") + +dnl ####################################################################### +dnl # Check for ncurses and other things used by the console UI +dnl ####################################################################### +GNT_LIBS="" +GNT_CFLAGS="" +AC_ARG_WITH(ncurses-headers, [AC_HELP_STRING([--with-ncurses-headers=DIR], + [compile gaim-text against the ncurses includes in DIR])], + [ac_ncurses_includes="$withval"], [ac_ncurses_includes=""]) +if test "x$enable_consoleui" = "xyes"; then + AC_CHECK_LIB(ncursesw, initscr, [GNT_LIBS="-lncursesw"], [enable_consoleui=no]) + AC_CHECK_LIB(panelw, update_panels, [GNT_LIBS="$GNT_LIBS -lpanelw"], [enable_consoleui=no]) + + if test "x$enable_consoleui" = "xyes"; then + dnl # Some distros put the headers in ncursesw/, some don't + found_ncurses_h=no + for location in $ac_ncurses_includes $NCURSES_HEADERS /usr/include/ncursesw /usr/include + do + f="$location/ncurses.h" + AC_CHECK_HEADER($f,[ + AC_MSG_CHECKING([if $f supports wide characters]) + AC_TRY_COMPILE([ + #define _XOPEN_SOURCE_EXTENDED + #include <$f> + ], [ + #ifndef get_wch + # error get_wch not found! + #endif + ], [ + dir=$location + if test x"$dir" != x"." ; then + GNT_CFLAGS="-I$dir/" + else + GNT_CFLAGS="" + fi + + found_ncurses_h=yes + AC_MSG_RESULT([yes]) + break + ], [ + AC_MSG_RESULT([no]) + ]) + ]) + done + + if test x"$found_ncurses_h" = x"no" ; then + GNT_LIBS="" + GNT_CFLAGS="" + enable_consoleui=no + AC_MSG_RESULT([no]) + else + AC_MSG_RESULT([yes]) + fi + else + # ncursesw was not found. Look for plain old ncurses + enable_consoleui=yes + AC_CHECK_LIB(ncurses, initscr, [GNT_LIBS="-lncurses"], [enable_consoleui=no]) + AC_CHECK_LIB(panel, update_panels, [GNT_LIBS="$GNT_LIBS -lpanel"], [enable_consoleui=no]) + AC_DEFINE(NO_WIDECHAR, 1, [Define to 1 if you don't have wide-character support.]) + if test x"$ac_ncurses_includes" != "x"; then + GNT_CFLAGS="-I$ac_ncurses_includes" + else + if test x"$NCURSES_HEADERS" != "x"; then + GNT_CFLAGS="-I$NCURSES_HEADERS" + fi + fi + fi + + PKG_CHECK_MODULES(X11, x11, + [AC_DEFINE(HAVE_X11, 1, [Define to 1 if you have X11])], [AC_MSG_RESULT(no)]) + AC_SUBST(X11_LIBS) + AC_SUBST(X11_CFLAGS) +fi + +AC_SUBST(GNT_LIBS) +AC_SUBST(GNT_CFLAGS) +AM_CONDITIONAL(ENABLE_GNT, test "x$enable_consoleui" = "xyes") + +#AC_CHECK_FUNC(wcwidth, [AC_DEFINE([HAVE_WCWIDTH], [1], [Define to 1 if you have wcwidth function.])]) + +dnl ####################################################################### +dnl # Check for LibXML2 (required) +dnl ####################################################################### +PKG_CHECK_MODULES(LIBXML, [libxml-2.0 >= 2.6.0], , [ + AC_MSG_RESULT(no) + AC_MSG_ERROR([ + +You must have libxml2 >= 2.6.0 development headers installed to build Gaim. +])]) +AC_SUBST(LIBXML_CFLAGS) +AC_SUBST(LIBXML_LIBS) + +dnl ####################################################################### +dnl # GConf schemas +dnl ####################################################################### +AC_PATH_PROG(GCONFTOOL, gconftool-2, no) +AM_CONDITIONAL(USE_GCONFTOOL, test "x$GCONFTOOL" != "xno") +AM_GCONF_SOURCE_2 + +dnl ####################################################################### +dnl # Check for GStreamer +dnl ####################################################################### +AC_ARG_ENABLE(gstreamer, + [AC_HELP_STRING([--disable-gstreamer], [compile without GStreamer audio support])], + enable_gst="$enableval", enable_gst="yes") +PKG_CHECK_MODULES(GSTREAMER, [gstreamer-0.10], , [ + AC_MSG_RESULT(no) + enable_gst="no" +]) +if test "x$enable_gst" != "xno"; then + AC_DEFINE(USE_GSTREAMER, 1, [Use GStreamer for playing sounds]) + AC_SUBST(GSTREAMER_CFLAGS) + AC_SUBST(GSTREAMER_LIBS) +fi dnl ####################################################################### dnl # Check for Meanwhile headers (for Sametime) dnl ####################################################################### -PKG_CHECK_MODULES(MEANWHILE, - [meanwhile >= 1.0.0 meanwhile < 2.0.0], - [have_meanwhile="yes"], - [have_meanwhile="no"]) +PKG_CHECK_MODULES(MEANWHILE, [meanwhile >= 1.0.0 meanwhile < 2.0.0], [ + have_meanwhile="yes" +], [ + AC_MSG_RESULT(no) + have_meanwhile="no" +]) AC_SUBST(MEANWHILE_CFLAGS) AC_SUBST(MEANWHILE_LIBS) - - dnl ####################################################################### dnl # Check for Howl headers (for Bonjour) dnl ####################################################################### -AC_ARG_WITH(howl-includes, [AC_HELP_STRING([--with-howl-includes=DIR], [Compile the Bonjour plugin against the Howl includes in DIR])], [ac_howl_includes="$withval"], [ac_howl_includes="no"]) -AC_ARG_WITH(howl-libs, [AC_HELP_STRING([--with-howl-libs=DIR], [Compile the Bonjour plugin against the Howl libs in DIR])], [ac_howl_libs="$withval"], [ac_howl_libs="no"]) +AC_ARG_WITH(howl-includes, [AC_HELP_STRING([--with-howl-includes=DIR], [compile the Bonjour plugin against the Howl includes in DIR])], [ac_howl_includes="$withval"], [ac_howl_includes="no"]) +AC_ARG_WITH(howl-libs, [AC_HELP_STRING([--with-howl-libs=DIR], [compile the Bonjour plugin against the Howl libs in DIR])], [ac_howl_libs="$withval"], [ac_howl_libs="no"]) HOWL_CFLAGS="" HOWL_LIBS="" +dnl Attempt to autodetect avahi-compat-howl +PKG_CHECK_MODULES(HOWL, avahi-compat-howl, [ + howlincludes="yes" + howllibs="yes" +], [ + AC_MSG_RESULT(no) + howlincludes="no" + howllibs="no" +]) + dnl Attempt to autodetect Howl -PKG_CHECK_MODULES(HOWL, howl, - [howlincludes="yes" howllibs="yes"], - [howlincludes="no" howllibs="no"]) +if test "x$howlincludes" = "xno"; then + PKG_CHECK_MODULES(HOWL, howl, [ + howlincludes="yes" + howllibs="yes" + ], [ + AC_MSG_RESULT(no) + howlincludes="no" + howllibs="no" + ]) +fi dnl Override HOWL_CFLAGS if the user specified an include dir if test "$ac_howl_includes" != "no"; then @@ -146,20 +532,18 @@ dnl Override HOWL_LIBS if the user specified a libs dir if test "$ac_howl_libs" != "no"; then - HOWL_LIBS="-L$ac_howl_libs" + HOWL_LIBS="-L$ac_howl_libs -lhowl" fi AC_CHECK_LIB(howl, sw_discovery_init, [howllibs=yes], [howllibs=no], $HOWL_LIBS) AC_SUBST(HOWL_CFLAGS) AC_SUBST(HOWL_LIBS) - - dnl ####################################################################### dnl # Check for SILC client includes and libraries dnl ####################################################################### -AC_ARG_WITH(silc-includes, [AC_HELP_STRING([--with-silc-includes=DIR], [Compile the SILC plugin against includes in DIR])], [ac_silc_includes="$withval"], [ac_silc_includes="no"]) -AC_ARG_WITH(silc-libs, [AC_HELP_STRING([--with-silc-libs=DIR], [Compile the SILC plugin against the SILC libs in DIR])], [ac_silc_libs="$withval"], [ac_silc_libs="no"]) +AC_ARG_WITH(silc-includes, [AC_HELP_STRING([--with-silc-includes=DIR], [compile the SILC plugin against includes in DIR])], [ac_silc_includes="$withval"], [ac_silc_includes="no"]) +AC_ARG_WITH(silc-libs, [AC_HELP_STRING([--with-silc-libs=DIR], [compile the SILC plugin against the SILC libs in DIR])], [ac_silc_libs="$withval"], [ac_silc_libs="no"]) SILC_CFLAGS="" SILC_LIBS="" if test -n "$with_silc_includes" || test -n "$with_silc_libs"; then @@ -172,14 +556,20 @@ have_silc="yes" silcincludes="yes" silcclient="yes" - ], have_silc="no") + ], [ + AC_MSG_RESULT(no) + have_silc="no" + ]) dnl If silcclient.pc wasn't found, check for just silc.pc if test "x$have_silc" = "xno"; then PKG_CHECK_MODULES(SILC, silc, [ have_silc="yes" silcincludes="yes" silcclient="yes" - ], have_silc="no") + ], [ + AC_MSG_RESULT(no) + have_silc="no" + ]) fi else if test "$ac_silc_includes" != "no"; then @@ -208,25 +598,93 @@ #include <silcmime.h> ], [], [ AC_MSG_RESULT(yes) - AC_DEFINE(HAVE_SILCMIME_H, 1, [define if we have silcmime.h]) + AC_DEFINE(HAVE_SILCMIME_H, 1, [Define if we have silcmime.h]) ], [ AC_MSG_RESULT(no) ]) CPPFLAGS="$CPPFLAGS_save" fi +dnl ####################################################################### +dnl # Check for Gadu-Gadu client includes and libraries +dnl ####################################################################### +AC_ARG_WITH(gadu-includes, [AC_HELP_STRING([--with-gadu-includes=DIR], [compile the Gadu-Gadu plugin against includes in DIR])], [ac_gadu_includes="$withval"], [ac_gadu_includes="no"]) +AC_ARG_WITH(gadu-libs, [AC_HELP_STRING([--with-gadu-libs=DIR], [compile the Gadu-Gadu plugin against the libs in DIR])], [ac_gadu_libs="$withval"], [ac_gadu_libs="no"]) +GADU_CFLAGS="" +GADU_LIBS="" +if test -n "$with_gadu_includes" || test -n "$with_gadu_libs"; then + gadu_manual_check="yes" +else + gadu_manual_check="no" +fi +if test "x$gadu_manual_check" = "xno"; then + PKG_CHECK_MODULES(GADU, libgadu, [ + gadu_includes="yes" + gadu_libs="yes" + ], [ + AC_MSG_RESULT(no) + ]) +else + if test "$ac_gadu_includes" != "no"; then + GADU_CFLAGS="-I$ac_gadu_includes" + fi + CPPFLAGS_save="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $GADU_CFLAGS" + AC_CHECK_HEADER(libgadu.h, [gadu_includes=yes]) + CPPFLAGS="$CPPFLAGS_save" + + if test "$ac_gadu_libs" != "no"; then + GADU_LIBS="-L$ac_gadu_libs" + fi + GADU_LIBS="$GADU_LIBS -lgadu" + AC_CHECK_LIB(gadu, gg_libgadu_version, [gadu_libs=yes], , $GADU_LIBS) +fi + +if test "x$gadu_libs" = "xyes"; then + AC_MSG_CHECKING(for libgadu GPL compatibility) + CPPFLAGS_save="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $GADU_CFLAGS" + AC_TRY_COMPILE([#include <libgadu.h>], [ +#ifdef __GG_LIBGADU_HAVE_OPENSSL +#error "libgadu is not compatible with the GPL when compiled with OpenSSL support." +#endif + ], [ + AC_MSG_RESULT(yes) + AC_DEFINE([HAVE_LIBGADU], [1], + [Define to 1 if you have libgadu.]) + ], [ + AC_MSG_RESULT(no) + echo + echo + echo "libgadu is not compatible with the GPL when compiled with OpenSSL support." + echo "Please recompile libgadu using:" + echo "./autogen.sh --disable-libgadu-openssl --disable-static --enable-shared" + echo "Then rerun this ./configure" + echo + echo + GADU_LIBS="" + GADU_CFLAGS="" + gadu_libs=no + ]) + CPPFLAGS="$CPPFLAGS_save" +fi + +AM_CONDITIONAL(USE_INTERNAL_LIBGADU, test "x$gadu_libs" != "xyes") + +AC_SUBST(GADU_LIBS) +AC_SUBST(GADU_CFLAGS) + AC_ARG_ENABLE(distrib,,,enable_distrib=no) AM_CONDITIONAL(DISTRIB, test "x$enable_distrib" = "xyes") -AC_ARG_ENABLE(prpls, [ --disable-prpls don't build dynamic protocol plugins],,enable_prpls=yes) DYNAMIC_PRPLS=all -AC_ARG_WITH(static-prpls, [ --with-static-prpls link in certain protocols statically],[STATIC_PRPLS=`echo $withval | $sedpath 's/,/ /g'`],STATIC_PRPLS="") +AC_ARG_WITH(static-prpls, [AC_HELP_STRING([--with-static-prpls], [Link to certain protocols statically])], [STATIC_PRPLS=`echo $withval | $sedpath 's/,/ /g'`], [STATIC_PRPLS=""]) if test "x$STATIC_PRPLS" != "x" -a "x$DYNAMIC_PRPLS" = "xall"; then DYNAMIC_PRPLS="" fi if test "x$STATIC_PRPLS" = "xall" ; then - STATIC_PRPLS="bonjour gg irc jabber msn novell oscar sametime silc simple yahoo zephyr" + STATIC_PRPLS="bonjour gg irc jabber msn novell oscar qq sametime silc simple yahoo zephyr" fi if test "x$have_meanwhile" != "xyes" ; then STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/sametime//'` @@ -258,6 +716,7 @@ msn) static_msn=yes ;; novell) static_novell=yes ;; oscar) static_oscar=yes ;; + qq) static_qq=yes ;; sametime) static_sametime=yes ;; silc) static_silc=yes ;; simple) static_simple=yes ;; @@ -274,6 +733,7 @@ AM_CONDITIONAL(STATIC_MSN, test "x$static_msn" = "xyes") AM_CONDITIONAL(STATIC_NOVELL, test "x$static_novell" = "xyes") AM_CONDITIONAL(STATIC_OSCAR, test "x$static_oscar" = "xyes") +AM_CONDITIONAL(STATIC_QQ, test "x$static_qq" = "xyes") AM_CONDITIONAL(STATIC_SAMETIME, test "x$static_sametime" = "xyes" -a "x$have_meanwhile" = "xyes") AM_CONDITIONAL(STATIC_SILC, test "x$static_silc" = "xyes" -a "x$silcincludes" = "xyes" -a "x$silcclient" = "xyes") AM_CONDITIONAL(STATIC_SIMPLE, test "x$static_simple" = "xyes") @@ -286,10 +746,10 @@ AC_ARG_WITH(dynamic_prpls, [AC_HELP_STRING([--with-dynamic-prpls], [specify which protocols to build dynamically])], [DYNAMIC_PRPLS=`echo $withval | $sedpath 's/,/ /g'`]) if test "x$DYNAMIC_PRPLS" = "xall" ; then - DYNAMIC_PRPLS="bonjour gg irc jabber msn novell oscar sametime silc simple yahoo zephyr" + DYNAMIC_PRPLS="bonjour gg irc jabber msn novell oscar qq sametime silc simple yahoo zephyr" fi if test "x$have_meanwhile" != "xyes"; then - DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/sametime//'` + DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/sametime//'` fi if test "x$howlincludes" != "xyes" -o "x$howllibs" != "xyes"; then DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/bonjour//'` @@ -307,6 +767,7 @@ msn) dynamic_msn=yes ;; novell) dynamic_novell=yes ;; oscar) dynamic_oscar=yes ;; + qq) dynamic_qq=yes ;; sametime) dynamic_sametime=yes ;; silc) dynamic_silc=yes ;; simple) dynamic_simple=yes ;; @@ -323,6 +784,7 @@ AM_CONDITIONAL(DYNAMIC_MSN, test "x$dynamic_msn" = "xyes") AM_CONDITIONAL(DYNAMIC_NOVELL, test "x$dynamic_novell" = "xyes") AM_CONDITIONAL(DYNAMIC_OSCAR, test "x$dynamic_oscar" = "xyes") +AM_CONDITIONAL(DYNAMIC_QQ, test "x$dynamic_qq" = "xyes") AM_CONDITIONAL(DYNAMIC_SAMETIME, test "x$dynamic_sametime" = "xyes" -a "x$have_meanwhile" = "xyes") AM_CONDITIONAL(DYNAMIC_SILC, test "x$dynamic_silc" = "xyes" -a "x$silcincludes" = "xyes" -a "x$silcclient" = "xyes") AM_CONDITIONAL(DYNAMIC_SIMPLE, test "x$dynamic_simple" = "xyes") @@ -330,44 +792,15 @@ AM_CONDITIONAL(DYNAMIC_YAHOO, test "x$dynamic_yahoo" = "xyes") AM_CONDITIONAL(DYNAMIC_ZEPHYR, test "x$dynamic_zephyr" = "xyes") -AC_ARG_ENABLE(audio, [ --disable-audio compile without libao/libaudiofile for sound playing],,enable_audio=yes) -AC_ARG_ENABLE(mono, [ --enable-mono compile with Mono runtime support],,enable_mono=no) -AC_ARG_ENABLE(plugins, [ --disable-plugins compile without plugin support],,enable_plugins=yes) -AC_ARG_ENABLE(perl, [ --disable-perl compile without perl scripting],,enable_perl=yes) -AC_ARG_ENABLE(tcl, [ --disable-tcl compile without Tcl scripting],,enable_tcl=yes) -AC_ARG_WITH(tclconfig, [ --with-tclconfig=DIR directory containing tclConfig.sh]) -AC_ARG_ENABLE(tk, [ --disable-tk compile without Tcl support for Tk],,enable_tk=yes) -AC_ARG_WITH(tkconfig, [ --with-tkconfig=DIR directory containing tkConfig.sh]) -AC_ARG_ENABLE(gtkspell, [ --disable-gtkspell compile without GtkSpell automatic spell checking],,enable_gtkspell=yes) -AC_ARG_ENABLE(debug, [ --enable-debug compile with debugging support],,enable_debug=no) -AC_ARG_ENABLE(fatal-asserts, [ --enable-fatal-asserts make assertions fatal (useful for debugging)],,enable_fatal_asserts=no) -dnl We know Gaim won't compile with deprecated APIs disabled. -dnl We have no desire to support two different versions of the -dnl same code when it's not necessary, so we're sticking with -dnl the deprecated APIs in many cases. -dnl This option is being left in case things change. -dnl AC_ARG_ENABLE(deprecated, [ --disable-deprecated compile without deprecated API usage],,enable_deprecated=yes) -AC_ARG_ENABLE(fortify, [ --disable-fortify compile without FORTIFY_SOURCE support],,enable_fortify=yes) -AC_ARG_ENABLE(screensaver, [ --disable-screensaver compile without X screensaver extension],,enable_xss=yes) -AC_ARG_ENABLE(sm, [ --disable-sm compile without X session management support],,enable_sm=yes) -AC_ARG_WITH(krb4, [ --with-krb4=PREFIX compile Zephyr plugin with Kerberos 4 support],kerberos="$withval",kerberos="no") -AC_ARG_WITH(zephyr, [ --with-zephyr=PREFIX compile Zephyr plugin against external libzephyr],zephyr="$withval",zephyr="no") +AC_ARG_ENABLE(plugins, [AC_HELP_STRING([--disable-plugins], [compile without plugin support])], , enable_plugins=yes) +AC_ARG_WITH(krb4, [AC_HELP_STRING([--with-krb4=PREFIX], [compile Zephyr plugin with Kerberos 4 support])], kerberos="$withval", kerberos="no") +AC_ARG_WITH(zephyr, [AC_HELP_STRING([--with-zephyr=PREFIX], [compile Zephyr plugin against external libzephyr])], zephyr="$withval", zephyr="no") AM_CONDITIONAL(EXTERNAL_LIBZEPHYR, test "x$zephyr" != "xno") AC_CHECK_HEADER(sys/utsname.h) AC_CHECK_FUNC(uname) -if test "x$enable_debug" = "xyes" ; then - AC_DEFINE(DEBUG, 1, [Define if debugging is enabled.]) -fi - -if test "x$enable_fatal_asserts" = "xyes" ; then - AC_DEFINE(GAIM_FATAL_ASSERTS, 1, [Define to make assertions fatal (useful for debugging).]) -fi - -if test "x$enable_deprecated" = "xno"; then - DEBUG_CFLAGS="$DEBUG_CFLAGS -DG_DISABLE_DEPRECATED -DGDK_DISABLE_DEPRECATED -DGDK_PIXBUF_DISABLE_DEPRECATED -DGTK_DISABLE_DEPRECATED" -fi +AC_ARG_ENABLE(fortify, [AC_HELP_STRING([--disable-fortify], [compile without FORTIFY_SOURCE support])], , enable_fortify=yes) if test "x$GCC" = "xyes"; then dnl We enable -Wall later. @@ -446,76 +879,65 @@ fi AC_SUBST(CFLAGS) -AM_PATH_GLIB_2_0(2.0.0,,AC_MSG_ERROR([ -*** GLib 2.0 is required to build Gaim; please make sure you have the GLib -*** development headers installed. The latest version of GLib is -*** always available at http://www.gtk.org/.])) -AM_PATH_GTK_2_0(2.0.0,,AC_MSG_ERROR([ -*** GTK+ 2.0 is required to build Gaim; please make sure you have the GTK+ -*** development headers installed. The latest version of GTK+ is -*** always available at http://www.gtk.org/.])) - AC_PATH_PROG(gaimpath, gaim) -AC_SUBST(GTK_CFLAGS) -AC_SUBST(GLIB_CFLAGS) - -AC_PATH_XTRA -# We can't assume that $x_libraries will be set, because autoconf does not -# set it in the case when the X libraries are in a standard place. -# Ditto for $x_includes -if test X"$x_libraries" = X"" || test X"$x_libraries" = XNONE; then - x_libpath_add= -else - x_libpath_add="-L$x_libraries" -fi -if test X"$x_includes" = X"" || test X"$x_includes" = XNONE; then - x_incpath_add= -else - x_incpath_add="-I$x_includes" -fi dnl ####################################################################### dnl # Check for DBUS libraries dnl ####################################################################### -AC_ARG_ENABLE(dbus, [ --enable-dbus enable DBUS support],,enable_dbus=yes) +AC_ARG_ENABLE(dbus, [AC_HELP_STRING([--enable-dbus], [enable DBUS support])], , enable_dbus=yes) if test "x$enable_dbus" = "xyes" ; then - AC_CHECK_PROG(enable_dbus, dbus-binding-tool, yes, no) + AC_CHECK_PROG(enable_dbus, dbus-binding-tool, yes, no) fi if test "x$enable_dbus" = "xyes" ; then - PKG_CHECK_MODULES(DBUS, [dbus-1 >= 0.35 dbus-glib-1 >= 0.35], - [ + PKG_CHECK_MODULES(DBUS, [dbus-1 >= 0.35 dbus-glib-1 >= 0.35], [ AC_SUBST(DBUS_CFLAGS) AC_SUBST(DBUS_LIBS) enable_dbus=yes + ], [ + AC_MSG_RESULT(no) + enable_dbus=no + ]) + +dnl Check for libnm_glib; if we don't have it, oh well + LIBNM_CFLAGS="" + LIBNM_LIBS="" + PKG_CHECK_MODULES(LIBNM, libnm_glib, + [ + AC_DEFINE(HAVE_LIBNM, 1, [Check to see if we have NetworkManager]) ], [ - enable_dbus=no + AC_MSG_RESULT(no) ]) + AC_SUBST(LIBNM_CFLAGS) + AC_SUBST(LIBNM_LIBS) fi -dnl Why do we need python? +dnl ####################################################################### +dnl # Check for Python +dnl ####################################################################### -dnl Python scripts are used to auto-generate about 3000 lines of C -dnl and XML code that wraps (part of) the existing Gaim API so that -dnl it is now accessible through DBUS. +dnl Python scripts are used to auto-generate about 3000 lines of C +dnl and XML code that wraps (part of) the existing Gaim API so that +dnl it is now accessible through DBUS. -dnl Python is only required if --enable-dbus is used, and only for -dnl the build process to generate the code, not for running gaim. -dnl This autogenerated code is system-independent, so in principle we -dnl can generate all of it before shipping. But I thought adding -dnl auto-generated stuff to the CVS is inelegant. Alternatively, -dnl these python scripts could be rewritten in C (brrrr ...). +dnl Python is only required if --enable-dbus is used, and only for +dnl the build process to generate the code, not for running gaim. +dnl This autogenerated code is system-independent, so in principle we +dnl can generate all of it before shipping. But I thought adding +dnl auto-generated stuff to the repository is inelegant. +dnl Alternatively, these python scripts could be rewritten +dnl in C (brrrr ...). AC_ARG_WITH([python], - AC_HELP_STRING([--with-python], - [Which python interpreter to use for dbus code generation]), + AC_HELP_STRING([--with-python=PATH], + [which python interpreter to use for dbus code generation]), PYTHON=$withval) if test "x$enable_dbus" = "xyes" ; then - if test -z "$PYTHON" ; then + if test -z "$PYTHON" -o "x$PYTHON" = "xyes"; then AC_PATH_PROG([PYTHON], [python], [no]) fi @@ -532,174 +954,72 @@ fi fi -dnl Here we locate the directory containing DBus .service files for -dnl the session bus. Adapted from the guifications project. +dnl ########################################################################### +dnl # Find the D-Bus services dir. +dnl # +dnl # This is a 3 step process that +dnl # +dnl # 1. checks if --with-dbus-services was set, if so use that. +dnl # 2. checks if --prefix was given, if so use that. +dnl # 3. fallbacks to installing into what should be the correct system +dnl # directories. +dnl # +dnl # This is still prone to error if one of the legacy directories exist +dnl # although a newer dbus is installed. But I have tried to order the +dnl # directory searching to keep this situation at a minimum. +dnl ########################################################################### +AC_ARG_WITH(dbus-services, [AC_HELP_STRING([--with-dbus-services=<dir>], [where the D-Bus services directory is located.])]) + +DBUS_SERVICES_DIR="" + +if test x"$enable_dbus" = "xyes" ; then + AC_MSG_CHECKING([location of the D-Bus services directory]) + if ! test -z "$with_dbus_services" ; then + if ! test -d "$with_dbus_services" ; then + AC_MSG_ERROR([$with_dbus_services does not exist, if this is the correct location please make sure that it exists.]) + fi -AC_ARG_WITH(dbus-session-dir, [ --with-dbus-session-dir=<dir> Location of the D-BUS session directory.]) + DBUS_SERVICES_DIR="$with_dbus_services" + else + if test x"$prefix" = x"NONE" ; then + dnl # no prefix given, so we look for the correct dbus system paths. + dnl # if a prefix is given, we use it. + + serviceprefixes="$datadir $libdir /usr/share /usr/local/share" + DBUS_SERVICES_DIR="" + + for d in $serviceprefixes ; do + dir="$d/dbus-1/services" + if test -d $dir ; then + DBUS_SERVICES_DIR="$dir" + break + fi + done + + if test -z $DBUS_SERVICES_DIR ; then + AC_MSG_ERROR([D-Bus services directory was not found! Please use --with-dbus-services and specify it's location.]) + fi + else + DBUS_SERVICES_DIR="$datadir/dbus-1/services" + fi + fi + AC_MSG_RESULT([$DBUS_SERVICES_DIR]) + AC_DEFINE(HAVE_DBUS, 1, [Define if we are re using DBUS.]) +fi +AC_SUBST(DBUS_SERVICES_DIR) if test "x$enable_dbus" = "xyes" ; then - AC_MSG_CHECKING([location of the D-BUS session directory]) - if ! test -z "$with_dbus_session_dir"; then - if ! test -d "$with_dbus_session_dir"; then - AC_MSG_WARN([$with_dbus_session_dir does not exist, if this is the correct location please make sure that it exists.]) - enable_dbus=no - fi - DBUS_SESSION_DIR="$with_dbus_session_dir" - else - dnl # add more to this as needed - servicesprefixes="$DATADIR $LIBDIR /usr/share /usr/local/share" - DBUS_SESSION_DIR="" - - for p in $servicesprefixes; do - dir="$p/dbus-1/services" - if test -d $dir; then - DBUS_SESSION_DIR="$dir" - break - fi - done - - if test -z $DBUS_SESSION_DIR; then - AC_MSG_WARN([D-BUS session directory was not found! Please use --with-dbus-session-dir and specify its location.]) - enable_dbus=no - fi - fi -fi - -if test "x$enable_dbus" = "xyes" ; then - AC_MSG_RESULT([$DBUS_SESSION_DIR]) - AC_SUBST(DBUS_SESSION_DIR) - AC_DEFINE(HAVE_DBUS, 1, [Define if we are re using DBUS.]) - echo "Building with DBUS support" + echo "Building with DBUS support" else - echo "Building without DBUS support" + echo "Building without DBUS support" fi AM_CONDITIONAL(ENABLE_DBUS, test "x$enable_dbus" = "xyes") - - - - - -dnl ####################################################################### -dnl # Check for startup notification -dnl ####################################################################### -AC_ARG_ENABLE(startup-notification, [ --disable-startup-notification compile without startup notification support],,enable_startup_notification=yes) - -if test "x$enable_startup_notification" = "xyes"; then - PKG_CHECK_MODULES(STARTUP_NOTIFICATION, libstartup-notification-1.0 >= 0.5, - [ - AC_DEFINE(HAVE_STARTUP_NOTIFICATION, 1, [Define if we're using libstartup-notification.]) - echo "Building with libstartup-notification" - enable_startup_notification=yes - ], - [ - echo "Building without libstartup-notification" - enable_startup_notification=no - ]) - - AC_SUBST(STARTUP_NOTIFICATION_CFLAGS) - AC_SUBST(STARTUP_NOTIFICATION_LIBS) -fi - - -dnl ####################################################################### -dnl # Check for stuff needed by the evolution integration plugin. -dnl ####################################################################### -build_gevo=no -AC_ARG_ENABLE(gevolution, [ --disable-gevolution compile without the Gaim-Evolution plugin],,enable_gevolution=yes) - -if test "x$enable_gevolution" = "xyes"; then - evo_deps="libebook-1.2 libedata-book-1.2" - PKG_CHECK_MODULES(EVOLUTION_ADDRESSBOOK, $evo_deps, [ - AC_DEFINE(HAVE_EVOLUTION_ADDRESSBOOK, 1, [Define if we're using evolution addressbook.]) - build_gevo=yes - ], build_gevo=no) - if test "x$build_gevo" = "xno"; then - evo_deps="libebook-1.0 libedata-book-1.0" - PKG_CHECK_MODULES(EVOLUTION_ADDRESSBOOK, $evo_deps, [ - AC_DEFINE(HAVE_EVOLUTION_ADDRESSBOOK, 1, [Define if we're using evolution addressbook.]) - build_gevo=yes - ], build_gevo=no) - fi - - AC_SUBST(EVOLUTION_ADDRESSBOOK_CFLAGS) - AC_SUBST(EVOLUTION_ADDRESSBOOK_LIBS) -fi - -AM_CONDITIONAL(BUILD_GEVOLUTION, test "x$build_gevo" = "xyes") - -dnl ####################################################################### -dnl # Check for XScreenSaver -dnl ####################################################################### -if test "x$enable_xss" = "xyes" ; then - old_LIBS="$LIBS" - LIBS="$LIBS $GTK_LIBS $x_libpath_add" - XSS_LIBS="no" - XSS_HEADERS="no" - AC_CHECK_LIB(Xext, XScreenSaverRegister,[XSS_LIBS="$X_LIBS $X_PRE_LIBS -lX11 -lXext $X_EXTRA_LIBS"],[],[-lX11 -lXext -lm]) - AC_CHECK_LIB(Xss, XScreenSaverRegister,[XSS_LIBS="$X_LIBS $X_PRE_LIBS -lX11 -lXext $X_LIBS $X_EXTRA_LIBS -lXss"],[],[-lX11 -lXext -lm]) - if test \! "$XSS_LIBS" = "no"; then - oldCPPFLAGS="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $x_incpath_add" - AC_TRY_COMPILE([ -#include <X11/Xlib.h> -#include <X11/extensions/scrnsaver.h> - ],[],[ - AC_DEFINE(USE_SCREENSAVER, 1, [Define if we're using XScreenSaver.])],[enable_xss=no] - ) - CPPFLAGS="$oldCPPFLAGS" - else - XSS_LIBS="" - enable_xss=no - fi - LIBS="$old_LIBS" -else - XSS_LIBS="" - enable_xss=no -fi -AC_SUBST(XSS_LIBS) - - -dnl ####################################################################### -dnl # Check for X session management libs -dnl ####################################################################### -if test "x$enable_sm" = "xyes"; then - enable_sm=no - AC_CHECK_LIB(SM, SmcSaveYourselfDone, found_sm_lib=true, , [$x_libpath_add -lICE]) - if test "$found_sm_lib" = "true"; then - oldCPPFLAGS="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $x_incpath_add" - AC_CHECK_HEADERS(X11/SM/SMlib.h, SM_LIBS="$x_libpath_add -lSM -lICE" enable_sm=yes) - CPPFLAGS="$oldCPPFLAGS" - fi -else - SM_LIBS="" - enable_sm=no -fi -AC_SUBST(SM_LIBS) -if test "$enable_sm" = "yes"; then - AC_DEFINE(USE_SM, 1, [Define if we're using X Session Management.]) -fi - - -AC_DEFUN([GC_TM_GMTOFF], -[AC_REQUIRE([AC_STRUCT_TM])dnl -AC_CACHE_CHECK([for tm_gmtoff in struct tm], ac_cv_struct_tm_gmtoff, -[AC_TRY_COMPILE([#include <sys/types.h> -#include <$ac_cv_struct_tm>], [struct tm tm; tm.tm_gmtoff;], - ac_cv_struct_tm_gmtoff=yes, ac_cv_struct_tm_gmtoff=no)]) -if test "$ac_cv_struct_tm_gmtoff" = yes; then - AC_DEFINE(HAVE_TM_GMTOFF, 1, [tm_gmtoff is available.]) -fi -]) - -GC_TM_GMTOFF - dnl ####################################################################### dnl # Check for Mono support dnl ####################################################################### - +AC_ARG_ENABLE(mono, [AC_HELP_STRING([--enable-mono], [compile with Mono runtime support (experimental)])], , enable_mono=no) if test x"$enable_mono" = x"yes" ; then AC_MSG_CHECKING(for Mono compile flags) MONO_CFLAGS=`pkg-config --cflags mono 2> /dev/null` @@ -711,19 +1031,19 @@ else MONO_LIBS=`pkg-config --libs mono 2> /dev/null` AC_MSG_RESULT(ok) - + oldLIBS="$LIBS" LIBS="$LIBS $MONO_LIBS" AC_MSG_CHECKING(for libmono) AC_CHECK_FUNCS(mono_jit_init, [], enable_mono=no) LIBS="$oldLIBS" - + oldCPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $MONO_CFLAGS" AC_CHECK_HEADERS(mono/jit/jit.h, [], enable_mono=no) AC_CHECK_HEADERS(mono/metadata/object.h, [], enable_mono=no) CPPFLAGS="$oldCPPFLAGS" - + AC_DEFINE(ENABLE_MONO, 1, [Define if mono enabled.]) fi else @@ -739,6 +1059,8 @@ dnl ####################################################################### dnl # Check for Perl support dnl ####################################################################### +AC_ARG_ENABLE(perl, [AC_HELP_STRING([--disable-perl], [compile without perl scripting])], , enable_perl=yes) + if test "$enable_plugins" = no ; then enable_perl=no fi @@ -788,10 +1110,10 @@ AM_CONDITIONAL(USE_PERL, false) AC_MSG_WARN(Compiling perl requires ExtUtils::MakeMaker) else + AC_DEFINE(HAVE_PERL, [1], [Compile with support for perl]) AC_SUBST(PERL_CFLAGS) AC_SUBST(PERL_LIBS) AM_CONDITIONAL(USE_PERL, true) - AC_DEFINE(HAVE_PERL, [1], [Compile with support for perl]) dnl This is almost definitely wrong, but in case there's dnl something I'm missing, I'll leave it in. @@ -813,8 +1135,8 @@ fi AC_ARG_WITH(perl-lib, - [ --with-perl-lib=[site|vendor|DIR] Specify where to install the - Perl libraries for gaim. Default is site.], + [AC_HELP_STRING([--with-perl-lib=[site|vendor|DIR]], + [specify where to install the Perl libraries for gaim. Default is site.])], [ if test "x$withval" = xsite; then PERL_MM_PARAMS="" @@ -879,26 +1201,26 @@ dnl These two are inverses of each other <-- stolen from evolution! AC_ARG_ENABLE(gnutls, - [ --enable-gnutls=[yes,no] Attempt to use GNUTLS for SSL support (preferred) [default=yes]], + [ --enable-gnutls=[yes,no] attempt to use GnuTLS for SSL support (preferred) [default=yes]], [enable_gnutls="$enableval"], [enable_gnutls="yes"]) AC_ARG_ENABLE(nss, - [ --enable-nss=[yes,no,static] Attempt to use Mozilla libnss for SSL support [default=yes]], + [ --enable-nss=[yes,no,static] attempt to use Mozilla libnss for SSL support [default=yes]], [enable_nss="$enableval"], [enable_nss="yes"]) -msg_ssl="None (MSN will not work without SSL!)" +msg_ssl="None (MSN and Google Talk will not work without SSL!)" dnl # -dnl # Check for GNUTLS if it's specified. +dnl # Check for GnuTLS if it's specified. dnl # if test "x$enable_gnutls" != "xno"; then enable_gnutls="no" prefix=`eval echo $prefix` AC_ARG_WITH(gnutls-includes, - [ --with-gnutls-includes=PREFIX Location of GNUTLS includes.], + [ --with-gnutls-includes=PREFIX location of GnuTLS includes.], [ with_gnutls_includes="$withval" ], [ with_gnutls_includes="$prefix/include" ]) @@ -907,7 +1229,7 @@ if test "x$with_gnutls_includes" != "xno"; then CPPFLAGS_save="$CPPFLAGS" - AC_MSG_CHECKING(for GNUTLS includes) + AC_MSG_CHECKING(for GnuTLS includes) AC_MSG_RESULT("") CPPFLAGS="$CPPFLAGS -I$with_gnutls_includes" @@ -923,35 +1245,35 @@ GNUTLS_CFLAGS="" fi else - AC_MSG_CHECKING(for GNUTLS includes) + AC_MSG_CHECKING(for GnuTLS includes) AC_MSG_RESULT(no) fi AC_ARG_WITH(gnutls-libs, - [ --with-gnutls-libs=PREFIX Location of GNUTLS libraries.], + [AC_HELP_STRING([--with-gnutls-libs=PREFIX], [location of GnuTLS libraries.])], [ with_gnutls_libs="$withval" ]) if test "x$with_gnutls_libs" != "xno" -a \ "x$have_gnutls_includes" != "xno"; then - LDFLAGS_save="$LDFLAGS" + LIBS_save="$LIBS" case $with_gnutls_libs in ""|-L*) ;; *) with_gnutls_libs="-L$with_gnutls_libs" ;; esac - AC_CACHE_CHECK([for GNUTLS libraries], gnutls_libs, + AC_CACHE_CHECK([for GnuTLS libraries], gnutls_libs, [ - LDFLAGS="$LDFLAGS $with_gnutls_libs -lgnutls -lgcrypt" + LIBS="$LIBS $with_gnutls_libs -lgnutls -lgcrypt" AC_TRY_LINK_FUNC(gnutls_init, gnutls_libs="yes", gnutls_libs="no") - LDFLAGS="$LDFLAGS_save" + LIBS="$LIBS_save" ]) if test "x$gnutls_libs" != "xno"; then - AC_DEFINE(HAVE_GNUTLS, 1, [Define if you have GNUTLS]) + AC_DEFINE(HAVE_GNUTLS, 1, [Define if you have GnuTLS]) AC_DEFINE(HAVE_SSL) - msg_gnutls="GNUTLS" + msg_gnutls="GnuTLS" GNUTLS_LIBS="$with_gnutls_libs -lgnutls -lgcrypt" enable_gnutls="yes" @@ -960,7 +1282,7 @@ GNUTLS_LIBS="" fi else - AC_MSG_CHECKING(for GNUTLS libraries) + AC_MSG_CHECKING(for GnuTLS libraries) AC_MSG_RESULT(no) fi else @@ -975,24 +1297,24 @@ dnl # -dnl # Check for NSS if it's specified, or if GNUTLS checks failed. +dnl # Check for NSS if it's specified, or if GnuTLS checks failed. dnl # if test "x$enable_nss" != "xno"; then AC_ARG_WITH(nspr-includes, - [ --with-nspr-includes=PREFIX Specify location of Mozilla nspr4 includes.], + [AC_HELP_STRING([--with-nspr-includes=PREFIX], [specify location of Mozilla nspr4 includes.])], [with_nspr_includes="$withval"]) AC_ARG_WITH(nspr-libs, - [ --with-nspr-libs=PREFIX Specify location of Mozilla nspr4 libs.], + [AC_HELP_STRING([--with-nspr-libs=PREFIX], [specify location of Mozilla nspr4 libs.])], [with_nspr_libs="$withval"]) AC_ARG_WITH(nss-includes, - [ --with-nss-includes=PREFIX Specify location of Mozilla nss3 includes.], + [AC_HELP_STRING([--with-nss-includes=PREFIX], [specify location of Mozilla nss3 includes.])], [with_nss_includes="$withval"]) AC_ARG_WITH(nss-libs, - [ --with-nss-libs=PREFIX Specify location of Mozilla nss3 libs.], + [AC_HELP_STRING([--with-nss-libs=PREFIX], [specify location of Mozilla nss3 libs.])], [with_nss_libs="$withval"]) @@ -1009,12 +1331,21 @@ if test "x$nss_manual_check" = "xno"; then if `$PKG_CONFIG --exists mozilla-nss`; then - PKG_CHECK_MODULES(NSS, mozilla-nss, have_nss="yes", have_nss="no") + PKG_CHECK_MODULES(NSS, mozilla-nss, [ + have_nss="yes" + ], [ + AC_MSG_RESULT(no) + have_nss="no" + ]) mozilla_nspr="mozilla-nspr" mozilla_nss="mozilla-nss" else if `$PKG_CONFIG --exists nss`; then - PKG_CHECK_MODULES(NSS, nss, have_nss="yes") + PKG_CHECK_MODULES(NSS, nss, [ + have_nss="yes" + ], [ + AC_MSG_RESULT(no) + ]) mozilla_nspr="nspr" mozilla_nss="nss" fi @@ -1180,7 +1511,7 @@ AC_CACHE_CHECK([for Mozilla nss libraries], moz_nss_libs, [ LIBS_save=$LIBS - LDFLAGS="$LDFLAGS -L$with_nspr_libs $nsprlibs -L$with_nss_libs $nsslibs" + LDFLAGS="$LDFLAGS -L$with_nspr_libs -L$with_nss_libs" LIBS="$nsslibs $nsprlibs" AC_TRY_LINK_FUNC(NSS_Init, @@ -1189,7 +1520,8 @@ if test "x$moz_nss_libs" = "xno"; then nsslibs="-lssl3 -lsmime3 -lnss3 -lsoftokn3" - LDFLAGS="$LDFLAGS -L$with_nspr_libs $nsprlibs -L$with_nss_libs $nsslibs" + LDFLAGS="$LDFLAGS -L$with_nspr_libs -L$with_nss_libs" + LIBS="$LIBS $nsslibs" AC_TRY_LINK_FUNC(NSS_Init, [moz_nss_libs="yes"], [moz_nss_libs="no"]) @@ -1241,20 +1573,28 @@ msg_ssl=$msg_gnutls fi -dnl Check for Tcl +dnl ####################################################################### +dnl # Check for Tcl +dnl ####################################################################### +AC_ARG_ENABLE(tcl, [AC_HELP_STRING([--disable-tcl], + [compile without Tcl scripting])], enable_tcl="$enableval", enable_tcl="yes") +AC_ARG_WITH(tclconfig, [AC_HELP_STRING([--with-tclconfig=DIR], + [directory containing tclConfig.sh])]) + if test "$enable_plugins" = no; then enable_tcl=no fi if test "$enable_tcl" = yes; then AC_MSG_CHECKING([for tclConfig.sh]) - TCLCONFIG=no + TCLCONFIG=no TCLCONFIGDIRS="/usr/lib \ - /usr/lib/tcl8.4 \ - /usr/lib/tcl8.3 \ - /usr/lib/tcl8.2 \ - /System/Library/Tcl/8.3 \ - /usr/local/lib" + /usr/lib64 \ + /usr/lib/tcl8.4 \ + /usr/lib/tcl8.3 \ + /usr/lib/tcl8.2 \ + /System/Library/Tcl/8.3 \ + /usr/local/lib" for dir in $with_tclconfig $TCLCONFIGDIRS; do if test -f $dir/tclConfig.sh; then TCLCONFIG=$dir/tclConfig.sh @@ -1279,9 +1619,9 @@ oldLIBS=$LIBS LIBS="$LIBS $TCL_LIB_SPEC" AC_TRY_LINK([#include <tcl.h>], - [Tcl_Interp *interp=NULL; Tcl_Init(interp)], - [AC_MSG_RESULT([yes]);enable_tcl=yes], - [AC_MSG_RESULT([no]);enable_tcl=no]) + [Tcl_Interp *interp=NULL; Tcl_Init(interp)], + [AC_MSG_RESULT([yes]);enable_tcl=yes], + [AC_MSG_RESULT([no]);enable_tcl=no]) CPPFLAGS="$oldCPPFLAGS" LIBS="$oldLIBS" fi @@ -1291,8 +1631,8 @@ if test "$enable_tcl" = yes; then AM_CONDITIONAL(USE_TCL, true) TCL_LIBS=$TCL_LIB_SPEC + AC_DEFINE(HAVE_TCL, [1], [Compile with support for the Tcl toolkit]) AC_SUBST(TCL_LIBS) - AC_DEFINE(HAVE_TCL, [1], [Compile with support for the Tcl toolkit]) TCL_CFLAGS="$TCL_INCLUDE_SPEC -I$TCL_PREFIX/include" if test "x$GCC" = "xyes"; then TCL_CFLAGS="$TCL_CFLAGS -fno-strict-aliasing" @@ -1302,15 +1642,23 @@ AM_CONDITIONAL(USE_TCL, false) fi -dnl Check for Tk +dnl ####################################################################### +dnl # Check for Tk +dnl ####################################################################### +AC_ARG_ENABLE(tk, [AC_HELP_STRING([--disable-tk], + [compile without Tcl support for Tk])], enable_tk="$enableval", enable_tk="yes") +AC_ARG_WITH(tkconfig, [AC_HELP_STRING([--with-tkconfig=DIR], + [directory containing tkConfig.sh])]) + if test "$enable_tcl" = yes -a "$enable_tk" = yes; then AC_MSG_CHECKING([for tkConfig.sh]) TKCONFIG=no TKCONFIGDIRS="/usr/lib \ - /usr/lib/tk8.4 \ - /usr/lib/tk8.3 \ - /usr/lib/tk8.2 \ - /usr/local/lib" + /usr/lib64 \ + /usr/lib/tk8.4 \ + /usr/lib/tk8.3 \ + /usr/lib/tk8.2 \ + /usr/local/lib" for dir in $with_tkconfig $TKCONFIGDIRS; do if test -f $dir/tkConfig.sh; then TKCONFIG=$dir/tkConfig.sh @@ -1329,9 +1677,9 @@ oldLIBS=$LIBS LIBS="$LIBS $TCL_LIB_SPEC $TK_LIB_SPEC" AC_TRY_LINK([#include <tk.h>], - [Tcl_Interp *interp=NULL; Tcl_Init(interp); Tk_Init(interp);], - [AC_MSG_RESULT([yes]);enable_tk=yes], - [AC_MSG_RESULT([no]);enable_tk=no]) + [Tcl_Interp *interp=NULL; Tcl_Init(interp); Tk_Init(interp);], + [AC_MSG_RESULT([yes]);enable_tk=yes], + [AC_MSG_RESULT([no]);enable_tk=no]) CPPFLAGS="$oldCPPFLAGS" LIBS="$oldLIBS" fi @@ -1348,33 +1696,6 @@ AM_CONDITIONAL(USE_TK, false) fi -dnl Thanks, Evan. -if test "$enable_gtkspell" = yes ; then - PKG_CHECK_MODULES(GTKSPELL, gtkspell-2.0 >= 2.0.2, , enable_gtkspell=no) - if test "$enable_gtkspell" = "yes" ; then - AC_SUBST(GTKSPELL_CFLAGS) - AC_SUBST(GTKSPELL_LIBS) - AC_DEFINE(USE_GTKSPELL,,[do we have gtkspell?]) - fi -fi - -if test "$enable_audio" = yes ; then - GAIM_PATH_AO(found_ao_lib=true) - - AM_PATH_AUDIOFILE([0.2.0], found_af_lib=true) - - if test "$found_ao_lib" = "true" -a "$found_af_lib" = "true"; then - SOUND_LIBS="$SOUND_LIBS $AO_LIBS $AUDIOFILE_LIBS" - AC_SUBST(SOUND_LIBS) - AC_DEFINE(USE_AO, 1, [Define if we're using libao and libaudiofile for sound playing]) - enable_audio=yes - else - enable_audio=no - fi -else - enable_audio=no -fi - if test "$ac_cv_cygwin" = yes ; then LDADD="$LDADD -static" AC_DEFINE(DEBUG, 1, [Define if debugging is enabled.]) @@ -1386,30 +1707,30 @@ if test "x$enable_plugins" = "xyes" ; then AC_DEFINE(GAIM_PLUGINS, 1, [Define if plugins are enabled.]) - AM_CONDITIONAL(PLUGINS, test "x$enable_plugins" = "xyes") + AM_CONDITIONAL(PLUGINS, true) else AM_CONDITIONAL(PLUGINS, false) - enable_plugins=no - enable_prpls=no fi -if test "x$enable_prpls" = "xyes" ; then - AM_CONDITIONAL(PRPLS, test "x$enable_plugins" = "xyes") -else - AM_CONDITIONAL(PRPLS, false) - enable_prpls=no -fi - -dnl checks for jabber +dnl ####################################################################### +dnl # Check for Cyrus-SASL (for Jabber) +dnl ####################################################################### dnl AC_CHECK_SIZEOF(short) AC_CHECK_FUNCS(snprintf connect) AC_SUBST(SASL_LIBS) AC_ARG_ENABLE(cyrus-sasl, AC_HELP_STRING([--enable-cyrus-sasl], [enable Cyrus SASL support for jabberd]), enable_cyrus_sasl=$enableval, enable_cyrus_sasl=no) -if test "x-$enable_cyrus_sasl" = "x-yes" ; then - AC_CHECK_LIB(sasl2, sasl_client_init, [AC_DEFINE(HAVE_CYRUS_SASL, [1], [Define to 1 if Cyrus SASL is present]) SASL_LIBS=-"lsasl2"], [AC_ERROR(Cyrus SASL library not found)]) +if test "x$enable_cyrus_sasl" = "xyes" ; then + AC_CHECK_LIB(sasl2, sasl_client_init, [ + AC_DEFINE(HAVE_CYRUS_SASL, [1], [Define to 1 if Cyrus SASL is present]) + SASL_LIBS=-"lsasl2" + ], [ + AC_ERROR(Cyrus SASL library not found) + ]) fi -dnl checks for zephyr +dnl ####################################################################### +dnl # Check for Kerberos (for Zephyr) +dnl ####################################################################### AC_DEFINE(ZEPHYR_INT32, long, [Size of an int32.]) AC_SUBST(KRB4_CFLAGS) AC_SUBST(KRB4_LDFLAGS) @@ -1445,13 +1766,15 @@ LDFLAGS="$orig_LDFLAGS" fi -dnl checks for an external libzephyr +dnl ####################################################################### +dnl # Check for external libzephyr +dnl ####################################################################### AC_SUBST(ZEPHYR_CFLAGS) AC_SUBST(ZEPHYR_LDFLAGS) AC_SUBST(ZEPHYR_LIBS) if test "$zephyr" != "no" ; then - if test "$zephyr" != "yes" ; then - ZEPHYR_CFLAGS="-I${zephyr}/include" + if test "$zephyr" != "yes" ; then + ZEPHYR_CFLAGS="-I${zephyr}/include" ZEPHYR_LDFLAGS="-L${zephyr}/lib" elif test -d /usr/athena/include/zephyr ; then ZEPHYR_CFLAGS="-I/usr/athena/include" @@ -1460,14 +1783,14 @@ elif test -d /usr/local/include/zephyr ; then ZEPHYR_CFLAGS="-I/usr/local/include" fi - AC_DEFINE(LIBZEPHYR_EXT, 1 , [Define if external libzephyr should be used.]) + AC_DEFINE(LIBZEPHYR_EXT, 1 , [Define if external libzephyr should be used.]) AM_CONDITIONAL(EXTERNAL_LIBZEPHYR, test "x$zephyr" != "xno") orig_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS $ZEPHYR_LDFLAGS" AC_CHECK_LIB(zephyr, ZInitialize, - [ZEPHYR_LIBS="-lzephyr"], - [AC_ERROR(Zephyr libraries not found)], - -lzephyr) + [ZEPHYR_LIBS="-lzephyr"], + [AC_ERROR(Zephyr libraries not found)], + -lzephyr) orig_LIBS="$LIBS" LIBS="$orig_LIBS" LDFLAGS="$orig_LDFLAGS" @@ -1485,174 +1808,134 @@ AC_CHECK_HEADERS(termios.h) AC_VAR_TIMEZONE_EXTERNALS +AC_CACHE_CHECK(for tm_gmtoff in struct tm, ac_cv_struct_tm_gmtoff, + AC_TRY_COMPILE([ + #include <time.h> + ], [ + struct tm tm; + tm.tm_gmtoff = 1; + ], ac_cv_struct_tm_gmtoff=yes, ac_cv_struct_tm_gmtoff=no)) +if test $ac_cv_struct_tm_gmtoff = yes; then + AC_DEFINE(HAVE_TM_GMTOFF, 1, [Define if you have a tm_gmtoff member in struct tm]) +fi + dnl ####################################################################### -dnl # Doxygen Stuff +dnl # Check for check dnl ####################################################################### -AC_ARG_ENABLE(doxygen, [ --enable-doxygen enable documentation with doxygen],,enable_doxygen=yes) -AC_ARG_ENABLE(dot, [ --enable-dot enable graphs in doxygen via 'dot'],,enable_dot=yes) +PKG_CHECK_MODULES(CHECK, check >= 0.9.4, have_check=yes, have_check=no) +AM_CONDITIONAL(HAVE_CHECK, test "x$have_check" = "xyes") +AC_SUBST(CHECK_CFLAGS) +AC_SUBST(CHECK_LIBS) + +dnl ####################################################################### +dnl # Check for Doxygen and dot (part of GraphViz) +dnl ####################################################################### +AC_ARG_ENABLE(doxygen, + [AC_HELP_STRING([--disable-doxygen], + [enable documentation with doxygen])], + enable_doxygen="$enableval", enable_doxygen="yes") +AC_ARG_ENABLE(dot, + [AC_HELP_STRING([--enable-dot], + [enable graphs in doxygen via 'dot'])], + enable_dot="$enableval", enable_dot="yes") if test "x$enable_doxygen" = xyes; then AC_CHECK_PROG(DOXYGEN, doxygen, true, false) if test $DOXYGEN = false; then - AC_MSG_WARN([*** doxygen not found, docs will not be available]) - enable_doxygen=no + AC_MSG_WARN([*** Doxygen not found, docs will not be available]) + enable_doxygen="no" else AC_DEFINE_UNQUOTED(HAVE_DOXYGEN, 1, [whether or not we have doxygen]) - if test "x$enable_dot" = xyes; then + if test "x$enable_dot" = "xyes"; then AC_CHECK_PROG(DOT, dot, true, false) if test $DOT = false; then enable_dot="no"; - AC_MSG_WARN([*** dot not found, graphs will not be available]) + AC_MSG_WARN([*** GraphViz dot not found, docs will not have graphs]) else AC_DEFINE_UNQUOTED(HAVE_DOT, 1, [whether or not we have dot]) fi - else - AC_MSG_WARN([*** dot not found, graphs will not be available]) fi fi else enable_dot="no" fi -if test "x$enable_doxygen" = xyes; then - AM_CONDITIONAL(HAVE_DOXYGEN, true) -else - AM_CONDITIONAL(HAVE_DOXYGEN, false) -fi - AC_SUBST(enable_doxygen) AC_SUBST(enable_dot) - -dnl ############################################################################## -dnl ## Mediastreamer stuff ####################################################### -dnl ############################################################################## -AC_ARG_ENABLE(vv, [ --enable-vv enable Voice and Video support],,enable_vv=no) -if test "x$enable_vv" = xyes; then - AC_DEFINE(HAVE_GLIB, 1, [Gaim always has GLib, but Linphone can be built without it]) - - dnl enable truespeech codec support - AC_ARG_ENABLE(truespeech, [ --enable-truespeech Turn on TrueSpeech support (x86 only)],,enable_truespeech=no) - TRUESPEECH_CFLAGS= - if test x$enable_truespeech = xyes ; then - TRUESPEECH_CFLAGS=-DTRUESPEECH - fi - VV_CFLAGS="$VV_CFLAGS $TRUESPEECH_CFLAGS" +AM_CONDITIONAL(HAVE_DOXYGEN, test "x$enable_doxygen" = "xyes") - found_sound=no - AC_CHECK_HEADERS(soundcard.h sys/soundcard.h machine/soundcard.h sys/audio.h) - if test "${ac_cv_header_sys_soundcard_h}" = "yes" || \ - test "${ac_cv_header_soundcard_h}" = "yes" || \ - test "${ac_cv_header_sys_audio_h}" = "yes" || \ - test "${ac_cv_header_machine_soundcard_h}" = "yes"; then - found_sound=yes - fi - if test "$found_sound" = "no"; then - AC_MSG_ERROR([Could not find a support sound driver]) - fi - if test "$alsa" = "true"; then - AC_CHECK_HEADERS(alsa/asoundlib.h, - [ AC_CHECK_LIB(asound,snd_pcm_open, - [ALSA_LIBS="-lasound" ; AC_DEFINE(__ALSA_ENABLED__,1,[Defined when alsa support is enabled]) ]) - ] - ) - fi - dnl Check for samplerate libraries - dnl Check for jack libraries (sound output plugin) - PKG_CHECK_MODULES(JACK,jack >= 0.15.0, - [ - dnl we've found jack devel files - PKG_CHECK_MODULES(SAMPLERATE, samplerate >= 0.0.13, [AC_DEFINE(__JACK_ENABLED__,1,[Jack support])] , - [echo "libsamplerate not found, jack support disabled."]) - VV_CFLAGS="$VV_CFLAGS $SAMPLERATE_CFLAGS" - VV_LIBS="$VV_LIBS $SAMPLERATE_LIBS" - ], - [echo "No jack support."] ) - VV_CFLAGS="$VV_CFLAGS $JACK_CFLAGS" - VV_LIBS="$VV_LIBS $JACK_LIBS" - - dnl check for installed version of speex - LP_CHECK_SPEEX - VV_CFLAGS="$VV_CFLAGS $SPEEX_CFLAGS" - VV_LIBS="$VV_LIBS $SPEEX_LIBS" - - PKG_CHECK_MODULES(ORTP, ortp >= 0.7.1, enable_ortp=yes, enable_ortp=no) - VV_CFLAGS="$VV_CFLAGS $ORTP_CFLAGS" - VV_LIBS="$VV_LIBS $ORTP_LIBS" - if test x$enable_ortp = xno; then - AC_MSG_ERROR([Could not find a suitable version of oRTP. Please install oRTP >= 0.7.1]) - fi -else - enable_vv=no +AC_ARG_ENABLE(debug, [AC_HELP_STRING([--enable-debug], + [compile with debugging support])], , enable_debug=no) +if test "x$enable_debug" = "xyes" ; then + AC_DEFINE(DEBUG, 1, [Define if debugging is enabled.]) fi -if test x$enable_vv = xyes; then - AM_CONDITIONAL(HAVE_VV, true) - AC_DEFINE(HAVE_VV, [1], [Compile with Voice/Video support]) -else - AM_CONDITIONAL(HAVE_VV, false) +AC_ARG_ENABLE(fatal-asserts, [AC_HELP_STRING([--enable-fatal-asserts], + [make assertions fatal (useful for debugging)])], , enable_fatal_asserts=no) +if test "x$enable_fatal_asserts" = "xyes" ; then + AC_DEFINE(GAIM_FATAL_ASSERTS, 1, [Define to make assertions fatal (useful for debugging).]) fi -AC_SUBST(VV_CFLAGS) -AC_SUBST(VV_LIBS) - -AC_CONFIG_COMMANDS_PRE([ - if test -e VERSION; then - cp -p VERSION VERSION.ac-save - fi -]) - -AC_CONFIG_COMMANDS_POST([ - cmp VERSION VERSION.ac-save || touch -r VERSION.ac-save VERSION - rm -f VERSION.ac-save -]) - AC_OUTPUT([Makefile Doxyfile gaim.apspec + gaim.service doc/Makefile doc/gaim.1 - intl/Makefile + doc/gaim-text.1 m4macros/Makefile - pixmaps/Makefile - pixmaps/smileys/Makefile - pixmaps/smileys/default/Makefile - pixmaps/smileys/none/Makefile - pixmaps/status/Makefile - pixmaps/status/default/Makefile - plugins/Makefile - plugins/docklet/Makefile - plugins/gevolution/Makefile - plugins/gestures/Makefile - plugins/mono/Makefile - plugins/mono/api/Makefile - plugins/mono/loader/Makefile - plugins/musicmessaging/Makefile - plugins/perl/Makefile - plugins/perl/common/Makefile.PL - plugins/ssl/Makefile - plugins/tcl/Makefile - plugins/ticker/Makefile + gtk/Makefile + gtk/pixmaps/Makefile + gtk/pixmaps/buddy_icons/Makefile + gtk/pixmaps/buddy_icons/qq/Makefile + gtk/pixmaps/smileys/Makefile + gtk/pixmaps/smileys/default/Makefile + gtk/pixmaps/smileys/none/Makefile + gtk/pixmaps/status/Makefile + gtk/pixmaps/status/default/Makefile + gtk/plugins/Makefile + gtk/plugins/cap/Makefile + gtk/plugins/gestures/Makefile + gtk/plugins/gevolution/Makefile + gtk/plugins/musicmessaging/Makefile + gtk/plugins/perl/Makefile + gtk/plugins/perl/common/Makefile.PL + gtk/plugins/ticker/Makefile + gtk/sounds/Makefile + libgaim/gconf/Makefile + libgaim/plugins/Makefile + libgaim/plugins/mono/Makefile + libgaim/plugins/mono/api/Makefile + libgaim/plugins/mono/loader/Makefile + libgaim/plugins/perl/Makefile + libgaim/plugins/perl/common/Makefile.PL + libgaim/plugins/ssl/Makefile + libgaim/plugins/tcl/Makefile + libgaim/Makefile + libgaim/protocols/Makefile + libgaim/protocols/bonjour/Makefile + libgaim/protocols/gg/Makefile + libgaim/protocols/irc/Makefile + libgaim/protocols/jabber/Makefile + libgaim/protocols/msn/Makefile + libgaim/protocols/novell/Makefile + libgaim/protocols/oscar/Makefile + libgaim/protocols/qq/Makefile + libgaim/protocols/sametime/Makefile + libgaim/protocols/silc/Makefile + libgaim/protocols/simple/Makefile + libgaim/protocols/toc/Makefile + libgaim/protocols/yahoo/Makefile + libgaim/protocols/zephyr/Makefile + libgaim/tests/Makefile + console/Makefile + console/libgnt/Makefile + console/libgnt/gnt.pc + console/libgnt/wms/Makefile + console/plugins/Makefile po/Makefile.in - sounds/Makefile - src/Makefile - src/mediastreamer/Makefile - src/protocols/Makefile - src/protocols/bonjour/Makefile - src/protocols/gg/Makefile - src/protocols/irc/Makefile - src/protocols/jabber/Makefile - src/protocols/msn/Makefile - src/protocols/napster/Makefile - src/protocols/novell/Makefile - src/protocols/oscar/Makefile - src/protocols/sametime/Makefile - src/protocols/silc/Makefile - src/protocols/simple/Makefile - src/protocols/toc/Makefile - src/protocols/yahoo/Makefile - src/protocols/zephyr/Makefile gaim.pc gaim.spec ]) @@ -1661,37 +1944,36 @@ echo $PACKAGE $VERSION echo -echo Build Protocol Plugins........ : $enable_prpls +echo Build GTK+ 2.x UI............. : $enable_gtkui +echo Build console UI.............. : $enable_consoleui +echo +echo Protocols to build dynamically : $DYNAMIC_PRPLS echo Protocols to link statically.. : $STATIC_PRPLS -echo Protocols to build dynamically : $DYNAMIC_PRPLS echo -echo UI Library.................... : GTK+ 2.x +echo Build with GStreamer support.. : $enable_gst +echo Build with DBUS support....... : $enable_dbus +if test "x$enable_dbus" = "xyes" ; then + eval eval echo DBUS services directory....... : $DBUS_SERVICES_DIR +fi echo SSL Library/Libraries......... : $msg_ssl +echo Build with Cyrus SASL support. : $enable_cyrus_sasl +echo Use kerberos 4 with zephyr.... : $kerberos +echo Use external libzephyr........ : $zephyr +echo Has you....................... : yes echo -echo Build with Plugin support..... : $enable_plugins +echo Use XScreenSaver Extension.... : $enable_screensaver +echo Use X Session Management...... : $enable_sm +echo Use startup notification...... : $enable_startup_notification +echo Build with GtkSpell support... : $enable_gtkspell +echo +echo Build with plugin support..... : $enable_plugins echo Build with Mono support....... : $enable_mono echo Build with Perl support....... : $enable_perl echo Build with Tcl support........ : $enable_tcl echo Build with Tk support......... : $enable_tk -echo Build with Audio support...... : $enable_audio -echo Build with GtkSpell support... : $enable_gtkspell -echo Build with Voice/Video support : $enable_vv -echo Build with DBUS support....... : $enable_dbus -if test x$enable_dbus = xyes ; then -echo DBUS session directory........ : $DBUS_SESSION_DIR -fi -echo Build with Cyrus SASL support. : $enable_cyrus_sasl -echo Has you....................... : yes -echo -echo -echo Use kerberos 4 with zephyr.... : $kerberos -echo Use external libzephyr........ : $zephyr -echo -echo Use XScreenSaver Extension.... : $enable_xss -echo Use X Session Management...... : $enable_sm -echo Use startup notification.......: $enable_startup_notification echo echo Print debugging messages...... : $enable_debug +echo Assertions are fatal.......... : $enable_fatal_asserts echo eval eval echo Gaim will be installed in $bindir. if test "x$gaimpath" != "x" ; then
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/Makefile.am Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,66 @@ +if ENABLE_GNT + +SUBDIRS = libgnt plugins + +bin_PROGRAMS = gaim-text + +gaim_text_SOURCES = \ + gntaccount.c \ + gntblist.c \ + gntconn.c \ + gntconv.c \ + gntdebug.c \ + gntgaim.c \ + gntnotify.c \ + gntplugin.c \ + gntprefs.c \ + gntrequest.c \ + gntstatus.c \ + gntui.c + +gaim_text_headers = \ + gntaccount.h \ + gntblist.h \ + gntconn.h \ + gntconv.h \ + gntdebug.h \ + gntgaim.h \ + gntnotify.h \ + gntplugin.h \ + gntprefs.h \ + gntrequest.h \ + gntstatus.h \ + gntui.h + +gaim_textincludedir=$(includedir)/gaim/gnt +gaim_textinclude_HEADERS = \ + $(gaim_text_headers) + +gaim_text_DEPENDENCIES = @LIBOBJS@ +gaim_text_LDFLAGS = -export-dynamic +gaim_text_LDADD = \ + @LIBOBJS@ \ + $(DBUS_LIBS) \ + $(INTLLIBS) \ + $(GLIB_LIBS) \ + $(LIBXML_LIBS) \ + $(GNT_LIBS) \ + ./libgnt/libgnt.la \ + $(top_builddir)/libgaim/libgaim.la + +AM_CPPFLAGS = \ + -DSTANDALONE \ + -DBR_PTHREADS=0 \ + -DDATADIR=\"$(datadir)\" \ + -DLIBDIR=\"$(libdir)/gaim/\" \ + -DLOCALEDIR=\"$(datadir)/locale\" \ + -DSYSCONFDIR=\"$(sysconfdir)\" \ + -I$(top_srcdir)/libgaim/ \ + -I$(top_srcdir) \ + -I$(srcdir)/libgnt/ \ + $(DEBUG_CFLAGS) \ + $(GLIB_CFLAGS) \ + $(DBUS_CFLAGS) \ + $(LIBXML_CFLAGS) \ + $(GNT_CFLAGS) +endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/getopt.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,737 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu + before changing it! + + 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, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* NOTE!!! AIX requires this to be the first thing in the file. + Do not put ANYTHING before it! */ +#if !defined (__GNUC__) && defined (_AIX) + #pragma alloca +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* Alver says we need this for IRIX. */ +#if HAVE_STRING_H +#include "string.h" +#endif + +#ifdef __GNUC__ +#define alloca __builtin_alloca +#else /* not __GNUC__ */ +#if defined (HAVE_ALLOCA_H) || (defined(sparc) && (defined(sun) || (!defined(USG) && !defined(SVR4) && !defined(__svr4__)))) +#include <alloca.h> +#else +#ifndef _AIX +char *alloca (); +#endif +#endif /* alloca.h */ +#endif /* not __GNUC__ */ + +#if !__STDC__ && !defined(const) && IN_GCC +#define const +#endif + +/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>. */ +#ifndef _NO_PROTO +#define _NO_PROTO +#endif + +#include <stdio.h> + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#if defined (_LIBC) || !defined (__GNU_LIBRARY__) + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +#undef alloca +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +#include <stdlib.h> +#else /* Not GNU C library. */ +#define __alloca alloca +#endif /* GNU C library. */ + +/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a + long-named option. Because this is not POSIX.2 compliant, it is + being phased out. */ +/* #define GETOPT_COMPAT */ + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg = 0; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* XXX 1003.2 says this must be 1 before any call. */ +int optind = 0; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return EOF with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +#include <string.h> +#define my_index strchr +#define my_bcopy(src, dst, n) memcpy ((dst), (src), (n)) +#else + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +char *getenv (); + +static char * +my_index (str, chr) + const char *str; + int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +static void +my_bcopy (from, to, size) + const char *from; + char *to; + int size; +{ + int i; + for (i = 0; i < size; i++) + to[i] = from[i]; +} +#endif /* GNU C library. */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +static void +exchange (argv) + char **argv; +{ + int nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *); + char **temp = (char **) __alloca (nonopts_size); + + /* Interchange the two blocks of data in ARGV. */ + + my_bcopy ((char *) &argv[first_nonopt], (char *) temp, nonopts_size); + my_bcopy ((char *) &argv[last_nonopt], (char *) &argv[first_nonopt], + (optind - last_nonopt) * sizeof (char *)); + my_bcopy ((char *) temp, + (char *) &argv[first_nonopt + optind - last_nonopt], + nonopts_size); + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns `EOF'. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + int option_index; + + optarg = 0; + + /* Initialize the internal data when the first call is made. + Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + if (optind == 0) + { + first_nonopt = last_nonopt = optind = 1; + + nextchar = NULL; + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (getenv ("POSIXLY_CORRECT") != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + } + + if (nextchar == NULL || *nextchar == '\0') + { + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Now skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc + && (argv[optind][0] != '-' || argv[optind][1] == '\0') +#ifdef GETOPT_COMPAT + && (longopts == NULL + || argv[optind][0] != '+' || argv[optind][1] == '\0') +#endif /* GETOPT_COMPAT */ + ) + optind++; + last_nonopt = optind; + } + + /* Special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return EOF; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if ((argv[optind][0] != '-' || argv[optind][1] == '\0') +#ifdef GETOPT_COMPAT + && (longopts == NULL + || argv[optind][0] != '+' || argv[optind][1] == '\0') +#endif /* GETOPT_COMPAT */ + ) + { + if (ordering == REQUIRE_ORDER) + return EOF; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Start decoding its characters. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + if (longopts != NULL + && ((argv[optind][0] == '-' + && (argv[optind][1] == '-' || long_only)) +#ifdef GETOPT_COMPAT + || argv[optind][0] == '+' +#endif /* GETOPT_COMPAT */ + )) + { + const struct option *p; + char *s = nextchar; + int exact = 0; + int ambig = 0; + const struct option *pfound = NULL; + int indfound; + + while (*s && *s != '=') + s++; + + /* Test all options for either exact match or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; + p++, option_index++) + if (!strncmp (p->name, nextchar, s - nextchar)) + { + if (s - nextchar == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (opterr) + fprintf (stderr, "%s: option `%s' is ambiguous\n", + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*s) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = s + 1; + else + { + if (opterr) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + "%s: option `--%s' doesn't allow an argument\n", + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + "%s: option `%c%s' doesn't allow an argument\n", + argv[0], argv[optind - 1][0], pfound->name); + } + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (opterr) + fprintf (stderr, "%s: option `%s' requires an argument\n", + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' +#ifdef GETOPT_COMPAT + || argv[optind][0] == '+' +#endif /* GETOPT_COMPAT */ + || my_index (optstring, *nextchar) == NULL) + { + if (opterr) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, "%s: unrecognized option `--%s'\n", + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, "%s: unrecognized option `%c%s'\n", + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + return '?'; + } + } + + /* Look at and handle the next option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (opterr) + { +#if 0 + if (c < 040 || c >= 0177) + fprintf (stderr, "%s: unrecognized option, character code 0%o\n", + argv[0], c); + else + fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c); +#else + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c); +#endif + } + optopt = c; + return '?'; + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = 0; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (opterr) + { +#if 0 + fprintf (stderr, "%s: option `-%c' requires an argument\n", + argv[0], c); +#else + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, "%s: option requires an argument -- %c\n", + argv[0], c); +#endif + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +#endif /* _LIBC or not __GNU_LIBRARY__. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == EOF) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/getopt.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,136 @@ +/* Declarations for getopt. + + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu + before changing it! + + 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, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifndef _GETOPT_H +#define _GETOPT_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns EOF, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ +#if __STDC__ + const char *name; +#else + char *name; +#endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +#if __STDC__ +#if defined(__GNU_LIBRARY__) +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ +extern int getopt (int argc, char *const *argv, const char *shortopts); +#else /* not __GNU_LIBRARY__ */ +extern int getopt (); +#endif /* not __GNU_LIBRARY__ */ +extern int getopt_long (int argc, char *const *argv, const char *shortopts, + const struct option *longopts, int *longind); +extern int getopt_long_only (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind); + +/* Internal only. Users should not call this directly. */ +extern int _getopt_internal (int argc, char *const *argv, + const char *shortopts, + const struct option *longopts, int *longind, + int long_only); +#else /* not __STDC__ */ +extern int getopt (); +extern int getopt_long (); +extern int getopt_long_only (); + +extern int _getopt_internal (); +#endif /* not __STDC__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* _GETOPT_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/getopt1.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,177 @@ +/* getopt_long and getopt_long_only entry points for GNU getopt. + 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, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "getopt.h" + +#if !__STDC__ && !defined(const) && IN_GCC +#define const +#endif + +#include <stdio.h> + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#if defined (_LIBC) || !defined (__GNU_LIBRARY__) + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +#include <stdlib.h> +#else +char *getenv (); +#endif + +#ifndef NULL +#define NULL 0 +#endif + +int +getopt_long (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +/* Like getopt_long, but '-' as well as '--' can indicate a long option. + If an option that starts with '-' (not '--') doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ + +int +getopt_long_only (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 1); +} + + +#endif /* _LIBC or not __GNU_LIBRARY__. */ + +#ifdef TEST + +#include <stdio.h> + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = + { + {"add", 1, 0, 0}, + {"append", 0, 0, 0}, + {"delete", 1, 0, 0}, + {"verbose", 0, 0, 0}, + {"create", 0, 0, 0}, + {"file", 1, 0, 0}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, "abc:d:0123456789", + long_options, &option_index); + if (c == EOF) + break; + + switch (c) + { + case 0: + printf ("option %s", long_options[option_index].name); + if (optarg) + printf (" with arg %s", optarg); + printf ("\n"); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case 'd': + printf ("option d with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/gntaccount.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,849 @@ +/** + * @file gntaccount.c GNT Account API + * @ingroup gntui + * + * gaim + * + * 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 + */ +#include <gnt.h> +#include <gntbox.h> +#include <gntbutton.h> +#include <gntcheckbox.h> +#include <gntcombobox.h> +#include <gntentry.h> +#include <gntlabel.h> +#include <gntline.h> +#include <gnttree.h> + +#include <account.h> +#include <accountopt.h> +#include <connection.h> +#include <notify.h> +#include <plugin.h> +#include <request.h> + +#include "gntaccount.h" +#include "gntgaim.h" + +#include <string.h> + +typedef struct +{ + GntWidget *window; + GntWidget *tree; +} GGAccountList; + +static GGAccountList accounts; + +typedef struct +{ + GaimAccount *account; /* NULL for a new account */ + + GntWidget *window; + + GntWidget *protocol; + GntWidget *screenname; + GntWidget *password; + GntWidget *alias; + + GntWidget *splits; + GList *split_entries; + + GList *prpl_entries; + GntWidget *prpls; + + GntWidget *newmail; + GntWidget *remember; +} AccountEditDialog; + +/* This is necessary to close an edit-dialog when an account is deleted */ +static GList *accountdialogs; + +static void +account_add(GaimAccount *account) +{ + gnt_tree_add_choice(GNT_TREE(accounts.tree), account, + gnt_tree_create_row(GNT_TREE(accounts.tree), + gaim_account_get_username(account), + gaim_account_get_protocol_name(account)), + NULL, NULL); + gnt_tree_set_choice(GNT_TREE(accounts.tree), account, + gaim_account_get_enabled(account, GAIM_GNT_UI)); +} + +static void +edit_dialog_destroy(AccountEditDialog *dialog) +{ + accountdialogs = g_list_remove(accountdialogs, dialog); + g_list_free(dialog->prpl_entries); + g_list_free(dialog->split_entries); + g_free(dialog); +} + +static void +save_account_cb(AccountEditDialog *dialog) +{ + GaimAccount *account; + GaimPlugin *plugin; + GaimPluginProtocolInfo *prplinfo; + const char *value; + GString *username; + + /* XXX: Do some error checking first. */ + + plugin = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(dialog->protocol)); + prplinfo = GAIM_PLUGIN_PROTOCOL_INFO(plugin); + + /* Screenname && user-splits */ + value = gnt_entry_get_text(GNT_ENTRY(dialog->screenname)); + + if (value == NULL || *value == '\0') + { + gaim_notify_error(NULL, _("Error"), _("Account was not added"), + _("Screenname of an account must be non-empty.")); + return; + } + + username = g_string_new(value); + + if (prplinfo != NULL) + { + GList *iter, *entries; + for (iter = prplinfo->user_splits, entries = dialog->split_entries; + iter && entries; iter = iter->next, entries = entries->next) + { + GaimAccountUserSplit *split = iter->data; + GntWidget *entry = entries->data; + + value = gnt_entry_get_text(GNT_ENTRY(entry)); + if (value == NULL || *value == '\0') + value = gaim_account_user_split_get_default_value(split); + g_string_append_printf(username, "%c%s", + gaim_account_user_split_get_separator(split), + value); + } + } + + if (dialog->account == NULL) + { + account = gaim_account_new(username->str, gaim_plugin_get_id(plugin)); + gaim_accounts_add(account); + } + else + { + account = dialog->account; + + /* Protocol */ + gaim_account_set_protocol_id(account, gaim_plugin_get_id(plugin)); + gaim_account_set_username(account, username->str); + } + g_string_free(username, TRUE); + + /* Alias */ + value = gnt_entry_get_text(GNT_ENTRY(dialog->alias)); + if (value && *value) + gaim_account_set_alias(account, value); + + /* Remember password and password */ + gaim_account_set_remember_password(account, + gnt_check_box_get_checked(GNT_CHECK_BOX(dialog->remember))); + value = gnt_entry_get_text(GNT_ENTRY(dialog->password)); + if (value && *value && gaim_account_get_remember_password(account)) + gaim_account_set_password(account, value); + else + gaim_account_set_password(account, NULL); + + /* Mail notification */ + gaim_account_set_check_mail(account, + gnt_check_box_get_checked(GNT_CHECK_BOX(dialog->newmail))); + + /* Protocol options */ + if (prplinfo) + { + GList *iter, *entries; + + for (iter = prplinfo->protocol_options, entries = dialog->prpl_entries; + iter && entries; iter = iter->next, entries = entries->next) + { + GaimAccountOption *option = iter->data; + GntWidget *entry = entries->data; + GaimPrefType type = gaim_account_option_get_type(option); + const char *setting = gaim_account_option_get_setting(option); + + if (type == GAIM_PREF_STRING) + { + const char *value = gnt_entry_get_text(GNT_ENTRY(entry)); + gaim_account_set_string(account, setting, value); + } + else if (type == GAIM_PREF_INT) + { + const char *str = gnt_entry_get_text(GNT_ENTRY(entry)); + int value = 0; + if (str) + value = atoi(str); + gaim_account_set_int(account, setting, value); + } + else if (type == GAIM_PREF_BOOLEAN) + { + gboolean value = gnt_check_box_get_checked(GNT_CHECK_BOX(entry)); + gaim_account_set_bool(account, setting, value); + } + else if (type == GAIM_PREF_STRING_LIST) + { + /* TODO: */ + } + else + { + g_assert_not_reached(); + } + } + } + + /* XXX: Proxy options */ + + gnt_widget_destroy(dialog->window); +} + +static void +update_user_splits(AccountEditDialog *dialog) +{ + GntWidget *hbox; + GaimPlugin *plugin; + GaimPluginProtocolInfo *prplinfo; + GList *iter, *entries; + char *username = NULL; + + if (dialog->splits) + { + gnt_box_remove_all(GNT_BOX(dialog->splits)); + g_list_free(dialog->split_entries); + } + else + { + dialog->splits = gnt_vbox_new(FALSE); + gnt_box_set_pad(GNT_BOX(dialog->splits), 0); + gnt_box_set_fill(GNT_BOX(dialog->splits), TRUE); + } + + dialog->split_entries = NULL; + + plugin = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(dialog->protocol)); + if (!plugin) + return; + prplinfo = GAIM_PLUGIN_PROTOCOL_INFO(plugin); + + username = dialog->account ? g_strdup(gaim_account_get_username(dialog->account)) : NULL; + + for (iter = prplinfo->user_splits; iter; iter = iter->next) + { + GaimAccountUserSplit *split = iter->data; + GntWidget *entry; + char *buf; + + hbox = gnt_hbox_new(TRUE); + gnt_box_add_widget(GNT_BOX(dialog->splits), hbox); + + buf = g_strdup_printf("%s:", gaim_account_user_split_get_text(split)); + gnt_box_add_widget(GNT_BOX(hbox), gnt_label_new(buf)); + + entry = gnt_entry_new(NULL); + gnt_box_add_widget(GNT_BOX(hbox), entry); + + dialog->split_entries = g_list_append(dialog->split_entries, entry); + g_free(buf); + } + + for (iter = g_list_last(prplinfo->user_splits), entries = g_list_last(dialog->split_entries); + iter && entries; iter = iter->prev, entries = entries->prev) + { + GntWidget *entry = entries->data; + GaimAccountUserSplit *split = iter->data; + const char *value = NULL; + char *s; + + if (dialog->account) + { + s = strrchr(username, gaim_account_user_split_get_separator(split)); + if (s != NULL) + { + *s = '\0'; + s++; + value = s; + } + } + if (value == NULL) + value = gaim_account_user_split_get_default_value(split); + + if (value != NULL) + gnt_entry_set_text(GNT_ENTRY(entry), value); + } + + if (username != NULL) + gnt_entry_set_text(GNT_ENTRY(dialog->screenname), username); + + g_free(username); +} + +static void +add_protocol_options(AccountEditDialog *dialog) +{ + GaimPlugin *plugin; + GaimPluginProtocolInfo *prplinfo; + GList *iter; + GntWidget *vbox, *box; + GaimAccount *account; + + if (dialog->prpls) + gnt_box_remove_all(GNT_BOX(dialog->prpls)); + else + { + dialog->prpls = vbox = gnt_vbox_new(FALSE); + gnt_box_set_pad(GNT_BOX(vbox), 0); + gnt_box_set_alignment(GNT_BOX(vbox), GNT_ALIGN_LEFT); + gnt_box_set_fill(GNT_BOX(vbox), TRUE); + } + + if (dialog->prpl_entries) + { + g_list_free(dialog->prpl_entries); + dialog->prpl_entries = NULL; + } + + vbox = dialog->prpls; + + plugin = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(dialog->protocol)); + if (!plugin) + return; + + prplinfo = GAIM_PLUGIN_PROTOCOL_INFO(plugin); + + account = dialog->account; + + for (iter = prplinfo->protocol_options; iter; iter = iter->next) + { + GaimAccountOption *option = iter->data; + GaimPrefType type = gaim_account_option_get_type(option); + + box = gnt_hbox_new(TRUE); + gnt_box_set_pad(GNT_BOX(box), 0); + gnt_box_add_widget(GNT_BOX(vbox), box); + + if (type == GAIM_PREF_BOOLEAN) + { + GntWidget *widget = gnt_check_box_new(gaim_account_option_get_text(option)); + gnt_box_add_widget(GNT_BOX(box), widget); + dialog->prpl_entries = g_list_append(dialog->prpl_entries, widget); + + if (account) + gnt_check_box_set_checked(GNT_CHECK_BOX(widget), + gaim_account_get_bool(account, + gaim_account_option_get_setting(option), + gaim_account_option_get_default_bool(option))); + else + gnt_check_box_set_checked(GNT_CHECK_BOX(widget), + gaim_account_option_get_default_bool(option)); + } + else + { + gnt_box_add_widget(GNT_BOX(box), + gnt_label_new(gaim_account_option_get_text(option))); + + if (type == GAIM_PREF_STRING_LIST) + { + /* TODO: Use a combobox */ + /* Don't forget to append the widget to prpl_entries */ + } + else + { + GntWidget *entry = gnt_entry_new(NULL); + gnt_box_add_widget(GNT_BOX(box), entry); + dialog->prpl_entries = g_list_append(dialog->prpl_entries, entry); + + if (type == GAIM_PREF_STRING) + { + const char *dv = gaim_account_option_get_default_string(option); + + if (account) + gnt_entry_set_text(GNT_ENTRY(entry), + gaim_account_get_string(account, + gaim_account_option_get_setting(option), dv)); + else + gnt_entry_set_text(GNT_ENTRY(entry), dv); + } + else if (type == GAIM_PREF_INT) + { + char str[32]; + int value = gaim_account_option_get_default_int(option); + if (account) + value = gaim_account_get_int(account, + gaim_account_option_get_setting(option), value); + snprintf(str, sizeof(str), "%d", value); + gnt_entry_set_flag(GNT_ENTRY(entry), GNT_ENTRY_FLAG_INT); + gnt_entry_set_text(GNT_ENTRY(entry), str); + } + else + { + g_assert_not_reached(); + } + } + } + } +} + +static void +update_user_options(AccountEditDialog *dialog) +{ + GaimPlugin *plugin; + GaimPluginProtocolInfo *prplinfo; + + plugin = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(dialog->protocol)); + if (!plugin) + return; + + prplinfo = GAIM_PLUGIN_PROTOCOL_INFO(plugin); + + if (dialog->newmail == NULL) + dialog->newmail = gnt_check_box_new(_("New mail notifications")); + if (dialog->account) + gnt_check_box_set_checked(GNT_CHECK_BOX(dialog->newmail), + gaim_account_get_check_mail(dialog->account)); + if (!prplinfo || !(prplinfo->options & OPT_PROTO_MAIL_CHECK)) + gnt_widget_set_visible(dialog->newmail, FALSE); + else + gnt_widget_set_visible(dialog->newmail, TRUE); + + if (dialog->remember == NULL) + dialog->remember = gnt_check_box_new(_("Remember password")); + if (dialog->account) + gnt_check_box_set_checked(GNT_CHECK_BOX(dialog->remember), + gaim_account_get_remember_password(dialog->account)); +} + +static void +prpl_changed_cb(GntWidget *combo, GaimPlugin *old, GaimPlugin *new, AccountEditDialog *dialog) +{ + update_user_splits(dialog); + add_protocol_options(dialog); + update_user_options(dialog); /* This may not be necessary here */ + gnt_box_readjust(GNT_BOX(dialog->window)); + gnt_widget_draw(dialog->window); +} + +static void +edit_account(GaimAccount *account) +{ + GntWidget *window, *hbox; + GntWidget *combo, *button, *entry; + GList *list, *iter; + AccountEditDialog *dialog; + + if (account) + { + GList *iter; + for (iter = accountdialogs; iter; iter = iter->next) + { + AccountEditDialog *dlg = iter->data; + if (dlg->account == account) + return; + } + } + + dialog = g_new0(AccountEditDialog, 1); + accountdialogs = g_list_prepend(accountdialogs, dialog); + + dialog->window = window = gnt_vbox_new(FALSE); + dialog->account = account; + gnt_box_set_toplevel(GNT_BOX(window), TRUE); + gnt_box_set_title(GNT_BOX(window), account ? _("Modify Account") : _("New Account")); + gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID); + gnt_box_set_pad(GNT_BOX(window), 0); + gnt_widget_set_name(window, "edit-account"); + gnt_box_set_fill(GNT_BOX(window), TRUE); + + hbox = gnt_hbox_new(TRUE); + gnt_box_set_pad(GNT_BOX(hbox), 0); + gnt_box_add_widget(GNT_BOX(window), hbox); + + dialog->protocol = combo = gnt_combo_box_new(); + list = gaim_plugins_get_protocols(); + for (iter = list; iter; iter = iter->next) + { + gnt_combo_box_add_data(GNT_COMBO_BOX(combo), iter->data, + ((GaimPlugin*)iter->data)->info->name); + } + + if (account) + gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), + gaim_plugins_find_with_id(gaim_account_get_protocol_id(account))); + else + gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), list->data); + + g_signal_connect(G_OBJECT(combo), "selection-changed", G_CALLBACK(prpl_changed_cb), dialog); + + gnt_box_add_widget(GNT_BOX(hbox), gnt_label_new(_("Protocol:"))); + gnt_box_add_widget(GNT_BOX(hbox), combo); + + hbox = gnt_hbox_new(TRUE); + gnt_box_set_pad(GNT_BOX(hbox), 0); + gnt_box_add_widget(GNT_BOX(window), hbox); + + dialog->screenname = entry = gnt_entry_new(NULL); + gnt_box_add_widget(GNT_BOX(hbox), gnt_label_new(_("Screen name:"))); + gnt_box_add_widget(GNT_BOX(hbox), entry); + + /* User splits */ + update_user_splits(dialog); + gnt_box_add_widget(GNT_BOX(window), dialog->splits); + + hbox = gnt_hbox_new(TRUE); + gnt_box_set_pad(GNT_BOX(hbox), 0); + gnt_box_add_widget(GNT_BOX(window), hbox); + + dialog->password = entry = gnt_entry_new(NULL); + gnt_entry_set_masked(GNT_ENTRY(entry), TRUE); + gnt_box_add_widget(GNT_BOX(hbox), gnt_label_new(_("Password:"))); + gnt_box_add_widget(GNT_BOX(hbox), entry); + if (account) + gnt_entry_set_text(GNT_ENTRY(entry), gaim_account_get_password(account)); + + hbox = gnt_hbox_new(TRUE); + gnt_box_set_pad(GNT_BOX(hbox), 0); + gnt_box_add_widget(GNT_BOX(window), hbox); + + dialog->alias = entry = gnt_entry_new(NULL); + gnt_box_add_widget(GNT_BOX(hbox), gnt_label_new(_("Alias:"))); + gnt_box_add_widget(GNT_BOX(hbox), entry); + if (account) + gnt_entry_set_text(GNT_ENTRY(entry), gaim_account_get_alias(account)); + + /* User options */ + update_user_options(dialog); + gnt_box_add_widget(GNT_BOX(window), dialog->remember); + gnt_box_add_widget(GNT_BOX(window), dialog->newmail); + + gnt_box_add_widget(GNT_BOX(window), gnt_line_new(FALSE)); + + /* The advanced box */ + add_protocol_options(dialog); + gnt_box_add_widget(GNT_BOX(window), dialog->prpls); + + /* TODO: Add proxy options */ + + /* The button box */ + hbox = gnt_hbox_new(FALSE); + gnt_box_add_widget(GNT_BOX(window), hbox); + gnt_box_set_alignment(GNT_BOX(hbox), GNT_ALIGN_MID); + + button = gnt_button_new(_("Cancel")); + gnt_box_add_widget(GNT_BOX(hbox), button); + g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gnt_widget_destroy), window); + + button = gnt_button_new(_("Save")); + gnt_box_add_widget(GNT_BOX(hbox), button); + g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(save_account_cb), dialog); + + g_signal_connect_swapped(G_OBJECT(window), "destroy", G_CALLBACK(edit_dialog_destroy), dialog); + + gnt_widget_show(window); + gnt_box_readjust(GNT_BOX(window)); + gnt_widget_draw(window); +} + +static void +add_account_cb(GntWidget *widget, gpointer null) +{ + edit_account(NULL); +} + +static void +modify_account_cb(GntWidget *widget, GntTree *tree) +{ + GaimAccount *account = gnt_tree_get_selection_data(tree); + if (!account) + return; + edit_account(account); +} + +static void +really_delete_account(GaimAccount *account) +{ + GList *iter; + for (iter = accountdialogs; iter; iter = iter->next) + { + AccountEditDialog *dlg = iter->data; + if (dlg->account == account) + { + gnt_widget_destroy(dlg->window); + break; + } + } + gaim_accounts_delete(account); +} + +static void +delete_account_cb(GntWidget *widget, GntTree *tree) +{ + GaimAccount *account; + char *prompt; + + account = gnt_tree_get_selection_data(tree); + if (!account) + return; + + prompt = g_strdup_printf(_("Are you sure you want to delete %s?"), + gaim_account_get_username(account)); + + gaim_request_close_with_handle(account); /* Close any other opened delete window */ + gaim_request_action(account, _("Delete Account"), prompt, NULL, 0, account, 2, + _("Delete"), really_delete_account, _("Cancel"), NULL); + g_free(prompt); +} + +static void +account_toggled(GntWidget *widget, void *key, gpointer null) +{ + GaimAccount *account = key; + + gaim_account_set_enabled(account, GAIM_GNT_UI, gnt_tree_get_choice(GNT_TREE(widget), key)); +} + +static void +reset_accounts_win(GntWidget *widget, gpointer null) +{ + accounts.window = NULL; + accounts.tree = NULL; +} + +void gg_accounts_show_all() +{ + GList *iter; + GntWidget *box, *button; + + if (accounts.window) + return; + + accounts.window = gnt_vbox_new(FALSE); + gnt_box_set_toplevel(GNT_BOX(accounts.window), TRUE); + gnt_box_set_title(GNT_BOX(accounts.window), _("Accounts")); + gnt_box_set_pad(GNT_BOX(accounts.window), 0); + gnt_box_set_alignment(GNT_BOX(accounts.window), GNT_ALIGN_MID); + gnt_widget_set_name(accounts.window, "accounts"); + + gnt_box_add_widget(GNT_BOX(accounts.window), + gnt_label_new(_("You can enable/disable accounts from the following list."))); + + gnt_box_add_widget(GNT_BOX(accounts.window), gnt_line_new(FALSE)); + + accounts.tree = gnt_tree_new_with_columns(2); + GNT_WIDGET_SET_FLAGS(accounts.tree, GNT_WIDGET_NO_BORDER); + + for (iter = gaim_accounts_get_all(); iter; iter = iter->next) + { + GaimAccount *account = iter->data; + account_add(account); + } + + g_signal_connect(G_OBJECT(accounts.tree), "toggled", G_CALLBACK(account_toggled), NULL); + + gnt_tree_set_col_width(GNT_TREE(accounts.tree), 0, 40); + gnt_tree_set_col_width(GNT_TREE(accounts.tree), 1, 10); + gnt_box_add_widget(GNT_BOX(accounts.window), accounts.tree); + + gnt_box_add_widget(GNT_BOX(accounts.window), gnt_line_new(FALSE)); + + box = gnt_hbox_new(FALSE); + + button = gnt_button_new(_("Add")); + gnt_box_add_widget(GNT_BOX(box), button); + g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(add_account_cb), NULL); + + button = gnt_button_new(_("Modify")); + gnt_box_add_widget(GNT_BOX(box), button); + g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(modify_account_cb), accounts.tree); + + button = gnt_button_new(_("Delete")); + gnt_box_add_widget(GNT_BOX(box), button); + g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(delete_account_cb), accounts.tree); + + gnt_box_add_widget(GNT_BOX(accounts.window), box); + + g_signal_connect(G_OBJECT(accounts.window), "destroy", G_CALLBACK(reset_accounts_win), NULL); + + gnt_widget_show(accounts.window); +} + +static gpointer +gg_accounts_get_handle() +{ + static int handle; + + return &handle; +} + +static void +account_added_callback(GaimAccount *account) +{ + if (accounts.window == NULL) + return; + account_add(account); + gnt_widget_draw(accounts.tree); +} + +static void +account_removed_callback(GaimAccount *account) +{ + if (accounts.window == NULL) + return; + + gnt_tree_remove(GNT_TREE(accounts.tree), account); +} + +void gg_accounts_init() +{ + GList *iter; + + gaim_signal_connect(gaim_accounts_get_handle(), "account-added", + gg_accounts_get_handle(), GAIM_CALLBACK(account_added_callback), + NULL); + gaim_signal_connect(gaim_accounts_get_handle(), "account-removed", + gg_accounts_get_handle(), GAIM_CALLBACK(account_removed_callback), + NULL); + + for (iter = gaim_accounts_get_all(); iter; iter = iter->next) { + if (gaim_account_get_enabled(iter->data, GAIM_GNT_UI)) + break; + } + if (!iter) + gg_accounts_show_all(); +} + +void gg_accounts_uninit() +{ + if (accounts.window) + gnt_widget_destroy(accounts.window); +} + +/* The following uiops stuff are copied from gtkaccount.c */ +typedef struct +{ + GaimAccount *account; + char *username; + char *alias; +} AddUserData; + +static char * +make_info(GaimAccount *account, GaimConnection *gc, const char *remote_user, + const char *id, const char *alias, const char *msg) +{ + if (msg != NULL && *msg == '\0') + msg = NULL; + + return g_strdup_printf(_("%s%s%s%s has made %s his or her buddy%s%s"), + remote_user, + (alias != NULL ? " (" : ""), + (alias != NULL ? alias : ""), + (alias != NULL ? ")" : ""), + (id != NULL + ? id + : (gaim_connection_get_display_name(gc) != NULL + ? gaim_connection_get_display_name(gc) + : gaim_account_get_username(account))), + (msg != NULL ? ": " : "."), + (msg != NULL ? msg : "")); +} + +static void +notify_added(GaimAccount *account, const char *remote_user, + const char *id, const char *alias, + const char *msg) +{ + char *buffer; + GaimConnection *gc; + + gc = gaim_account_get_connection(account); + + buffer = make_info(account, gc, remote_user, id, alias, msg); + + gaim_notify_info(NULL, NULL, buffer, NULL); + + g_free(buffer); +} + +static void +free_add_user_data(AddUserData *data) +{ + g_free(data->username); + + if (data->alias != NULL) + g_free(data->alias); + + g_free(data); +} + +static void +add_user_cb(AddUserData *data) +{ + GaimConnection *gc = gaim_account_get_connection(data->account); + + if (g_list_find(gaim_connections_get_all(), gc)) + { + gaim_blist_request_add_buddy(data->account, data->username, + NULL, data->alias); + } + + free_add_user_data(data); +} + +static void +request_add(GaimAccount *account, const char *remote_user, + const char *id, const char *alias, + const char *msg) +{ + char *buffer; + GaimConnection *gc; + AddUserData *data; + + gc = gaim_account_get_connection(account); + + data = g_new0(AddUserData, 1); + data->account = account; + data->username = g_strdup(remote_user); + data->alias = (alias != NULL ? g_strdup(alias) : NULL); + + buffer = make_info(account, gc, remote_user, id, alias, msg); + gaim_request_action(NULL, NULL, _("Add buddy to your list?"), + buffer, GAIM_DEFAULT_ACTION_NONE, data, 2, + _("Add"), G_CALLBACK(add_user_cb), + _("Cancel"), G_CALLBACK(free_add_user_data)); + g_free(buffer); +} + +static GaimAccountUiOps ui_ops = +{ + .notify_added = notify_added, + .status_changed = NULL, + .request_add = request_add +}; + +GaimAccountUiOps *gg_accounts_get_ui_ops() +{ + return &ui_ops; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/gntaccount.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,59 @@ +/** + * @file gntaccount.h GNT Account API + * @ingroup gntui + * + * gaim + * + * 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 _GNT_ACCOUNT_H +#define _GNT_ACCOUNT_H + +#include "account.h" + +/********************************************************************** + * @name GNT Account API + **********************************************************************/ +/*@{*/ + +/** + * Get the ui-functions. + * + * @return The GaimAccountUiOps structure populated with the appropriate functions. + */ +GaimAccountUiOps *gg_accounts_get_ui_ops(void); + +/** + * Perform necessary initializations. + */ +void gg_accounts_init(void); + +/** + * Perform necessary uninitializations. + */ +void gg_accounts_uninit(void); + +/** + * Show the account-manager dialog. + */ +void gg_accounts_show_all(void); + +/*@}*/ + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/gntblist.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,2204 @@ +/** + * @file gntblist.c GNT BuddyList API + * @ingroup gntui + * + * gaim + * + * 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 + */ +#include <account.h> +#include <blist.h> +#include <notify.h> +#include <request.h> +#include <savedstatuses.h> +#include <server.h> +#include <signal.h> +#include <status.h> +#include <util.h> +#include "debug.h" + +#include "gntgaim.h" +#include "gntbox.h" +#include "gntcombobox.h" +#include "gntentry.h" +#include "gntlabel.h" +#include "gntline.h" +#include "gntmenu.h" +#include "gntmenuitem.h" +#include "gntmenuitemcheck.h" +#include "gnttree.h" +#include "gntutils.h" +#include "gntwindow.h" + +#include "gntblist.h" +#include "gntconv.h" +#include "gntstatus.h" +#include <string.h> + +#define PREF_ROOT "/gaim/gnt/blist" +#define TYPING_TIMEOUT 4000 + +typedef struct +{ + GntWidget *window; + GntWidget *tree; + + GntWidget *tooltip; + GaimBlistNode *tnode; /* Who is the tooltip being displayed for? */ + GList *tagged; /* A list of tagged blistnodes */ + + GntWidget *context; + GaimBlistNode *cnode; + + /* XXX: I am KISSing */ + GntWidget *status; /* Dropdown with the statuses */ + GntWidget *statustext; /* Status message */ + int typing; + + GntWidget *menu; + /* These are the menuitems that get regenerated */ + GntMenuItem *accounts; + GntMenuItem *plugins; +} GGBlist; + +typedef enum +{ + STATUS_PRIMITIVE = 0, + STATUS_SAVED_POPULAR, + STATUS_SAVED_ALL, + STATUS_SAVED_NEW +} StatusType; + +typedef struct +{ + StatusType type; + union + { + GaimStatusPrimitive prim; + GaimSavedStatus *saved; + } u; +} StatusBoxItem; + +GGBlist *ggblist; + +static void add_buddy(GaimBuddy *buddy, GGBlist *ggblist); +static void add_contact(GaimContact *contact, GGBlist *ggblist); +static void add_group(GaimGroup *group, GGBlist *ggblist); +static void add_chat(GaimChat *chat, GGBlist *ggblist); +static void add_node(GaimBlistNode *node, GGBlist *ggblist); +static void draw_tooltip(GGBlist *ggblist); +static gboolean remove_typing_cb(gpointer null); +static void remove_peripherals(GGBlist *ggblist); +static const char * get_display_name(GaimBlistNode *node); +static void savedstatus_changed(GaimSavedStatus *now, GaimSavedStatus *old); +static void blist_show(GaimBuddyList *list); +static void update_buddy_display(GaimBuddy *buddy, GGBlist *ggblist); +static void account_signed_on_cb(void); + +/* Sort functions */ +static int blist_node_compare_text(GaimBlistNode *n1, GaimBlistNode *n2); +static int blist_node_compare_status(GaimBlistNode *n1, GaimBlistNode *n2); +static int blist_node_compare_log(GaimBlistNode *n1, GaimBlistNode *n2); + +static gboolean +is_contact_online(GaimContact *contact) +{ + GaimBlistNode *node; + for (node = ((GaimBlistNode*)contact)->child; node; node = node->next) { + if (GAIM_BUDDY_IS_ONLINE((GaimBuddy*)node)) + return TRUE; + } + return FALSE; +} + +static gboolean +is_group_online(GaimGroup *group) +{ + GaimBlistNode *node; + for (node = ((GaimBlistNode*)group)->child; node; node = node->next) { + if (GAIM_BLIST_NODE_IS_CHAT(node)) + return TRUE; + else if (is_contact_online((GaimContact*)node)) + return TRUE; + } + return FALSE; +} + +static void +new_node(GaimBlistNode *node) +{ +} + +static void add_node(GaimBlistNode *node, GGBlist *ggblist) +{ + if (GAIM_BLIST_NODE_IS_BUDDY(node)) + add_buddy((GaimBuddy*)node, ggblist); + else if (GAIM_BLIST_NODE_IS_CONTACT(node)) + add_contact((GaimContact*)node, ggblist); + else if (GAIM_BLIST_NODE_IS_GROUP(node)) + add_group((GaimGroup*)node, ggblist); + else if (GAIM_BLIST_NODE_IS_CHAT(node)) + add_chat((GaimChat *)node, ggblist); + draw_tooltip(ggblist); +} + +static void +remove_tooltip(GGBlist *ggblist) +{ + gnt_widget_destroy(ggblist->tooltip); + ggblist->tooltip = NULL; + ggblist->tnode = NULL; +} + +static void +node_remove(GaimBuddyList *list, GaimBlistNode *node) +{ + GGBlist *ggblist = list->ui_data; + + if (ggblist == NULL || node->ui_data == NULL) + return; + + gnt_tree_remove(GNT_TREE(ggblist->tree), node); + node->ui_data = NULL; + if (ggblist->tagged) + ggblist->tagged = g_list_remove(ggblist->tagged, node); + + if (GAIM_BLIST_NODE_IS_BUDDY(node)) { + GaimContact *contact = (GaimContact*)node->parent; + if ((!gaim_prefs_get_bool(PREF_ROOT "/showoffline") && !is_contact_online(contact)) || + contact->currentsize < 1) + node_remove(list, (GaimBlistNode*)contact); + } else if (GAIM_BLIST_NODE_IS_CONTACT(node)) { + GaimGroup *group = (GaimGroup*)node->parent; + if ((!gaim_prefs_get_bool(PREF_ROOT "/showoffline") && !is_group_online(group)) || + group->currentsize < 1) + node_remove(list, node->parent); + for (node = node->child; node; node = node->next) + node->ui_data = NULL; + } + + draw_tooltip(ggblist); +} + +static void +node_update(GaimBuddyList *list, GaimBlistNode *node) +{ + /* It really looks like this should never happen ... but it does. + This will at least emit a warning to the log when it + happens, so maybe someone will figure it out. */ + g_return_if_fail(node != NULL); + + if (list->ui_data == NULL) + return; /* XXX: this is probably the place to auto-join chats */ + + if (node->ui_data != NULL) { + gnt_tree_change_text(GNT_TREE(ggblist->tree), node, + 0, get_display_name(node)); + gnt_tree_sort_row(GNT_TREE(ggblist->tree), node); + } + + if (GAIM_BLIST_NODE_IS_BUDDY(node)) { + GaimBuddy *buddy = (GaimBuddy*)node; + if (gaim_account_is_connected(buddy->account) && + (GAIM_BUDDY_IS_ONLINE(buddy) || gaim_prefs_get_bool(PREF_ROOT "/showoffline"))) + add_node((GaimBlistNode*)buddy, list->ui_data); + else + node_remove(gaim_get_blist(), node); + + node_update(list, node->parent); + } else if (GAIM_BLIST_NODE_IS_CHAT(node)) { + add_chat((GaimChat *)node, list->ui_data); + } else if (GAIM_BLIST_NODE_IS_CONTACT(node)) { + GaimContact *contact = (GaimContact*)node; + if ((!gaim_prefs_get_bool(PREF_ROOT "/showoffline") && !is_contact_online(contact)) || + contact->currentsize < 1) + node_remove(gaim_get_blist(), node); + else + add_node(node, list->ui_data); + } else if (GAIM_BLIST_NODE_IS_GROUP(node)) { + GaimGroup *group = (GaimGroup*)node; + if ((!gaim_prefs_get_bool(PREF_ROOT "/showoffline") && !is_group_online(group)) || + group->currentsize < 1) + node_remove(list, node); + } +} + +static void +new_list(GaimBuddyList *list) +{ + if (ggblist) + return; + + ggblist = g_new0(GGBlist, 1); + list->ui_data = ggblist; +} + +static void +add_buddy_cb(void *data, GaimRequestFields *allfields) +{ + const char *username = gaim_request_fields_get_string(allfields, "screenname"); + const char *alias = gaim_request_fields_get_string(allfields, "alias"); + const char *group = gaim_request_fields_get_string(allfields, "group"); + GaimAccount *account = gaim_request_fields_get_account(allfields, "account"); + const char *error = NULL; + GaimGroup *grp; + GaimBuddy *buddy; + + if (!username) + error = _("You must provide a screename for the buddy."); + else if (!group) + error = _("You must provide a group."); + else if (!account) + error = _("You must select an account."); + + if (error) + { + gaim_notify_error(NULL, _("Error"), _("Error adding buddy"), error); + return; + } + + grp = gaim_find_group(group); + if (!grp) + { + grp = gaim_group_new(group); + gaim_blist_add_group(grp, NULL); + } + + buddy = gaim_buddy_new(account, username, alias); + gaim_blist_add_buddy(buddy, NULL, grp, NULL); + gaim_account_add_buddy(account, buddy); +} + +static void +gg_request_add_buddy(GaimAccount *account, const char *username, const char *grp, const char *alias) +{ + GaimRequestFields *fields = gaim_request_fields_new(); + GaimRequestFieldGroup *group = gaim_request_field_group_new(NULL); + GaimRequestField *field; + + gaim_request_fields_add_group(fields, group); + + field = gaim_request_field_string_new("screenname", _("Screen Name"), username, FALSE); + gaim_request_field_group_add_field(group, field); + + field = gaim_request_field_string_new("alias", _("Alias"), alias, FALSE); + gaim_request_field_group_add_field(group, field); + + field = gaim_request_field_string_new("group", _("Group"), grp, FALSE); + gaim_request_field_group_add_field(group, field); + + field = gaim_request_field_account_new("account", _("Account"), NULL); + gaim_request_field_account_set_show_all(field, FALSE); + if (account) + gaim_request_field_account_set_value(field, account); + gaim_request_field_group_add_field(group, field); + + gaim_request_fields(NULL, _("Add Buddy"), NULL, _("Please enter buddy information."), + fields, _("Add"), G_CALLBACK(add_buddy_cb), _("Cancel"), NULL, NULL); +} + +static void +add_chat_cb(void *data, GaimRequestFields *allfields) +{ + GaimAccount *account; + const char *alias, *name, *group; + GaimChat *chat; + GaimGroup *grp; + GHashTable *hash = NULL; + GaimConnection *gc; + + account = gaim_request_fields_get_account(allfields, "account"); + name = gaim_request_fields_get_string(allfields, "name"); + alias = gaim_request_fields_get_string(allfields, "alias"); + group = gaim_request_fields_get_string(allfields, "group"); + + if (!gaim_account_is_connected(account) || !name || !*name) + return; + + if (!group || !*group) + group = _("Chats"); + + gc = gaim_account_get_connection(account); + + if (GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info_defaults != NULL) + hash = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info_defaults(gc, name); + + chat = gaim_chat_new(account, name, hash); + + if (chat != NULL) { + if ((grp = gaim_find_group(group)) == NULL) { + grp = gaim_group_new(group); + gaim_blist_add_group(grp, NULL); + } + gaim_blist_add_chat(chat, grp, NULL); + gaim_blist_alias_chat(chat, alias); + } +} + +static void +gg_request_add_chat(GaimAccount *account, GaimGroup *grp, const char *alias, const char *name) +{ + GaimRequestFields *fields = gaim_request_fields_new(); + GaimRequestFieldGroup *group = gaim_request_field_group_new(NULL); + GaimRequestField *field; + + gaim_request_fields_add_group(fields, group); + + field = gaim_request_field_account_new("account", _("Account"), NULL); + gaim_request_field_account_set_show_all(field, FALSE); + if (account) + gaim_request_field_account_set_value(field, account); + gaim_request_field_group_add_field(group, field); + + field = gaim_request_field_string_new("name", _("Name"), name, FALSE); + gaim_request_field_group_add_field(group, field); + + field = gaim_request_field_string_new("alias", _("Alias"), alias, FALSE); + gaim_request_field_group_add_field(group, field); + + field = gaim_request_field_string_new("group", _("Group"), grp ? grp->name : NULL, FALSE); + gaim_request_field_group_add_field(group, field); + + gaim_request_fields(NULL, _("Add Chat"), NULL, + _("You can edit more information from the context menu later."), + fields, _("Add"), G_CALLBACK(add_chat_cb), _("Cancel"), NULL, NULL); +} + +static void +add_group_cb(gpointer null, const char *group) +{ + GaimGroup *grp; + + if (!group || !*group) + { + gaim_notify_error(NULL, _("Error"), _("Error adding group"), + _("You must give a name for the group to add.")); + return; + } + + grp = gaim_find_group(group); + if (!grp) + { + grp = gaim_group_new(group); + gaim_blist_add_group(grp, NULL); + } + else + { + gaim_notify_error(NULL, _("Error"), _("Error adding group"), + _("A group with the name already exists.")); + } +} + +static void +gg_request_add_group() +{ + gaim_request_input(NULL, _("Add Group"), NULL, _("Enter the name of the group"), + NULL, FALSE, FALSE, NULL, + _("Add"), G_CALLBACK(add_group_cb), _("Cancel"), NULL, NULL); +} + +static GaimBlistUiOps blist_ui_ops = +{ + new_list, + new_node, + blist_show, + node_update, + node_remove, + NULL, + NULL, + .request_add_buddy = gg_request_add_buddy, + .request_add_chat = gg_request_add_chat, + .request_add_group = gg_request_add_group +}; + +static gpointer +gg_blist_get_handle() +{ + static int handle; + + return &handle; +} + +static void +add_group(GaimGroup *group, GGBlist *ggblist) +{ + GaimBlistNode *node = (GaimBlistNode *)group; + if (node->ui_data) + return; + node->ui_data = gnt_tree_add_row_after(GNT_TREE(ggblist->tree), group, + gnt_tree_create_row(GNT_TREE(ggblist->tree), get_display_name(node)), NULL, NULL); +} + +static const char * +get_display_name(GaimBlistNode *node) +{ + static char text[2096]; + char status[8] = " "; + const char *name = NULL; + + if (GAIM_BLIST_NODE_IS_CONTACT(node)) + node = (GaimBlistNode*)gaim_contact_get_priority_buddy((GaimContact*)node); /* XXX: this can return NULL?! */ + + if (node == NULL) + return NULL; + + if (GAIM_BLIST_NODE_IS_BUDDY(node)) + { + GaimBuddy *buddy = (GaimBuddy *)node; + GaimStatusPrimitive prim; + GaimPresence *presence; + GaimStatus *now; + gboolean ascii = gnt_ascii_only(); + + presence = gaim_buddy_get_presence(buddy); + now = gaim_presence_get_active_status(presence); + + prim = gaim_status_type_get_primitive(gaim_status_get_type(now)); + + switch(prim) + { + case GAIM_STATUS_OFFLINE: + strncpy(status, ascii ? "x" : "⊗", sizeof(status) - 1); + break; + case GAIM_STATUS_AVAILABLE: + strncpy(status, ascii ? "o" : "◯", sizeof(status) - 1); + break; + default: + strncpy(status, ascii ? "." : "⊖", sizeof(status) - 1); + break; + } + name = gaim_buddy_get_alias(buddy); + } + else if (GAIM_BLIST_NODE_IS_CHAT(node)) + { + GaimChat *chat = (GaimChat*)node; + name = gaim_chat_get_name(chat); + + strncpy(status, "~", sizeof(status) - 1); + } + else if (GAIM_BLIST_NODE_IS_GROUP(node)) + return ((GaimGroup*)node)->name; + + snprintf(text, sizeof(text) - 1, "%s %s", status, name); + + return text; +} + +static void +add_chat(GaimChat *chat, GGBlist *ggblist) +{ + GaimGroup *group; + GaimBlistNode *node = (GaimBlistNode *)chat; + if (node->ui_data) + return; + if (!gaim_account_is_connected(chat->account)) + return; + + group = gaim_chat_get_group(chat); + add_node((GaimBlistNode*)group, ggblist); + + node->ui_data = gnt_tree_add_row_after(GNT_TREE(ggblist->tree), chat, + gnt_tree_create_row(GNT_TREE(ggblist->tree), get_display_name(node)), + group, NULL); +} + +static void +add_contact(GaimContact *contact, GGBlist *ggblist) +{ + GaimGroup *group; + GaimBlistNode *node = (GaimBlistNode*)contact; + const char *name; + + if (node->ui_data) + return; + + name = get_display_name(node); + if (name == NULL) + return; + + group = (GaimGroup*)node->parent; + add_node((GaimBlistNode*)group, ggblist); + + node->ui_data = gnt_tree_add_row_after(GNT_TREE(ggblist->tree), contact, + gnt_tree_create_row(GNT_TREE(ggblist->tree), name), + group, NULL); + + gnt_tree_set_expanded(GNT_TREE(ggblist->tree), contact, FALSE); +} + +static void +add_buddy(GaimBuddy *buddy, GGBlist *ggblist) +{ + GaimContact *contact; + GaimBlistNode *node = (GaimBlistNode *)buddy; + if (node->ui_data) + return; + + contact = (GaimContact*)node->parent; + if (!contact) /* When a new buddy is added and show-offline is set */ + return; + add_node((GaimBlistNode*)contact, ggblist); + + node->ui_data = gnt_tree_add_row_after(GNT_TREE(ggblist->tree), buddy, + gnt_tree_create_row(GNT_TREE(ggblist->tree), get_display_name(node)), + contact, NULL); + if (gaim_presence_is_idle(gaim_buddy_get_presence(buddy))) { + gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), buddy, GNT_TEXT_FLAG_DIM); + gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), contact, GNT_TEXT_FLAG_DIM); + } else { + gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), buddy, 0); + gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), contact, 0); + } +} + +#if 0 +static void +buddy_signed_on(GaimBuddy *buddy, GGBlist *ggblist) +{ + add_node((GaimBlistNode*)buddy, ggblist); +} + +static void +buddy_signed_off(GaimBuddy *buddy, GGBlist *ggblist) +{ + node_remove(gaim_get_blist(), (GaimBlistNode*)buddy); +} +#endif + +GaimBlistUiOps *gg_blist_get_ui_ops() +{ + return &blist_ui_ops; +} + +static void +selection_activate(GntWidget *widget, GGBlist *ggblist) +{ + GntTree *tree = GNT_TREE(ggblist->tree); + GaimBlistNode *node = gnt_tree_get_selection_data(tree); + + if (!node) + return; + + if (GAIM_BLIST_NODE_IS_CONTACT(node)) + node = (GaimBlistNode*)gaim_contact_get_priority_buddy((GaimContact*)node); + + if (GAIM_BLIST_NODE_IS_BUDDY(node)) + { + GaimBuddy *buddy = (GaimBuddy *)node; + GaimConversation *conv = gaim_conversation_new(GAIM_CONV_TYPE_IM, + gaim_buddy_get_account(buddy), + gaim_buddy_get_name(buddy)); + gg_conversation_set_active(conv); + } + else if (GAIM_BLIST_NODE_IS_CHAT(node)) + { + GaimChat *chat = (GaimChat*)node; + serv_join_chat(chat->account->gc, chat->components); + } +} + +static void +context_menu_callback(GntMenuItem *item, gpointer data) +{ + GaimMenuAction *action = data; + GaimBlistNode *node = ggblist->cnode; + if (action) { + void (*callback)(GaimBlistNode *, gpointer); + callback = (void (*)(GaimBlistNode *, gpointer))action->callback; + if (callback) + callback(action->data, node); + else + return; + } +} + +static void +gnt_append_menu_action(GntMenu *menu, GaimMenuAction *action, gpointer parent) +{ + GList *list; + GntMenuItem *item; + + if (action == NULL) + return; + + item = gnt_menuitem_new(action->label); + if (action->callback) + gnt_menuitem_set_callback(GNT_MENUITEM(item), context_menu_callback, action); + gnt_menu_add_item(menu, GNT_MENUITEM(item)); + + if (action->children) { + GntWidget *sub = gnt_menu_new(GNT_MENU_POPUP); + gnt_menuitem_set_submenu(item, GNT_MENU(sub)); + for (list = action->children; list; list = list->next) + gnt_append_menu_action(GNT_MENU(sub), list->data, action); + } +} + +static void +append_proto_menu(GntMenu *menu, GaimConnection *gc, GaimBlistNode *node) +{ + GList *list; + GaimPluginProtocolInfo *prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(gc->prpl); + + if(!prpl_info || !prpl_info->blist_node_menu) + return; + + for(list = prpl_info->blist_node_menu(node); list; + list = g_list_delete_link(list, list)) + { + GaimMenuAction *act = (GaimMenuAction *) list->data; + act->data = node; + gnt_append_menu_action(menu, act, NULL); + } +} + +static void +add_custom_action(GntMenu *menu, const char *label, GaimCallback callback, + gpointer data) +{ + GaimMenuAction *action = gaim_menu_action_new(label, callback, data, NULL); + gnt_append_menu_action(menu, action, NULL); + g_signal_connect_swapped(G_OBJECT(menu), "destroy", + G_CALLBACK(gaim_menu_action_free), action); +} + +static void +chat_components_edit_ok(GaimChat *chat, GaimRequestFields *allfields) +{ + GList *groups, *fields; + + for (groups = gaim_request_fields_get_groups(allfields); groups; groups = groups->next) { + fields = gaim_request_field_group_get_fields(groups->data); + for (; fields; fields = fields->next) { + GaimRequestField *field = fields->data; + const char *id; + char *val; + + id = gaim_request_field_get_id(field); + if (gaim_request_field_get_type(field) == GAIM_REQUEST_FIELD_INTEGER) + val = g_strdup_printf("%d", gaim_request_field_int_get_value(field)); + else + val = g_strdup(gaim_request_field_string_get_value(field)); + + g_hash_table_replace(chat->components, g_strdup(id), val); /* val should not be free'd */ + } + } +} + +static void +chat_components_edit(GaimChat *chat, GaimBlistNode *selected) +{ + GaimRequestFields *fields = gaim_request_fields_new(); + GaimRequestFieldGroup *group = gaim_request_field_group_new(NULL); + GaimRequestField *field; + GList *parts, *iter; + struct proto_chat_entry *pce; + + gaim_request_fields_add_group(fields, group); + + parts = GAIM_PLUGIN_PROTOCOL_INFO(chat->account->gc->prpl)->chat_info(chat->account->gc); + + for (iter = parts; iter; iter = iter->next) { + pce = iter->data; + if (pce->is_int) { + int val; + const char *str = g_hash_table_lookup(chat->components, pce->identifier); + if (!str || sscanf(str, "%d", &val) != 1) + val = pce->min; + field = gaim_request_field_int_new(pce->identifier, pce->label, val); + } else { + field = gaim_request_field_string_new(pce->identifier, pce->label, + g_hash_table_lookup(chat->components, pce->identifier), FALSE); + } + + gaim_request_field_group_add_field(group, field); + g_free(pce); + } + + g_list_free(parts); + + gaim_request_fields(NULL, _("Edit Chat"), NULL, _("Please Update the necessary fields."), + fields, _("Edit"), G_CALLBACK(chat_components_edit_ok), _("Cancel"), NULL, chat); +} + +static void +autojoin_toggled(GntMenuItem *item, gpointer data) +{ + GaimMenuAction *action = data; + gaim_blist_node_set_bool(action->data, "gnt-autojoin", + gnt_menuitem_check_get_checked(GNT_MENUITEM_CHECK(item))); +} + +static void +create_chat_menu(GntMenu *menu, GaimChat *chat) +{ + GaimMenuAction *action = gaim_menu_action_new(_("Auto-join"), NULL, chat, NULL); + GntMenuItem *check = gnt_menuitem_check_new(action->label); + gnt_menuitem_check_set_checked(GNT_MENUITEM_CHECK(check), + gaim_blist_node_get_bool((GaimBlistNode*)chat, "gnt-autojoin")); + gnt_menu_add_item(menu, check); + gnt_menuitem_set_callback(check, autojoin_toggled, action); + g_signal_connect_swapped(G_OBJECT(menu), "destroy", + G_CALLBACK(gaim_menu_action_free), action); + + add_custom_action(menu, _("Edit Settings"), (GaimCallback)chat_components_edit, chat); +} + +static void +gg_add_buddy(GaimGroup *grp, GaimBlistNode *selected) +{ + gaim_blist_request_add_buddy(NULL, NULL, grp ? grp->name : NULL, NULL); +} + +static void +gg_add_group(GaimGroup *grp, GaimBlistNode *selected) +{ + gaim_blist_request_add_group(); +} + +static void +gg_add_chat(GaimGroup *grp, GaimBlistNode *selected) +{ + gaim_blist_request_add_chat(NULL, grp, NULL, NULL); +} + +static void +create_group_menu(GntMenu *menu, GaimGroup *group) +{ + add_custom_action(menu, _("Add Buddy"), + GAIM_CALLBACK(gg_add_buddy), group); + add_custom_action(menu, _("Add Chat"), + GAIM_CALLBACK(gg_add_chat), group); + add_custom_action(menu, _("Add Group"), + GAIM_CALLBACK(gg_add_group), group); +} + +static void +gg_blist_get_buddy_info_cb(GaimBuddy *buddy, GaimBlistNode *selected) +{ + serv_get_info(buddy->account->gc, gaim_buddy_get_name(buddy)); +} + +static void +create_buddy_menu(GntMenu *menu, GaimBuddy *buddy) +{ + GaimPluginProtocolInfo *prpl_info; + + prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(buddy->account->gc->prpl); + if (prpl_info && prpl_info->get_info) + { + add_custom_action(menu, _("Get Info"), + GAIM_CALLBACK(gg_blist_get_buddy_info_cb), buddy); + } + +#if 0 + add_custom_action(tree, _("Add Buddy Pounce"), + GAIM_CALLBACK(gg_blist_add_buddy_pounce_cb)), buddy); + + if (prpl_info && prpl_info->send_file) + { + if (!prpl_info->can_receive_file || + prpl_info->can_receive_file(buddy->account->gc, buddy->name)) + add_custom_action(tree, _("Send File"), + GAIM_CALLBACK(gg_blist_show_file_cb)), buddy); + } + + add_custom_action(tree, _("View Log"), + GAIM_CALLBACK(gg_blist_view_log_cb)), buddy); +#endif + + /* Protocol actions */ + append_proto_menu(menu, + gaim_account_get_connection(gaim_buddy_get_account(buddy)), + (GaimBlistNode*)buddy); +} + +static void +append_extended_menu(GntMenu *menu, GaimBlistNode *node) +{ + GList *iter; + + for (iter = gaim_blist_node_get_extended_menu(node); + iter; iter = g_list_delete_link(iter, iter)) + { + gnt_append_menu_action(menu, iter->data, NULL); + } +} + +/* Xerox'd from gtkdialogs.c:gaim_gtkdialogs_remove_contact_cb */ +static void +remove_contact(GaimContact *contact) +{ + GaimBlistNode *bnode, *cnode; + GaimGroup *group; + + cnode = (GaimBlistNode *)contact; + group = (GaimGroup*)cnode->parent; + for (bnode = cnode->child; bnode; bnode = bnode->next) { + GaimBuddy *buddy = (GaimBuddy*)bnode; + if (gaim_account_is_connected(buddy->account)) + gaim_account_remove_buddy(buddy->account, buddy, group); + } + gaim_blist_remove_contact(contact); +} + +static void +rename_blist_node(GaimBlistNode *node, const char *newname) +{ + const char *name = newname; + if (name && !*name) + name = NULL; + + if (GAIM_BLIST_NODE_IS_CONTACT(node)) { + GaimContact *contact = (GaimContact*)node; + GaimBuddy *buddy = gaim_contact_get_priority_buddy(contact); + gaim_blist_alias_contact(contact, name); + gaim_blist_alias_buddy(buddy, name); + serv_alias_buddy(buddy); + } else if (GAIM_BLIST_NODE_IS_BUDDY(node)) { + gaim_blist_alias_buddy((GaimBuddy*)node, name); + serv_alias_buddy((GaimBuddy*)node); + } else if (GAIM_BLIST_NODE_IS_CHAT(node)) + gaim_blist_alias_chat((GaimChat*)node, name); + else if (GAIM_BLIST_NODE_IS_GROUP(node) && (name != NULL)) + gaim_blist_rename_group((GaimGroup*)node, name); + else + g_return_if_reached(); +} + +static void +gg_blist_rename_node_cb(GaimBlistNode *node, GaimBlistNode *selected) +{ + const char *name = NULL; + char *prompt; + + if (GAIM_BLIST_NODE_IS_CONTACT(node)) + name = gaim_contact_get_alias((GaimContact*)node); + else if (GAIM_BLIST_NODE_IS_BUDDY(node)) + name = gaim_buddy_get_contact_alias((GaimBuddy*)node); + else if (GAIM_BLIST_NODE_IS_CHAT(node)) + name = gaim_chat_get_name((GaimChat*)node); + else if (GAIM_BLIST_NODE_IS_GROUP(node)) + name = ((GaimGroup*)node)->name; + else + g_return_if_reached(); + + prompt = g_strdup_printf(_("Please enter the new name for %s"), name); + + gaim_request_input(node, _("Rename"), prompt, _("Enter empty string to reset the name."), + name, FALSE, FALSE, NULL, _("Rename"), G_CALLBACK(rename_blist_node), + _("Cancel"), NULL, node); + + g_free(prompt); +} + +/* Xeroxed from gtkdialogs.c:gaim_gtkdialogs_remove_group_cb*/ +static void +remove_group(GaimGroup *group) +{ + GaimBlistNode *cnode, *bnode; + + cnode = ((GaimBlistNode*)group)->child; + + while (cnode) { + if (GAIM_BLIST_NODE_IS_CONTACT(cnode)) { + bnode = cnode->child; + cnode = cnode->next; + while (bnode) { + GaimBuddy *buddy; + if (GAIM_BLIST_NODE_IS_BUDDY(bnode)) { + buddy = (GaimBuddy*)bnode; + bnode = bnode->next; + if (gaim_account_is_connected(buddy->account)) { + gaim_account_remove_buddy(buddy->account, buddy, group); + gaim_blist_remove_buddy(buddy); + } + } else { + bnode = bnode->next; + } + } + } else if (GAIM_BLIST_NODE_IS_CHAT(cnode)) { + GaimChat *chat = (GaimChat *)cnode; + cnode = cnode->next; + if (gaim_account_is_connected(chat->account)) + gaim_blist_remove_chat(chat); + } else { + cnode = cnode->next; + } + } + + gaim_blist_remove_group(group); +} + +static void +gg_blist_remove_node(GaimBlistNode *node) +{ + if (GAIM_BLIST_NODE_IS_CONTACT(node)) { + remove_contact((GaimContact*)node); + } else if (GAIM_BLIST_NODE_IS_BUDDY(node)) { + GaimBuddy *buddy = (GaimBuddy*)node; + GaimGroup *group = gaim_buddy_get_group(buddy); + gaim_account_remove_buddy(gaim_buddy_get_account(buddy), buddy, group); + gaim_blist_remove_buddy(buddy); + } else if (GAIM_BLIST_NODE_IS_CHAT(node)) { + gaim_blist_remove_chat((GaimChat*)node); + } else if (GAIM_BLIST_NODE_IS_GROUP(node)) { + remove_group((GaimGroup*)node); + } +} + +static void +gg_blist_remove_node_cb(GaimBlistNode *node, GaimBlistNode *selected) +{ + char *primary; + const char *name, *sec = NULL; + + /* XXX: could be a contact */ + if (GAIM_BLIST_NODE_IS_CONTACT(node)) { + GaimContact *c = (GaimContact*)node; + name = gaim_contact_get_alias(c); + if (c->totalsize > 1) + sec = _("Removing this contact will also remove all the buddies in the contact"); + } else if (GAIM_BLIST_NODE_IS_BUDDY(node)) + name = gaim_buddy_get_name((GaimBuddy*)node); + else if (GAIM_BLIST_NODE_IS_CHAT(node)) + name = gaim_chat_get_name((GaimChat*)node); + else if (GAIM_BLIST_NODE_IS_GROUP(node)) + { + name = ((GaimGroup*)node)->name; + sec = _("Removing this group will also remove all the buddies in the group"); + } + else + return; + + primary = g_strdup_printf(_("Are you sure you want to remove %s?"), name); + + /* XXX: anything to do with the returned ui-handle? */ + gaim_request_action(node, _("Confirm Remove"), + primary, sec, + 1, node, 2, + _("Remove"), gg_blist_remove_node, + _("Cancel"), NULL); + g_free(primary); +} + +static void +gg_blist_toggle_tag_buddy(GaimBlistNode *node) +{ + GList *iter; + if (node == NULL) + return; + if (GAIM_BLIST_NODE_IS_CHAT(node) || GAIM_BLIST_NODE_IS_GROUP(node)) + return; + if (ggblist->tagged && (iter = g_list_find(ggblist->tagged, node)) != NULL) { + ggblist->tagged = g_list_delete_link(ggblist->tagged, iter); + } else { + ggblist->tagged = g_list_prepend(ggblist->tagged, node); + } + if (GAIM_BLIST_NODE_IS_CONTACT(node)) + node = (GaimBlistNode*)gaim_contact_get_priority_buddy((GaimContact*)node); + update_buddy_display((GaimBuddy*)node, ggblist); +} + +static void +gg_blist_place_tagged(GaimBlistNode *target) +{ + GaimGroup *tg = NULL; + GaimContact *tc = NULL; + + if (target == NULL) + return; + + /* This target resolution probably needs more clarification; for + * example, if I tag a buddy in a contact, then place on + * another buddy in the same contact, I probably intend to + * place the tagged buddy immediately after (before?) the + * target buddy -- this will simply move the tagged buddy + * within the same contact without reference to position. */ + if (GAIM_BLIST_NODE_IS_GROUP(target)) + tg = (GaimGroup*)target; + else if (GAIM_BLIST_NODE_IS_CONTACT(target)) + tc = (GaimContact*)target; + else /* Buddy or Chat */ + tc = (GaimContact*)target->parent; + + if (ggblist->tagged) { + GList *list = ggblist->tagged; + ggblist->tagged = NULL; + + while (list) { + GaimBlistNode *node = list->data; + list = g_list_delete_link(list, list); + if (tg) { + if (GAIM_BLIST_NODE_IS_CONTACT(node)) + gaim_blist_add_contact((GaimContact*)node, tg, NULL); + else + gaim_blist_add_buddy((GaimBuddy*)node, NULL, tg, NULL); + } else { + if (GAIM_BLIST_NODE_IS_BUDDY(node)) + gaim_blist_add_buddy((GaimBuddy*)node, tc, + gaim_buddy_get_group(gaim_contact_get_priority_buddy(tc)), NULL); + else if (GAIM_BLIST_NODE_IS_CONTACT(node)) + gaim_blist_merge_contact((GaimContact*)node, target); + } + } + } +} + +static void +context_menu_destroyed(GntWidget *widget, GGBlist *ggblist) +{ + ggblist->context = NULL; +} + +static void +draw_context_menu(GGBlist *ggblist) +{ + GaimBlistNode *node = NULL; + GntWidget *context = NULL; + GntTree *tree = NULL; + int x, y, top, width; + char *title = NULL; + + tree = GNT_TREE(ggblist->tree); + + node = gnt_tree_get_selection_data(tree); + + if (ggblist->tooltip) + remove_tooltip(ggblist); + + ggblist->cnode = node; + + ggblist->context = context = gnt_menu_new(GNT_MENU_POPUP); + g_signal_connect(G_OBJECT(context), "destroy", G_CALLBACK(context_menu_destroyed), ggblist); + + if (!node) { + create_group_menu(GNT_MENU(context), NULL); + title = g_strdup(_("Buddy List")); + } else if (GAIM_BLIST_NODE_IS_CONTACT(node)) { + create_buddy_menu(GNT_MENU(context), + gaim_contact_get_priority_buddy((GaimContact*)node)); + title = g_strdup(gaim_contact_get_alias((GaimContact*)node)); + } else if (GAIM_BLIST_NODE_IS_BUDDY(node)) { + GaimBuddy *buddy = (GaimBuddy *)node; + create_buddy_menu(GNT_MENU(context), buddy); + title = g_strdup(gaim_buddy_get_name(buddy)); + } else if (GAIM_BLIST_NODE_IS_CHAT(node)) { + GaimChat *chat = (GaimChat*)node; + create_chat_menu(GNT_MENU(context), chat); + title = g_strdup(gaim_chat_get_name(chat)); + } else if (GAIM_BLIST_NODE_IS_GROUP(node)) { + GaimGroup *group = (GaimGroup *)node; + create_group_menu(GNT_MENU(context), group); + title = g_strdup(group->name); + } + + append_extended_menu(GNT_MENU(context), node); + + /* These are common for everything */ + if (node) { + add_custom_action(GNT_MENU(context), _("Rename"), + GAIM_CALLBACK(gg_blist_rename_node_cb), node); + add_custom_action(GNT_MENU(context), _("Remove"), + GAIM_CALLBACK(gg_blist_remove_node_cb), node); + + if (ggblist->tagged && (GAIM_BLIST_NODE_IS_CONTACT(node) + || GAIM_BLIST_NODE_IS_GROUP(node))) { + add_custom_action(GNT_MENU(context), _("Place tagged"), + GAIM_CALLBACK(gg_blist_place_tagged), node); + } + + if (GAIM_BLIST_NODE_IS_BUDDY(node) || GAIM_BLIST_NODE_IS_CONTACT(node)) { + add_custom_action(GNT_MENU(context), _("Toggle Tag"), + GAIM_CALLBACK(gg_blist_toggle_tag_buddy), node); + } + } + + /* Set the position for the popup */ + gnt_widget_get_position(GNT_WIDGET(tree), &x, &y); + gnt_widget_get_size(GNT_WIDGET(tree), &width, NULL); + top = gnt_tree_get_selection_visible_line(tree); + + x += width; + y += top - 1; + + gnt_widget_set_position(context, x, y); + gnt_screen_menu_show(GNT_MENU(context)); + g_free(title); +} + +static void +tooltip_for_buddy(GaimBuddy *buddy, GString *str) +{ + GaimPlugin *prpl; + GaimPluginProtocolInfo *prpl_info; + GaimAccount *account; + GaimNotifyUserInfo *user_info; + const char *alias = gaim_buddy_get_alias(buddy); + char *tmp, *strip; + + user_info = gaim_notify_user_info_new(); + + account = gaim_buddy_get_account(buddy); + + if (g_utf8_collate(gaim_buddy_get_name(buddy), alias)) + gaim_notify_user_info_add_pair(user_info, _("Nickname"), alias); + + tmp = g_strdup_printf("%s (%s)", + gaim_account_get_username(account), + gaim_account_get_protocol_name(account)); + gaim_notify_user_info_add_pair(user_info, _("Account"), tmp); + g_free(tmp); + + prpl = gaim_find_prpl(gaim_account_get_protocol_id(account)); + prpl_info = GAIM_PLUGIN_PROTOCOL_INFO(prpl); + if (prpl_info && prpl_info->tooltip_text) { + prpl_info->tooltip_text(buddy, user_info, TRUE); + } + + if (gaim_prefs_get_bool("/gaim/gnt/blist/idletime")) { + GaimPresence *pre = gaim_buddy_get_presence(buddy); + if (gaim_presence_is_idle(pre)) { + time_t idle = gaim_presence_get_idle_time(pre); + if (idle > 0) { + char *st = gaim_str_seconds_to_string(time(NULL) - idle); + gaim_notify_user_info_add_pair(user_info, _("Idle"), st); + g_free(st); + } + } + } + + tmp = gaim_notify_user_info_get_text_with_newline(user_info, "<BR>"); + gaim_notify_user_info_destroy(user_info); + + strip = gaim_markup_strip_html(tmp); + g_string_append(str, strip); + g_free(strip); + g_free(tmp); +} + +static GString* +make_sure_text_fits(GString *string) +{ + int maxw = getmaxx(stdscr) - 3; + char *str = gnt_util_onscreen_fit_string(string->str, maxw); + string = g_string_assign(string, str); + g_free(str); + return string; +} + +static gboolean +draw_tooltip_real(GGBlist *ggblist) +{ + GaimBlistNode *node; + int x, y, top, width, w, h; + GString *str; + GntTree *tree; + GntWidget *widget, *box, *tv; + char *title = NULL; + int lastseen = 0; + + widget = ggblist->tree; + tree = GNT_TREE(widget); + + if (!gnt_widget_has_focus(ggblist->tree) || + (ggblist->context && !GNT_WIDGET_IS_FLAG_SET(ggblist->context, GNT_WIDGET_INVISIBLE))) + return FALSE; + + if (ggblist->tooltip) + { + /* XXX: Once we can properly redraw on expose events, this can be removed at the end + * to avoid the blinking*/ + remove_tooltip(ggblist); + } + + node = gnt_tree_get_selection_data(tree); + if (!node) + return FALSE; + + str = g_string_new(""); + + if (GAIM_BLIST_NODE_IS_CONTACT(node)) { + GaimBuddy *pr = gaim_contact_get_priority_buddy((GaimContact*)node); + gboolean offline = !GAIM_BUDDY_IS_ONLINE(pr); + gboolean showoffline = gaim_prefs_get_bool(PREF_ROOT "/showoffline"); + const char *name = gaim_buddy_get_name(pr); + + title = g_strdup(name); + tooltip_for_buddy(pr, str); + for (node = node->child; node; node = node->next) { + GaimBuddy *buddy = (GaimBuddy*)node; + if (offline) { + int value = gaim_blist_node_get_int(node, "last_seen"); + if (value > lastseen) + lastseen = value; + } + if (node == (GaimBlistNode*)pr) + continue; + if (!gaim_account_is_connected(buddy->account)) + continue; + if (!showoffline && !GAIM_BUDDY_IS_ONLINE(buddy)) + continue; + str = g_string_append(str, "\n----------\n"); + tooltip_for_buddy(buddy, str); + } + } else if (GAIM_BLIST_NODE_IS_BUDDY(node)) { + GaimBuddy *buddy = (GaimBuddy *)node; + tooltip_for_buddy(buddy, str); + title = g_strdup(gaim_buddy_get_name(buddy)); + if (!GAIM_BUDDY_IS_ONLINE((GaimBuddy*)node)) + lastseen = gaim_blist_node_get_int(node, "last_seen"); + } else if (GAIM_BLIST_NODE_IS_GROUP(node)) { + GaimGroup *group = (GaimGroup *)node; + + g_string_append_printf(str, _("Online: %d\nTotal: %d"), + gaim_blist_get_group_online_count(group), + gaim_blist_get_group_size(group, FALSE)); + + title = g_strdup(group->name); + } else if (GAIM_BLIST_NODE_IS_CHAT(node)) { + GaimChat *chat = (GaimChat *)node; + GaimAccount *account = chat->account; + + g_string_append_printf(str, _("Account: %s (%s)"), + gaim_account_get_username(account), + gaim_account_get_protocol_name(account)); + + title = g_strdup(gaim_chat_get_name(chat)); + } else { + g_string_free(str, TRUE); + return FALSE; + } + + if (lastseen > 0) { + char *tmp = gaim_str_seconds_to_string(time(NULL) - lastseen); + g_string_append_printf(str, _("\nLast Seen: %s ago"), tmp); + g_free(tmp); + } + + gnt_widget_get_position(widget, &x, &y); + gnt_widget_get_size(widget, &width, NULL); + top = gnt_tree_get_selection_visible_line(tree); + + x += width; + y += top - 1; + + box = gnt_box_new(FALSE, FALSE); + gnt_box_set_toplevel(GNT_BOX(box), TRUE); + GNT_WIDGET_SET_FLAGS(box, GNT_WIDGET_NO_SHADOW); + gnt_box_set_title(GNT_BOX(box), title); + + str = make_sure_text_fits(str); + gnt_util_get_text_bound(str->str, &w, &h); + h = MAX(2, h); + tv = gnt_text_view_new(); + gnt_widget_set_size(tv, w + 1, h); + gnt_box_add_widget(GNT_BOX(box), tv); + + gnt_widget_set_position(box, x, y); + GNT_WIDGET_UNSET_FLAGS(box, GNT_WIDGET_CAN_TAKE_FOCUS); + GNT_WIDGET_SET_FLAGS(box, GNT_WIDGET_TRANSIENT); + gnt_widget_draw(box); + + gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(tv), str->str, GNT_TEXT_FLAG_NORMAL); + gnt_text_view_scroll(GNT_TEXT_VIEW(tv), 0); + + g_free(title); + g_string_free(str, TRUE); + ggblist->tooltip = box; + ggblist->tnode = node; + + gnt_widget_set_name(ggblist->tooltip, "tooltip"); + return FALSE; +} + +static void +draw_tooltip(GGBlist *ggblist) +{ + /* When an account has signed off, it removes one buddy at a time. + * Drawing the tooltip after removing each buddy is expensive. On + * top of that, if the selected buddy belongs to the disconnected + * account, then retreiving the tooltip for that causes crash. So + * let's make sure we wait for all the buddies to be removed first.*/ + int id = g_timeout_add(0, (GSourceFunc)draw_tooltip_real, ggblist); + g_object_set_data_full(G_OBJECT(ggblist->window), "draw_tooltip_calback", + GINT_TO_POINTER(id), (GDestroyNotify)g_source_remove); +} + +static void +selection_changed(GntWidget *widget, gpointer old, gpointer current, GGBlist *ggblist) +{ + draw_tooltip(ggblist); +} + +static gboolean +context_menu(GntWidget *widget, GGBlist *ggblist) +{ + draw_context_menu(ggblist); + return TRUE; +} + +static gboolean +key_pressed(GntWidget *widget, const char *text, GGBlist *ggblist) +{ + if (text[0] == 27 && text[1] == 0) { + /* Escape was pressed */ + remove_peripherals(ggblist); + } else if (strcmp(text, GNT_KEY_CTRL_O) == 0) { + gaim_prefs_set_bool(PREF_ROOT "/showoffline", + !gaim_prefs_get_bool(PREF_ROOT "/showoffline")); + } else if (GNT_TREE(ggblist->tree)->search == NULL) { + if (strcmp(text, "t") == 0) { + gg_blist_toggle_tag_buddy(gnt_tree_get_selection_data(GNT_TREE(ggblist->tree))); + gnt_bindable_perform_action_named(GNT_BINDABLE(ggblist->tree), "move-down"); + } else if (strcmp(text, "a") == 0) { + gg_blist_place_tagged(gnt_tree_get_selection_data(GNT_TREE(ggblist->tree))); + } else + return FALSE; + } else + return FALSE; + + return TRUE; +} + +static void +update_buddy_display(GaimBuddy *buddy, GGBlist *ggblist) +{ + GaimContact *contact; + GntTextFormatFlags bflag = 0, cflag = 0; + + contact = gaim_buddy_get_contact(buddy); + + gnt_tree_change_text(GNT_TREE(ggblist->tree), buddy, 0, get_display_name((GaimBlistNode*)buddy)); + gnt_tree_change_text(GNT_TREE(ggblist->tree), contact, 0, get_display_name((GaimBlistNode*)contact)); + + if (ggblist->tagged && g_list_find(ggblist->tagged, buddy)) + bflag |= GNT_TEXT_FLAG_BOLD; + if (ggblist->tagged && g_list_find(ggblist->tagged, contact)) + cflag |= GNT_TEXT_FLAG_BOLD; + + if (ggblist->tnode == (GaimBlistNode*)buddy) + draw_tooltip(ggblist); + + if (gaim_presence_is_idle(gaim_buddy_get_presence(buddy))) { + gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), buddy, bflag | GNT_TEXT_FLAG_DIM); + gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), contact, cflag | GNT_TEXT_FLAG_DIM); + } else { + gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), buddy, bflag); + gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), contact, cflag); + } +} + +static void +buddy_status_changed(GaimBuddy *buddy, GaimStatus *old, GaimStatus *now, GGBlist *ggblist) +{ + update_buddy_display(buddy, ggblist); +} + +static void +buddy_idle_changed(GaimBuddy *buddy, int old, int new, GGBlist *ggblist) +{ + update_buddy_display(buddy, ggblist); +} + +static void +remove_peripherals(GGBlist *ggblist) +{ + if (ggblist->tooltip) + remove_tooltip(ggblist); + else if (ggblist->context) + gnt_widget_destroy(ggblist->context); +} + +static void +size_changed_cb(GntWidget *w, int wi, int h) +{ + int width, height; + gnt_widget_get_size(w, &width, &height); + gaim_prefs_set_int(PREF_ROOT "/size/width", width); + gaim_prefs_set_int(PREF_ROOT "/size/height", height); +} + +static void +save_position_cb(GntWidget *w, int x, int y) +{ + gaim_prefs_set_int(PREF_ROOT "/position/x", x); + gaim_prefs_set_int(PREF_ROOT "/position/y", y); +} + +static void +reset_blist_window(GntWidget *window, gpointer null) +{ + GaimBlistNode *node; + gaim_signals_disconnect_by_handle(gg_blist_get_handle()); + gaim_get_blist()->ui_data = NULL; + + node = gaim_blist_get_root(); + while (node) { + node->ui_data = NULL; + node = gaim_blist_node_next(node, TRUE); + } + + if (ggblist->typing) + g_source_remove(ggblist->typing); + remove_peripherals(ggblist); + if (ggblist->tagged) + g_list_free(ggblist->tagged); + g_free(ggblist); + ggblist = NULL; +} + +static void +populate_buddylist() +{ + GaimBlistNode *node; + GaimBuddyList *list; + + if (strcmp(gaim_prefs_get_string(PREF_ROOT "/sort_type"), "text") == 0) { + gnt_tree_set_compare_func(GNT_TREE(ggblist->tree), + (GCompareFunc)blist_node_compare_text); + } else if (strcmp(gaim_prefs_get_string(PREF_ROOT "/sort_type"), "status") == 0) { + gnt_tree_set_compare_func(GNT_TREE(ggblist->tree), + (GCompareFunc)blist_node_compare_status); + } else if (strcmp(gaim_prefs_get_string(PREF_ROOT "/sort_type"), "log") == 0) { + gnt_tree_set_compare_func(GNT_TREE(ggblist->tree), + (GCompareFunc)blist_node_compare_log); + } + + list = gaim_get_blist(); + node = gaim_blist_get_root(); + while (node) + { + node_update(list, node); + node = gaim_blist_node_next(node, FALSE); + } +} + +static void +destroy_status_list(GList *list) +{ + g_list_foreach(list, (GFunc)g_free, NULL); + g_list_free(list); +} + +static void +populate_status_dropdown() +{ + int i; + GList *iter; + GList *items = NULL; + StatusBoxItem *item = NULL; + + /* First the primitives */ + GaimStatusPrimitive prims[] = {GAIM_STATUS_AVAILABLE, GAIM_STATUS_AWAY, + GAIM_STATUS_INVISIBLE, GAIM_STATUS_OFFLINE, GAIM_STATUS_UNSET}; + + gnt_combo_box_remove_all(GNT_COMBO_BOX(ggblist->status)); + + for (i = 0; prims[i] != GAIM_STATUS_UNSET; i++) + { + item = g_new0(StatusBoxItem, 1); + item->type = STATUS_PRIMITIVE; + item->u.prim = prims[i]; + items = g_list_prepend(items, item); + gnt_combo_box_add_data(GNT_COMBO_BOX(ggblist->status), item, + gaim_primitive_get_name_from_type(prims[i])); + } + + /* Now the popular statuses */ + for (iter = gaim_savedstatuses_get_popular(6); iter; iter = iter->next) + { + item = g_new0(StatusBoxItem, 1); + item->type = STATUS_SAVED_POPULAR; + item->u.saved = iter->data; + items = g_list_prepend(items, item); + gnt_combo_box_add_data(GNT_COMBO_BOX(ggblist->status), item, + gaim_savedstatus_get_title(iter->data)); + } + + /* New savedstatus */ + item = g_new0(StatusBoxItem, 1); + item->type = STATUS_SAVED_NEW; + items = g_list_prepend(items, item); + gnt_combo_box_add_data(GNT_COMBO_BOX(ggblist->status), item, + _("New...")); + + /* More savedstatuses */ + item = g_new0(StatusBoxItem, 1); + item->type = STATUS_SAVED_ALL; + items = g_list_prepend(items, item); + gnt_combo_box_add_data(GNT_COMBO_BOX(ggblist->status), item, + _("Saved...")); + + /* The keys for the combobox are created here, and never used + * anywhere else. So make sure the keys are freed when the widget + * is destroyed. */ + g_object_set_data_full(G_OBJECT(ggblist->status), "list of statuses", + items, (GDestroyNotify)destroy_status_list); +} + +static void +redraw_blist(const char *name, GaimPrefType type, gconstpointer val, gpointer data) +{ + GaimBlistNode *node, *sel; + if (ggblist == NULL || ggblist->window == NULL) + return; + + sel = gnt_tree_get_selection_data(GNT_TREE(ggblist->tree)); + gnt_tree_remove_all(GNT_TREE(ggblist->tree)); + node = gaim_blist_get_root(); + for (; node; node = gaim_blist_node_next(node, TRUE)) + node->ui_data = NULL; + populate_buddylist(); + gnt_tree_set_selected(GNT_TREE(ggblist->tree), sel); + draw_tooltip(ggblist); +} + +void gg_blist_init() +{ + gaim_prefs_add_none(PREF_ROOT); + gaim_prefs_add_none(PREF_ROOT "/size"); + gaim_prefs_add_int(PREF_ROOT "/size/width", 20); + gaim_prefs_add_int(PREF_ROOT "/size/height", 17); + gaim_prefs_add_none(PREF_ROOT "/position"); + gaim_prefs_add_int(PREF_ROOT "/position/x", 0); + gaim_prefs_add_int(PREF_ROOT "/position/y", 0); + gaim_prefs_add_bool(PREF_ROOT "/idletime", TRUE); + gaim_prefs_add_bool(PREF_ROOT "/showoffline", FALSE); + gaim_prefs_add_string(PREF_ROOT "/sort_type", "text"); + + gaim_prefs_connect_callback(gg_blist_get_handle(), + PREF_ROOT "/showoffline", redraw_blist, NULL); + gaim_prefs_connect_callback(gg_blist_get_handle(), + PREF_ROOT "/sort_type", redraw_blist, NULL); + + gaim_signal_connect(gaim_connections_get_handle(), "signed-on", gaim_blist_get_handle(), + G_CALLBACK(account_signed_on_cb), NULL); + return; +} + +static gboolean +remove_typing_cb(gpointer null) +{ + GaimSavedStatus *current; + const char *message, *newmessage; + GaimStatusPrimitive prim, newprim; + StatusBoxItem *item; + + current = gaim_savedstatus_get_current(); + message = gaim_savedstatus_get_message(current); + prim = gaim_savedstatus_get_type(current); + + newmessage = gnt_entry_get_text(GNT_ENTRY(ggblist->statustext)); + item = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(ggblist->status)); + g_return_val_if_fail(item->type == STATUS_PRIMITIVE, FALSE); + newprim = item->u.prim; + + if (newprim != prim || ((message && !newmessage) || + (!message && newmessage) || + (message && newmessage && g_utf8_collate(message, newmessage) != 0))) + { + GaimSavedStatus *status = gaim_savedstatus_find_transient_by_type_and_message(newprim, newmessage); + /* Holy Crap! That's a LAWNG function name */ + if (status == NULL) + { + status = gaim_savedstatus_new(NULL, newprim); + gaim_savedstatus_set_message(status, newmessage); + } + + gaim_savedstatus_activate(status); + } + + gnt_box_give_focus_to_child(GNT_BOX(ggblist->window), ggblist->tree); + if (ggblist->typing) + g_source_remove(ggblist->typing); + ggblist->typing = 0; + return FALSE; +} + +static void +status_selection_changed(GntComboBox *box, StatusBoxItem *old, StatusBoxItem *now, gpointer null) +{ + gnt_entry_set_text(GNT_ENTRY(ggblist->statustext), NULL); + if (now->type == STATUS_SAVED_POPULAR) + { + /* Set the status immediately */ + gaim_savedstatus_activate(now->u.saved); + } + else if (now->type == STATUS_PRIMITIVE) + { + /* Move the focus to the entry box */ + /* XXX: Make sure the selected status can have a message */ + gnt_box_move_focus(GNT_BOX(ggblist->window), 1); + ggblist->typing = g_timeout_add(TYPING_TIMEOUT, (GSourceFunc)remove_typing_cb, NULL); + } + else if (now->type == STATUS_SAVED_ALL) + { + /* Restore the selection to reflect current status. */ + savedstatus_changed(gaim_savedstatus_get_current(), NULL); + gnt_box_give_focus_to_child(GNT_BOX(ggblist->window), ggblist->tree); + gg_savedstatus_show_all(); + } + else if (now->type == STATUS_SAVED_NEW) + { + savedstatus_changed(gaim_savedstatus_get_current(), NULL); + gnt_box_give_focus_to_child(GNT_BOX(ggblist->window), ggblist->tree); + gg_savedstatus_edit(NULL); + } + else + g_return_if_reached(); +} + +static gboolean +status_text_changed(GntEntry *entry, const char *text, gpointer null) +{ + if ((text[0] == 27 || (text[0] == '\t' && text[1] == '\0')) && ggblist->typing == 0) + return FALSE; + + if (ggblist->typing) + g_source_remove(ggblist->typing); + ggblist->typing = 0; + + if (text[0] == '\r' && text[1] == 0) + { + /* Set the status only after you press 'Enter' */ + remove_typing_cb(NULL); + return TRUE; + } + + ggblist->typing = g_timeout_add(TYPING_TIMEOUT, (GSourceFunc)remove_typing_cb, NULL); + return FALSE; +} + +static void +savedstatus_changed(GaimSavedStatus *now, GaimSavedStatus *old) +{ + GList *list; + GaimStatusPrimitive prim; + const char *message; + + if (!ggblist) + return; + + /* Block the signals we don't want to emit */ + g_signal_handlers_block_matched(ggblist->status, G_SIGNAL_MATCH_FUNC, + 0, 0, NULL, status_selection_changed, NULL); + g_signal_handlers_block_matched(ggblist->statustext, G_SIGNAL_MATCH_FUNC, + 0, 0, NULL, status_text_changed, NULL); + + prim = gaim_savedstatus_get_type(now); + message = gaim_savedstatus_get_message(now); + + /* Rebuild the status dropdown */ + populate_status_dropdown(); + + list = g_object_get_data(G_OBJECT(ggblist->status), "list of statuses"); + for (; list; list = list->next) + { + StatusBoxItem *item = list->data; + if (item->type == STATUS_PRIMITIVE && item->u.prim == prim) + { + char *mess = gaim_unescape_html(message); + gnt_combo_box_set_selected(GNT_COMBO_BOX(ggblist->status), item); + gnt_entry_set_text(GNT_ENTRY(ggblist->statustext), mess); + gnt_widget_draw(ggblist->status); + g_free(mess); + break; + } + } + + g_signal_handlers_unblock_matched(ggblist->status, G_SIGNAL_MATCH_FUNC, + 0, 0, NULL, status_selection_changed, NULL); + g_signal_handlers_unblock_matched(ggblist->statustext, G_SIGNAL_MATCH_FUNC, + 0, 0, NULL, status_text_changed, NULL); +} + +static int +blist_node_compare_text(GaimBlistNode *n1, GaimBlistNode *n2) +{ + const char *s1, *s2; + char *us1, *us2; + int ret; + + g_return_val_if_fail(n1->type == n2->type, -1); + + switch (n1->type) + { + case GAIM_BLIST_GROUP_NODE: + s1 = ((GaimGroup*)n1)->name; + s2 = ((GaimGroup*)n2)->name; + break; + case GAIM_BLIST_CHAT_NODE: + s1 = gaim_chat_get_name((GaimChat*)n1); + s2 = gaim_chat_get_name((GaimChat*)n2); + break; + case GAIM_BLIST_BUDDY_NODE: + return gaim_presence_compare(gaim_buddy_get_presence((GaimBuddy*)n1), + gaim_buddy_get_presence((GaimBuddy*)n2)); + break; + case GAIM_BLIST_CONTACT_NODE: + s1 = gaim_contact_get_alias((GaimContact*)n1); + s2 = gaim_contact_get_alias((GaimContact*)n2); + break; + default: + return -1; + } + + us1 = g_utf8_strup(s1, -1); + us2 = g_utf8_strup(s2, -1); + ret = g_utf8_collate(us1, us2); + g_free(us1); + g_free(us2); + + return ret; +} + +static int +blist_node_compare_status(GaimBlistNode *n1, GaimBlistNode *n2) +{ + int ret; + + g_return_val_if_fail(n1->type == n2->type, -1); + + switch (n1->type) { + case GAIM_BLIST_CONTACT_NODE: + n1 = (GaimBlistNode*)gaim_contact_get_priority_buddy((GaimContact*)n1); + n2 = (GaimBlistNode*)gaim_contact_get_priority_buddy((GaimContact*)n2); + /* now compare the presence of the priority buddies */ + case GAIM_BLIST_BUDDY_NODE: + ret = gaim_presence_compare(gaim_buddy_get_presence((GaimBuddy*)n1), + gaim_buddy_get_presence((GaimBuddy*)n2)); + if (ret != 0) + return ret; + break; + default: + break; + } + + /* Sort alphabetically if presence is not comparable */ + ret = blist_node_compare_text(n1, n2); + + return ret; +} + +static int +get_contact_log_size(GaimBlistNode *c) +{ + int log = 0; + GaimBlistNode *node; + + for (node = c->child; node; node = node->next) { + GaimBuddy *b = (GaimBuddy*)node; + log += gaim_log_get_total_size(GAIM_LOG_IM, b->name, b->account); + } + + return log; +} + +static int +blist_node_compare_log(GaimBlistNode *n1, GaimBlistNode *n2) +{ + int ret; + GaimBuddy *b1, *b2; + + g_return_val_if_fail(n1->type == n2->type, -1); + + switch (n1->type) { + case GAIM_BLIST_BUDDY_NODE: + b1 = (GaimBuddy*)n1; + b2 = (GaimBuddy*)n2; + ret = gaim_log_get_total_size(GAIM_LOG_IM, b2->name, b2->account) - + gaim_log_get_total_size(GAIM_LOG_IM, b1->name, b1->account); + if (ret != 0) + return ret; + break; + case GAIM_BLIST_CONTACT_NODE: + ret = get_contact_log_size(n2) - get_contact_log_size(n1); + if (ret != 0) + return ret; + break; + default: + break; + } + ret = blist_node_compare_text(n1, n2); + return ret; +} + +static gboolean +blist_clicked(GntTree *tree, GntMouseEvent event, int x, int y, gpointer ggblist) +{ + if (event == GNT_RIGHT_MOUSE_DOWN) { + draw_context_menu(ggblist); + } + return FALSE; +} + +static void +plugin_action(GntMenuItem *item, gpointer data) +{ + GaimPluginAction *action = data; + if (action && action->callback) + action->callback(action); +} + +static void +build_plugin_actions(GntMenuItem *item, GaimPlugin *plugin, gpointer context) +{ + GntWidget *sub = gnt_menu_new(GNT_MENU_POPUP); + GList *actions; + GntMenuItem *menuitem; + + gnt_menuitem_set_submenu(item, GNT_MENU(sub)); + for (actions = GAIM_PLUGIN_ACTIONS(plugin, context); actions; + actions = g_list_delete_link(actions, actions)) { + if (actions->data) { + GaimPluginAction *action = actions->data; + action->plugin = plugin; + action->context = context; + menuitem = gnt_menuitem_new(action->label); + gnt_menu_add_item(GNT_MENU(sub), menuitem); + + gnt_menuitem_set_callback(menuitem, plugin_action, action); + g_object_set_data_full(G_OBJECT(menuitem), "plugin_action", + action, (GDestroyNotify)gaim_plugin_action_free); + } + } +} + +static void +reconstruct_plugins_menu() +{ + GntWidget *sub; + GntMenuItem *plg; + GList *iter; + + if (!ggblist) + return; + + if (ggblist->plugins == NULL) + ggblist->plugins = gnt_menuitem_new(_("Plugins")); + + plg = ggblist->plugins; + sub = gnt_menu_new(GNT_MENU_POPUP); + gnt_menuitem_set_submenu(plg, GNT_MENU(sub)); + + for (iter = gaim_plugins_get_loaded(); iter; iter = iter->next) { + GaimPlugin *plugin = iter->data; + GntMenuItem *item; + if (GAIM_IS_PROTOCOL_PLUGIN(plugin)) + continue; + + if (!GAIM_PLUGIN_HAS_ACTIONS(plugin)) + continue; + + item = gnt_menuitem_new(_(plugin->info->name)); + gnt_menu_add_item(GNT_MENU(sub), item); + build_plugin_actions(item, plugin, NULL); + } +} + +static void +reconstruct_accounts_menu() +{ + GntWidget *sub; + GntMenuItem *acc, *item; + GList *iter; + + if (!ggblist) + return; + + if (ggblist->accounts == NULL) + ggblist->accounts = gnt_menuitem_new(_("Accounts")); + + acc = ggblist->accounts; + sub = gnt_menu_new(GNT_MENU_POPUP); + gnt_menuitem_set_submenu(acc, GNT_MENU(sub)); + + for (iter = gaim_accounts_get_all_active(); iter; + iter = g_list_delete_link(iter, iter)) { + GaimAccount *account = iter->data; + GaimConnection *gc = gaim_account_get_connection(account); + GaimPlugin *prpl; + + if (!gc || !GAIM_CONNECTION_IS_CONNECTED(gc)) + continue; + prpl = gc->prpl; + + if (GAIM_PLUGIN_HAS_ACTIONS(prpl)) { + item = gnt_menuitem_new(gaim_account_get_username(account)); + gnt_menu_add_item(GNT_MENU(sub), item); + build_plugin_actions(item, prpl, gc); + } + } +} + +static void +account_signed_on_cb() +{ + GaimBlistNode *node; + + for (node = gaim_blist_get_root(); node; + node = gaim_blist_node_next(node, FALSE)) { + if (GAIM_BLIST_NODE_IS_CHAT(node)) { + GaimChat *chat = (GaimChat*)node; + if (gaim_blist_node_get_bool(node, "gnt-autojoin")) + serv_join_chat(gaim_account_get_connection(chat->account), chat->components); + } + } +} + +static void show_offline_cb(GntMenuItem *item, gpointer n) +{ + gaim_prefs_set_bool(PREF_ROOT "/showoffline", + !gaim_prefs_get_bool(PREF_ROOT "/showoffline")); +} + +static void sort_blist_change_cb(GntMenuItem *item, gpointer n) +{ + gaim_prefs_set_string(PREF_ROOT "/sort_type", n); +} + +/* XXX: send_im_select* -- Xerox */ +static void +send_im_select_cb(gpointer data, GaimRequestFields *fields) +{ + GaimAccount *account; + const char *username; + + account = gaim_request_fields_get_account(fields, "account"); + username = gaim_request_fields_get_string(fields, "screenname"); + + gaim_conversation_new(GAIM_CONV_TYPE_IM, account, username); +} + +static void +send_im_select(GntMenuItem *item, gpointer n) +{ + GaimRequestFields *fields; + GaimRequestFieldGroup *group; + GaimRequestField *field; + + fields = gaim_request_fields_new(); + + group = gaim_request_field_group_new(NULL); + gaim_request_fields_add_group(fields, group); + + field = gaim_request_field_string_new("screenname", _("_Name"), NULL, FALSE); + gaim_request_field_set_type_hint(field, "screenname"); + gaim_request_field_set_required(field, TRUE); + gaim_request_field_group_add_field(group, field); + + field = gaim_request_field_account_new("account", _("_Account"), NULL); + gaim_request_field_set_type_hint(field, "account"); + gaim_request_field_set_visible(field, + (gaim_connections_get_all() != NULL && + gaim_connections_get_all()->next != NULL)); + gaim_request_field_set_required(field, TRUE); + gaim_request_field_group_add_field(group, field); + + gaim_request_fields(gaim_get_blist(), _("New Instant Message"), + NULL, + _("Please enter the screen name or alias of the person " + "you would like to IM."), + fields, + _("OK"), G_CALLBACK(send_im_select_cb), + _("Cancel"), NULL, + NULL); +} + +static void +create_menu() +{ + GntWidget *menu, *sub; + GntMenuItem *item; + GntWindow *window; + + if (!ggblist) + return; + + window = GNT_WINDOW(ggblist->window); + ggblist->menu = menu = gnt_menu_new(GNT_MENU_TOPLEVEL); + gnt_window_set_menu(window, GNT_MENU(menu)); + + item = gnt_menuitem_new(_("Options")); + gnt_menu_add_item(GNT_MENU(menu), item); + + sub = gnt_menu_new(GNT_MENU_POPUP); + gnt_menuitem_set_submenu(item, GNT_MENU(sub)); + + item = gnt_menuitem_new(_("Send IM...")); + gnt_menu_add_item(GNT_MENU(sub), item); + gnt_menuitem_set_callback(GNT_MENUITEM(item), send_im_select, NULL); + + item = gnt_menuitem_new(_("Toggle offline buddies")); + gnt_menu_add_item(GNT_MENU(sub), item); + gnt_menuitem_set_callback(GNT_MENUITEM(item), show_offline_cb, NULL); + + item = gnt_menuitem_new(_("Sort by status")); + gnt_menu_add_item(GNT_MENU(sub), item); + gnt_menuitem_set_callback(GNT_MENUITEM(item), sort_blist_change_cb, "status"); + + item = gnt_menuitem_new(_("Sort alphabetically")); + gnt_menu_add_item(GNT_MENU(sub), item); + gnt_menuitem_set_callback(GNT_MENUITEM(item), sort_blist_change_cb, "text"); + + item = gnt_menuitem_new(_("Sort by log size")); + gnt_menu_add_item(GNT_MENU(sub), item); + gnt_menuitem_set_callback(GNT_MENUITEM(item), sort_blist_change_cb, "log"); + + reconstruct_accounts_menu(); + gnt_menu_add_item(GNT_MENU(menu), ggblist->accounts); + + reconstruct_plugins_menu(); + gnt_menu_add_item(GNT_MENU(menu), ggblist->plugins); +} + +void gg_blist_show() +{ + blist_show(gaim_get_blist()); +} + +static void +blist_show(GaimBuddyList *list) +{ + if (ggblist == NULL) + new_list(list); + else if (ggblist->window) + return; + + ggblist->window = gnt_vwindow_new(FALSE); + gnt_widget_set_name(ggblist->window, "buddylist"); + gnt_box_set_toplevel(GNT_BOX(ggblist->window), TRUE); + gnt_box_set_title(GNT_BOX(ggblist->window), _("Buddy List")); + gnt_box_set_pad(GNT_BOX(ggblist->window), 0); + + ggblist->tree = gnt_tree_new(); + + GNT_WIDGET_SET_FLAGS(ggblist->tree, GNT_WIDGET_NO_BORDER); + gnt_widget_set_size(ggblist->tree, gaim_prefs_get_int(PREF_ROOT "/size/width"), + gaim_prefs_get_int(PREF_ROOT "/size/height")); + gnt_widget_set_position(ggblist->window, gaim_prefs_get_int(PREF_ROOT "/position/x"), + gaim_prefs_get_int(PREF_ROOT "/position/y")); + + gnt_tree_set_col_width(GNT_TREE(ggblist->tree), 0, + gaim_prefs_get_int(PREF_ROOT "/size/width") - 1); + + gnt_box_add_widget(GNT_BOX(ggblist->window), ggblist->tree); + + ggblist->status = gnt_combo_box_new(); + gnt_box_add_widget(GNT_BOX(ggblist->window), ggblist->status); + ggblist->statustext = gnt_entry_new(NULL); + gnt_box_add_widget(GNT_BOX(ggblist->window), ggblist->statustext); + + gnt_widget_show(ggblist->window); + + gaim_signal_connect(gaim_connections_get_handle(), "signed-on", gg_blist_get_handle(), + GAIM_CALLBACK(reconstruct_accounts_menu), NULL); + gaim_signal_connect(gaim_connections_get_handle(), "signed-off", gg_blist_get_handle(), + GAIM_CALLBACK(reconstruct_accounts_menu), NULL); + gaim_signal_connect(gaim_blist_get_handle(), "buddy-status-changed", gg_blist_get_handle(), + GAIM_CALLBACK(buddy_status_changed), ggblist); + gaim_signal_connect(gaim_blist_get_handle(), "buddy-idle-changed", gg_blist_get_handle(), + GAIM_CALLBACK(buddy_idle_changed), ggblist); + + gaim_signal_connect(gaim_plugins_get_handle(), "plugin-load", gg_blist_get_handle(), + GAIM_CALLBACK(reconstruct_plugins_menu), NULL); + gaim_signal_connect(gaim_plugins_get_handle(), "plugin-unload", gg_blist_get_handle(), + GAIM_CALLBACK(reconstruct_plugins_menu), NULL); + +#if 0 + gaim_signal_connect(gaim_blist_get_handle(), "buddy-signed-on", gg_blist_get_handle(), + GAIM_CALLBACK(buddy_signed_on), ggblist); + gaim_signal_connect(gaim_blist_get_handle(), "buddy-signed-off", gg_blist_get_handle(), + GAIM_CALLBACK(buddy_signed_off), ggblist); + + /* These I plan to use to indicate unread-messages etc. */ + gaim_signal_connect(gaim_conversations_get_handle(), "received-im-msg", gg_blist_get_handle(), + GAIM_CALLBACK(received_im_msg), list); + gaim_signal_connect(gaim_conversations_get_handle(), "sent-im-msg", gg_blist_get_handle(), + GAIM_CALLBACK(sent_im_msg), NULL); + + gaim_signal_connect(gaim_conversations_get_handle(), "received-chat-msg", gg_blist_get_handle(), + GAIM_CALLBACK(received_chat_msg), list); +#endif + + g_signal_connect(G_OBJECT(ggblist->tree), "selection_changed", G_CALLBACK(selection_changed), ggblist); + g_signal_connect(G_OBJECT(ggblist->tree), "key_pressed", G_CALLBACK(key_pressed), ggblist); + g_signal_connect(G_OBJECT(ggblist->tree), "context-menu", G_CALLBACK(context_menu), ggblist); + g_signal_connect_after(G_OBJECT(ggblist->tree), "clicked", G_CALLBACK(blist_clicked), ggblist); + g_signal_connect(G_OBJECT(ggblist->tree), "activate", G_CALLBACK(selection_activate), ggblist); + g_signal_connect_data(G_OBJECT(ggblist->tree), "gained-focus", G_CALLBACK(draw_tooltip), + ggblist, 0, G_CONNECT_AFTER | G_CONNECT_SWAPPED); + g_signal_connect_data(G_OBJECT(ggblist->tree), "lost-focus", G_CALLBACK(remove_peripherals), + ggblist, 0, G_CONNECT_AFTER | G_CONNECT_SWAPPED); + g_signal_connect(G_OBJECT(ggblist->tree), "size_changed", G_CALLBACK(size_changed_cb), NULL); + g_signal_connect(G_OBJECT(ggblist->window), "position_set", G_CALLBACK(save_position_cb), NULL); + g_signal_connect(G_OBJECT(ggblist->window), "destroy", G_CALLBACK(reset_blist_window), NULL); + + /* Status signals */ + gaim_signal_connect(gaim_savedstatuses_get_handle(), "savedstatus-changed", gg_blist_get_handle(), + GAIM_CALLBACK(savedstatus_changed), NULL); + g_signal_connect(G_OBJECT(ggblist->status), "selection_changed", + G_CALLBACK(status_selection_changed), NULL); + g_signal_connect(G_OBJECT(ggblist->statustext), "key_pressed", + G_CALLBACK(status_text_changed), NULL); + + create_menu(); + + populate_buddylist(); + + savedstatus_changed(gaim_savedstatus_get_current(), NULL); +} + +void gg_blist_uninit() +{ + if (ggblist == NULL) + return; + + gnt_widget_destroy(ggblist->window); + g_free(ggblist); + ggblist = NULL; +} + +gboolean gg_blist_get_position(int *x, int *y) +{ + if (!ggblist || !ggblist->window) + return FALSE; + gnt_widget_get_position(ggblist->window, x, y); + return TRUE; +} + +void gg_blist_set_position(int x, int y) +{ + gnt_widget_set_position(ggblist->window, x, y); +} + +gboolean gg_blist_get_size(int *width, int *height) +{ + if (!ggblist || !ggblist->window) + return FALSE; + gnt_widget_get_size(ggblist->window, width, height); + return TRUE; +} + +void gg_blist_set_size(int width, int height) +{ + gnt_widget_set_size(ggblist->window, width, height); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/gntblist.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,95 @@ +/** + * @file gntblist.h GNT BuddyList API + * @ingroup gntui + * + * gaim + * + * 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 _GNT_BLIST_H +#define _GNT_BLIST_H + +#include "blist.h" + +/********************************************************************** + * @name GNT BuddyList API + **********************************************************************/ +/*@{*/ + +/** + * Get the ui-functions. + * + * @return The GaimBlistUiOps structure populated with the appropriate functions. + */ +GaimBlistUiOps * gg_blist_get_ui_ops(void); + +/** + * Perform necessary initializations. + */ +void gg_blist_init(void); + +/** + * Perform necessary uninitializations. + */ +void gg_blist_uninit(void); + +/** + * Show the buddy list. + */ +void gg_blist_show(void); + +/** + * Get the position of the buddy list. + * + * @param x The x-coordinate is set here if not @ NULL. + * @param y The y-coordinate is set here if not @c NULL. + * + * @return Returns @c TRUE if the values were set, @c FALSE otherwise. + */ +gboolean gg_blist_get_position(int *x, int *y); + +/** + * Set the position of the buddy list. + * + * @param x The x-coordinate of the buddy list. + * @param y The y-coordinate of the buddy list. + */ +void gg_blist_set_position(int x, int y); + +/** + * Get the size of the buddy list. + * + * @param width The width is set here if not @ NULL. + * @param height The height is set here if not @c NULL. + * + * @return Returns @c TRUE if the values were set, @c FALSE otherwise. + */ +gboolean gg_blist_get_size(int *width, int *height); + +/** + * Set the size of the buddy list. + * + * @param width The width of the buddy list. + * @param height The height of the buddy list. + */ +void gg_blist_set_size(int width, int height); + +/*@}*/ + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/gntconn.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,69 @@ +/** + * @file gntconn.c GNT Connection API + * @ingroup gntui + * + * gaim + * + * 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 + */ +#include "notify.h" + +#include "gntconn.h" +#include "gntgaim.h" + +static void +gg_connection_report_disconnect(GaimConnection *gc, const char *text) +{ + char *act, *primary, *secondary; + GaimAccount *account = gaim_connection_get_account(gc); + + act = g_strdup_printf(_("%s (%s)"), gaim_account_get_username(account), + gaim_account_get_protocol_name(account)); + + primary = g_strdup_printf(_("%s disconnected."), act); + secondary = g_strdup_printf(_("%s was disconnected due to the following error:\n%s"), + act, text); + + gaim_notify_error(account, _("Connection Error"), primary, secondary); + + g_free(act); + g_free(primary); + g_free(secondary); +} + +static GaimConnectionUiOps ops = +{ + .connect_progress = NULL, + .connected = NULL, + .disconnected = NULL, + .notice = NULL, + .report_disconnect = gg_connection_report_disconnect +}; + +GaimConnectionUiOps *gg_connections_get_ui_ops() +{ + return &ops; +} + +void gg_connections_init() +{} + +void gg_connections_uninit() +{} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/gntconn.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,54 @@ +/** + * @file gntconn.h GNT Connection API + * @ingroup gntui + * + * gaim + * + * 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 _GNT_CONN_H +#define _GNT_CONN_H + +#include "connection.h" + +/********************************************************************** + * @name GNT Connection API + **********************************************************************/ +/*@{*/ + +/** + * Get the ui-functions. + * + * @return The GaimConnectionUiOps structure populated with the appropriate functions. + */ +GaimConnectionUiOps *gg_connections_get_ui_ops(void); + +/** + * Perform necessary initializations. + */ +void gg_connections_init(void); + +/** + * Perform necessary uninitializations. + */ +void gg_connections_uninit(void); + +/*@}*/ + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/gntconv.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,756 @@ +/** + * @file gntconv.c GNT Conversation API + * @ingroup gntui + * + * gaim + * + * 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 + */ +#include <string.h> + +#include <cmds.h> +#include <prefs.h> +#include <util.h> + +#include "gntgaim.h" +#include "gntaccount.h" +#include "gntblist.h" +#include "gntconv.h" +#include "gntdebug.h" +#include "gntplugin.h" +#include "gntprefs.h" +#include "gntstatus.h" + +#include "gnt.h" +#include "gntbox.h" +#include "gntentry.h" +#include "gnttextview.h" + +#define PREF_ROOT "/gaim/gnt/conversations" + +#include "config.h" + +static void +send_typing_notification(GntWidget *w, GGConv *ggconv) +{ + const char *text = gnt_entry_get_text(GNT_ENTRY(ggconv->entry)); + gboolean empty = (!text || !*text); + if (gaim_prefs_get_bool("/gaim/gnt/conversations/notify_typing")) { + GaimConversation *conv = ggconv->active_conv; + GaimConvIm *im = GAIM_CONV_IM(conv); + if (!empty) { + gboolean send = (gaim_conv_im_get_send_typed_timeout(im) == 0); + + gaim_conv_im_stop_send_typed_timeout(im); + gaim_conv_im_start_send_typed_timeout(im); + if (send || (gaim_conv_im_get_type_again(im) != 0 && + time(NULL) > gaim_conv_im_get_type_again(im))) { + unsigned int timeout; + timeout = serv_send_typing(gaim_conversation_get_gc(conv), + gaim_conversation_get_name(conv), + GAIM_TYPING); + gaim_conv_im_set_type_again(im, timeout); + } + } else { + gaim_conv_im_stop_send_typed_timeout(im); + + serv_send_typing(gaim_conversation_get_gc(conv), + gaim_conversation_get_name(conv), + GAIM_NOT_TYPING); + } + } +} + +static gboolean +entry_key_pressed(GntWidget *w, const char *key, GGConv *ggconv) +{ + if (key[0] == '\r' && key[1] == 0) + { + const char *text = gnt_entry_get_text(GNT_ENTRY(ggconv->entry)); + if (*text == '/') + { + GaimConversation *conv = ggconv->active_conv; + GaimCmdStatus status; + const char *cmdline = text + 1; + char *error = NULL, *escape; + + escape = g_markup_escape_text(cmdline, -1); + status = gaim_cmd_do_command(conv, cmdline, escape, &error); + g_free(escape); + + switch (status) + { + case GAIM_CMD_STATUS_OK: + break; + case GAIM_CMD_STATUS_NOT_FOUND: + gaim_conversation_write(conv, "", _("No such command."), + GAIM_MESSAGE_NO_LOG, time(NULL)); + break; + case GAIM_CMD_STATUS_WRONG_ARGS: + gaim_conversation_write(conv, "", _("Syntax Error: You typed the wrong number of arguments " + "to that command."), + GAIM_MESSAGE_NO_LOG, time(NULL)); + break; + case GAIM_CMD_STATUS_FAILED: + gaim_conversation_write(conv, "", error ? error : _("Your command failed for an unknown reason."), + GAIM_MESSAGE_NO_LOG, time(NULL)); + break; + case GAIM_CMD_STATUS_WRONG_TYPE: + if(gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM) + gaim_conversation_write(conv, "", _("That command only works in chats, not IMs."), + GAIM_MESSAGE_NO_LOG, time(NULL)); + else + gaim_conversation_write(conv, "", _("That command only works in IMs, not chats."), + GAIM_MESSAGE_NO_LOG, time(NULL)); + break; + case GAIM_CMD_STATUS_WRONG_PRPL: + gaim_conversation_write(conv, "", _("That command doesn't work on this protocol."), + GAIM_MESSAGE_NO_LOG, time(NULL)); + break; + } + g_free(error); +#if 0 + gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv), + _("Commands are not supported yet. Message was NOT sent."), + GNT_TEXT_FLAG_DIM | GNT_TEXT_FLAG_UNDERLINE); + gnt_text_view_next_line(GNT_TEXT_VIEW(ggconv->tv)); + gnt_text_view_scroll(GNT_TEXT_VIEW(ggconv->tv), 0); +#endif + } + else + { + char *escape = g_markup_escape_text(text, -1); + char *apos = gaim_strreplace(escape, "'", "'"); + g_free(escape); + escape = apos; + switch (gaim_conversation_get_type(ggconv->active_conv)) + { + case GAIM_CONV_TYPE_IM: + gaim_conv_im_send_with_flags(GAIM_CONV_IM(ggconv->active_conv), escape, GAIM_MESSAGE_SEND); + break; + case GAIM_CONV_TYPE_CHAT: + gaim_conv_chat_send(GAIM_CONV_CHAT(ggconv->active_conv), escape); + break; + default: + g_free(escape); + g_return_val_if_reached(FALSE); + } + g_free(escape); + } + gnt_entry_add_to_history(GNT_ENTRY(ggconv->entry), text); + gnt_entry_clear(GNT_ENTRY(ggconv->entry)); + return TRUE; + } + else if (key[0] == 27) + { + if (strcmp(key, GNT_KEY_DOWN) == 0) + gnt_text_view_scroll(GNT_TEXT_VIEW(ggconv->tv), 1); + else if (strcmp(key, GNT_KEY_UP) == 0) + gnt_text_view_scroll(GNT_TEXT_VIEW(ggconv->tv), -1); + else if (strcmp(key, GNT_KEY_PGDOWN) == 0) + gnt_text_view_scroll(GNT_TEXT_VIEW(ggconv->tv), ggconv->tv->priv.height - 2); + else if (strcmp(key, GNT_KEY_PGUP) == 0) + gnt_text_view_scroll(GNT_TEXT_VIEW(ggconv->tv), -(ggconv->tv->priv.height - 2)); + else + return FALSE; + return TRUE; + } + else + { + } + + return FALSE; +} + +static void +closing_window(GntWidget *window, GGConv *ggconv) +{ + GList *list = ggconv->list; + ggconv->window = NULL; + while (list) { + GaimConversation *conv = list->data; + list = list->next; + gaim_conversation_destroy(conv); + } +} + +static void +size_changed_cb(GntWidget *widget, int width, int height) +{ + int w, h; + gnt_widget_get_size(widget, &w, &h); + gaim_prefs_set_int(PREF_ROOT "/size/width", w); + gaim_prefs_set_int(PREF_ROOT "/size/height", h); +} + +static void +save_position_cb(GntWidget *w, int x, int y) +{ + gaim_prefs_set_int(PREF_ROOT "/position/x", x); + gaim_prefs_set_int(PREF_ROOT "/position/y", y); +} + +static GaimConversation * +find_conv_with_contact(GaimConversation *conv) +{ + GaimBlistNode *node; + GaimBuddy *buddy = gaim_find_buddy(conv->account, conv->name); + GaimConversation *ret = NULL; + + if (!buddy) + return NULL; + + for (node = ((GaimBlistNode*)buddy)->parent->child; node; node = node->next) { + if (node == (GaimBlistNode*)buddy) + continue; + if ((ret = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, + ((GaimBuddy*)node)->name, ((GaimBuddy*)node)->account)) != NULL) + break; + } + return ret; +} + +static char * +get_conversation_title(GaimConversation *conv, GaimAccount *account) +{ + return g_strdup_printf(_("%s (%s -- %s)"), gaim_conversation_get_title(conv), + gaim_account_get_username(account), gaim_account_get_protocol_name(account)); +} + +static void +update_buddy_typing(GaimAccount *account, const char *who, gpointer null) +{ + GaimConversation *conv; + GGConv *ggc; + GaimConvIm *im = NULL; + char *title, *str; + + conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, who, account); + + if (!conv) + return; + + im = GAIM_CONV_IM(conv); + ggc = conv->ui_data; + + if (gaim_conv_im_get_typing_state(im) == GAIM_TYPING) { + int scroll; + str = get_conversation_title(conv, account); + title = g_strdup_printf(_("%s [%s]"), str, + gnt_ascii_only() ? "T" : "\342\243\277"); + g_free(str); + + scroll = gnt_text_view_get_lines_below(GNT_TEXT_VIEW(ggc->tv)); + str = g_strdup_printf(_("\n%s is typing..."), gaim_conversation_get_name(conv)); + /* Updating is a little buggy. So just remove and add a new one */ + gnt_text_view_tag_change(GNT_TEXT_VIEW(ggc->tv), "typing", NULL, TRUE); + gnt_text_view_append_text_with_tag(GNT_TEXT_VIEW(ggc->tv), + str, GNT_TEXT_FLAG_DIM, "typing"); + g_free(str); + if (scroll <= 1) + gnt_text_view_scroll(GNT_TEXT_VIEW(ggc->tv), 0); + } else { + title = get_conversation_title(conv, account); + gnt_text_view_tag_change(GNT_TEXT_VIEW(ggc->tv), "typing", NULL, TRUE); + } + gnt_screen_rename_widget(ggc->window, title); + g_free(title); +} + +static gpointer +gg_conv_get_handle() +{ + static int handle; + return &handle; +} + +static void +gg_create_conversation(GaimConversation *conv) +{ + GGConv *ggc = conv->ui_data; + char *title; + GaimConversationType type; + GaimConversation *cc; + GaimAccount *account; + + if (ggc) + return; + + cc = find_conv_with_contact(conv); + if (cc && cc->ui_data) + ggc = cc->ui_data; + else + ggc = g_new0(GGConv, 1); + + ggc->list = g_list_prepend(ggc->list, conv); + ggc->active_conv = conv; + conv->ui_data = ggc; + + if (cc && cc->ui_data) { + gg_conversation_set_active(conv); + return; + } + + account = gaim_conversation_get_account(conv); + type = gaim_conversation_get_type(conv); + title = get_conversation_title(conv, account); + + ggc->window = gnt_box_new(FALSE, TRUE); + gnt_box_set_title(GNT_BOX(ggc->window), title); + gnt_box_set_toplevel(GNT_BOX(ggc->window), TRUE); + gnt_box_set_pad(GNT_BOX(ggc->window), 0); + gnt_widget_set_name(ggc->window, "conversation-window"); + + ggc->tv = gnt_text_view_new(); + gnt_box_add_widget(GNT_BOX(ggc->window), ggc->tv); + gnt_widget_set_name(ggc->tv, "conversation-window-textview"); + gnt_widget_set_size(ggc->tv, gaim_prefs_get_int(PREF_ROOT "/size/width"), + gaim_prefs_get_int(PREF_ROOT "/size/height")); + + ggc->entry = gnt_entry_new(NULL); + gnt_box_add_widget(GNT_BOX(ggc->window), ggc->entry); + gnt_widget_set_name(ggc->entry, "conversation-window-entry"); + gnt_entry_set_history_length(GNT_ENTRY(ggc->entry), -1); + gnt_entry_set_word_suggest(GNT_ENTRY(ggc->entry), TRUE); + gnt_entry_set_always_suggest(GNT_ENTRY(ggc->entry), FALSE); + + g_signal_connect_after(G_OBJECT(ggc->entry), "key_pressed", G_CALLBACK(entry_key_pressed), ggc); + g_signal_connect(G_OBJECT(ggc->entry), "text_changed", G_CALLBACK(send_typing_notification), ggc); + g_signal_connect(G_OBJECT(ggc->window), "destroy", G_CALLBACK(closing_window), ggc); + + gnt_widget_set_position(ggc->window, gaim_prefs_get_int(PREF_ROOT "/position/x"), + gaim_prefs_get_int(PREF_ROOT "/position/y")); + gnt_widget_show(ggc->window); + + g_signal_connect(G_OBJECT(ggc->tv), "size_changed", G_CALLBACK(size_changed_cb), NULL); + g_signal_connect(G_OBJECT(ggc->window), "position_set", G_CALLBACK(save_position_cb), NULL); + + gaim_signal_connect(gaim_conversations_get_handle(), "buddy-typing", gg_conv_get_handle(), + GAIM_CALLBACK(update_buddy_typing), NULL); + gaim_signal_connect(gaim_conversations_get_handle(), "buddy-typing-stopped", gg_conv_get_handle(), + GAIM_CALLBACK(update_buddy_typing), NULL); + + g_free(title); +} + +static void +gg_destroy_conversation(GaimConversation *conv) +{ + /* do stuff here */ + GGConv *ggc = conv->ui_data; + ggc->list = g_list_remove(ggc->list, conv); + if (ggc->list && conv == ggc->active_conv) + ggc->active_conv = ggc->list->data; + + if (ggc->list == NULL) { + gnt_widget_destroy(ggc->window); + g_free(ggc); + } +} + +static void +gg_write_common(GaimConversation *conv, const char *who, const char *message, + GaimMessageFlags flags, time_t mtime) +{ + GGConv *ggconv = conv->ui_data; + char *strip, *newline; + GntTextFormatFlags fl = 0; + int pos; + gboolean notify; + + g_return_if_fail(ggconv != NULL); + + if (ggconv->active_conv != conv) { + if (flags & (GAIM_MESSAGE_SEND | GAIM_MESSAGE_RECV)) + gg_conversation_set_active(conv); + else + return; + } + + pos = gnt_text_view_get_lines_below(GNT_TEXT_VIEW(ggconv->tv)); + + notify = !!gnt_text_view_tag_change(GNT_TEXT_VIEW(ggconv->tv), "typing", NULL, TRUE); + gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv), "\n", GNT_TEXT_FLAG_NORMAL); + + /* Unnecessary to print the timestamp for delayed message */ + if (!(flags & GAIM_MESSAGE_DELAYED) && + gaim_prefs_get_bool("/gaim/gnt/conversations/timestamps")) + gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv), + gaim_utf8_strftime("(%H:%M:%S) ", localtime(&mtime)), GNT_TEXT_FLAG_DIM); + + if (flags & GAIM_MESSAGE_AUTO_RESP) + gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv), + _("<AUTO-REPLY> "), GNT_TEXT_FLAG_BOLD); + + if (who && *who && (flags & (GAIM_MESSAGE_SEND | GAIM_MESSAGE_RECV))) + { + char * name = NULL; + + if (gaim_message_meify((char*)message, -1)) + name = g_strdup_printf("*** %s ", who); + else + name = g_strdup_printf("%s: ", who); + + gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv), + name, GNT_TEXT_FLAG_BOLD); + g_free(name); + } + else + fl = GNT_TEXT_FLAG_DIM; + + if (flags & GAIM_MESSAGE_ERROR) + fl |= GNT_TEXT_FLAG_BOLD; + if (flags & GAIM_MESSAGE_NICK) + fl |= GNT_TEXT_FLAG_UNDERLINE; + + /* XXX: Remove this workaround when textview can parse messages. */ + newline = gaim_strdup_withhtml(message); + strip = gaim_markup_strip_html(newline); + gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv), + strip, fl); + + g_free(newline); + g_free(strip); + + if (notify) { + strip = g_strdup_printf(_("\n%s is typing..."), gaim_conversation_get_name(conv)); + gnt_text_view_append_text_with_tag(GNT_TEXT_VIEW(ggconv->tv), + strip, GNT_TEXT_FLAG_DIM, "typing"); + g_free(strip); + } + + if (pos <= 1) + gnt_text_view_scroll(GNT_TEXT_VIEW(ggconv->tv), 0); + + if (flags & (GAIM_MESSAGE_RECV | GAIM_MESSAGE_NICK | GAIM_MESSAGE_ERROR)) + gnt_widget_set_urgent(ggconv->tv); +} + +static void +gg_write_chat(GaimConversation *conv, const char *who, const char *message, + GaimMessageFlags flags, time_t mtime) +{ + gaim_conversation_write(conv, who, message, flags, mtime); +} + +static void +gg_write_im(GaimConversation *conv, const char *who, const char *message, + GaimMessageFlags flags, time_t mtime) +{ + GaimAccount *account = gaim_conversation_get_account(conv); + if (flags & GAIM_MESSAGE_SEND) + { + who = gaim_connection_get_display_name(gaim_account_get_connection(account)); + if (!who) + who = gaim_account_get_alias(account); + if (!who) + who = gaim_account_get_username(account); + } + else if (flags & GAIM_MESSAGE_RECV) + { + GaimBuddy *buddy; + who = gaim_conversation_get_name(conv); + buddy = gaim_find_buddy(account, who); + if (buddy) + who = gaim_buddy_get_contact_alias(buddy); + } + + gaim_conversation_write(conv, who, message, flags, mtime); +} + +static void +gg_write_conv(GaimConversation *conv, const char *who, const char *alias, + const char *message, GaimMessageFlags flags, time_t mtime) +{ + const char *name; + if (alias && *alias) + name = alias; + else if (who && *who) + name = who; + else + name = NULL; + + gg_write_common(conv, name, message, flags, mtime); +} + +static void +gg_chat_add_users(GaimConversation *conv, GList *users, gboolean new_arrivals) +{ + GGConv *ggc = conv->ui_data; + GntEntry *entry = GNT_ENTRY(ggc->entry); + + if (!new_arrivals) + { + /* Print the list of users in the room */ + GString *string = g_string_new(_("List of users:\n")); + GList *iter; + + for (iter = users; iter; iter = iter->next) + { + GaimConvChatBuddy *cbuddy = iter->data; + char *str; + + if ((str = cbuddy->alias) == NULL) + str = cbuddy->name; + g_string_append_printf(string, "[ %s ]", str); + } + + gaim_conversation_write(conv, NULL, string->str, + GAIM_MESSAGE_SYSTEM, time(NULL)); + g_string_free(string, TRUE); + } + + for (; users; users = users->next) + { + GaimConvChatBuddy *cbuddy = users->data; + gnt_entry_add_suggest(entry, cbuddy->name); + gnt_entry_add_suggest(entry, cbuddy->alias); + } +} + +static void +gg_chat_rename_user(GaimConversation *conv, const char *old, const char *new_n, const char *new_a) +{ + /* Update the name for string completion */ + GGConv *ggc = conv->ui_data; + GntEntry *entry = GNT_ENTRY(ggc->entry); + gnt_entry_remove_suggest(entry, old); + gnt_entry_add_suggest(entry, new_n); + gnt_entry_add_suggest(entry, new_a); +} + +static void +gg_chat_remove_user(GaimConversation *conv, GList *list) +{ + /* Remove the name from string completion */ + GGConv *ggc = conv->ui_data; + GntEntry *entry = GNT_ENTRY(ggc->entry); + for (; list; list = list->next) + gnt_entry_remove_suggest(entry, list->data); +} + +static void +gg_chat_update_user(GaimConversation *conv, const char *user) +{ +} + +static GaimConversationUiOps conv_ui_ops = +{ + .create_conversation = gg_create_conversation, + .destroy_conversation = gg_destroy_conversation, + .write_chat = gg_write_chat, + .write_im = gg_write_im, + .write_conv = gg_write_conv, + .chat_add_users = gg_chat_add_users, + .chat_rename_user = gg_chat_rename_user, + .chat_remove_users = gg_chat_remove_user, + .chat_update_user = gg_chat_update_user, + .present = NULL, + .has_focus = NULL, + .custom_smiley_add = NULL, + .custom_smiley_write = NULL, + .custom_smiley_close = NULL +}; + +GaimConversationUiOps *gg_conv_get_ui_ops() +{ + return &conv_ui_ops; +} + +/* Xerox */ +static GaimCmdRet +say_command_cb(GaimConversation *conv, + const char *cmd, char **args, char **error, void *data) +{ + if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM) + gaim_conv_im_send(GAIM_CONV_IM(conv), args[0]); + else if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_CHAT) + gaim_conv_chat_send(GAIM_CONV_CHAT(conv), args[0]); + + return GAIM_CMD_RET_OK; +} + +/* Xerox */ +static GaimCmdRet +me_command_cb(GaimConversation *conv, + const char *cmd, char **args, char **error, void *data) +{ + char *tmp; + + tmp = g_strdup_printf("/me %s", args[0]); + + if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_IM) + gaim_conv_im_send(GAIM_CONV_IM(conv), tmp); + else if (gaim_conversation_get_type(conv) == GAIM_CONV_TYPE_CHAT) + gaim_conv_chat_send(GAIM_CONV_CHAT(conv), tmp); + + g_free(tmp); + return GAIM_CMD_RET_OK; +} + +/* Xerox */ +static GaimCmdRet +debug_command_cb(GaimConversation *conv, + const char *cmd, char **args, char **error, void *data) +{ + char *tmp, *markup; + GaimCmdStatus status; + + if (!g_ascii_strcasecmp(args[0], "version")) { + tmp = g_strdup_printf("me is using %s.", VERSION); + markup = g_markup_escape_text(tmp, -1); + + status = gaim_cmd_do_command(conv, tmp, markup, error); + + g_free(tmp); + g_free(markup); + return status; + } else { + gaim_conversation_write(conv, NULL, _("Supported debug options are: version"), + GAIM_MESSAGE_NO_LOG|GAIM_MESSAGE_ERROR, time(NULL)); + return GAIM_CMD_STATUS_OK; + } +} + +/* Xerox */ +static GaimCmdRet +clear_command_cb(GaimConversation *conv, + const char *cmd, char **args, char **error, void *data) +{ + GGConv *ggconv = conv->ui_data; + gnt_text_view_clear(GNT_TEXT_VIEW(ggconv->tv)); + return GAIM_CMD_STATUS_OK; +} + +/* Xerox */ +static GaimCmdRet +help_command_cb(GaimConversation *conv, + const char *cmd, char **args, char **error, void *data) +{ + GList *l, *text; + GString *s; + + if (args[0] != NULL) { + s = g_string_new(""); + text = gaim_cmd_help(conv, args[0]); + + if (text) { + for (l = text; l; l = l->next) + if (l->next) + g_string_append_printf(s, "%s\n", (char *)l->data); + else + g_string_append_printf(s, "%s", (char *)l->data); + } else { + g_string_append(s, _("No such command (in this context).")); + } + } else { + s = g_string_new(_("Use \"/help <command>\" for help on a specific command.\n" + "The following commands are available in this context:\n")); + + text = gaim_cmd_list(conv); + for (l = text; l; l = l->next) + if (l->next) + g_string_append_printf(s, "%s, ", (char *)l->data); + else + g_string_append_printf(s, "%s.", (char *)l->data); + g_list_free(text); + } + + gaim_conversation_write(conv, NULL, s->str, GAIM_MESSAGE_NO_LOG, time(NULL)); + g_string_free(s, TRUE); + + return GAIM_CMD_STATUS_OK; +} + +static GaimCmdRet +cmd_show_window(GaimConversation *conv, const char *cmd, char **args, char **error, gpointer data) +{ + void (*callback)() = data; + callback(); + return GAIM_CMD_STATUS_OK; +} + +void gg_conversation_init() +{ + gaim_prefs_add_none(PREF_ROOT); + gaim_prefs_add_none(PREF_ROOT "/size"); + gaim_prefs_add_int(PREF_ROOT "/size/width", 70); + gaim_prefs_add_int(PREF_ROOT "/size/height", 20); + gaim_prefs_add_none(PREF_ROOT "/position"); + gaim_prefs_add_int(PREF_ROOT "/position/x", 0); + gaim_prefs_add_int(PREF_ROOT "/position/y", 0); + + /* Xerox the commands */ + gaim_cmd_register("say", "S", GAIM_CMD_P_DEFAULT, + GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_IM, NULL, + say_command_cb, _("say <message>: Send a message normally as if you weren't using a command."), NULL); + gaim_cmd_register("me", "S", GAIM_CMD_P_DEFAULT, + GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_IM, NULL, + me_command_cb, _("me <action>: Send an IRC style action to a buddy or chat."), NULL); + gaim_cmd_register("debug", "w", GAIM_CMD_P_DEFAULT, + GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_IM, NULL, + debug_command_cb, _("debug <option>: Send various debug information to the current conversation."), NULL); + gaim_cmd_register("clear", "", GAIM_CMD_P_DEFAULT, + GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_IM, NULL, + clear_command_cb, _("clear: Clears the conversation scrollback."), NULL); + gaim_cmd_register("help", "w", GAIM_CMD_P_DEFAULT, + GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_ALLOW_WRONG_ARGS, NULL, + help_command_cb, _("help <command>: Help on a specific command."), NULL); + + /* Now some commands to bring up some other windows */ + gaim_cmd_register("plugins", "", GAIM_CMD_P_DEFAULT, + GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_IM, NULL, + cmd_show_window, _("plugins: Show the plugins window."), gg_plugins_show_all); + gaim_cmd_register("buddylist", "", GAIM_CMD_P_DEFAULT, + GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_IM, NULL, + cmd_show_window, _("buddylist: Show the buddylist."), gg_blist_show); + gaim_cmd_register("accounts", "", GAIM_CMD_P_DEFAULT, + GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_IM, NULL, + cmd_show_window, _("accounts: Show the accounts window."), gg_accounts_show_all); + gaim_cmd_register("debugwin", "", GAIM_CMD_P_DEFAULT, + GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_IM, NULL, + cmd_show_window, _("debugwin: Show the debug window."), gg_debug_window_show); + gaim_cmd_register("prefs", "", GAIM_CMD_P_DEFAULT, + GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_IM, NULL, + cmd_show_window, _("prefs: Show the preference window."), gg_prefs_show_all); + gaim_cmd_register("status", "", GAIM_CMD_P_DEFAULT, + GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_IM, NULL, + cmd_show_window, _("statuses: Show the savedstatuses window."), gg_savedstatus_show_all); +} + +void gg_conversation_uninit() +{ +} + +void gg_conversation_set_active(GaimConversation *conv) +{ + GGConv *ggconv = conv->ui_data; + GaimAccount *account; + char *title; + + g_return_if_fail(ggconv); + g_return_if_fail(g_list_find(ggconv->list, conv)); + + ggconv->active_conv = conv; + account = gaim_conversation_get_account(conv); + title = get_conversation_title(conv, account); + gnt_screen_rename_widget(ggconv->window, title); + g_free(title); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/gntconv.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,94 @@ +/** + * @file gntconv.h GNT Conversation API + * @ingroup gntui + * + * gaim + * + * 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 _GNT_CONV_H +#define _GNT_CONV_H + +#include <gnt.h> +#include <gntwidget.h> + +#include "conversation.h" + +/*************************************************************************** + * @name GNT Conversations API + ***************************************************************************/ +/*@{*/ + +typedef struct _GGConv GGConv; +typedef struct _GGConvChat GGConvChat; +typedef struct _GGConvIm GGConvIm; + +struct _GGConv +{ + GList *list; + GaimConversation *active_conv; + + GntWidget *window; /* the container */ + GntWidget *entry; /* entry */ + GntWidget *tv; /* text-view */ + + union + { + GGConvChat *chat; + GGConvIm *im; + } u; +}; + +struct _GGConvChat +{ + GntWidget *userlist; /* the userlist */ +}; + +struct _GGConvIm +{ + void *nothing_for_now; +}; + +/** + * Get the ui-functions. + * + * @return The GaimConversationUiOps populated with the appropriate functions. + */ +GaimConversationUiOps *gg_conv_get_ui_ops(void); + +/** + * Perform the necessary initializations. + */ +void gg_conversation_init(void); + +/** + * Perform the necessary uninitializations. + */ +void gg_conversation_uninit(void); + +/** + * Set a conversation as active in a contactized conversation + * + * @param conv The conversation to make active. + */ +void gg_conversation_set_active(GaimConversation *conv); + +/*@}*/ + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/gntdebug.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,299 @@ +/** + * @file gntdebug.c GNT Debug API + * @ingroup gntui + * + * gaim + * + * 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 + */ +#include <gnt.h> +#include <gntbox.h> +#include <gnttextview.h> +#include <gntbutton.h> +#include <gntcheckbox.h> +#include <gntline.h> + +#include "gntdebug.h" +#include "gntgaim.h" +#include "util.h" + +#include <stdio.h> +#include <string.h> + +#define PREF_ROOT "/gaim/gnt/debug" + +static struct +{ + GntWidget *window; + GntWidget *tview; + gboolean paused; + gboolean timestamps; +} debug; + +static gboolean +debug_window_kpress_cb(GntWidget *wid, const char *key, GntTextView *view) +{ + if (key[0] == 27) + { + if (strcmp(key, GNT_KEY_DOWN) == 0) + gnt_text_view_scroll(view, 1); + else if (strcmp(key, GNT_KEY_UP) == 0) + gnt_text_view_scroll(view, -1); + else if (strcmp(key, GNT_KEY_PGDOWN) == 0) + gnt_text_view_scroll(view, wid->priv.height - 2); + else if (strcmp(key, GNT_KEY_PGUP) == 0) + gnt_text_view_scroll(view, -(wid->priv.height - 2)); + else + return FALSE; + return TRUE; + } + return FALSE; +} + +static void +gg_debug_print(GaimDebugLevel level, const char *category, + const char *args) +{ + if (debug.window && !debug.paused) + { + int pos = gnt_text_view_get_lines_below(GNT_TEXT_VIEW(debug.tview)); + GntTextFormatFlags flag = GNT_TEXT_FLAG_NORMAL; + + if (debug.timestamps) { + const char *mdate; + time_t mtime = time(NULL); + mdate = gaim_utf8_strftime("%H:%M:%S ", localtime(&mtime)); + gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(debug.tview), + mdate, flag); + } + + gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(debug.tview), + category, GNT_TEXT_FLAG_BOLD); + gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(debug.tview), + ": ", GNT_TEXT_FLAG_BOLD); + + switch (level) + { + case GAIM_DEBUG_WARNING: + flag |= GNT_TEXT_FLAG_UNDERLINE; + case GAIM_DEBUG_ERROR: + case GAIM_DEBUG_FATAL: + flag |= GNT_TEXT_FLAG_BOLD; + break; + default: + break; + } + + gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(debug.tview), args, flag); + if (pos <= 1) + gnt_text_view_scroll(GNT_TEXT_VIEW(debug.tview), 0); + } +} + +static GaimDebugUiOps uiops = +{ + gg_debug_print, +}; + +GaimDebugUiOps *gg_debug_get_ui_ops() +{ + return &uiops; +} + +static void +reset_debug_win(GntWidget *w, gpointer null) +{ + debug.window = debug.tview = NULL; +} + +static void +clear_debug_win(GntWidget *w, GntTextView *tv) +{ + gnt_text_view_clear(tv); +} + +static void +print_stderr(const char *string) +{ + g_printerr("%s", string); +} + +static void +toggle_pause(GntWidget *w, gpointer n) +{ + debug.paused = !debug.paused; +} + +static void +toggle_timestamps(GntWidget *w, gpointer n) +{ + debug.timestamps = !debug.timestamps; + gaim_prefs_set_bool("/core/debug/timestamps", debug.timestamps); +} + +/* Xerox */ +static void +gaim_glib_log_handler(const gchar *domain, GLogLevelFlags flags, + const gchar *msg, gpointer user_data) +{ + GaimDebugLevel level; + char *new_msg = NULL; + char *new_domain = NULL; + + if ((flags & G_LOG_LEVEL_ERROR) == G_LOG_LEVEL_ERROR) + level = GAIM_DEBUG_ERROR; + else if ((flags & G_LOG_LEVEL_CRITICAL) == G_LOG_LEVEL_CRITICAL) + level = GAIM_DEBUG_FATAL; + else if ((flags & G_LOG_LEVEL_WARNING) == G_LOG_LEVEL_WARNING) + level = GAIM_DEBUG_WARNING; + else if ((flags & G_LOG_LEVEL_MESSAGE) == G_LOG_LEVEL_MESSAGE) + level = GAIM_DEBUG_INFO; + else if ((flags & G_LOG_LEVEL_INFO) == G_LOG_LEVEL_INFO) + level = GAIM_DEBUG_INFO; + else if ((flags & G_LOG_LEVEL_DEBUG) == G_LOG_LEVEL_DEBUG) + level = GAIM_DEBUG_MISC; + else + { + gaim_debug_warning("gntdebug", + "Unknown glib logging level in %d\n", flags); + + level = GAIM_DEBUG_MISC; /* This will never happen. */ + } + + if (msg != NULL) + new_msg = gaim_utf8_try_convert(msg); + + if (domain != NULL) + new_domain = gaim_utf8_try_convert(domain); + + if (new_msg != NULL) + { + gaim_debug(level, (new_domain != NULL ? new_domain : "g_log"), + "%s\n", new_msg); + + g_free(new_msg); + } + + g_free(new_domain); +} + +static void +size_changed_cb(GntWidget *widget, int oldw, int oldh) +{ + int w, h; + gnt_widget_get_size(widget, &w, &h); + gaim_prefs_set_int(PREF_ROOT "/size/width", w); + gaim_prefs_set_int(PREF_ROOT "/size/height", h); +} + +void gg_debug_window_show() +{ + debug.paused = FALSE; + debug.timestamps = gaim_prefs_get_bool("/core/debug/timestamps"); + if (debug.window == NULL) + { + GntWidget *wid, *box; + debug.window = gnt_vbox_new(FALSE); + gnt_box_set_toplevel(GNT_BOX(debug.window), TRUE); + gnt_box_set_title(GNT_BOX(debug.window), _("Debug Window")); + gnt_box_set_pad(GNT_BOX(debug.window), 0); + gnt_box_set_alignment(GNT_BOX(debug.window), GNT_ALIGN_MID); + + debug.tview = gnt_text_view_new(); + gnt_box_add_widget(GNT_BOX(debug.window), debug.tview); + gnt_widget_set_size(debug.tview, + gaim_prefs_get_int(PREF_ROOT "/size/width"), + gaim_prefs_get_int(PREF_ROOT "/size/height")); + g_signal_connect(G_OBJECT(debug.tview), "size_changed", G_CALLBACK(size_changed_cb), NULL); + + gnt_box_add_widget(GNT_BOX(debug.window), gnt_line_new(FALSE)); + + box = gnt_hbox_new(FALSE); + gnt_box_set_alignment(GNT_BOX(box), GNT_ALIGN_MID); + gnt_box_set_fill(GNT_BOX(box), FALSE); + + /* XXX: Setting the GROW_Y for the following widgets don't make sense. But right now + * it's necessary to make the width of the debug window resizable ... like I said, + * it doesn't make sense. The bug is likely in the packing in gntbox.c. + */ + wid = gnt_button_new(_("Clear")); + g_signal_connect(G_OBJECT(wid), "activate", G_CALLBACK(clear_debug_win), debug.tview); + GNT_WIDGET_SET_FLAGS(wid, GNT_WIDGET_GROW_Y); + gnt_box_add_widget(GNT_BOX(box), wid); + + wid = gnt_check_box_new(_("Pause")); + g_signal_connect(G_OBJECT(wid), "toggled", G_CALLBACK(toggle_pause), NULL); + GNT_WIDGET_SET_FLAGS(wid, GNT_WIDGET_GROW_Y); + gnt_box_add_widget(GNT_BOX(box), wid); + + wid = gnt_check_box_new(_("Timestamps")); + gnt_check_box_set_checked(GNT_CHECK_BOX(wid), debug.timestamps); + g_signal_connect(G_OBJECT(wid), "toggled", G_CALLBACK(toggle_timestamps), NULL); + GNT_WIDGET_SET_FLAGS(wid, GNT_WIDGET_GROW_Y); + gnt_box_add_widget(GNT_BOX(box), wid); + + gnt_box_add_widget(GNT_BOX(debug.window), box); + GNT_WIDGET_SET_FLAGS(box, GNT_WIDGET_GROW_Y); + + gnt_widget_set_name(debug.window, "debug-window"); + + g_signal_connect(G_OBJECT(debug.window), "destroy", G_CALLBACK(reset_debug_win), NULL); + g_signal_connect(G_OBJECT(debug.window), "key_pressed", G_CALLBACK(debug_window_kpress_cb), debug.tview); + } + + gnt_widget_show(debug.window); +} + +static gboolean +start_with_debugwin(gpointer null) +{ + gg_debug_window_show(); + return FALSE; +} + +void gg_debug_init() +{ +/* Xerox */ +#define REGISTER_G_LOG_HANDLER(name) \ + g_log_set_handler((name), G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL \ + | G_LOG_FLAG_RECURSION, \ + gaim_glib_log_handler, NULL) + + /* Register the glib log handlers. */ + REGISTER_G_LOG_HANDLER(NULL); + REGISTER_G_LOG_HANDLER("GLib"); + REGISTER_G_LOG_HANDLER("GModule"); + REGISTER_G_LOG_HANDLER("GLib-GObject"); + REGISTER_G_LOG_HANDLER("GThread"); + + g_set_print_handler(print_stderr); /* Redirect the debug messages to stderr */ + + gaim_prefs_add_none(PREF_ROOT); + gaim_prefs_add_none(PREF_ROOT "/size"); + gaim_prefs_add_int(PREF_ROOT "/size/width", 60); + gaim_prefs_add_int(PREF_ROOT "/size/height", 15); + + if (gaim_debug_is_enabled()) + g_timeout_add(0, start_with_debugwin, NULL); +} + +void gg_debug_uninit() +{ +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/gntdebug.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,59 @@ +/** + * @file gntdebug.h GNT Debug API + * @ingroup gntui + * + * gaim + * + * 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 _GNT_DEBUG_H +#define _GNT_DEBUG_H + +#include "debug.h" + +/********************************************************************** + * @name GNT Debug API + **********************************************************************/ +/*@{*/ + +/** + * Get the ui-functions. + * + * @return The GaimDebugUiOps structure populated with the appropriate functions. + */ +GaimDebugUiOps *gg_debug_get_ui_ops(void); + +/** + * Perform necessary initializations. + */ +void gg_debug_init(void); + +/** + * Perform necessary uninitializations. + */ +void gg_debug_uninit(void); + +/** + * Show the debug window. + */ +void gg_debug_window_show(void); + +/*@}*/ + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/gntgaim.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,417 @@ +/** + * gaim + * + * 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 + */ +#include "account.h" +#include "conversation.h" +#include "core.h" +#include "debug.h" +#include "eventloop.h" +#include "ft.h" +#include "log.h" +#include "notify.h" +#include "prefix.h" +#include "prefs.h" +#include "prpl.h" +#include "pounce.h" +#include "savedstatuses.h" +#include "sound.h" +#include "status.h" +#include "util.h" +#include "whiteboard.h" + +#include "gntdebug.h" +#include "gntgaim.h" +#include "gntprefs.h" +#include "gntui.h" + +#define _GNU_SOURCE +#include <getopt.h> + +#include "config.h" + +static void +debug_init() +{ + gg_debug_init(); + gaim_debug_set_ui_ops(gg_debug_get_ui_ops()); +} + +static GaimCoreUiOps core_ops = +{ + gg_prefs_init, + debug_init, + gnt_ui_init, + gnt_ui_uninit +}; + +static GaimCoreUiOps * +gnt_core_get_ui_ops() +{ + return &core_ops; +} + +/* Anything IO-related is directly copied from gtkgaim's source tree */ + +#define GAIM_GTK_READ_COND (G_IO_IN | G_IO_HUP | G_IO_ERR) +#define GAIM_GTK_WRITE_COND (G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL) + +typedef struct _GaimGtkIOClosure { + GaimInputFunction function; + guint result; + gpointer data; + +} GaimGtkIOClosure; + +static void gaim_gnt_io_destroy(gpointer data) +{ + g_free(data); +} + +static gboolean gaim_gnt_io_invoke(GIOChannel *source, GIOCondition condition, gpointer data) +{ + GaimGtkIOClosure *closure = data; + GaimInputCondition gaim_cond = 0; + + if (condition & GAIM_GTK_READ_COND) + gaim_cond |= GAIM_INPUT_READ; + if (condition & GAIM_GTK_WRITE_COND) + gaim_cond |= GAIM_INPUT_WRITE; + +#if 0 + gaim_debug(GAIM_DEBUG_MISC, "gtk_eventloop", + "CLOSURE: callback for %d, fd is %d\n", + closure->result, g_io_channel_unix_get_fd(source)); +#endif + +#ifdef _WIN32 + if(! gaim_cond) { +#if DEBUG + gaim_debug_misc("gnt_eventloop", + "CLOSURE received GIOCondition of 0x%x, which does not" + " match 0x%x (READ) or 0x%x (WRITE)\n", + condition, GAIM_GTK_READ_COND, GAIM_GTK_WRITE_COND); +#endif /* DEBUG */ + + return TRUE; + } +#endif /* _WIN32 */ + + closure->function(closure->data, g_io_channel_unix_get_fd(source), + gaim_cond); + + return TRUE; +} + +static guint gnt_input_add(gint fd, GaimInputCondition condition, GaimInputFunction function, + gpointer data) +{ + GaimGtkIOClosure *closure = g_new0(GaimGtkIOClosure, 1); + GIOChannel *channel; + GIOCondition cond = 0; + + closure->function = function; + closure->data = data; + + if (condition & GAIM_INPUT_READ) + cond |= GAIM_GTK_READ_COND; + if (condition & GAIM_INPUT_WRITE) + cond |= GAIM_GTK_WRITE_COND; + + channel = g_io_channel_unix_new(fd); + closure->result = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond, + gaim_gnt_io_invoke, closure, gaim_gnt_io_destroy); + + g_io_channel_unref(channel); + return closure->result; +} + +static GaimEventLoopUiOps eventloop_ops = +{ + g_timeout_add, + (guint (*)(guint))g_source_remove, + gnt_input_add, + (guint (*)(guint))g_source_remove +}; + +static GaimEventLoopUiOps * +gnt_eventloop_get_ui_ops(void) +{ + return &eventloop_ops; +} + +/* This is copied from gtkgaim */ +static char * +gnt_find_binary_location(void *symbol, void *data) +{ + static char *fullname = NULL; + static gboolean first = TRUE; + + char *argv0 = data; + struct stat st; + char *basebuf, *linkbuf, *fullbuf; + + if (!first) + /* We've already been through this. */ + return strdup(fullname); + + first = FALSE; + + if (!argv0) + return NULL; + + + basebuf = g_find_program_in_path(argv0); + + /* But we still need to deal with symbolic links */ + g_lstat(basebuf, &st); + while ((st.st_mode & S_IFLNK) == S_IFLNK) { + int written; + linkbuf = g_malloc(1024); + written = readlink(basebuf, linkbuf, 1024 - 1); + if (written == -1) + { + /* This really shouldn't happen, but do we + * need something better here? */ + g_free(linkbuf); + continue; + } + linkbuf[written] = '\0'; + if (linkbuf[0] == G_DIR_SEPARATOR) { + /* an absolute path */ + fullbuf = g_strdup(linkbuf); + } else { + char *dirbuf = g_path_get_dirname(basebuf); + /* a relative path */ + fullbuf = g_strdup_printf("%s%s%s", + dirbuf, G_DIR_SEPARATOR_S, + linkbuf); + g_free(dirbuf); + } + /* There's no memory leak here. Really! */ + g_free(linkbuf); + g_free(basebuf); + basebuf = fullbuf; + g_lstat(basebuf, &st); + } + + fullname = basebuf; + return strdup(fullname); +} + + +/* This is mostly copied from gtkgaim's source tree */ +static void +show_usage(const char *name, gboolean terse) +{ + char *text; + + if (terse) { + text = g_strdup_printf(_("%s. Try `%s -h' for more information.\n"), VERSION, name); + } else { + text = g_strdup_printf(_("%s\n" + "Usage: %s [OPTION]...\n\n" + " -c, --config=DIR use DIR for config files\n" + " -d, --debug print debugging messages to stdout\n" + " -h, --help display this help and exit\n" + " -n, --nologin don't automatically login\n" + " -v, --version display the current version and exit\n"), VERSION, name); + } + + gaim_print_utf8_to_console(stdout, text); + g_free(text); +} + +static int +init_libgaim(int argc, char **argv) +{ + char *path; + int opt; + gboolean opt_help = FALSE; + gboolean opt_nologin = FALSE; + gboolean opt_version = FALSE; + char *opt_config_dir_arg = NULL; + char *opt_session_arg = NULL; + gboolean debug_enabled = FALSE; + + struct option long_options[] = { + {"config", required_argument, NULL, 'c'}, + {"debug", no_argument, NULL, 'd'}, + {"help", no_argument, NULL, 'h'}, + {"nologin", no_argument, NULL, 'n'}, + {"session", required_argument, NULL, 's'}, + {"version", no_argument, NULL, 'v'}, + {0, 0, 0, 0} + }; + + gaim_br_set_locate_fallback_func(gnt_find_binary_location, argv[0]); + +#ifdef ENABLE_NLS + bindtextdomain(PACKAGE, LOCALEDIR); + bind_textdomain_codeset(PACKAGE, "UTF-8"); + textdomain(PACKAGE); +#endif + +#ifdef HAVE_SETLOCALE + setlocale(LC_ALL, ""); +#endif + + /* scan command-line options */ + opterr = 1; + while ((opt = getopt_long(argc, argv, +#ifndef _WIN32 + "c:dhn::s:v", +#else + "c:dhn::v", +#endif + long_options, NULL)) != -1) { + switch (opt) { + case 'c': /* config dir */ + g_free(opt_config_dir_arg); + opt_config_dir_arg = g_strdup(optarg); + break; + case 'd': /* debug */ + debug_enabled = TRUE; + break; + case 'h': /* help */ + opt_help = TRUE; + break; + case 'n': /* no autologin */ + opt_nologin = TRUE; + break; + case 's': /* use existing session ID */ + g_free(opt_session_arg); + opt_session_arg = g_strdup(optarg); + break; + case 'v': /* version */ + opt_version = TRUE; + break; + case '?': /* show terse help */ + default: + show_usage(argv[0], TRUE); + return 0; + break; + } + } + + /* show help message */ + if (opt_help) { + show_usage(argv[0], FALSE); + return 0; + } + /* show version message */ + if (opt_version) { + printf("gaim-text %s\n", VERSION); + return 0; + } + + /* set a user-specified config directory */ + if (opt_config_dir_arg != NULL) { + gaim_util_set_user_dir(opt_config_dir_arg); + g_free(opt_config_dir_arg); + } + + /* + * We're done piddling around with command line arguments. + * Fire up this baby. + */ + + /* Because we don't want debug-messages to show up and corrup the display */ + gaim_debug_set_enabled(debug_enabled); + + gaim_core_set_ui_ops(gnt_core_get_ui_ops()); + gaim_eventloop_set_ui_ops(gnt_eventloop_get_ui_ops()); + + path = g_build_filename(gaim_user_dir(), "plugins", NULL); + gaim_plugins_add_search_path(path); + g_free(path); + + gaim_plugins_add_search_path(LIBDIR); + + if (!gaim_core_init(GAIM_GNT_UI)) + { + fprintf(stderr, + "Initialization of the Gaim core failed. Dumping core.\n" + "Please report this!\n"); + abort(); + } + + /* TODO: Move blist loading into gaim_blist_init() */ + gaim_set_blist(gaim_blist_new()); + gaim_blist_load(); + + /* TODO: Move prefs loading into gaim_prefs_init() */ + gaim_prefs_load(); + gaim_prefs_update_old(); + + /* load plugins we had when we quit */ + gaim_plugins_load_saved("/gaim/gnt/plugins/loaded"); + + /* TODO: Move pounces loading into gaim_pounces_init() */ + gaim_pounces_load(); + + if (opt_nologin) + { + /* Set all accounts to "offline" */ + GaimSavedStatus *saved_status; + + /* If we've used this type+message before, lookup the transient status */ + saved_status = gaim_savedstatus_find_transient_by_type_and_message( + GAIM_STATUS_OFFLINE, NULL); + + /* If this type+message is unique then create a new transient saved status */ + if (saved_status == NULL) + saved_status = gaim_savedstatus_new(NULL, GAIM_STATUS_OFFLINE); + + /* Set the status for each account */ + gaim_savedstatus_activate(saved_status); + } + else + { + /* Everything is good to go--sign on already */ + if (!gaim_prefs_get_bool("/core/savedstatus/startup_current_status")) + gaim_savedstatus_activate(gaim_savedstatus_get_startup()); + gaim_accounts_restore_current_statuses(); + } + + return 1; +} + +int main(int argc, char **argv) +{ + /* XXX: Don't puke */ + freopen(".error", "w", stderr); + + signal(SIGPIPE, SIG_IGN); + + /* Initialize the libgaim stuff */ + if (!init_libgaim(argc, argv)) + return 0; + + gaim_blist_show(); + gnt_main(); + +#ifdef STANDALONE + gaim_core_quit(); +#endif + + return 0; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/gntgaim.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,27 @@ +/** + * gaim + * + * 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 + */ +#include <glib.h> + +#include "libgaim/internal.h" + +#define GAIM_GNT_UI "gnt-gaim" +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/gntnotify.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,374 @@ +/** + * @file gntnotify.c GNT Notify API + * @ingroup gntui + * + * gaim + * + * 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 + */ +#include <gnt.h> +#include <gntbox.h> +#include <gntbutton.h> +#include <gntlabel.h> +#include <gnttree.h> + +#include <util.h> + +#include "gntnotify.h" +#include "gntgaim.h" + +static struct +{ + GntWidget *window; + GntWidget *tree; +} emaildialog; + +static void +notify_msg_window_destroy_cb(GntWidget *window, GaimNotifyMsgType type) +{ + gaim_notify_close(type, window); +} + +static void * +gg_notify_message(GaimNotifyMsgType type, const char *title, + const char *primary, const char *secondary) +{ + GntWidget *window, *button; + GntTextFormatFlags pf = 0, sf = 0; + + switch (type) + { + case GAIM_NOTIFY_MSG_ERROR: + sf |= GNT_TEXT_FLAG_BOLD; + case GAIM_NOTIFY_MSG_WARNING: + pf |= GNT_TEXT_FLAG_UNDERLINE; + case GAIM_NOTIFY_MSG_INFO: + pf |= GNT_TEXT_FLAG_BOLD; + break; + } + + window = gnt_box_new(FALSE, TRUE); + gnt_box_set_toplevel(GNT_BOX(window), TRUE); + gnt_box_set_title(GNT_BOX(window), title); + gnt_box_set_fill(GNT_BOX(window), FALSE); + gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID); + + if (primary) + gnt_box_add_widget(GNT_BOX(window), + gnt_label_new_with_format(primary, pf)); + if (secondary) + gnt_box_add_widget(GNT_BOX(window), + gnt_label_new_with_format(secondary, sf)); + + button = gnt_button_new(_("OK")); + gnt_box_add_widget(GNT_BOX(window), button); + g_signal_connect_swapped(G_OBJECT(button), "activate", + G_CALLBACK(gnt_widget_destroy), window); + g_signal_connect(G_OBJECT(window), "destroy", + G_CALLBACK(notify_msg_window_destroy_cb), GINT_TO_POINTER(type)); + + gnt_widget_show(window); + return window; +} + +/* handle is, in all/most occasions, a GntWidget * */ +static void gg_close_notify(GaimNotifyType type, void *handle) +{ + GntWidget *widget = handle; + + if (!widget) + return; + + while (widget->parent) + widget = widget->parent; + + if (type == GAIM_NOTIFY_SEARCHRESULTS) + gaim_notify_searchresults_free(g_object_get_data(handle, "notify-results")); +#if 1 + /* This did not seem to be necessary */ + g_signal_handlers_disconnect_by_func(G_OBJECT(widget), + G_CALLBACK(notify_msg_window_destroy_cb), GINT_TO_POINTER(type)); +#endif + gnt_widget_destroy(widget); +} + +static void *gg_notify_formatted(const char *title, const char *primary, + const char *secondary, const char *text) +{ + /* XXX: For now, simply strip the html and use _notify_message. For future use, + * there should be some way of parsing the makrups from GntTextView */ + char *unformat = gaim_markup_strip_html(text); + char *t = g_strdup_printf("%s%s%s", + secondary ? secondary : "", + secondary ? "\n" : "", + unformat ? unformat : ""); + + void *ret = gg_notify_message(GAIM_NOTIFY_FORMATTED, title, primary, t); + + g_free(t); + g_free(unformat); + + return ret; +} + +static void +reset_email_dialog() +{ + emaildialog.window = NULL; + emaildialog.tree = NULL; +} + +static void +setup_email_dialog() +{ + GntWidget *box, *tree, *button; + if (emaildialog.window) + return; + + emaildialog.window = box = gnt_vbox_new(FALSE); + gnt_box_set_toplevel(GNT_BOX(box), TRUE); + gnt_box_set_title(GNT_BOX(box), _("Emails")); + gnt_box_set_fill(GNT_BOX(box), FALSE); + gnt_box_set_alignment(GNT_BOX(box), GNT_ALIGN_MID); + gnt_box_set_pad(GNT_BOX(box), 0); + + gnt_box_add_widget(GNT_BOX(box), + gnt_label_new_with_format(_("You have mail!"), GNT_TEXT_FLAG_BOLD)); + + emaildialog.tree = tree = gnt_tree_new_with_columns(3); + gnt_tree_set_column_titles(GNT_TREE(tree), _("Account"), _("From"), _("Subject")); + gnt_tree_set_show_title(GNT_TREE(tree), TRUE); + gnt_tree_set_col_width(GNT_TREE(tree), 0, 15); + gnt_tree_set_col_width(GNT_TREE(tree), 1, 25); + gnt_tree_set_col_width(GNT_TREE(tree), 2, 25); + + gnt_box_add_widget(GNT_BOX(box), tree); + + button = gnt_button_new(_("Close")); + gnt_box_add_widget(GNT_BOX(box), button); + + g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gnt_widget_destroy), box); + g_signal_connect(G_OBJECT(box), "destroy", G_CALLBACK(reset_email_dialog), NULL); +} + +static void * +gg_notify_emails(GaimConnection *gc, size_t count, gboolean detailed, + const char **subjects, const char **froms, const char **tos, + const char **urls) +{ + GaimAccount *account = gaim_connection_get_account(gc); + GString *message = g_string_new(NULL); + void *ret; + + if (!detailed) + { + g_string_append_printf(message, + ngettext("%s (%s) has %d new message.", + "%s (%s) has %d new messages.", + (int)count), + tos ? *tos : gaim_account_get_username(account), + gaim_account_get_protocol_name(account), (int)count); + } + else + { + char *to; + + setup_email_dialog(); + + to = g_strdup_printf("%s (%s)", tos ? *tos : gaim_account_get_username(account), + gaim_account_get_protocol_name(account)); + gnt_tree_add_row_after(GNT_TREE(emaildialog.tree), GINT_TO_POINTER(time(NULL)), + gnt_tree_create_row(GNT_TREE(emaildialog.tree), to, + froms ? *froms : "[Unknown sender]", + *subjects), + NULL, NULL); + g_free(to); + gnt_widget_show(emaildialog.window); + return NULL; + } + + ret = gg_notify_message(GAIM_NOTIFY_EMAIL, _("New Mail"), _("You have mail!"), message->str); + g_string_free(message, TRUE); + return ret; +} + +static void * +gg_notify_email(GaimConnection *gc, const char *subject, const char *from, + const char *to, const char *url) +{ + return gg_notify_emails(gc, 1, subject != NULL, + subject ? &subject : NULL, + from ? &from : NULL, + to ? &to : NULL, + url ? &url : NULL); +} + +static void * +gg_notify_userinfo(GaimConnection *gc, const char *who, GaimNotifyUserInfo *user_info) +{ + /* Xeroxed from gtknotify.c */ + char *primary; + char *info; + void *ui_handle; + + primary = g_strdup_printf(_("Info for %s"), who); + info = gaim_notify_user_info_get_text_with_newline(user_info, "<BR>"); + ui_handle = gg_notify_formatted(_("Buddy Information"), primary, NULL, info); + g_free(info); + g_free(primary); + return ui_handle; +} + +static void +notify_button_activated(GntWidget *widget, GaimNotifySearchButton *b) +{ + GList *list = NULL; + GaimAccount *account = g_object_get_data(G_OBJECT(widget), "notify-account"); + gpointer data = g_object_get_data(G_OBJECT(widget), "notify-data"); + + list = gnt_tree_get_selection_text_list(GNT_TREE(widget)); + + b->callback(gaim_account_get_connection(account), list, data); + g_list_foreach(list, (GFunc)g_free, NULL); + g_list_free(list); +} + +static void +gg_notify_sr_new_rows(GaimConnection *gc, + GaimNotifySearchResults *results, void *data) +{ + GntTree *tree = GNT_TREE(data); + GList *o; + + /* XXX: Do I need to empty the tree here? */ + + for (o = results->rows; o; o = o->next) + { + gnt_tree_add_row_after(GNT_TREE(tree), o->data, + gnt_tree_create_row_from_list(GNT_TREE(tree), o->data), + NULL, NULL); + } +} + +static void * +gg_notify_searchresults(GaimConnection *gc, const char *title, + const char *primary, const char *secondary, + GaimNotifySearchResults *results, gpointer data) +{ + GntWidget *window, *tree, *box, *button; + GList *iter; + + window = gnt_vbox_new(FALSE); + gnt_box_set_toplevel(GNT_BOX(window), TRUE); + gnt_box_set_title(GNT_BOX(window), title); + gnt_box_set_fill(GNT_BOX(window), FALSE); + gnt_box_set_pad(GNT_BOX(window), 0); + gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID); + + gnt_box_add_widget(GNT_BOX(window), + gnt_label_new_with_format(primary, GNT_TEXT_FLAG_BOLD)); + gnt_box_add_widget(GNT_BOX(window), + gnt_label_new_with_format(secondary, GNT_TEXT_FLAG_NORMAL)); + + tree = gnt_tree_new_with_columns(g_list_length(results->columns)); + gnt_tree_set_show_title(GNT_TREE(tree), TRUE); + gnt_box_add_widget(GNT_BOX(window), tree); + + box = gnt_hbox_new(TRUE); + + for (iter = results->buttons; iter; iter = iter->next) + { + GaimNotifySearchButton *b = iter->data; + const char *text; + + switch (b->type) + { + case GAIM_NOTIFY_BUTTON_LABELED: + text = b->label; + break; + case GAIM_NOTIFY_BUTTON_CONTINUE: + text = _("Continue"); + break; + case GAIM_NOTIFY_BUTTON_ADD: + text = _("Add"); + break; + case GAIM_NOTIFY_BUTTON_INFO: + text = _("Info"); + break; + case GAIM_NOTIFY_BUTTON_IM: + text = _("IM"); + break; + case GAIM_NOTIFY_BUTTON_JOIN: + text = _("Join"); + break; + case GAIM_NOTIFY_BUTTON_INVITE: + text = _("Invite"); + break; + default: + text = _("(none)"); + } + + button = gnt_button_new(text); + g_object_set_data(G_OBJECT(button), "notify-account", gaim_connection_get_account(gc)); + g_object_set_data(G_OBJECT(button), "notify-data", data); + g_signal_connect_swapped(G_OBJECT(button), "activate", + G_CALLBACK(notify_button_activated), b); + + gnt_box_add_widget(GNT_BOX(box), button); + } + + gnt_box_add_widget(GNT_BOX(window), box); + + gg_notify_sr_new_rows(gc, results, tree); + + gnt_widget_show(window); + g_object_set_data(G_OBJECT(window), "notify-results", results); + + return tree; +} + +static GaimNotifyUiOps ops = +{ + .notify_message = gg_notify_message, + .close_notify = gg_close_notify, /* The rest of the notify-uiops return a GntWidget. + These widgets should be destroyed from here. */ + .notify_formatted = gg_notify_formatted, + .notify_email = gg_notify_email, + .notify_emails = gg_notify_emails, + .notify_userinfo = gg_notify_userinfo, + + .notify_searchresults = gg_notify_searchresults, + .notify_searchresults_new_rows = gg_notify_sr_new_rows, + .notify_uri = NULL /* This is of low-priority to me */ +}; + +GaimNotifyUiOps *gg_notify_get_ui_ops() +{ + return &ops; +} + +void gg_notify_init() +{ +} + +void gg_notify_uninit() +{ +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/gntnotify.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,55 @@ +/** + * @file gntnotify.h GNT Notify API + * @ingroup gntui + * + * gaim + * + * 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 _GNT_NOTIFY_H +#define _GNT_NOTIFY_H + +#include "notify.h" + +/********************************************************************** + * @name GNT Notify API + **********************************************************************/ +/*@{*/ + +/** + * Get the ui-functions. + * + * @return The GaimNotifyUiOps structure populated with the appropriate functions. + */ +GaimNotifyUiOps *gg_notify_get_ui_ops(void); + +/** + * Perform necessary initializations. + */ +void gg_notify_init(void); + +/** + * Perform necessary uninitializations. + */ +void gg_notify_uninit(void); + +/*@}*/ + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/gntplugin.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,274 @@ +/** + * @file gntplugin.c GNT Plugins API + * @ingroup gntui + * + * gaim + * + * 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 + */ +#include <gnt.h> +#include <gntbox.h> +#include <gntbutton.h> +#include <gntlabel.h> +#include <gntline.h> +#include <gnttree.h> + +#include "notify.h" + +#include "gntgaim.h" +#include "gntplugin.h" + +static struct +{ + GntWidget *tree; + GntWidget *window; + GntWidget *aboot; + GntWidget *conf; +} plugins; + +static GHashTable *confwins; + +static void +decide_conf_button(GaimPlugin *plugin) +{ + if (gaim_plugin_is_loaded(plugin) && + ((GAIM_IS_GNT_PLUGIN(plugin) && + GAIM_GNT_PLUGIN_UI_INFO(plugin) != NULL) || + (plugin->info->prefs_info && + plugin->info->prefs_info->get_plugin_pref_frame))) + gnt_widget_set_visible(plugins.conf, TRUE); + else + gnt_widget_set_visible(plugins.conf, FALSE); + + gnt_box_readjust(GNT_BOX(plugins.window)); + gnt_widget_draw(plugins.window); +} + +static void +plugin_toggled_cb(GntWidget *tree, GaimPlugin *plugin, gpointer null) +{ + if (gnt_tree_get_choice(GNT_TREE(tree), plugin)) + { + if(!gaim_plugin_load(plugin)) + gaim_notify_error(NULL, "ERROR", "loading plugin failed", NULL); + } + else + { + GntWidget *win; + + if (!gaim_plugin_unload(plugin)) + gaim_notify_error(NULL, "ERROR", "unloading plugin failed", NULL); + + if ((win = g_hash_table_lookup(confwins, plugin)) != NULL) + { + gnt_widget_destroy(win); + } + } + decide_conf_button(plugin); + gg_plugins_save_loaded(); +} + +/* Xerox */ +void +gg_plugins_save_loaded(void) +{ + gaim_plugins_save_loaded("/gaim/gnt/plugins/loaded"); +} + +static void +selection_changed(GntWidget *widget, gpointer old, gpointer current, gpointer null) +{ + GaimPlugin *plugin = current; + char *text; + + /* XXX: Use formatting and stuff */ + gnt_text_view_clear(GNT_TEXT_VIEW(plugins.aboot)); + text = g_strdup_printf(_("Name: %s\nVersion: %s\nDescription: %s\nAuthor: %s\nWebsite: %s\nFilename: %s\n"), + SAFE(plugin->info->name), SAFE(plugin->info->version), SAFE(plugin->info->description), + SAFE(plugin->info->author), SAFE(plugin->info->homepage), SAFE(plugin->path)); + gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(plugins.aboot), + text, GNT_TEXT_FLAG_NORMAL); + gnt_text_view_scroll(GNT_TEXT_VIEW(plugins.aboot), 0); + g_free(text); + decide_conf_button(plugin); +} + +static void +reset_plugin_window(GntWidget *window, gpointer null) +{ + plugins.window = NULL; + plugins.tree = NULL; + plugins.aboot = NULL; +} + +static int +plugin_compare(GaimPlugin *p1, GaimPlugin *p2) +{ + char *s1 = g_utf8_strup(p1->info->name, -1); + char *s2 = g_utf8_strup(p2->info->name, -1); + int ret = g_utf8_collate(s1, s2); + g_free(s1); + g_free(s2); + return ret; +} + +static void +confwin_init() +{ + confwins = g_hash_table_new(g_direct_hash, g_direct_equal); +} + +static void +remove_confwin(GntWidget *window, gpointer plugin) +{ + g_hash_table_remove(confwins, plugin); +} + +static void +configure_plugin_cb(GntWidget *button, gpointer null) +{ + GaimPlugin *plugin; + GGPluginFrame callback; + + g_return_if_fail(plugins.tree != NULL); + + plugin = gnt_tree_get_selection_data(GNT_TREE(plugins.tree)); + if (!gaim_plugin_is_loaded(plugin)) + { + gaim_notify_error(plugin, _("Error"), + _("Plugin need to be loaded before you can configure it."), NULL); + return; + } + + if (confwins && g_hash_table_lookup(confwins, plugin)) + return; + + if (GAIM_IS_GNT_PLUGIN(plugin) && + (callback = GAIM_GNT_PLUGIN_UI_INFO(plugin)) != NULL) + { + GntWidget *window = gnt_vbox_new(FALSE); + GntWidget *box, *button; + + gnt_box_set_toplevel(GNT_BOX(window), TRUE); + gnt_box_set_title(GNT_BOX(window), plugin->info->name); + gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID); + + box = callback(); + gnt_box_add_widget(GNT_BOX(window), box); + + box = gnt_hbox_new(FALSE); + gnt_box_add_widget(GNT_BOX(window), box); + + button = gnt_button_new(_("Close")); + gnt_box_add_widget(GNT_BOX(box), button); + g_signal_connect_swapped(G_OBJECT(button), "activate", + G_CALLBACK(gnt_widget_destroy), window); + g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(remove_confwin), plugin); + + gnt_widget_show(window); + + if (confwins == NULL) + confwin_init(); + g_hash_table_insert(confwins, plugin, window); + } + else if (plugin->info->prefs_info && + plugin->info->prefs_info->get_plugin_pref_frame) + { + gaim_notify_info(plugin, _("..."), + _("Still need to do something about this."), NULL); + return; + } + else + { + gaim_notify_info(plugin, _("Error"), + _("No configuration options for this plugin."), NULL); + return; + } +} + +void gg_plugins_show_all() +{ + GntWidget *window, *tree, *box, *aboot, *button; + GList *iter; + if (plugins.window) + return; + + gaim_plugins_probe(G_MODULE_SUFFIX); + + plugins.window = window = gnt_vbox_new(FALSE); + gnt_box_set_toplevel(GNT_BOX(window), TRUE); + gnt_box_set_title(GNT_BOX(window), _("Plugins")); + gnt_box_set_pad(GNT_BOX(window), 0); + gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID); + + gnt_box_add_widget(GNT_BOX(window), + gnt_label_new(_("You can (un)load plugins from the following list."))); + gnt_box_add_widget(GNT_BOX(window), gnt_hline_new()); + + box = gnt_hbox_new(FALSE); + gnt_box_add_widget(GNT_BOX(window), box); + gnt_box_add_widget(GNT_BOX(window), gnt_hline_new()); + + gnt_box_set_pad(GNT_BOX(box), 0); + plugins.tree = tree = gnt_tree_new(); + gnt_tree_set_compare_func(GNT_TREE(tree), (GCompareFunc)plugin_compare); + GNT_WIDGET_SET_FLAGS(tree, GNT_WIDGET_NO_BORDER); + gnt_box_add_widget(GNT_BOX(box), tree); + gnt_box_add_widget(GNT_BOX(box), gnt_vline_new()); + + plugins.aboot = aboot = gnt_text_view_new(); + gnt_widget_set_size(aboot, 40, 20); + gnt_box_add_widget(GNT_BOX(box), aboot); + + for (iter = gaim_plugins_get_all(); iter; iter = iter->next) + { + GaimPlugin *plug = iter->data; + + if (plug->info->type != GAIM_PLUGIN_STANDARD || + (plug->info->flags & GAIM_PLUGIN_FLAG_INVISIBLE) || + plug->error) + continue; + + gnt_tree_add_choice(GNT_TREE(tree), plug, + gnt_tree_create_row(GNT_TREE(tree), plug->info->name), NULL, NULL); + gnt_tree_set_choice(GNT_TREE(tree), plug, gaim_plugin_is_loaded(plug)); + } + gnt_tree_set_col_width(GNT_TREE(tree), 0, 30); + g_signal_connect(G_OBJECT(tree), "toggled", G_CALLBACK(plugin_toggled_cb), NULL); + g_signal_connect(G_OBJECT(tree), "selection_changed", G_CALLBACK(selection_changed), NULL); + + box = gnt_hbox_new(FALSE); + gnt_box_add_widget(GNT_BOX(window), box); + + button = gnt_button_new(_("Close")); + gnt_box_add_widget(GNT_BOX(box), button); + g_signal_connect_swapped(G_OBJECT(button), "activate", + G_CALLBACK(gnt_widget_destroy), window); + + plugins.conf = button = gnt_button_new(_("Configure Plugin")); + gnt_box_add_widget(GNT_BOX(box), button); + g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(configure_plugin_cb), NULL); + + g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(reset_plugin_window), NULL); + + gnt_widget_show(window); + + decide_conf_button(gnt_tree_get_selection_data(GNT_TREE(tree))); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/gntplugin.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,71 @@ +/** + * @file gntplugin.h GNT Plugins API + * @ingroup gntui + * + * gaim + * + * 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 _GNT_PLUGIN_H +#define _GNT_PLUGIN_H + +#include <gnt.h> + +#include <plugin.h> + +#include <string.h> + +#include "gntgaim.h" + +/********************************************************************** + * @name GNT Plugins API + **********************************************************************/ +/*@{*/ + +typedef GntWidget* (*GGPluginFrame) (); + +/* Guess where these came from */ +#define GAIM_GNT_PLUGIN_TYPE GAIM_GNT_UI + +/** + * Decide whether a plugin is a GNT-plugin. + */ +#define GAIM_IS_GNT_PLUGIN(plugin) \ + ((plugin)->info != NULL && (plugin)->info->ui_info != NULL && \ + !strcmp((plugin)->info->ui_requirement, GAIM_GNT_PLUGIN_TYPE)) + +/** + * Get the ui-info from GNT-plugins. + */ +#define GAIM_GNT_PLUGIN_UI_INFO(plugin) \ + (GGPluginFrame)((plugin)->info->ui_info) + +/** + * Show a list of plugins. + */ +void gg_plugins_show_all(void); + +/** + * Save the list of loaded plugins. + */ +void gg_plugins_save_loaded(void); + +/*@}*/ + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/gntprefs.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,226 @@ +/** + * @file gntprefs.c GNT Preferences API + * @ingroup gntui + * + * gaim + * + * 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 + */ +#include <prefs.h> + +#include "gntgaim.h" +#include "gntprefs.h" +#include "gntrequest.h" + +#include <string.h> + +void gg_prefs_init() +{ + gaim_prefs_add_none("/gaim"); + gaim_prefs_add_none("/gaim/gnt"); + + gaim_prefs_add_none("/gaim/gnt/plugins"); + gaim_prefs_add_path_list("/gaim/gnt/plugins/loaded", NULL); + + gaim_prefs_add_none("/gaim/gnt/conversations"); + gaim_prefs_add_bool("/gaim/gnt/conversations/timestamps", TRUE); + gaim_prefs_add_bool("/gaim/gnt/conversations/notify_typing", FALSE); /* XXX: Not functional yet */ +} + +typedef struct +{ + GaimPrefType type; + const char *pref; + const char *label; + GList *(*lv)(); /* If the value is to be selected from a number of choices */ +} Prefs; + +static GList * +get_log_options() +{ + return gaim_log_logger_get_options(); +} + +static GaimRequestField * +get_pref_field(Prefs *prefs) +{ + GaimRequestField *field = NULL; + + if (prefs->lv == NULL) + { + switch (prefs->type) + { + case GAIM_PREF_BOOLEAN: + field = gaim_request_field_bool_new(prefs->pref, _(prefs->label), + gaim_prefs_get_bool(prefs->pref)); + break; + case GAIM_PREF_INT: + field = gaim_request_field_int_new(prefs->pref, _(prefs->label), + gaim_prefs_get_int(prefs->pref)); + break; + case GAIM_PREF_STRING: + field = gaim_request_field_string_new(prefs->pref, _(prefs->label), + gaim_prefs_get_string(prefs->pref), FALSE); + break; + default: + break; + } + } + else + { + GList *list = prefs->lv(), *iter; + field = gaim_request_field_list_new(prefs->pref, _(prefs->label)); + for (iter = list; iter; iter = iter->next) + { + gboolean select = FALSE; + const char *data = iter->data; + iter = iter->next; + switch (prefs->type) + { + case GAIM_PREF_BOOLEAN: + if (gaim_prefs_get_bool(prefs->pref) == GPOINTER_TO_INT(iter->data)) + select = TRUE; + break; + case GAIM_PREF_INT: + if (gaim_prefs_get_int(prefs->pref) == GPOINTER_TO_INT(iter->data)) + select = TRUE; + break; + case GAIM_PREF_STRING: + if (strcmp(gaim_prefs_get_string(prefs->pref), iter->data) == 0) + select = TRUE; + break; + default: + break; + } + gaim_request_field_list_add(field, data, iter->data); + if (select) + gaim_request_field_list_add_selected(field, data); + } + g_list_free(list); + } + return field; +} + +static Prefs blist[] = +{ + {GAIM_PREF_BOOLEAN, "/gaim/gnt/blist/idletime", N_("Show Idle Time"), NULL}, + {GAIM_PREF_BOOLEAN, "/gaim/gnt/blist/showoffline", N_("Show Offline Buddies"), NULL}, + {GAIM_PREF_NONE, NULL, NULL, NULL} +}; + +static Prefs convs[] = +{ + {GAIM_PREF_BOOLEAN, "/gaim/gnt/conversations/timestamps", N_("Show Timestamps"), NULL}, + {GAIM_PREF_BOOLEAN, "/gaim/gnt/conversations/notify_typing", N_("Notify buddies when you are typing"), NULL}, + {GAIM_PREF_NONE, NULL, NULL, NULL} +}; + +static Prefs logging[] = +{ + {GAIM_PREF_STRING, "/core/logging/format", N_("Log format"), get_log_options}, + {GAIM_PREF_BOOLEAN, "/core/logging/log_ims", N_("Log IMs"), NULL}, + {GAIM_PREF_BOOLEAN, "/core/logging/log_chats", N_("Log chats"), NULL}, + {GAIM_PREF_BOOLEAN, "/core/logging/log_system", N_("Log status change events"), NULL}, + {GAIM_PREF_NONE, NULL, NULL, NULL}, +}; + +static void +save_cb(void *data, GaimRequestFields *allfields) +{ + GList *list; + for (list = gaim_request_fields_get_groups(allfields); list; list = list->next) + { + GaimRequestFieldGroup *group = list->data; + GList *fields = gaim_request_field_group_get_fields(group); + + for (; fields ; fields = fields->next) + { + GaimRequestField *field = fields->data; + GaimRequestFieldType type = gaim_request_field_get_type(field); + GaimPrefType pt; + gpointer val = NULL; + const char *id = gaim_request_field_get_id(field); + + switch (type) + { + case GAIM_REQUEST_FIELD_LIST: + val = gaim_request_field_list_get_selected(field)->data; + break; + case GAIM_REQUEST_FIELD_BOOLEAN: + val = GINT_TO_POINTER(gaim_request_field_bool_get_value(field)); + break; + case GAIM_REQUEST_FIELD_INTEGER: + val = GINT_TO_POINTER(gaim_request_field_int_get_value(field)); + break; + case GAIM_REQUEST_FIELD_STRING: + val = (gpointer)gaim_request_field_string_get_value(field); + break; + default: + break; + } + + pt = gaim_prefs_get_type(id); + switch (pt) + { + case GAIM_PREF_INT: + gaim_prefs_set_int(id, GPOINTER_TO_INT(val)); + break; + case GAIM_PREF_BOOLEAN: + gaim_prefs_set_bool(id, GPOINTER_TO_INT(val)); + break; + case GAIM_PREF_STRING: + gaim_prefs_set_string(id, val); + break; + default: + break; + } + } + } +} + +static void +add_pref_group(GaimRequestFields *fields, const char *title, Prefs *prefs) +{ + GaimRequestField *field; + GaimRequestFieldGroup *group; + int i; + + group = gaim_request_field_group_new(title); + gaim_request_fields_add_group(fields, group); + for (i = 0; prefs[i].pref; i++) + { + field = get_pref_field(prefs + i); + gaim_request_field_group_add_field(group, field); + } +} + +void gg_prefs_show_all() +{ + GaimRequestFields *fields; + + fields = gaim_request_fields_new(); + + add_pref_group(fields, _("Buddy List"), blist); + add_pref_group(fields, _("Conversations"), convs); + add_pref_group(fields, _("Logging"), logging); + + gaim_request_fields(NULL, _("Preferences"), NULL, NULL, fields, + _("Save"), G_CALLBACK(save_cb), _("Cancel"), NULL, NULL); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/gntprefs.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,45 @@ +/** + * @file gntprefs.h GNT Preferences API + * @ingroup gntui + * + * gaim + * + * 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 _GNT_PREFS_H +#define _GNT_PREFS_H + +/********************************************************************** + * @name GNT Preferences API + **********************************************************************/ +/*@{*/ + +/** + * Perform necessary initializations. + */ +void gg_prefs_init(void); + +/** + * Show the preferences dialog. + */ +void gg_prefs_show_all(void); + +/*@}*/ + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/gntrequest.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,536 @@ +/** + * @file gntrequest.c GNT Request API + * @ingroup gntui + * + * gaim + * + * 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 + */ +#include <gnt.h> +#include <gntbox.h> +#include <gntbutton.h> +#include <gntcheckbox.h> +#include <gntcombobox.h> +#include <gntentry.h> +#include <gntlabel.h> +#include <gntline.h> +#include <gnttree.h> + +#include "gntgaim.h" +#include "gntrequest.h" + +static GntWidget * +setup_request_window(const char *title, const char *primary, + const char *secondary, GaimRequestType type) +{ + GntWidget *window; + + window = gnt_vbox_new(FALSE); + gnt_box_set_toplevel(GNT_BOX(window), TRUE); + gnt_box_set_title(GNT_BOX(window), title); + gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID); + + if (primary) + gnt_box_add_widget(GNT_BOX(window), + gnt_label_new_with_format(primary, GNT_TEXT_FLAG_BOLD)); + if (secondary) + gnt_box_add_widget(GNT_BOX(window), gnt_label_new(secondary)); + + g_signal_connect_swapped(G_OBJECT(window), "destroy", G_CALLBACK(gaim_request_close), + GINT_TO_POINTER(type)); + + return window; +} + +static GntWidget * +setup_button_box(gpointer userdata, gpointer cb, gpointer data, ...) +{ + GntWidget *box, *button; + va_list list; + const char *text; + gpointer callback; + + box = gnt_hbox_new(FALSE); + + va_start(list, data); + + while ((text = va_arg(list, const char *))) + { + callback = va_arg(list, gpointer); + button = gnt_button_new(text); + gnt_box_add_widget(GNT_BOX(box), button); + g_object_set_data(G_OBJECT(button), "activate-callback", callback); + g_object_set_data(G_OBJECT(button), "activate-userdata", userdata); + g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(cb), data); + } + + va_end(list); + return box; +} + +static void +notify_input_cb(GntWidget *button, GntWidget *entry) +{ + GaimRequestInputCb callback = g_object_get_data(G_OBJECT(button), "activate-callback"); + gpointer data = g_object_get_data(G_OBJECT(button), "activate-userdata"); + const char *text = gnt_entry_get_text(GNT_ENTRY(entry)); + + if (callback) + callback(data, text); + + while (button->parent) + button = button->parent; + + gaim_request_close(GAIM_REQUEST_INPUT, button); +} + +static void * +gg_request_input(const char *title, const char *primary, + const char *secondary, const char *default_value, + gboolean multiline, gboolean masked, gchar *hint, + const char *ok_text, GCallback ok_cb, + const char *cancel_text, GCallback cancel_cb, + void *user_data) +{ + GntWidget *window, *box, *entry; + + window = setup_request_window(title, primary, secondary, GAIM_REQUEST_INPUT); + + entry = gnt_entry_new(default_value); + if (masked) + gnt_entry_set_masked(GNT_ENTRY(entry), TRUE); + gnt_box_add_widget(GNT_BOX(window), entry); + + box = setup_button_box(user_data, notify_input_cb, entry, + ok_text, ok_cb, cancel_text, cancel_cb, NULL); + gnt_box_add_widget(GNT_BOX(window), box); + + gnt_widget_show(window); + + return window; +} + +static void +gg_close_request(GaimRequestType type, gpointer ui_handle) +{ + GntWidget *widget = GNT_WIDGET(ui_handle); + while (widget->parent) + widget = widget->parent; + gnt_widget_destroy(widget); +} + +static void +request_choice_cb(GntWidget *button, GntComboBox *combo) +{ + GaimRequestChoiceCb callback = g_object_get_data(G_OBJECT(button), "activate-callback"); + gpointer data = g_object_get_data(G_OBJECT(button), "activate-userdata"); + int choice = GPOINTER_TO_INT(gnt_combo_box_get_selected_data(GNT_COMBO_BOX(combo))) - 1; + + if (callback) + callback(data, choice); + + while (button->parent) + button = button->parent; + + gaim_request_close(GAIM_REQUEST_INPUT, button); +} + +static void * +gg_request_choice(const char *title, const char *primary, + const char *secondary, unsigned int default_value, + const char *ok_text, GCallback ok_cb, + const char *cancel_text, GCallback cancel_cb, + void *user_data, va_list choices) +{ + GntWidget *window, *combo, *box; + const char *text; + int val; + + window = setup_request_window(title, primary, secondary, GAIM_REQUEST_CHOICE); + + combo = gnt_combo_box_new(); + gnt_box_add_widget(GNT_BOX(window), combo); + while ((text = va_arg(choices, const char *))) + { + val = va_arg(choices, int); + gnt_combo_box_add_data(GNT_COMBO_BOX(combo), GINT_TO_POINTER(val + 1), text); + } + gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), GINT_TO_POINTER(default_value + 1)); + + box = setup_button_box(user_data, request_choice_cb, combo, + ok_text, ok_cb, cancel_text, cancel_cb, NULL); + gnt_box_add_widget(GNT_BOX(window), box); + + gnt_widget_show(window); + + return window; +} + +static void +request_action_cb(GntWidget *button, GntWidget *window) +{ + GaimRequestActionCb callback = g_object_get_data(G_OBJECT(button), "activate-callback"); + gpointer data = g_object_get_data(G_OBJECT(button), "activate-userdata"); + int id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(button), "activate-id")); + + if (callback) + callback(data, id); + + gaim_request_close(GAIM_REQUEST_ACTION, window); +} + +static void* +gg_request_action(const char *title, const char *primary, + const char *secondary, unsigned int default_value, + void *user_data, size_t actioncount, + va_list actions) +{ + GntWidget *window, *box, *button; + int i; + + window = setup_request_window(title, primary, secondary, GAIM_REQUEST_ACTION); + + box = gnt_hbox_new(FALSE); + gnt_box_add_widget(GNT_BOX(window), box); + for (i = 0; i < actioncount; i++) + { + const char *text = va_arg(actions, const char *); + GaimRequestActionCb callback = va_arg(actions, GaimRequestActionCb); + + button = gnt_button_new(text); + gnt_box_add_widget(GNT_BOX(box), button); + + g_object_set_data(G_OBJECT(button), "activate-callback", callback); + g_object_set_data(G_OBJECT(button), "activate-userdata", user_data); + g_object_set_data(G_OBJECT(button), "activate-id", GINT_TO_POINTER(i)); + g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(request_action_cb), window); + } + + gnt_widget_show(window); + + return window; +} + +static void +request_fields_cb(GntWidget *button, GaimRequestFields *fields) +{ + GaimRequestFieldsCb callback = g_object_get_data(G_OBJECT(button), "activate-callback"); + gpointer data = g_object_get_data(G_OBJECT(button), "activate-userdata"); + GList *list; + + /* Update the data of the fields. GtkGaim does this differently. Instead of + * updating the fields at the end like here, it updates the appropriate field + * instantly whenever a change is made. That allows it to make sure the + * 'required' fields are entered before the user can hit OK. It's not the case + * here, althought it can be done. I am not honouring the 'required' fields + * for the moment. */ + for (list = gaim_request_fields_get_groups(fields); list; list = list->next) + { + GaimRequestFieldGroup *group = list->data; + GList *fields = gaim_request_field_group_get_fields(group); + + for (; fields ; fields = fields->next) + { + GaimRequestField *field = fields->data; + GaimRequestFieldType type = gaim_request_field_get_type(field); + if (type == GAIM_REQUEST_FIELD_BOOLEAN) + { + GntWidget *check = field->ui_data; + gboolean value = gnt_check_box_get_checked(GNT_CHECK_BOX(check)); + gaim_request_field_bool_set_value(field, value); + } + else if (type == GAIM_REQUEST_FIELD_STRING) + { + GntWidget *entry = field->ui_data; + const char *text = gnt_entry_get_text(GNT_ENTRY(entry)); + gaim_request_field_string_set_value(field, (text && *text) ? text : NULL); + } + else if (type == GAIM_REQUEST_FIELD_INTEGER) + { + GntWidget *entry = field->ui_data; + const char *text = gnt_entry_get_text(GNT_ENTRY(entry)); + int value = (text && *text) ? atoi(text) : 0; + gaim_request_field_int_set_value(field, value); + } + else if (type == GAIM_REQUEST_FIELD_CHOICE) + { + GntWidget *combo = field->ui_data; + int id; + id = GPOINTER_TO_INT(gnt_combo_box_get_selected_data(GNT_COMBO_BOX(combo))); + gaim_request_field_choice_set_value(field, id); + } + else if (type == GAIM_REQUEST_FIELD_LIST) + { + GList *list = NULL; + if (gaim_request_field_list_get_multi_select(field)) + { + const GList *iter; + GntWidget *tree = field->ui_data; + + iter = gaim_request_field_list_get_items(field); + for (; iter; iter = iter->next) + { + const char *text = iter->data; + gpointer key = gaim_request_field_list_get_data(field, text); + if (gnt_tree_get_choice(GNT_TREE(tree), key)) + list = g_list_prepend(list, key); + } + } + else + { + GntWidget *combo = field->ui_data; + gpointer data = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(combo)); + list = g_list_append(list, data); + } + + gaim_request_field_list_set_selected(field, list); + g_list_free(list); + } + else if (type == GAIM_REQUEST_FIELD_ACCOUNT) + { + GntWidget *combo = field->ui_data; + GaimAccount *acc = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(combo)); + gaim_request_field_account_set_value(field, acc); + } + } + } + + if (callback) + callback(data, fields); + + while (button->parent) + button = button->parent; + + gaim_request_close(GAIM_REQUEST_FIELDS, button); +} + +static void * +gg_request_fields(const char *title, const char *primary, + const char *secondary, GaimRequestFields *allfields, + const char *ok, GCallback ok_cb, + const char *cancel, GCallback cancel_cb, + void *userdata) +{ + GntWidget *window, *box; + GList *grlist; + + window = setup_request_window(title, primary, secondary, GAIM_REQUEST_FIELDS); + + /* This is how it's going to work: the request-groups are going to be + * stacked vertically one after the other. A GntLine will be separating + * the groups. */ + box = gnt_vbox_new(FALSE); + gnt_box_set_pad(GNT_BOX(box), 0); + gnt_box_set_fill(GNT_BOX(box), TRUE); + for (grlist = gaim_request_fields_get_groups(allfields); grlist; grlist = grlist->next) + { + GaimRequestFieldGroup *group = grlist->data; + GList *fields = gaim_request_field_group_get_fields(group); + GntWidget *hbox; + const char *title = gaim_request_field_group_get_title(group); + + if (title) + gnt_box_add_widget(GNT_BOX(box), + gnt_label_new_with_format(title, GNT_TEXT_FLAG_BOLD)); + + for (; fields ; fields = fields->next) + { + /* XXX: Break each of the fields into a separate function? */ + GaimRequestField *field = fields->data; + GaimRequestFieldType type = gaim_request_field_get_type(field); + const char *label = gaim_request_field_get_label(field); + + hbox = gnt_hbox_new(TRUE); /* hrm */ + gnt_box_add_widget(GNT_BOX(box), hbox); + + if (type != GAIM_REQUEST_FIELD_BOOLEAN && label) + { + GntWidget *l = gnt_label_new(label); + gnt_widget_set_size(l, 0, 1); + gnt_box_add_widget(GNT_BOX(hbox), l); + } + + if (type == GAIM_REQUEST_FIELD_BOOLEAN) + { + GntWidget *check = gnt_check_box_new(label); + gnt_check_box_set_checked(GNT_CHECK_BOX(check), + gaim_request_field_bool_get_default_value(field)); + gnt_box_add_widget(GNT_BOX(hbox), check); + field->ui_data = check; + } + else if (type == GAIM_REQUEST_FIELD_STRING) + { + GntWidget *entry = gnt_entry_new( + gaim_request_field_string_get_default_value(field)); + gnt_entry_set_masked(GNT_ENTRY(entry), + gaim_request_field_string_is_masked(field)); + gnt_box_add_widget(GNT_BOX(hbox), entry); + field->ui_data = entry; + } + else if (type == GAIM_REQUEST_FIELD_INTEGER) + { + char str[256]; + int val = gaim_request_field_int_get_default_value(field); + GntWidget *entry; + + snprintf(str, sizeof(str), "%d", val); + entry = gnt_entry_new(str); + gnt_entry_set_flag(GNT_ENTRY(entry), GNT_ENTRY_FLAG_INT); + gnt_box_add_widget(GNT_BOX(hbox), entry); + field->ui_data = entry; + } + else if (type == GAIM_REQUEST_FIELD_CHOICE) + { + int id; + const GList *list; + GntWidget *combo = gnt_combo_box_new(); + gnt_box_add_widget(GNT_BOX(hbox), combo); + field->ui_data = combo; + + list = gaim_request_field_choice_get_labels(field); + for (id = 1; list; list = list->next, id++) + { + gnt_combo_box_add_data(GNT_COMBO_BOX(combo), + GINT_TO_POINTER(id), list->data); + } + gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), + GINT_TO_POINTER(gaim_request_field_choice_get_default_value(field))); + } + else if (type == GAIM_REQUEST_FIELD_LIST) + { + const GList *list; + gboolean multi = gaim_request_field_list_get_multi_select(field); + if (multi) + { + GntWidget *tree = gnt_tree_new(); + gnt_box_add_widget(GNT_BOX(hbox), tree); + field->ui_data = tree; + + list = gaim_request_field_list_get_items(field); + for (; list; list = list->next) + { + const char *text = list->data; + gpointer key = gaim_request_field_list_get_data(field, text); + gnt_tree_add_choice(GNT_TREE(tree), key, + gnt_tree_create_row(GNT_TREE(tree), text), NULL, NULL); + if (gaim_request_field_list_is_selected(field, text)) + gnt_tree_set_choice(GNT_TREE(tree), key, TRUE); + } + } + else + { + GntWidget *combo = gnt_combo_box_new(); + gnt_box_set_alignment(GNT_BOX(hbox), GNT_ALIGN_MID); + gnt_box_add_widget(GNT_BOX(hbox), combo); + field->ui_data = combo; + + list = gaim_request_field_list_get_items(field); + for (; list; list = list->next) + { + const char *text = list->data; + gpointer key = gaim_request_field_list_get_data(field, text); + gnt_combo_box_add_data(GNT_COMBO_BOX(combo), key, text); + if (gaim_request_field_list_is_selected(field, text)) + gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), key); + } + } + } + else if (type == GAIM_REQUEST_FIELD_ACCOUNT) + { + gboolean all; + GaimAccount *def; + GList *list; + GntWidget *combo = gnt_combo_box_new(); + gnt_box_set_alignment(GNT_BOX(hbox), GNT_ALIGN_MID); + gnt_box_add_widget(GNT_BOX(hbox), combo); + field->ui_data = combo; + + all = gaim_request_field_account_get_show_all(field); + def = gaim_request_field_account_get_default_value(field); + + if (all) + list = gaim_accounts_get_all(); + else + list = gaim_connections_get_all(); + + for (; list; list = list->next) + { + GaimAccount *account; + char *text; + + if (all) + account = list->data; + else + account = gaim_connection_get_account(list->data); + + text = g_strdup_printf("%s (%s)", + gaim_account_get_username(account), + gaim_account_get_protocol_name(account)); + gnt_combo_box_add_data(GNT_COMBO_BOX(combo), account, text); + g_free(text); + if (account == def) + gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), account); + } + gnt_widget_set_size(combo, 20, 3); /* ew */ + } + else + { + gnt_box_add_widget(GNT_BOX(hbox), + gnt_label_new_with_format(_("Not implemented yet."), + GNT_TEXT_FLAG_BOLD)); + } + } + if (grlist->next) + gnt_box_add_widget(GNT_BOX(box), gnt_hline_new()); + } + gnt_box_add_widget(GNT_BOX(window), box); + + box = setup_button_box(userdata, request_fields_cb, allfields, + ok, ok_cb, cancel, cancel_cb, NULL); + gnt_box_add_widget(GNT_BOX(window), box); + + gnt_widget_show(window); + + return window; +} + +static GaimRequestUiOps uiops = +{ + .request_input = gg_request_input, + .close_request = gg_close_request, + .request_choice = gg_request_choice, + .request_action = gg_request_action, + .request_fields = gg_request_fields, + .request_file = NULL, /* No plans for these */ + .request_folder = NULL +}; + +GaimRequestUiOps *gg_request_get_ui_ops() +{ + return &uiops; +} + +void gg_request_init() +{ +} + +void gg_request_uninit() +{ +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/gntrequest.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,54 @@ +/** + * @file gntrequest.h GNT Request API + * @ingroup gntui + * + * gaim + * + * 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 _GNT_REQUEST_H +#define _GNT_REQUEST_H + +#include "request.h" + +/********************************************************************** + * @name GNT Request API + **********************************************************************/ +/*@{*/ + +/** + * Get the ui-functions. + * + * @return The GaimRequestUiOps structure populated with the appropriate functions. + */ +GaimRequestUiOps *gg_request_get_ui_ops(void); + +/** + * Perform necessary initializations. + */ +void gg_request_init(void); + +/** + * Perform necessary uninitializations. + */ +void gg_request_uninit(void); + +/*@}*/ + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/gntstatus.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,606 @@ +/** + * @file gntstatus.c GNT Status API + * @ingroup gntui + * + * gaim + * + * 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 + */ +#include <gnt.h> +#include <gntbox.h> +#include <gntbutton.h> +#include <gntcombobox.h> +#include <gntentry.h> +#include <gntlabel.h> +#include <gntline.h> +#include <gnttree.h> + +#include <notify.h> +#include <request.h> + +#include "gntgaim.h" +#include "gntstatus.h" + +static struct +{ + GntWidget *window; + GntWidget *tree; +} statuses; + +typedef struct +{ + GaimSavedStatus *saved; + GntWidget *window; + GntWidget *title; + GntWidget *type; + GntWidget *message; + GntWidget *tree; + GHashTable *hash; /* list of windows for substatuses */ +} EditStatus; + +typedef struct +{ + GaimAccount *account; + const GaimStatusType *type; + char *message; +} RowInfo; + +typedef struct +{ + GntWidget *window; + GntWidget *type; + GntWidget *message; + + EditStatus *parent; + RowInfo *key; +} EditSubStatus; + +static GList *edits; /* List of opened edit-status dialogs */ + +static void +reset_status_window(GntWidget *widget, gpointer null) +{ + statuses.window = NULL; + statuses.tree = NULL; +} + +static void +populate_statuses(GntTree *tree) +{ + const GList *list; + + for (list = gaim_savedstatuses_get_all(); list; list = list->next) + { + GaimSavedStatus *saved = list->data; + const char *title, *type, *message; + + if (gaim_savedstatus_is_transient(saved)) + continue; + + title = gaim_savedstatus_get_title(saved); + type = gaim_primitive_get_name_from_type(gaim_savedstatus_get_type(saved)); + message = gaim_savedstatus_get_message(saved); /* XXX: Strip possible markups */ + + gnt_tree_add_row_last(tree, saved, + gnt_tree_create_row(tree, title, type, message), NULL); + } +} + +static void +really_delete_status(GaimSavedStatus *saved) +{ + GList *iter; + + for (iter = edits; iter; iter = iter->next) + { + EditStatus *edit = iter->data; + if (edit->saved == saved) + { + gnt_widget_destroy(edit->window); + break; + } + } + + if (statuses.tree) + gnt_tree_remove(GNT_TREE(statuses.tree), saved); + + gaim_savedstatus_delete(gaim_savedstatus_get_title(saved)); +} + +static void +ask_before_delete(GntWidget *button, gpointer null) +{ + char *ask; + GaimSavedStatus *saved; + + g_return_if_fail(statuses.tree != NULL); + + saved = gnt_tree_get_selection_data(GNT_TREE(statuses.tree)); + ask = g_strdup_printf(_("Are you sure you want to delete \"%s\""), + gaim_savedstatus_get_title(saved)); + + gaim_request_action(saved, _("Delete Status"), ask, NULL, 0, saved, 2, + _("Delete"), really_delete_status, _("Cancel"), NULL); + g_free(ask); +} + +static void +use_savedstatus_cb(GntWidget *widget, gpointer null) +{ + g_return_if_fail(statuses.tree != NULL); + + gaim_savedstatus_activate(gnt_tree_get_selection_data(GNT_TREE(statuses.tree))); +} + +static void +edit_savedstatus_cb(GntWidget *widget, gpointer null) +{ + g_return_if_fail(statuses.tree != NULL); + + gg_savedstatus_edit(gnt_tree_get_selection_data(GNT_TREE(statuses.tree))); +} + +void gg_savedstatus_show_all() +{ + GntWidget *window, *tree, *box, *button; + if (statuses.window) + return; + + statuses.window = window = gnt_vbox_new(FALSE); + gnt_box_set_toplevel(GNT_BOX(window), TRUE); + gnt_box_set_title(GNT_BOX(window), _("Saved Statuses")); + gnt_box_set_fill(GNT_BOX(window), FALSE); + gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID); + gnt_box_set_pad(GNT_BOX(window), 0); + + /* XXX: Add some sorting function to sort alphabetically, perhaps */ + statuses.tree = tree = gnt_tree_new_with_columns(3); + gnt_tree_set_column_titles(GNT_TREE(tree), _("Title"), _("Type"), _("Message")); + gnt_tree_set_show_title(GNT_TREE(tree), TRUE); + gnt_tree_set_col_width(GNT_TREE(tree), 0, 25); + gnt_tree_set_col_width(GNT_TREE(tree), 1, 12); + gnt_tree_set_col_width(GNT_TREE(tree), 2, 35); + gnt_box_add_widget(GNT_BOX(window), tree); + + populate_statuses(GNT_TREE(tree)); + + box = gnt_hbox_new(FALSE); + gnt_box_add_widget(GNT_BOX(window), box); + + button = gnt_button_new(_("Use")); + gnt_box_add_widget(GNT_BOX(box), button); + g_signal_connect(G_OBJECT(button), "activate", + G_CALLBACK(use_savedstatus_cb), NULL); + + button = gnt_button_new(_("Add")); + gnt_box_add_widget(GNT_BOX(box), button); + g_signal_connect_swapped(G_OBJECT(button), "activate", + G_CALLBACK(gg_savedstatus_edit), NULL); + + button = gnt_button_new(_("Edit")); + gnt_box_add_widget(GNT_BOX(box), button); + g_signal_connect(G_OBJECT(button), "activate", + G_CALLBACK(edit_savedstatus_cb), NULL); + + button = gnt_button_new(_("Delete")); + gnt_box_add_widget(GNT_BOX(box), button); + g_signal_connect(G_OBJECT(button), "activate", + G_CALLBACK(ask_before_delete), NULL); + + button = gnt_button_new(_("Close")); + gnt_box_add_widget(GNT_BOX(box), button); + g_signal_connect_swapped(G_OBJECT(button), "activate", + G_CALLBACK(gnt_widget_destroy), window); + + g_signal_connect(G_OBJECT(window), "destroy", + G_CALLBACK(reset_status_window), NULL); + gnt_widget_show(window); +} + +static void +destroy_substatus_win(GaimAccount *account, EditSubStatus *sub, gpointer null) +{ + gnt_widget_destroy(sub->window); /* the "destroy" callback will remove entry from the hashtable */ +} + +static void +free_key(gpointer key, gpointer n) +{ + RowInfo *row = key; + g_free(row->message); + g_free(key); +} + + +static void +update_edit_list(GntWidget *widget, EditStatus *edit) +{ + edits = g_list_remove(edits, edit); + gaim_notify_close_with_handle(edit); + g_hash_table_foreach(edit->hash, (GHFunc)destroy_substatus_win, NULL); + g_list_foreach((GList*)gnt_tree_get_rows(GNT_TREE(edit->tree)), free_key, NULL); + g_free(edit); +} + +static void +set_substatuses(EditStatus *edit) +{ + const GList *iter; + for (iter = gnt_tree_get_rows(GNT_TREE(edit->tree)); iter; iter = iter->next) { + RowInfo *key = iter->data; + if (gnt_tree_get_choice(GNT_TREE(edit->tree), key)) { + gaim_savedstatus_set_substatus(edit->saved, key->account, key->type, key->message); + } + } +} + + +static void +use_trans_status_cb(GntWidget *button, EditStatus *edit) +{ + const char *message; + GaimStatusPrimitive prim; + GaimSavedStatus *saved; + + message = gnt_entry_get_text(GNT_ENTRY(edit->message)); + prim = GPOINTER_TO_INT(gnt_combo_box_get_selected_data(GNT_COMBO_BOX(edit->type))); + + saved = gaim_savedstatus_find_transient_by_type_and_message(prim, message); + if (saved == NULL) { + saved = gaim_savedstatus_new(NULL, prim); + edit->saved = saved; + set_substatuses(edit); + } + gaim_savedstatus_set_message(saved, message); + gaim_savedstatus_activate(saved); + gnt_widget_destroy(edit->window); +} + +static void +save_savedstatus_cb(GntWidget *button, EditStatus *edit) +{ + const char *title, *message; + GaimStatusPrimitive prim; + GaimSavedStatus *find; + + title = gnt_entry_get_text(GNT_ENTRY(edit->title)); + message = gnt_entry_get_text(GNT_ENTRY(edit->message)); + if (!message || !*message) + message = NULL; + + prim = GPOINTER_TO_INT(gnt_combo_box_get_selected_data(GNT_COMBO_BOX(edit->type))); + + if (!title || !*title) + { + gaim_notify_error(edit, _("Error"), _("Invalid title"), + _("Please enter a non-empty title for the status.")); + return; + } + + find = gaim_savedstatus_find(title); + if (find && find != edit->saved) + { + gaim_notify_error(edit, _("Error"), _("Duplicate title"), + _("Please enter a different title for the status.")); + return; + } + + if (edit->saved == NULL) + { + edit->saved = gaim_savedstatus_new(title, prim); + gaim_savedstatus_set_message(edit->saved, message); + set_substatuses(edit); + if (statuses.tree) + gnt_tree_add_row_last(GNT_TREE(statuses.tree), edit->saved, + gnt_tree_create_row(GNT_TREE(statuses.tree), title, + gaim_primitive_get_name_from_type(prim), message), NULL); + } + else + { + gaim_savedstatus_set_title(edit->saved, title); + gaim_savedstatus_set_type(edit->saved, prim); + gaim_savedstatus_set_message(edit->saved, message); + if (statuses.tree) + { + gnt_tree_change_text(GNT_TREE(statuses.tree), edit->saved, 0, title); + gnt_tree_change_text(GNT_TREE(statuses.tree), edit->saved, 1, + gaim_primitive_get_name_from_type(prim)); + gnt_tree_change_text(GNT_TREE(statuses.tree), edit->saved, 2, message); + } + } + + if (g_object_get_data(G_OBJECT(button), "use")) + gaim_savedstatus_activate(edit->saved); + + gnt_widget_destroy(edit->window); +} + +static void +add_substatus(EditStatus *edit, GaimAccount *account) +{ + char *name; + const char *type = NULL, *message = NULL; + GaimSavedStatusSub *sub = NULL; + RowInfo *key; + + if (!edit || !edit->tree) + return; + + if (edit->saved) + sub = gaim_savedstatus_get_substatus(edit->saved, account); + + key = g_new0(RowInfo, 1); + key->account = account; + + if (sub) + { + key->type = gaim_savedstatus_substatus_get_type(sub); + type = gaim_status_type_get_name(key->type); + message = gaim_savedstatus_substatus_get_message(sub); + key->message = g_strdup(message); + } + + name = g_strdup_printf("%s (%s)", gaim_account_get_username(account), + gaim_account_get_protocol_name(account)); + gnt_tree_add_choice(GNT_TREE(edit->tree), key, + gnt_tree_create_row(GNT_TREE(edit->tree), + name, type ? type : "", message ? message : ""), NULL, NULL); + + if (sub) + gnt_tree_set_choice(GNT_TREE(edit->tree), key, TRUE); + g_free(name); +} + +static void +substatus_window_destroy_cb(GntWidget *window, EditSubStatus *sub) +{ + g_hash_table_remove(sub->parent->hash, sub->key->account); + g_free(sub); +} + +static void +save_substatus_cb(GntWidget *widget, EditSubStatus *sub) +{ + GaimSavedStatus *saved = sub->parent->saved; + RowInfo *row = sub->key; + const char *message; + GaimStatusType *type; + + type = gnt_combo_box_get_selected_data(GNT_COMBO_BOX(sub->type)); + message = gnt_entry_get_text(GNT_ENTRY(sub->message)); + + row->type = type; + row->message = g_strdup(message); + + if (saved) /* Save the substatus if the savedstatus actually exists. */ + gaim_savedstatus_set_substatus(saved, row->account, type, message); + + gnt_tree_set_choice(GNT_TREE(sub->parent->tree), row, TRUE); + gnt_tree_change_text(GNT_TREE(sub->parent->tree), row, 1, + gaim_status_type_get_name(type)); + gnt_tree_change_text(GNT_TREE(sub->parent->tree), row, 2, message); + + gnt_widget_destroy(sub->window); +} + +static gboolean +popup_substatus(GntTree *tree, const char *key, EditStatus *edit) +{ + if (key[0] == ' ' && key[1] == 0) + { + EditSubStatus *sub; + GntWidget *window, *combo, *entry, *box, *button, *l; + GaimSavedStatusSub *substatus = NULL; + const GList *iter; + char *name; + RowInfo *selected = gnt_tree_get_selection_data(tree); + GaimAccount *account = selected->account; + + if (gnt_tree_get_choice(tree, selected)) + { + /* There was a savedstatus for this account. Now remove it. */ + g_free(selected->message); + selected->type = NULL; + selected->message = NULL; + /* XXX: should we really be saving it right now? */ + gaim_savedstatus_unset_substatus(edit->saved, account); + gnt_tree_change_text(tree, account, 1, NULL); + gnt_tree_change_text(tree, account, 2, NULL); + return FALSE; + } + + if (g_hash_table_lookup(edit->hash, account)) + return TRUE; + + if (edit->saved) + substatus = gaim_savedstatus_get_substatus(edit->saved, account); + + sub = g_new0(EditSubStatus, 1); + sub->parent = edit; + sub->key = selected; + + sub->window = window = gnt_vbox_new(FALSE); + gnt_box_set_toplevel(GNT_BOX(window), TRUE); + gnt_box_set_title(GNT_BOX(window), _("Substatus")); /* XXX: a better title */ + + box = gnt_hbox_new(FALSE); + gnt_box_add_widget(GNT_BOX(box), gnt_label_new(_("Account:"))); + name = g_strdup_printf("%s (%s)", gaim_account_get_username(account), + gaim_account_get_protocol_name(account)); + gnt_box_add_widget(GNT_BOX(box), gnt_label_new(name)); + g_free(name); + gnt_box_add_widget(GNT_BOX(window), box); + + box = gnt_hbox_new(FALSE); + gnt_box_add_widget(GNT_BOX(box), (l = gnt_label_new(_("Status:")))); + gnt_widget_set_size(l, 0, 1); /* I don't like having to do this */ + sub->type = combo = gnt_combo_box_new(); + gnt_box_add_widget(GNT_BOX(box), combo); + gnt_box_add_widget(GNT_BOX(window), box); + + for (iter = gaim_account_get_status_types(account); iter; iter = iter->next) + { + GaimStatusType *type = iter->data; + if (!gaim_status_type_is_user_settable(type)) + continue; + gnt_combo_box_add_data(GNT_COMBO_BOX(combo), type, gaim_status_type_get_name(type)); + } + + box = gnt_hbox_new(FALSE); + gnt_box_add_widget(GNT_BOX(box), gnt_label_new(_("Message:"))); + sub->message = entry = gnt_entry_new(substatus ? gaim_savedstatus_substatus_get_message(substatus) : NULL); + gnt_box_add_widget(GNT_BOX(box), entry); + gnt_box_add_widget(GNT_BOX(window), box); + + box = gnt_hbox_new(FALSE); + button = gnt_button_new(_("Cancel")); + g_signal_connect_swapped(G_OBJECT(button), "activate", G_CALLBACK(gnt_widget_destroy), window); + gnt_box_add_widget(GNT_BOX(box), button); + button = gnt_button_new(_("Save")); + g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(save_substatus_cb), sub); + gnt_box_add_widget(GNT_BOX(box), button); + gnt_box_add_widget(GNT_BOX(window), box); + + gnt_widget_show(window); + + g_hash_table_insert(edit->hash, account, sub); + + g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(substatus_window_destroy_cb), sub); + + return TRUE; + } + return FALSE; +} + +void gg_savedstatus_edit(GaimSavedStatus *saved) +{ + EditStatus *edit; + GntWidget *window, *box, *button, *entry, *combo, *label, *tree; + GaimStatusPrimitive prims[] = {GAIM_STATUS_AVAILABLE, GAIM_STATUS_AWAY, + GAIM_STATUS_INVISIBLE, GAIM_STATUS_OFFLINE, GAIM_STATUS_UNSET}, current; + GList *iter; + int i; + + if (saved) + { + GList *iter; + for (iter = edits; iter; iter = iter->next) + { + edit = iter->data; + if (edit->saved == saved) + return; + } + } + + edit = g_new0(EditStatus, 1); + edit->saved = saved; + edit->window = window = gnt_vbox_new(FALSE); + gnt_box_set_toplevel(GNT_BOX(window), TRUE); + gnt_box_set_title(GNT_BOX(window), _("Edit Status")); + gnt_box_set_fill(GNT_BOX(window), TRUE); + gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_LEFT); + gnt_box_set_pad(GNT_BOX(window), 0); + + edits = g_list_append(edits, edit); + + /* Title */ + box = gnt_hbox_new(FALSE); + gnt_box_set_alignment(GNT_BOX(box), GNT_ALIGN_LEFT); + gnt_box_add_widget(GNT_BOX(window), box); + gnt_box_add_widget(GNT_BOX(box), gnt_label_new(_("Title"))); + + edit->title = entry = gnt_entry_new(saved ? gaim_savedstatus_get_title(saved) : NULL); + gnt_box_add_widget(GNT_BOX(box), entry); + + /* Type */ + box = gnt_hbox_new(FALSE); + gnt_box_add_widget(GNT_BOX(window), box); + gnt_box_add_widget(GNT_BOX(box), label = gnt_label_new(_("Status"))); + gnt_widget_set_size(label, 0, 1); + + edit->type = combo = gnt_combo_box_new(); + gnt_box_add_widget(GNT_BOX(box), combo); + current = saved ? gaim_savedstatus_get_type(saved) : GAIM_STATUS_UNSET; + for (i = 0; prims[i] != GAIM_STATUS_UNSET; i++) + { + gnt_combo_box_add_data(GNT_COMBO_BOX(combo), GINT_TO_POINTER(prims[i]), + gaim_primitive_get_name_from_type(prims[i])); + if (prims[i] == current) + gnt_combo_box_set_selected(GNT_COMBO_BOX(combo), GINT_TO_POINTER(current)); + } + + /* Message */ + box = gnt_hbox_new(FALSE); + gnt_box_add_widget(GNT_BOX(window), box); + gnt_box_add_widget(GNT_BOX(box), gnt_label_new(_("Message"))); + + edit->message = entry = gnt_entry_new(saved ? gaim_savedstatus_get_message(saved) : NULL); + gnt_box_add_widget(GNT_BOX(window), entry); + + gnt_box_add_widget(GNT_BOX(window), gnt_hline_new()); + gnt_box_add_widget(GNT_BOX(window), gnt_label_new(_("Use different status for following accounts"))); + + edit->hash = g_hash_table_new(g_direct_hash, g_direct_equal); + edit->tree = tree = gnt_tree_new_with_columns(3); + gnt_box_add_widget(GNT_BOX(window), tree); + gnt_tree_set_show_title(GNT_TREE(tree), TRUE); + gnt_tree_set_column_titles(GNT_TREE(tree), _("Account"), _("Status"), _("Message")); + gnt_tree_set_col_width(GNT_TREE(tree), 0, 30); + gnt_tree_set_col_width(GNT_TREE(tree), 1, 10); + gnt_tree_set_col_width(GNT_TREE(tree), 2, 30); + + for (iter = gaim_accounts_get_all(); iter; iter = iter->next) + { + add_substatus(edit, iter->data); + } + + g_signal_connect(G_OBJECT(tree), "key_pressed", G_CALLBACK(popup_substatus), edit); + + /* The buttons */ + box = gnt_hbox_new(FALSE); + gnt_box_add_widget(GNT_BOX(window), box); + + /* Use */ + button = gnt_button_new(_("Use")); + gnt_box_add_widget(GNT_BOX(box), button); + g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(use_trans_status_cb), edit); + + /* Save */ + button = gnt_button_new(_("Save")); + gnt_box_add_widget(GNT_BOX(box), button); + g_object_set_data(G_OBJECT(button), "use", NULL); + g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(save_savedstatus_cb), edit); + + /* Save & Use */ + button = gnt_button_new(_("Save & Use")); + gnt_box_add_widget(GNT_BOX(box), button); + g_object_set_data(G_OBJECT(button), "use", GINT_TO_POINTER(TRUE)); + g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(save_savedstatus_cb), edit); + + /* Cancel */ + button = gnt_button_new(_("Cancel")); + gnt_box_add_widget(GNT_BOX(box), button); + g_signal_connect_swapped(G_OBJECT(button), "activate", + G_CALLBACK(gnt_widget_destroy), window); + + g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(update_edit_list), edit); + + gnt_widget_show(window); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/gntstatus.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,50 @@ +/** + * @file gntstatus.h GNT Status API + * @ingroup gntui + * + * gaim + * + * 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 _GNT_STATUS_H +#define _GNT_STATUS_H + +#include <status.h> +#include <savedstatuses.h> + +/********************************************************************** + * @name GNT BuddyList API + **********************************************************************/ +/*@{*/ + +/** + * Show a dialog with all the saved statuses. + */ +void gg_savedstatus_show_all(void); + +/** + * Show a dialog to edit a status. + * + * @param saved The saved status to edit. Set it to @c NULL to create a new status. + */ +void gg_savedstatus_edit(GaimSavedStatus *saved); + +/*@}*/ + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/gntui.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,105 @@ +/** + * gaim + * + * 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 + */ +#include "gntui.h" + +#include "gntaccount.h" +#include "gntblist.h" +#include "gntconn.h" +#include "gntconv.h" +#include "gntdebug.h" +#include "gntnotify.h" +#include "gntplugin.h" +#include "gntprefs.h" +#include "gntrequest.h" +#include "gntstatus.h" + +#include "internal.h" + +#include <prefs.h> + +void gnt_ui_init() +{ +#ifdef STANDALONE + gnt_init(); +#endif + + gaim_prefs_add_none("/gaim/gnt"); + + /* Accounts */ + gg_accounts_init(); + gaim_accounts_set_ui_ops(gg_accounts_get_ui_ops()); + + /* Connections */ + gg_connections_init(); + gaim_connections_set_ui_ops(gg_connections_get_ui_ops()); + + /* Initialize the buddy list */ + gg_blist_init(); + gaim_blist_set_ui_ops(gg_blist_get_ui_ops()); + + /* Now the conversations */ + gg_conversation_init(); + gaim_conversations_set_ui_ops(gg_conv_get_ui_ops()); + + /* Notify */ + gg_notify_init(); + gaim_notify_set_ui_ops(gg_notify_get_ui_ops()); + + gg_request_init(); + gaim_request_set_ui_ops(gg_request_get_ui_ops()); + + gnt_register_action(_("Accounts"), gg_accounts_show_all); + gnt_register_action(_("Buddy List"), gg_blist_show); + gnt_register_action(_("Debug Window"), gg_debug_window_show); + gnt_register_action(_("Plugins"), gg_plugins_show_all); + gnt_register_action(_("Preferences"), gg_prefs_show_all); + gnt_register_action(_("Statuses"), gg_savedstatus_show_all); + +#ifdef STANDALONE + + gg_plugins_save_loaded(); +} + +void gnt_ui_uninit() +{ + gaim_accounts_set_ui_ops(NULL); + gg_accounts_uninit(); + + gaim_connections_set_ui_ops(NULL); + gg_connections_uninit(); + + gaim_blist_set_ui_ops(NULL); + gg_blist_uninit(); + + gaim_conversations_set_ui_ops(NULL); + gg_conversation_uninit(); + + gaim_notify_set_ui_ops(NULL); + gg_notify_uninit(); + + gaim_request_set_ui_ops(NULL); + gg_request_uninit(); + + gnt_quit(); +#endif +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/gntui.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,30 @@ +/** + * gaim + * + * 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 _GNT_UI_H +#define _GNT_UI_H + +#include "gnt.h" + +void gnt_ui_init(void); +void gnt_ui_uninit(void); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/COPYING Sun May 20 06:19:49 2007 +0000 @@ -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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 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.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/INSTALL Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,229 @@ +Copyright 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software +Foundation, Inc. + + This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. (Caching is +disabled by default to prevent problems with accidental use of stale +cache files.) + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You only need +`configure.ac' if you want to change it or regenerate `configure' using +a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. Run `./configure --help' +for details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not support the `VPATH' +variable, you have to compile the package for one architecture at a +time in the source code directory. After you have installed the +package for one architecture, use `make distclean' before reconfiguring +for another architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, `configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the `--target=TYPE' option to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + + Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +will cause the specified gcc to be used as the C compiler (unless it is +overridden in the site shell script). + +`configure' Invocation +====================== + + `configure' recognizes the following options to control how it +operates. + +`--help' +`-h' + Print a summary of the options to `configure', and exit. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/Makefile.am Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,79 @@ +EXTRA_DIST=genmarshal + +SUBDIRS = . wms +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = gnt.pc + +lib_LTLIBRARIES = libgnt.la + +libgnt_la_SOURCES = \ + gntmarshal.c \ + gntwidget.c \ + gntbindable.c \ + gntbox.c \ + gntbutton.c \ + gntcheckbox.c \ + gntcolors.c \ + gntcombobox.c \ + gntentry.c \ + gntkeys.c \ + gntlabel.c \ + gntline.c \ + gntmenu.c \ + gntmenuitem.c \ + gntmenuitemcheck.c \ + gntstyle.c \ + gnttextview.c \ + gnttree.c \ + gntutils.c \ + gntwindow.c \ + gntwm.c \ + gntmain.c + +libgnt_la_headers = \ + gntwidget.h \ + gntbindable.h \ + gntbox.h \ + gntbutton.h \ + gntcheckbox.h \ + gntcolors.h \ + gntcombobox.h \ + gntentry.h \ + gntkeys.h \ + gntlabel.h \ + gntline.h \ + gntmarshal.h \ + gntmenu.h \ + gntmenuitem.h \ + gntmenuitemcheck.h \ + gntstyle.h \ + gnttextview.h \ + gnttree.h \ + gntutils.h \ + gntwindow.h \ + gntwm.h \ + gnt.h + +CLEANFILES = \ + gntmarshal.h \ + gntmarshal.c + +gntmarshal.c: genmarshal gntmarshal.h + cat genmarshal | glib-genmarshal --prefix=gnt_closure_marshal --body > $@ + +gntmarshal.h: genmarshal + cat genmarshal | glib-genmarshal --prefix=gnt_closure_marshal --header > $@ + +libgnt_laincludedir=$(includedir)/gnt +libgnt_lainclude_HEADERS = \ + $(libgnt_la_headers) + +libgnt_la_DEPENDENCIES = +libgnt_la_LDFLAGS = -export-dynamic +libgnt_la_LIBADD = \ + $(GLIB_LIBS) \ + $(GNT_LIBS) + +AM_CPPFLAGS = \ + $(GLIB_CFLAGS) \ + $(GNT_CFLAGS)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/autogen.sh Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,48 @@ +#!/bin/sh + +(libtoolize --version) < /dev/null > /dev/null 2>&1 || { + echo; + echo "You must have libtool installed to compile LibGNT"; + echo; + exit; +} + +(automake --version) < /dev/null > /dev/null 2>&1 || { + echo; + echo "You must have automake installed to compile LibGNT"; + echo; + exit; +} + +(autoconf --version) < /dev/null > /dev/null 2>&1 || { + echo; + echo "You must have autoconf installed to compile LibGNT"; + echo; + exit; +} + +echo "Generating configuration files for LibGNT, please wait...." +echo; + +echo "Running libtoolize, please ignore non-fatal messages...." +echo n | libtoolize --copy --force || exit; + +# Add other directories to this list if people continue to experience +# brokennesses ... Obviously the real answer is for them to fix it +# themselves, but for Luke's sake we have this. +for dir in "/usr/local/share/aclocal" \ + "/opt/gnome-1.4/share/aclocal" +do + if test -d $dir ; then + ACLOCAL_FLAGS="$ACLOCAL_FLAGS -I $dir" + fi +done + +libtoolize -c -f --automake +aclocal $ACLOCAL_FLAGS || exit; +autoheader || exit; +automake --add-missing --copy; +autoconf || exit; +automake || exit; +./configure $@ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/configure.ac Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,257 @@ +dnl Process this file with autoconf to produce a configure script. +AC_INIT([libgnt], [0.0.0dev], [gaim-devel@lists.sourceforge.net]) +AC_CANONICAL_SYSTEM +AM_CONFIG_HEADER(config.h) +AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION) + +AC_PREREQ([2.50]) + +AC_PATH_PROG(sedpath, sed) + +dnl Storing configure arguments +AC_DEFINE_UNQUOTED(CONFIG_ARGS, "$ac_configure_args", [configure arguments]) + +dnl Checks for programs. +AC_PROG_CC +AC_DISABLE_STATIC +AM_PROG_LIBTOOL +LIBTOOL="$LIBTOOL --silent" +AC_PROG_INSTALL + +dnl we don't use autobreak on cygwin!! +dnl AC_CYGWIN + +dnl Checks for header files. +AC_HEADER_STDC +AC_HEADER_SYS_WAIT +AC_CHECK_HEADERS(arpa/nameser_compat.h fcntl.h sys/time.h unistd.h locale.h signal.h stdint.h regex.h) + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_STRUCT_TM + +AC_C_BIGENDIAN + +dnl Checks for library functions. +AC_TYPE_SIGNAL +AC_FUNC_STRFTIME +AC_CHECK_FUNCS(strdup strstr atexit setlocale) + +dnl to prevent the g_stat()/g_unlink() crash, +dnl (09:50:07) Robot101: LSchiere2: it's easy. +LC_SYS_LARGEFILE somewhere in configure.ac +AC_SYS_LARGEFILE + +dnl FreeBSD doesn't have libdl, dlopen is provided by libc +AC_CHECK_FUNC(dlopen, LIBDL="", [AC_CHECK_LIB(dl, dlopen, LIBDL="-ldl")]) + +AC_MSG_CHECKING(for the %z format string in strftime()) +AC_TRY_RUN([ +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#include <time.h> +#include <stdio.h> + +int main() +{ + char buf[6]; + time_t t = time(NULL); + + if (strftime(buf, sizeof(buf), "%z", localtime(&t)) != 5) + return 1; + + fprintf(stderr, "strftime(\"%%z\") yields: \"%s\"\n", buf); + + return !((buf[0] == '-' || buf[0] == '+') && + (buf[1] >= '0' && buf[1] <= '9') && + (buf[2] >= '0' && buf[2] <= '9') && + (buf[3] >= '0' && buf[3] <= '9') && + (buf[4] >= '0' && buf[4] <= '9') + ); +} +], +[ + AC_MSG_RESULT(yes) + AC_DEFINE([HAVE_STRFTIME_Z_FORMAT], [1], + [Define to 1 if you have a strftime() that supports the %z format string.]) +], +[ + AC_MSG_RESULT(no) +], +[ + # Fallback for Cross Compiling... + # This will enable the compatibility code. + AC_MSG_RESULT(no) +] +) + + +AC_CHECK_HEADER(sys/utsname.h) +AC_CHECK_FUNC(uname) + +if test "x$enable_debug" = "xyes" ; then + AC_DEFINE(DEBUG, 1, [Define if debugging is enabled.]) + enable_fatal_asserts="yes" +fi + +if test "x$enable_fatal_asserts" = "xyes" ; then + AC_DEFINE(GAIM_FATAL_ASSERTS, 1, [Define to make assertions fatal (useful for debugging).]) +fi + +if test "x$enable_deprecated" = "xno"; then + DEBUG_CFLAGS="$DEBUG_CFLAGS -DG_DISABLE_DEPRECATED -DGDK_DISABLE_DEPRECATED -DGDK_PIXBUF_DISABLE_DEPRECATED -DGTK_DISABLE_DEPRECATED" +fi + +if test "x$GCC" = "xyes"; then + dnl We enable -Wall later. + dnl If it's set after the warning CFLAGS in the compiler invocation, it counteracts the -Wno... flags. + dnl This leads to warnings we don't want. + CFLAGS=`echo $CFLAGS |$sedpath 's/-Wall//'` + + dnl ENABLE WARNINGS SUPPORTED BY THE VERSION OF GCC IN USE + dnl + dnl Future Possibilities + dnl + dnl Consider adding -Wbad-function-cast. + dnl This leads to spurious warnings using GPOINTER_TO_INT(), et al. directly on a function call. + dnl We'd need an intermediate variable. + dnl + dnl Consider adding -Wfloat-equal. + dnl This leads to warnings with Perl. + dnl Perhaps we could write ugly configure magic and pass -Wno-float-equal down to that subdirectory. + dnl On the other hand, it's probably actually broken, so maybe the Perl folks should fix that? + dnl + dnl Consider removing -Wno-sign-compare (from the -Wextra set) and fixing all those cases. + dnl This is likely non-trivial. + dnl + for newflag in \ + "-Waggregate-return" \ + "-Wcast-align" \ + "-Wdeclaration-after-statement" \ + "-Werror-implicit-function-declaration" \ + "-Wextra -Wno-sign-compare -Wno-unused-parameter" \ + "-Winit-self" \ + "-Wmissing-declarations" \ + "-Wmissing-prototypes" \ + "-Wnested-externs" \ + "-Wpointer-arith" \ + "-Wundef" \ + ; do + orig_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $newflag" + AC_MSG_CHECKING(for $newflag option to gcc) + AC_TRY_COMPILE([], [ + int main() {return 0;} + ], [ + AC_MSG_RESULT(yes) + CFLAGS="$orig_CFLAGS" + DEBUG_CFLAGS="$DEBUG_CFLAGS $newflag" + ], [ + AC_MSG_RESULT(no) + CFLAGS="$orig_CFLAGS" + ]) + done + + if test "x$enable_fortify" = "xyes"; then + AC_MSG_CHECKING(for FORTIFY_SOURCE support) + AC_TRY_COMPILE([#include <features.h>], [ + int main() { + #if !(__GNUC_PREREQ (4, 1) \ + || (defined __GNUC_RH_RELEASE__ && __GNUC_PREREQ (4, 0)) \ + || (defined __GNUC_RH_RELEASE__ && __GNUC_PREREQ (3, 4) \ + && __GNUC_MINOR__ == 4 \ + && (__GNUC_PATCHLEVEL__ > 2 \ + || (__GNUC_PATCHLEVEL__ == 2 && __GNUC_RH_RELEASE__ >= 8)))) + #error No FORTIFY_SOURCE support + #endif + return 0; + } + ], [ + AC_MSG_RESULT(yes) + DEBUG_CFLAGS="$DEBUG_CFLAGS -D_FORTIFY_SOURCE=2" + ], [ + AC_MSG_RESULT(no) + ]) + fi + + DEBUG_CFLAGS="-Wall $DEBUG_CFLAGS" + CFLAGS="-g $CFLAGS" +fi +AC_SUBST(CFLAGS) + +PKG_CHECK_MODULES(GLIB, [glib-2.0 >= 2.0.0 gobject-2.0 gmodule-2.0],, + [ + AC_MSG_ERROR([ +*** GLib 2.0 is required to build LibGNT; please make sure you have the GLib +*** development headers installed. The latest version of GLib is +*** always available at http://www.gtk.org/.]) + ]) +AC_SUBST(GLIB_CFLAGS) +AC_SUBST(GLIB_LIBS) + + +AC_MSG_CHECKING(for me pot o' gold) +AC_MSG_RESULT(no) +AC_CHECK_FUNCS(gethostid lrand48) +AC_CHECK_FUNCS(memcpy memmove random strchr strerror vprintf) +AC_CHECK_HEADERS(malloc.h paths.h sgtty.h stdarg.h sys/cdefs.h) +AC_CHECK_HEADERS(sys/file.h sys/filio.h sys/ioctl.h sys/msgbuf.h) +AC_CHECK_HEADERS(sys/select.h sys/uio.h sys/utsname.h sys/wait.h) +AC_CHECK_HEADERS(termios.h) +#AC_CHECK_FUNC(wcwidth, [AC_DEFINE([HAVE_WCWIDTH], [1], [Define to 1 if you have wcwidth function.])]) +#AC_VAR_TIMEZONE_EXTERNALS + +GNT_CFLAGS= +GNT_LIBS= +AC_CHECK_LIB(ncursesw, initscr, [GNT_LIBS="-lncursesw"], [enable_gnt=no]) +AC_CHECK_LIB(panelw, update_panels, [GNT_LIBS="$GNT_LIBS -lpanelw"], [enable_gnt=no]) + +# If ncursesw is not found, look for plain old ncurses +if test "x$enable_gnt" = "xno"; then + AC_CHECK_LIB(ncurses, initscr, [[GNT_LIBS="-lncurses"] [enable_gnt=yes]], [enable_gnt=no]) + AC_CHECK_LIB(panel, update_panels, [[GNT_LIBS="$GNT_LIBS -lpanel"] [enable_gnt=yes]], [enable_gnt=no]) + AC_DEFINE(NO_WIDECHAR, [1], [Define to 1 if you do not have ncursesw.]) +else + dnl # Some distros put the headers in ncursesw/, some don't + found_ncurses_h=no + for f in /usr/include/ncursesw/ncurses.h /usr/include/ncurses.h + do + AC_CHECK_HEADER($f,[ + AC_MSG_CHECKING([if $f supports wide characters]) + AC_TRY_COMPILE([ + #define _XOPEN_SOURCE_EXTENDED + #include <$f> + ], [ + #ifndef get_wch + # error get_wch not found! + #endif + ], [ + dir=`dirname $f` + if test x"$dir" != x"." ; then + GNT_CFLAGS="-I$dir/" + else + GNT_CFLAGS="" + fi + + found_ncurses_h=yes + AC_MSG_RESULT([yes]) + break + ], [ + AC_MSG_RESULT([no]) + ]) + ]) + done +fi +AC_SUBST(GNT_CFLAGS) +AC_SUBST(GNT_LIBS) + +if test "x$enable_gnt" = "xno"; then + AC_MSG_ERROR([ +*** You need ncursesw or ncurses.]) +fi + +AC_OUTPUT([Makefile + gnt.pc + wms/Makefile + ]) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/genmarshal Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,10 @@ +BOOLEAN:VOID +BOOLEAN:STRING +VOID:INT,INT,INT,INT +VOID:INT,INT +VOID:POINTER,POINTER +BOOLEAN:INT,INT +BOOLEAN:INT,INT,INT +BOOLEAN:POINTER,POINTER,POINTER +BOOLEAN:INT,INT,INT,POINTER +VOID:STRING,STRING
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gnt-skel.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,106 @@ +#include "gnt-skel.h" + +enum +{ + SIGS = 1, +}; + +static GntWidgetClass *parent_class = NULL; +static guint signals[SIGS] = { 0 }; + +static void +gnt_skel_draw(GntWidget *widget) +{ + GNTDEBUG; +} + +static void +gnt_skel_size_request(GntWidget *widget) +{ +} + +static void +gnt_skel_map(GntWidget *widget) +{ + if (widget->priv.width == 0 || widget->priv.height == 0) + gnt_widget_size_request(widget); + GNTDEBUG; +} + +static gboolean +gnt_skel_key_pressed(GntWidget *widget, const char *text) +{ + return FALSE; +} + +static void +gnt_skel_destroy(GntWidget *widget) +{ +} + +static void +gnt_skel_class_init(GntSkelClass *klass) +{ + GObjectClass *obj_class = G_OBJECT_CLASS(klass); + + parent_class = GNT_WIDGET_CLASS(klass); + parent_class->destroy = gnt_skel_destroy; + parent_class->draw = gnt_skel_draw; + parent_class->map = gnt_skel_map; + parent_class->size_request = gnt_skel_size_request; + parent_class->key_pressed = gnt_skel_key_pressed; + + parent_class->actions = g_hash_table_duplicate(parent_class->actions, g_str_hash, + g_str_equal, NULL, (GDestroyNotify)gnt_widget_action_free); + parent_class->bindings = g_hash_table_duplicate(parent_class->bindings, g_str_hash, + g_str_equal, NULL, (GDestroyNotify)gnt_widget_action_param_free); + + gnt_widget_actions_read(G_OBJECT_CLASS_TYPE(klass), klass); + + GNTDEBUG; +} + +static void +gnt_skel_init(GTypeInstance *instance, gpointer class) +{ + GNTDEBUG; +} + +/****************************************************************************** + * GntSkel API + *****************************************************************************/ +GType +gnt_skel_get_gtype(void) +{ + static GType type = 0; + + if(type == 0) + { + static const GTypeInfo info = { + sizeof(GntSkelClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)gnt_skel_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof(GntSkel), + 0, /* n_preallocs */ + gnt_skel_init, /* instance_init */ + }; + + type = g_type_register_static(GNT_TYPE_WIDGET, + "GntSkel", + &info, 0); + } + + return type; +} + +GntWidget *gnt_skel_new() +{ + GntWidget *widget = g_object_new(GNT_TYPE_SKEL, NULL); + GntSkel *skel = GNT_SKEL(widget); + + return widget; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gnt-skel.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,47 @@ +#ifndef GNT_SKEL_H +#define GNT_SKEL_H + +#include "gntwidget.h" +#include "gnt.h" +#include "gntcolors.h" +#include "gntkeys.h" + +#define GNT_TYPE_SKEL (gnt_skel_get_gtype()) +#define GNT_SKEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_SKEL, GntSkel)) +#define GNT_SKEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_SKEL, GntSkelClass)) +#define GNT_IS_SKEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_SKEL)) +#define GNT_IS_SKEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GNT_TYPE_SKEL)) +#define GNT_SKEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GNT_TYPE_SKEL, GntSkelClass)) + +#define GNT_SKEL_FLAGS(obj) (GNT_SKEL(obj)->priv.flags) +#define GNT_SKEL_SET_FLAGS(obj, flags) (GNT_SKEL_FLAGS(obj) |= flags) +#define GNT_SKEL_UNSET_FLAGS(obj, flags) (GNT_SKEL_FLAGS(obj) &= ~(flags)) + +typedef struct _GnSkel GntSkel; +typedef struct _GnSkelPriv GntSkelPriv; +typedef struct _GnSkelClass GntSkelClass; + +struct _GnSkel +{ + GntWidget parent; +}; + +struct _GnSkelClass +{ + GntWidgetClass parent; + + void (*gnt_reserved1)(void); + void (*gnt_reserved2)(void); + void (*gnt_reserved3)(void); + void (*gnt_reserved4)(void); +}; + +G_BEGIN_DECLS + +GType gnt_skel_get_gtype(void); + +GntWidget *gnt_skel_new(); + +G_END_DECLS + +#endif /* GNT_SKEL_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gnt.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,35 @@ +#include <glib.h> +#include "gntwidget.h" +#include "gntcolors.h" +#include "gntkeys.h" + +void gnt_init(); + +void gnt_main(); + +gboolean gnt_ascii_only(); + +void gnt_screen_occupy(GntWidget *widget); + +void gnt_screen_release(GntWidget *widget); + +void gnt_screen_update(GntWidget *widget); + +void gnt_screen_take_focus(GntWidget *widget); + +void gnt_screen_resize_widget(GntWidget *widget, int width, int height); + +void gnt_screen_move_widget(GntWidget *widget, int x, int y); + +void gnt_screen_rename_widget(GntWidget *widget, const char *text); + +gboolean gnt_widget_has_focus(GntWidget *widget); + +void gnt_widget_set_urgent(GntWidget *widget); + +void gnt_register_action(const char *label, void (*callback)()); + +gboolean gnt_screen_menu_show(gpointer menu); + +void gnt_quit(); +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gnt.pc.in Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,13 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ +datadir=@datadir@ +sysconfdir=@sysconfdir@ + +Name: LibGNT +Description: Gaim Ncurses Toolkit is a collection of curses-widgets. +Version: @VERSION@ +Requires: glib-2.0 +Cflags: -I${includedir}/gnt +Libs: -L${libdir} -lgnt
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntbindable.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,229 @@ +#include "gntbindable.h" +#include "gntstyle.h" +#include "gnt.h" +#include "gntutils.h" + +static GObjectClass *parent_class = NULL; + +static void +gnt_bindable_class_init(GntBindableClass *klass) +{ + parent_class = g_type_class_peek_parent(klass); + + klass->actions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + (GDestroyNotify)gnt_bindable_action_free); + klass->bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + (GDestroyNotify)gnt_bindable_action_param_free); + + gnt_style_read_actions(G_OBJECT_CLASS_TYPE(klass), GNT_BINDABLE_CLASS(klass)); + GNTDEBUG; +} + +static gpointer +bindable_clone(GntBindableAction *action) +{ + GntBindableAction *ret = g_new0(GntBindableAction, 1); + ret->name = g_strdup(action->name); + ret->u = action->u; + return ret; +} + +static gpointer +binding_clone(GntBindableActionParam *param) +{ + GntBindableActionParam *p = g_new0(GntBindableActionParam, 1); + p->list = g_list_copy(param->list); + p->action = param->action; + return p; +} + +static void +duplicate_hashes(GntBindableClass *klass) +{ + /* Duplicate the bindings from parent class */ + if (klass->actions) { + klass->actions = g_hash_table_duplicate(klass->actions, g_str_hash, + g_str_equal, g_free, (GDestroyNotify)gnt_bindable_action_free, + (GDupFunc)g_strdup, (GDupFunc)bindable_clone); + klass->bindings = g_hash_table_duplicate(klass->bindings, g_str_hash, + g_str_equal, g_free, (GDestroyNotify)gnt_bindable_action_param_free, + (GDupFunc)g_strdup, (GDupFunc)binding_clone); + } else { + klass->actions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + (GDestroyNotify)gnt_bindable_action_free); + klass->bindings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + (GDestroyNotify)gnt_bindable_action_param_free); + } + + GNTDEBUG; +} + +/****************************************************************************** + * GntBindable API + *****************************************************************************/ +GType +gnt_bindable_get_gtype(void) +{ + static GType type = 0; + + if(type == 0) { + static const GTypeInfo info = { + sizeof(GntBindableClass), + (GBaseInitFunc)duplicate_hashes, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)gnt_bindable_class_init, + NULL, + NULL, /* class_data */ + sizeof(GntBindable), + 0, /* n_preallocs */ + NULL, /* instance_init */ + }; + + type = g_type_register_static(G_TYPE_OBJECT, + "GntBindable", + &info, G_TYPE_FLAG_ABSTRACT); + } + + return type; +} + +/** + * Key Remaps + */ +const char * +gnt_bindable_remap_keys(GntBindable *bindable, const char *text) +{ + const char *remap = NULL; + GType type = G_OBJECT_TYPE(bindable); + GntBindableClass *klass = GNT_BINDABLE_CLASS(GNT_BINDABLE_GET_CLASS(bindable)); + + if (klass->remaps == NULL) + { + klass->remaps = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + gnt_styles_get_keyremaps(type, klass->remaps); + } + + remap = g_hash_table_lookup(klass->remaps, text); + + return (remap ? remap : text); +} + +/** + * Actions and Bindings + */ +gboolean +gnt_bindable_perform_action_named(GntBindable *bindable, const char *name, ...) +{ + GntBindableClass *klass = GNT_BINDABLE_CLASS(GNT_BINDABLE_GET_CLASS(bindable)); + GList *list = NULL; + va_list args; + GntBindableAction *action; + void *p; + + va_start(args, name); + while ((p = va_arg(args, void *)) != NULL) + list = g_list_append(list, p); + va_end(args); + + action = g_hash_table_lookup(klass->actions, name); + if (action && action->u.action) { + return action->u.action(bindable, list); + } + return FALSE; +} + +gboolean +gnt_bindable_perform_action_key(GntBindable *bindable, const char *keys) +{ + GntBindableClass *klass = GNT_BINDABLE_CLASS(GNT_BINDABLE_GET_CLASS(bindable)); + GntBindableActionParam *param = g_hash_table_lookup(klass->bindings, keys); + + if (param && param->action) { + if (param->list) + return param->action->u.action(bindable, param->list); + else + return param->action->u.action_noparam(bindable); + } + return FALSE; +} + +static void +register_binding(GntBindableClass *klass, const char *name, const char *trigger, GList *list) +{ + GntBindableActionParam *param; + GntBindableAction *action; + + if (name == NULL || *name == '\0') { + g_hash_table_remove(klass->bindings, (char*)trigger); + return; + } + + action = g_hash_table_lookup(klass->actions, name); + if (!action) { + g_printerr("GntWidget: Invalid action name %s for %s\n", + name, g_type_name(G_OBJECT_CLASS_TYPE(klass))); + if (list) + g_list_free(list); + return; + } + + param = g_new0(GntBindableActionParam, 1); + param->action = action; + param->list = list; + g_hash_table_replace(klass->bindings, g_strdup(trigger), param); +} + +void gnt_bindable_register_binding(GntBindableClass *klass, const char *name, + const char *trigger, ...) +{ + GList *list = NULL; + va_list args; + void *data; + + va_start(args, trigger); + while ((data = va_arg(args, void *))) { + list = g_list_append(list, data); + } + va_end(args); + + register_binding(klass, name, trigger, list); +} + +void gnt_bindable_class_register_action(GntBindableClass *klass, const char *name, + GntBindableActionCallback callback, const char *trigger, ...) +{ + void *data; + va_list args; + GntBindableAction *action = g_new0(GntBindableAction, 1); + GList *list; + + action->name = g_strdup(name); + action->u.action = callback; + + g_hash_table_replace(klass->actions, g_strdup(name), action); + + if (trigger && *trigger) { + list = NULL; + va_start(args, trigger); + while ((data = va_arg(args, void *))) { + list = g_list_append(list, data); + } + va_end(args); + + register_binding(klass, name, trigger, list); + } +} + +void gnt_bindable_action_free(GntBindableAction *action) +{ + g_free(action->name); + g_free(action); +} + +void gnt_bindable_action_param_free(GntBindableActionParam *param) +{ + g_list_free(param->list); /* XXX: There may be a leak here for string parameters */ + g_free(param); +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntbindable.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,90 @@ +#ifndef GNT_BINDABLE_H +#define GNT_BINDABLE_H + +#include <stdio.h> +#include <glib.h> +#include <glib-object.h> +#include <ncurses.h> + +#define GNT_TYPE_BINDABLE (gnt_bindable_get_gtype()) +#define GNT_BINDABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_BINDABLE, GntBindable)) +#define GNT_BINDABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_BINDABLE, GntBindableClass)) +#define GNT_IS_BINDABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_BINDABLE)) +#define GNT_IS_BINDABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GNT_TYPE_BINDABLE)) +#define GNT_BINDABLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GNT_TYPE_BINDABLE, GntBindableClass)) + +#define GNTDEBUG fprintf(stderr, "%s\n", __FUNCTION__) + +typedef struct _GnBindable GntBindable; +typedef struct _GnBindableClass GntBindableClass; + +struct _GnBindable +{ + GObject inherit; +}; + +struct _GnBindableClass +{ + GObjectClass parent; + + GHashTable *remaps; /* Key remaps */ + GHashTable *actions; /* name -> Action */ + GHashTable *bindings; /* key -> ActionParam */ + + void (*gnt_reserved1)(void); + void (*gnt_reserved2)(void); + void (*gnt_reserved3)(void); + void (*gnt_reserved4)(void); +}; + +G_BEGIN_DECLS + +GType gnt_bindable_get_gtype(void); + +/******************/ +/* Key Remaps */ +/******************/ +const char * gnt_bindable_remap_keys(GntBindable *bindable, const char *text); + +/******************/ +/* Bindable Actions */ +/******************/ +typedef gboolean (*GntBindableActionCallback) (GntBindable *bindable, GList *params); +typedef gboolean (*GntBindableActionCallbackNoParam)(GntBindable *bindable); + +typedef struct _GnBindableAction GntBindableAction; +typedef struct _GnBindableActionParam GntBindableActionParam; + +struct _GnBindableAction +{ + char *name; /* The name of the action */ + union { + gboolean (*action)(GntBindable *bindable, GList *params); + gboolean (*action_noparam)(GntBindable *bindable); + } u; +}; + +struct _GnBindableActionParam +{ + GntBindableAction *action; + GList *list; +}; + + +/*GntBindableAction *gnt_bindable_action_parse(const char *name);*/ + +void gnt_bindable_action_free(GntBindableAction *action); +void gnt_bindable_action_param_free(GntBindableActionParam *param); + +void gnt_bindable_class_register_action(GntBindableClass *klass, const char *name, + GntBindableActionCallback callback, const char *trigger, ...); +void gnt_bindable_register_binding(GntBindableClass *klass, const char *name, + const char *trigger, ...); + +gboolean gnt_bindable_perform_action_key(GntBindable *bindable, const char *keys); +gboolean gnt_bindable_perform_action_named(GntBindable *bindable, const char *name, ...); + +G_END_DECLS + +#endif /* GNT_BINDABLE_H */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntbox.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,787 @@ +#include "gntbox.h" +#include "gntutils.h" + +#include <string.h> + +enum +{ + SIGS = 1, +}; + +static GntWidgetClass *parent_class = NULL; + +static GntWidget * find_focusable_widget(GntBox *box); + +static void +add_to_focus(gpointer value, gpointer data) +{ + GntBox *box = GNT_BOX(data); + GntWidget *w = GNT_WIDGET(value); + + if (GNT_IS_BOX(w)) + g_list_foreach(GNT_BOX(w)->list, add_to_focus, box); + else if (GNT_WIDGET_IS_FLAG_SET(w, GNT_WIDGET_CAN_TAKE_FOCUS)) + box->focus = g_list_append(box->focus, w); +} + +static void +get_title_thingies(GntBox *box, char *title, int *p, int *r) +{ + GntWidget *widget = GNT_WIDGET(box); + int len; + char *end = (char*)gnt_util_onscreen_width_to_pointer(title, widget->priv.width - 4, &len); + + if (p) + *p = (widget->priv.width - len) / 2; + if (r) + *r = (widget->priv.width + len) / 2; + *end = '\0'; +} + +static void +gnt_box_draw(GntWidget *widget) +{ + GntBox *box = GNT_BOX(widget); + + if (box->focus == NULL && widget->parent == NULL) + g_list_foreach(box->list, add_to_focus, box); + + g_list_foreach(box->list, (GFunc)gnt_widget_draw, NULL); + + gnt_box_sync_children(box); + + if (box->title && !GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_NO_BORDER)) + { + int pos, right; + char *title = g_strdup(box->title); + + get_title_thingies(box, title, &pos, &right); + + if (gnt_widget_has_focus(widget)) + wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_TITLE)); + else + wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_TITLE_D)); + mvwaddch(widget->window, 0, pos-1, ACS_RTEE | COLOR_PAIR(GNT_COLOR_NORMAL)); + mvwprintw(widget->window, 0, pos, title); + mvwaddch(widget->window, 0, right, ACS_LTEE | COLOR_PAIR(GNT_COLOR_NORMAL)); + g_free(title); + } + + GNTDEBUG; +} + +static void +reposition_children(GntWidget *widget) +{ + GList *iter; + GntBox *box = GNT_BOX(widget); + int w, h, curx, cury, max; + gboolean has_border = FALSE; + + w = h = 0; + max = 0; + curx = widget->priv.x; + cury = widget->priv.y; + if (!(GNT_WIDGET_FLAGS(widget) & GNT_WIDGET_NO_BORDER)) + { + has_border = TRUE; + curx += 1; + cury += 1; + } + + for (iter = box->list; iter; iter = iter->next) + { + if (GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(iter->data), GNT_WIDGET_INVISIBLE)) + continue; + gnt_widget_set_position(GNT_WIDGET(iter->data), curx, cury); + gnt_widget_get_size(GNT_WIDGET(iter->data), &w, &h); + if (box->vertical) + { + if (h) + { + cury += h + box->pad; + if (max < w) + max = w; + } + } + else + { + if (w) + { + curx += w + box->pad; + if (max < h) + max = h; + } + } + } + + if (has_border) + { + curx += 1; + cury += 1; + max += 2; + } + + if (box->list) + { + if (box->vertical) + cury -= box->pad; + else + curx -= box->pad; + } + + if (box->vertical) + { + widget->priv.width = max; + widget->priv.height = cury - widget->priv.y; + } + else + { + widget->priv.width = curx - widget->priv.x; + widget->priv.height = max; + } +} + +static void +gnt_box_set_position(GntWidget *widget, int x, int y) +{ + GList *iter; + int changex, changey; + + changex = widget->priv.x - x; + changey = widget->priv.y - y; + + for (iter = GNT_BOX(widget)->list; iter; iter = iter->next) + { + GntWidget *w = GNT_WIDGET(iter->data); + gnt_widget_set_position(w, w->priv.x - changex, + w->priv.y - changey); + } +} + +static void +gnt_box_size_request(GntWidget *widget) +{ + GntBox *box = GNT_BOX(widget); + GList *iter; + int maxw = 0, maxh = 0; + + g_list_foreach(box->list, (GFunc)gnt_widget_size_request, NULL); + + for (iter = box->list; iter; iter = iter->next) + { + int w, h; + gnt_widget_get_size(GNT_WIDGET(iter->data), &w, &h); + if (maxh < h) + maxh = h; + if (maxw < w) + maxw = w; + } + + for (iter = box->list; iter; iter = iter->next) + { + int w, h; + GntWidget *wid = GNT_WIDGET(iter->data); + + gnt_widget_get_size(wid, &w, &h); + + if (box->homogeneous) + { + if (box->vertical) + h = maxh; + else + w = maxw; + } + if (box->fill) + { + if (box->vertical) + w = maxw; + else + h = maxh; + } + + gnt_widget_set_size(wid, w, h); + } + + reposition_children(widget); +} + +static void +gnt_box_map(GntWidget *widget) +{ + if (widget->priv.width == 0 || widget->priv.height == 0) + { + gnt_widget_size_request(widget); + find_focusable_widget(GNT_BOX(widget)); + } + GNTDEBUG; +} + +/* Ensures that the current widget can take focus */ +static GntWidget * +find_focusable_widget(GntBox *box) +{ + /* XXX: Make sure the widget is visible? */ + if (box->focus == NULL && GNT_WIDGET(box)->parent == NULL) + g_list_foreach(box->list, add_to_focus, box); + + if (box->active == NULL && box->focus) + box->active = box->focus->data; + + return box->active; +} + +static void +find_next_focus(GntBox *box) +{ + gpointer last = box->active; + do + { + GList *iter = g_list_find(box->focus, box->active); + if (iter && iter->next) + box->active = iter->next->data; + else if (box->focus) + box->active = box->focus->data; + if (!GNT_WIDGET_IS_FLAG_SET(box->active, GNT_WIDGET_INVISIBLE)) + break; + } while (box->active != last); +} + +static void +find_prev_focus(GntBox *box) +{ + gpointer last = box->active; + + if (!box->focus) + return; + + do + { + GList *iter = g_list_find(box->focus, box->active); + if (!iter) + box->active = box->focus->data; + else if (!iter->prev) + box->active = g_list_last(box->focus)->data; + else + box->active = iter->prev->data; + if (!GNT_WIDGET_IS_FLAG_SET(box->active, GNT_WIDGET_INVISIBLE)) + break; + } while (box->active != last); +} + +static gboolean +gnt_box_key_pressed(GntWidget *widget, const char *text) +{ + GntBox *box = GNT_BOX(widget); + GntWidget *now; + + if (box->active == NULL && !find_focusable_widget(box)) + return FALSE; + + if (gnt_widget_key_pressed(box->active, text)) + return TRUE; + + now = box->active; + + if (text[0] == 27) + { + if (strcmp(text, GNT_KEY_LEFT) == 0) + { + find_prev_focus(box); + } + else if (strcmp(text, GNT_KEY_RIGHT) == 0) + { + find_next_focus(box); + } + } + else if (text[0] == '\t') + { + find_next_focus(box); + } + + if (now && now != box->active) + { + gnt_widget_set_focus(now, FALSE); + gnt_widget_set_focus(box->active, TRUE); + return TRUE; + } + + return FALSE; +} + +static void +gnt_box_lost_focus(GntWidget *widget) +{ + GntWidget *w = GNT_BOX(widget)->active; + if (w) + gnt_widget_set_focus(w, FALSE); + gnt_widget_draw(widget); +} + +static void +gnt_box_gained_focus(GntWidget *widget) +{ + GntWidget *w = GNT_BOX(widget)->active; + if (w) + gnt_widget_set_focus(w, TRUE); + gnt_widget_draw(widget); +} + +static void +gnt_box_destroy(GntWidget *w) +{ + GntBox *box = GNT_BOX(w); + + gnt_box_remove_all(box); + gnt_screen_release(w); +} + +static void +gnt_box_expose(GntWidget *widget, int x, int y, int width, int height) +{ + WINDOW *win = newwin(height, width, widget->priv.y + y, widget->priv.x + x); + copywin(widget->window, win, y, x, 0, 0, height - 1, width - 1, FALSE); + wrefresh(win); + delwin(win); +} + +static gboolean +gnt_box_confirm_size(GntWidget *widget, int width, int height) +{ + GList *iter; + GntBox *box = GNT_BOX(widget); + int wchange, hchange; + + if (widget->priv.width != width && !GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_GROW_X)) + return FALSE; + if (widget->priv.height != height && !GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_GROW_Y)) + return FALSE; + + if (!box->list) + return TRUE; + + wchange = widget->priv.width - width; + hchange = widget->priv.height - height; + + if (wchange == 0 && hchange == 0) + return TRUE; /* Quit playing games */ + + /* XXX: Right now, I am trying to just apply all the changes to + * just one widget. It should be possible to distribute the + * changes to all the widgets in the box. */ + for (iter = box->list; iter; iter = iter->next) + { + GntWidget *wid = iter->data; + int w, h; + + gnt_widget_get_size(wid, &w, &h); + + if (gnt_widget_confirm_size(wid, w - wchange, h - hchange)) + { + GList *i; + + for (i = box->list; i; i = i->next) + { + int tw, th; + if (i == iter) continue; + gnt_widget_get_size(GNT_WIDGET(i->data), &tw, &th); + if (box->vertical) + { + if (!gnt_widget_confirm_size(i->data, tw - wchange, th)) + return FALSE; + } + else + { + if (!gnt_widget_confirm_size(i->data, tw, th - hchange)) + return FALSE; + } + } +#if 0 + gnt_widget_set_size(wid, w - wchange, h - hchange); + if (box->vertical) + hchange = 0; + else + wchange = 0; + + for (i = box->list; i; i = i->next) + { + int tw, th; + if (i == iter) continue; + gnt_widget_get_size(GNT_WIDGET(i->data), &tw, &th); + gnt_widget_set_size(i->data, tw - wchange, th - hchange); + } +#endif + g_object_set_data(G_OBJECT(box), "size-queued", wid); + return TRUE; + } + } + + return FALSE; +} + +static void +gnt_box_size_changed(GntWidget *widget, int oldw, int oldh) +{ + int wchange, hchange; + GList *i; + GntBox *box = GNT_BOX(widget); + GntWidget *wid; + int tw, th; + + wchange = widget->priv.width - oldw; + hchange = widget->priv.height - oldh; + + wid = g_object_get_data(G_OBJECT(box), "size-queued"); + if (wid) + { + gnt_widget_get_size(wid, &tw, &th); + gnt_widget_set_size(wid, tw + wchange, th + hchange); + g_object_set_data(G_OBJECT(box), "size-queued", NULL); + } + + if (box->vertical) + hchange = 0; + else + wchange = 0; + + for (i = box->list; i; i = i->next) + { + if (wid != i->data) + { + gnt_widget_get_size(GNT_WIDGET(i->data), &tw, &th); + gnt_widget_set_size(i->data, tw + wchange, th + hchange); + } + } + + reposition_children(widget); +} + +static gboolean +gnt_box_clicked(GntWidget *widget, GntMouseEvent event, int cx, int cy) +{ + GList *iter; + for (iter = GNT_BOX(widget)->list; iter; iter = iter->next) { + int x, y, w, h; + GntWidget *wid = iter->data; + + gnt_widget_get_position(wid, &x, &y); + gnt_widget_get_size(wid, &w, &h); + + if (cx >= x && cx < x + w && cy >= y && cy < y + h) { + if (event <= GNT_MIDDLE_MOUSE_DOWN && + GNT_WIDGET_IS_FLAG_SET(wid, GNT_WIDGET_CAN_TAKE_FOCUS)) { + while (widget->parent) + widget = widget->parent; + gnt_box_give_focus_to_child(GNT_BOX(widget), wid); + } + return gnt_widget_clicked(wid, event, cx, cy); + } + } + return FALSE; +} + +static void +gnt_box_class_init(GntBoxClass *klass) +{ + parent_class = GNT_WIDGET_CLASS(klass); + parent_class->destroy = gnt_box_destroy; + parent_class->draw = gnt_box_draw; + parent_class->expose = gnt_box_expose; + parent_class->map = gnt_box_map; + parent_class->size_request = gnt_box_size_request; + parent_class->set_position = gnt_box_set_position; + parent_class->key_pressed = gnt_box_key_pressed; + parent_class->clicked = gnt_box_clicked; + parent_class->lost_focus = gnt_box_lost_focus; + parent_class->gained_focus = gnt_box_gained_focus; + parent_class->confirm_size = gnt_box_confirm_size; + parent_class->size_changed = gnt_box_size_changed; + + GNTDEBUG; +} + +static void +gnt_box_init(GTypeInstance *instance, gpointer class) +{ + GntWidget *widget = GNT_WIDGET(instance); + GntBox *box = GNT_BOX(widget); + /* Initially make both the height and width resizable. + * Update the flags as necessary when widgets are added to it. */ + GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_GROW_X | GNT_WIDGET_GROW_Y); + GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_CAN_TAKE_FOCUS); + GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW); + box->pad = 1; + box->fill = TRUE; + GNTDEBUG; +} + +/****************************************************************************** + * GntBox API + *****************************************************************************/ +GType +gnt_box_get_gtype(void) +{ + static GType type = 0; + + if(type == 0) + { + static const GTypeInfo info = { + sizeof(GntBoxClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)gnt_box_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof(GntBox), + 0, /* n_preallocs */ + gnt_box_init, /* instance_init */ + }; + + type = g_type_register_static(GNT_TYPE_WIDGET, + "GntBox", + &info, 0); + } + + return type; +} + +GntWidget *gnt_box_new(gboolean homo, gboolean vert) +{ + GntWidget *widget = g_object_new(GNT_TYPE_BOX, NULL); + GntBox *box = GNT_BOX(widget); + + box->homogeneous = homo; + box->vertical = vert; + box->alignment = vert ? GNT_ALIGN_LEFT : GNT_ALIGN_MID; + + return widget; +} + +void gnt_box_add_widget(GntBox *b, GntWidget *widget) +{ + b->list = g_list_append(b->list, widget); + widget->parent = GNT_WIDGET(b); + + if (b->vertical) + { + if (!GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_GROW_X)) + GNT_WIDGET_UNSET_FLAGS(GNT_WIDGET(b), GNT_WIDGET_GROW_X); + } + else + { + if (!GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_GROW_Y)) + GNT_WIDGET_UNSET_FLAGS(GNT_WIDGET(b), GNT_WIDGET_GROW_Y); + } +} + +void gnt_box_set_title(GntBox *b, const char *title) +{ + char *prev = b->title; + GntWidget *w = GNT_WIDGET(b); + b->title = g_strdup(title); + if (w->window && !GNT_WIDGET_IS_FLAG_SET(w, GNT_WIDGET_NO_BORDER)) { + /* Erase the old title */ + int pos, right; + get_title_thingies(b, prev, &pos, &right); + mvwhline(w->window, 0, pos - 1, ACS_HLINE | COLOR_PAIR(GNT_COLOR_NORMAL), + right - pos + 2); + g_free(prev); + } +} + +void gnt_box_set_pad(GntBox *box, int pad) +{ + box->pad = pad; + /* XXX: Perhaps redraw if already showing? */ +} + +void gnt_box_set_toplevel(GntBox *box, gboolean set) +{ + GntWidget *widget = GNT_WIDGET(box); + if (set) + { + GNT_WIDGET_UNSET_FLAGS(widget, GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW); + GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_CAN_TAKE_FOCUS); + } + else + { + GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW); + GNT_WIDGET_UNSET_FLAGS(widget, GNT_WIDGET_CAN_TAKE_FOCUS); + } +} + +void gnt_box_sync_children(GntBox *box) +{ + GList *iter; + GntWidget *widget = GNT_WIDGET(box); + int pos = 1; + + if (GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_NO_BORDER)) + pos = 0; + + for (iter = box->list; iter; iter = iter->next) + { + GntWidget *w = GNT_WIDGET(iter->data); + int height, width; + int x, y; + + if (GNT_WIDGET_IS_FLAG_SET(w, GNT_WIDGET_INVISIBLE)) + continue; + + if (GNT_IS_BOX(w)) + gnt_box_sync_children(GNT_BOX(w)); + + gnt_widget_get_size(w, &width, &height); + + x = w->priv.x - widget->priv.x; + y = w->priv.y - widget->priv.y; + + if (box->vertical) + { + x = pos; + if (box->alignment == GNT_ALIGN_RIGHT) + x += widget->priv.width - width; + else if (box->alignment == GNT_ALIGN_MID) + x += (widget->priv.width - width)/2; + if (x + width > widget->priv.width - pos) + x -= x + width - (widget->priv.width - pos); + } + else + { + y = pos; + if (box->alignment == GNT_ALIGN_BOTTOM) + y += widget->priv.height - height; + else if (box->alignment == GNT_ALIGN_MID) + y += (widget->priv.height - height)/2; + if (y + height >= widget->priv.height - pos) + y = widget->priv.height - height - pos; + } + + copywin(w->window, widget->window, 0, 0, + y, x, y + height - 1, x + width - 1, FALSE); + gnt_widget_set_position(w, x + widget->priv.x, y + widget->priv.y); + } +} + +void gnt_box_set_alignment(GntBox *box, GntAlignment alignment) +{ + box->alignment = alignment; +} + +void gnt_box_remove(GntBox *box, GntWidget *widget) +{ + box->list = g_list_remove(box->list, widget); + if (GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_CAN_TAKE_FOCUS) + && GNT_WIDGET(box)->parent == NULL && box->focus) + { + if (widget == box->active) + { + find_next_focus(box); + if (box->active == widget) /* There's only one widget */ + box->active = NULL; + } + box->focus = g_list_remove(box->focus, widget); + } + + if (GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(box), GNT_WIDGET_MAPPED)) + gnt_widget_draw(GNT_WIDGET(box)); +} + +void gnt_box_remove_all(GntBox *box) +{ + g_list_foreach(box->list, (GFunc)gnt_widget_destroy, NULL); + g_list_free(box->list); + g_list_free(box->focus); + box->list = NULL; + box->focus = NULL; + GNT_WIDGET(box)->priv.width = 0; + GNT_WIDGET(box)->priv.height = 0; +} + +void gnt_box_readjust(GntBox *box) +{ + GList *iter; + GntWidget *wid; + int width, height; + + if (GNT_WIDGET(box)->parent != NULL) + return; + + for (iter = box->list; iter; iter = iter->next) + { + GntWidget *w = iter->data; + if (GNT_IS_BOX(w)) + gnt_box_readjust(GNT_BOX(w)); + else + { + GNT_WIDGET_UNSET_FLAGS(w, GNT_WIDGET_MAPPED); + w->priv.width = 0; + w->priv.height = 0; + } + } + + wid = GNT_WIDGET(box); + GNT_WIDGET_UNSET_FLAGS(wid, GNT_WIDGET_MAPPED); + wid->priv.width = 0; + wid->priv.height = 0; + + if (wid->parent == NULL) + { + g_list_free(box->focus); + box->focus = NULL; + box->active = NULL; + gnt_widget_size_request(wid); + gnt_widget_get_size(wid, &width, &height); + gnt_screen_resize_widget(wid, width, height); + find_focusable_widget(box); + } +} + +void gnt_box_set_fill(GntBox *box, gboolean fill) +{ + box->fill = fill; +} + +void gnt_box_move_focus(GntBox *box, int dir) +{ + GntWidget *now; + + if (box->active == NULL) + { + find_focusable_widget(box); + return; + } + + now = box->active; + + if (dir == 1) + find_next_focus(box); + else if (dir == -1) + find_prev_focus(box); + + if (now && now != box->active) + { + gnt_widget_set_focus(now, FALSE); + gnt_widget_set_focus(box->active, TRUE); + } + + if (GNT_WIDGET(box)->window) + gnt_widget_draw(GNT_WIDGET(box)); +} + +void gnt_box_give_focus_to_child(GntBox *box, GntWidget *widget) +{ + GList *find = g_list_find(box->focus, widget); + gpointer now = box->active; + if (find) + box->active = widget; + if (now && now != box->active) + { + gnt_widget_set_focus(now, FALSE); + gnt_widget_set_focus(box->active, TRUE); + } + + if (GNT_WIDGET(box)->window) + gnt_widget_draw(GNT_WIDGET(box)); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntbox.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,98 @@ +#ifndef GNT_BOX_H +#define GNT_BOX_H + +#include "gnt.h" +#include "gntwidget.h" + +#define GNT_TYPE_BOX (gnt_box_get_gtype()) +#define GNT_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_BOX, GntBox)) +#define GNT_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_BOX, GntBoxClass)) +#define GNT_IS_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_BOX)) +#define GNT_IS_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GNT_TYPE_BOX)) +#define GNT_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GNT_TYPE_BOX, GntBoxClass)) + +typedef struct _GnBox GntBox; +typedef struct _GnBoxClass GntBoxClass; + +typedef enum +{ + /* These for vertical boxes */ + GNT_ALIGN_LEFT, + GNT_ALIGN_RIGHT, + + GNT_ALIGN_MID, + + /* These for horizontal boxes */ + GNT_ALIGN_TOP, + GNT_ALIGN_BOTTOM +} GntAlignment; + +struct _GnBox +{ + GntWidget parent; + + gboolean vertical; + gboolean homogeneous; + gboolean fill; + GList *list; /* List of widgets */ + + GntWidget *active; + int pad; /* Number of spaces to use between widgets */ + GntAlignment alignment; /* How are the widgets going to be aligned? */ + + char *title; + GList *focus; /* List of widgets to cycle focus (only valid for parent boxes) */ + + void (*gnt_reserved1)(void); + void (*gnt_reserved2)(void); + void (*gnt_reserved3)(void); + void (*gnt_reserved4)(void); +}; + +struct _GnBoxClass +{ + GntWidgetClass parent; + + void (*gnt_reserved1)(void); + void (*gnt_reserved2)(void); + void (*gnt_reserved3)(void); + void (*gnt_reserved4)(void); +}; + +G_BEGIN_DECLS + +GType gnt_box_get_gtype(void); + +#define gnt_vbox_new(homo) gnt_box_new(homo, TRUE) +#define gnt_hbox_new(homo) gnt_box_new(homo, FALSE) + +GntWidget *gnt_box_new(gboolean homo, gboolean vert); + +void gnt_box_add_widget(GntBox *box, GntWidget *widget); + +void gnt_box_set_title(GntBox *box, const char *title); + +void gnt_box_set_pad(GntBox *box, int pad); + +void gnt_box_set_toplevel(GntBox *box, gboolean set); + +void gnt_box_sync_children(GntBox *box); + +void gnt_box_set_alignment(GntBox *box, GntAlignment alignment); + +void gnt_box_remove(GntBox *box, GntWidget *widget); /* XXX: does NOT destroy widget */ + +void gnt_box_remove_all(GntBox *box); /* Removes AND destroys all the widgets in it */ + +void gnt_box_readjust(GntBox *box); + +void gnt_box_set_fill(GntBox *box, gboolean fill); + +void gnt_box_move_focus(GntBox *box, int dir); /* +1 to move forward, -1 for backward */ + +void gnt_box_give_focus_to_child(GntBox *box, GntWidget *widget); + +G_END_DECLS + +#endif /* GNT_BOX_H */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntbutton.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,135 @@ +#include <string.h> + +#include "gntbutton.h" +#include "gntutils.h" + +enum +{ + SIGS = 1, +}; + +static GntWidgetClass *parent_class = NULL; + +static void +gnt_button_draw(GntWidget *widget) +{ + GntButton *button = GNT_BUTTON(widget); + GntColorType type; + + if (gnt_widget_has_focus(widget)) + type = GNT_COLOR_HIGHLIGHT; + else + type = GNT_COLOR_NORMAL; + + wbkgdset(widget->window, '\0' | COLOR_PAIR(type)); + mvwprintw(widget->window, 1, 2, button->priv->text); + + GNTDEBUG; +} + +static void +gnt_button_size_request(GntWidget *widget) +{ + GntButton *button = GNT_BUTTON(widget); + gnt_util_get_text_bound(button->priv->text, + &widget->priv.width, &widget->priv.height); + widget->priv.width += 4; + if (!GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_NO_BORDER)) + widget->priv.height += 2; +} + +static void +gnt_button_map(GntWidget *widget) +{ + if (widget->priv.width == 0 || widget->priv.height == 0) + gnt_widget_size_request(widget); + GNTDEBUG; +} + +static gboolean +gnt_button_key_pressed(GntWidget *widget, const char *key) +{ + if (strcmp(key, GNT_KEY_ENTER) == 0) + { + gnt_widget_activate(widget); + return TRUE; + } + return FALSE; +} + +static gboolean +gnt_button_clicked(GntWidget *widget, GntMouseEvent event, int x, int y) +{ + if (event == GNT_LEFT_MOUSE_DOWN) { + gnt_widget_activate(widget); + return TRUE; + } + return FALSE; +} + +static void +gnt_button_class_init(GntWidgetClass *klass) +{ + parent_class = GNT_WIDGET_CLASS(klass); + parent_class->draw = gnt_button_draw; + parent_class->map = gnt_button_map; + parent_class->size_request = gnt_button_size_request; + parent_class->key_pressed = gnt_button_key_pressed; + parent_class->clicked = gnt_button_clicked; + + GNTDEBUG; +} + +static void +gnt_button_init(GTypeInstance *instance, gpointer class) +{ + GntWidget *widget = GNT_WIDGET(instance); + GntButton *button = GNT_BUTTON(instance); + button->priv = g_new0(GntButtonPriv, 1); + + GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_GROW_X); + + widget->priv.minw = 4; + widget->priv.minh = 3; + GNTDEBUG; +} + +/****************************************************************************** + * GntButton API + *****************************************************************************/ +GType +gnt_button_get_gtype(void) { + static GType type = 0; + + if(type == 0) { + static const GTypeInfo info = { + sizeof(GntButtonClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)gnt_button_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof(GntButton), + 0, /* n_preallocs */ + gnt_button_init, /* instance_init */ + }; + + type = g_type_register_static(GNT_TYPE_WIDGET, + "GntButton", + &info, 0); + } + + return type; +} + +GntWidget *gnt_button_new(const char *text) +{ + GntWidget *widget = g_object_new(GNT_TYPE_BUTTON, NULL); + GntButton *button = GNT_BUTTON(widget); + + button->priv->text = gnt_util_onscreen_fit_string(text, -1); + gnt_widget_set_take_focus(widget, TRUE); + + return widget; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntbutton.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,55 @@ +#ifndef GNT_BUTTON_H +#define GNT_BUTTON_H + +#include <glib.h> +#include <glib-object.h> +#include "gnt.h" +#include "gntwidget.h" + +#define GNT_TYPE_BUTTON (gnt_button_get_gtype()) +#define GNT_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_BUTTON, GntButton)) +#define GNT_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_BUTTON, GntButtonClass)) +#define GNT_IS_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_BUTTON)) +#define GNT_IS_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GNT_TYPE_BUTTON)) +#define GNT_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GNT_TYPE_BUTTON, GntButtonClass)) + +typedef struct _GnButton GntButton; +typedef struct _GnButtonPriv GntButtonPriv; +typedef struct _GnButtonClass GntButtonClass; + +struct _GnButtonPriv +{ + char *text; +}; + +struct _GnButton +{ + GntWidget parent; + + GntButtonPriv *priv; + + void (*gnt_reserved1)(void); + void (*gnt_reserved2)(void); + void (*gnt_reserved3)(void); + void (*gnt_reserved4)(void); +}; + +struct _GnButtonClass +{ + GntWidgetClass parent; + + void (*gnt_reserved1)(void); + void (*gnt_reserved2)(void); + void (*gnt_reserved3)(void); + void (*gnt_reserved4)(void); +}; + +G_BEGIN_DECLS + +GType gnt_button_get_gtype(void); + +GntWidget *gnt_button_new(const char *text); + +G_END_DECLS + +#endif /* GNT_BUTTON_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntcheckbox.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,155 @@ +#include "gntcheckbox.h" + +enum +{ + SIG_TOGGLED = 1, + SIGS, +}; + +static GntButtonClass *parent_class = NULL; +static guint signals[SIGS] = { 0 }; + +static void +gnt_check_box_draw(GntWidget *widget) +{ + GntCheckBox *cb = GNT_CHECK_BOX(widget); + GntColorType type; + char *text; + + if (gnt_widget_has_focus(widget)) + type = GNT_COLOR_HIGHLIGHT; + else + type = GNT_COLOR_NORMAL; + + wbkgdset(widget->window, '\0' | COLOR_PAIR(type)); + + text = g_strdup_printf("[%c]", cb->checked ? 'X' : ' '); + mvwprintw(widget->window, 0, 0, text); + g_free(text); + + wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_NORMAL)); + mvwprintw(widget->window, 0, 4, GNT_BUTTON(cb)->priv->text); + + GNTDEBUG; +} + +static void +toggle_selection(GntWidget *widget) +{ + GNT_CHECK_BOX(widget)->checked = !GNT_CHECK_BOX(widget)->checked; + g_signal_emit(widget, signals[SIG_TOGGLED], 0); + gnt_widget_draw(widget); +} + +static gboolean +gnt_check_box_key_pressed(GntWidget *widget, const char *text) +{ + if (text[0] == ' ' && text[1] == '\0') + { + toggle_selection(widget); + return TRUE; + } + + return FALSE; +} + +static gboolean +gnt_check_box_clicked(GntWidget *widget, GntMouseEvent event, int x, int y) +{ + if (event == GNT_LEFT_MOUSE_DOWN) { + toggle_selection(widget); + return TRUE; + } + return FALSE; +} + +static void +gnt_check_box_class_init(GntCheckBoxClass *klass) +{ + GntWidgetClass *wclass = GNT_WIDGET_CLASS(klass); + + parent_class = GNT_BUTTON_CLASS(klass); + /*parent_class->destroy = gnt_check_box_destroy;*/ + wclass->draw = gnt_check_box_draw; + /*parent_class->map = gnt_check_box_map;*/ + /*parent_class->size_request = gnt_check_box_size_request;*/ + wclass->key_pressed = gnt_check_box_key_pressed; + wclass->clicked = gnt_check_box_clicked; + + signals[SIG_TOGGLED] = + g_signal_new("toggled", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntCheckBoxClass, toggled), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + GNTDEBUG; +} + +static void +gnt_check_box_init(GTypeInstance *instance, gpointer class) +{ + GntWidget *widget = GNT_WIDGET(instance); + widget->priv.minh = 1; + widget->priv.minw = 4; + GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW); + GNTDEBUG; +} + +/****************************************************************************** + * GntCheckBox API + *****************************************************************************/ +GType +gnt_check_box_get_gtype(void) +{ + static GType type = 0; + + if(type == 0) + { + static const GTypeInfo info = { + sizeof(GntCheckBoxClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)gnt_check_box_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof(GntCheckBox), + 0, /* n_preallocs */ + gnt_check_box_init, /* instance_init */ + }; + + type = g_type_register_static(GNT_TYPE_BUTTON, + "GntCheckBox", + &info, 0); + } + + return type; +} + +GntWidget *gnt_check_box_new(const char *text) +{ + GntWidget *widget = g_object_new(GNT_TYPE_CHECK_BOX, NULL); + + GNT_BUTTON(widget)->priv->text = g_strdup(text); + gnt_widget_set_take_focus(widget, TRUE); + + return widget; +} + +void gnt_check_box_set_checked(GntCheckBox *box, gboolean set) +{ + if (set != box->checked) + { + box->checked = set; + g_signal_emit(box, signals[SIG_TOGGLED], 0); + } +} + +gboolean gnt_check_box_get_checked(GntCheckBox *box) +{ + return box->checked; +} + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntcheckbox.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,54 @@ +#ifndef GNT_CHECK_BOX_H +#define GNT_CHECK_BOX_H + +#include "gntbutton.h" +#include "gnt.h" +#include "gntcolors.h" +#include "gntkeys.h" + +#define GNT_TYPE_CHECK_BOX (gnt_check_box_get_gtype()) +#define GNT_CHECK_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_CHECK_BOX, GntCheckBox)) +#define GNT_CHECK_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_CHECK_BOX, GntCheckBoxClass)) +#define GNT_IS_CHECK_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_CHECK_BOX)) +#define GNT_IS_CHECK_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GNT_TYPE_CHECK_BOX)) +#define GNT_CHECK_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GNT_TYPE_CHECK_BOX, GntCheckBoxClass)) + +#define GNT_CHECK_BOX_FLAGS(obj) (GNT_CHECK_BOX(obj)->priv.flags) +#define GNT_CHECK_BOX_SET_FLAGS(obj, flags) (GNT_CHECK_BOX_FLAGS(obj) |= flags) +#define GNT_CHECK_BOX_UNSET_FLAGS(obj, flags) (GNT_CHECK_BOX_FLAGS(obj) &= ~(flags)) + +typedef struct _GnCheckBox GntCheckBox; +typedef struct _GnCheckBoxPriv GntCheckBoxPriv; +typedef struct _GnCheckBoxClass GntCheckBoxClass; + +struct _GnCheckBox +{ + GntButton parent; + gboolean checked; +}; + +struct _GnCheckBoxClass +{ + GntButtonClass parent; + + void (*toggled)(void); + + void (*gnt_reserved1)(void); + void (*gnt_reserved2)(void); + void (*gnt_reserved3)(void); + void (*gnt_reserved4)(void); +}; + +G_BEGIN_DECLS + +GType gnt_check_box_get_gtype(void); + +GntWidget *gnt_check_box_new(const char *text); + +void gnt_check_box_set_checked(GntCheckBox *box, gboolean set); + +gboolean gnt_check_box_get_checked(GntCheckBox *box); + +G_END_DECLS + +#endif /* GNT_CHECK_BOX_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntcolors.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,248 @@ +#include "config.h" + +#include <ncurses.h> + +#include "gntcolors.h" +#include "gntstyle.h" + +#include <glib.h> + +#include <stdlib.h> +#include <string.h> + +static struct +{ + short r, g, b; +} colors[GNT_TOTAL_COLORS]; + +static void +backup_colors() +{ + short i; + for (i = 0; i < GNT_TOTAL_COLORS; i++) + { + color_content(i, &colors[i].r, + &colors[i].g, &colors[i].b); + } +} + +static gboolean +can_use_custom_color() +{ + return (gnt_style_get_bool(GNT_STYLE_COLOR, FALSE) && can_change_color()); +} + +static void +restore_colors() +{ + short i; + for (i = 0; i < GNT_TOTAL_COLORS; i++) + { + init_color(i, colors[i].r, + colors[i].g, colors[i].b); + } +} + +void gnt_init_colors() +{ + static gboolean init = FALSE; + int defaults; + + if (init) + return; + init = TRUE; + + start_color(); + defaults = use_default_colors(); + + if (can_use_custom_color()) + { + backup_colors(); + + /* Do some init_color()s */ + init_color(GNT_COLOR_BLACK, 0, 0, 0); + init_color(GNT_COLOR_RED, 1000, 0, 0); + init_color(GNT_COLOR_GREEN, 0, 1000, 0); + init_color(GNT_COLOR_BLUE, 250, 250, 700); + init_color(GNT_COLOR_WHITE, 1000, 1000, 1000); + init_color(GNT_COLOR_GRAY, 699, 699, 699); + init_color(GNT_COLOR_DARK_GRAY, 256, 256, 256); + + /* Now some init_pair()s */ + init_pair(GNT_COLOR_NORMAL, GNT_COLOR_BLACK, GNT_COLOR_WHITE); + init_pair(GNT_COLOR_HIGHLIGHT, GNT_COLOR_WHITE, GNT_COLOR_BLUE); + init_pair(GNT_COLOR_SHADOW, GNT_COLOR_BLACK, GNT_COLOR_DARK_GRAY); + + init_pair(GNT_COLOR_TITLE, GNT_COLOR_WHITE, GNT_COLOR_BLUE); + init_pair(GNT_COLOR_TITLE_D, GNT_COLOR_WHITE, GNT_COLOR_GRAY); + + init_pair(GNT_COLOR_TEXT_NORMAL, GNT_COLOR_WHITE, GNT_COLOR_BLUE); + init_pair(GNT_COLOR_HIGHLIGHT_D, GNT_COLOR_BLACK, GNT_COLOR_GRAY); + init_pair(GNT_COLOR_DISABLED, GNT_COLOR_GRAY, GNT_COLOR_WHITE); + init_pair(GNT_COLOR_URGENT, GNT_COLOR_WHITE, GNT_COLOR_RED); + } + else + { + int bg; + + if (defaults == OK) { + init_pair(GNT_COLOR_NORMAL, -1, -1); + bg = -1; + } else { + init_pair(GNT_COLOR_NORMAL, COLOR_BLACK, COLOR_WHITE); + bg = COLOR_WHITE; + } + init_pair(GNT_COLOR_DISABLED, COLOR_YELLOW, bg); + init_pair(GNT_COLOR_URGENT, COLOR_GREEN, bg); + + init_pair(GNT_COLOR_HIGHLIGHT, COLOR_WHITE, COLOR_BLUE); + init_pair(GNT_COLOR_SHADOW, COLOR_BLACK, COLOR_BLACK); + init_pair(GNT_COLOR_TITLE, COLOR_WHITE, COLOR_BLUE); + init_pair(GNT_COLOR_TITLE_D, COLOR_WHITE, COLOR_BLACK); + init_pair(GNT_COLOR_TEXT_NORMAL, COLOR_WHITE, COLOR_BLUE); + init_pair(GNT_COLOR_HIGHLIGHT_D, COLOR_CYAN, COLOR_BLACK); + } +} + +void +gnt_uninit_colors() +{ + if (can_use_custom_color()) + restore_colors(); +} + +static int +get_color(char *key) +{ + int color; + gboolean custom = can_use_custom_color(); + + key = g_strstrip(key); + + if (strcmp(key, "black") == 0) + color = custom ? GNT_COLOR_BLACK : COLOR_BLACK; + else if (strcmp(key, "red") == 0) + color = custom ? GNT_COLOR_RED : COLOR_RED; + else if (strcmp(key, "green") == 0) + color = custom ? GNT_COLOR_GREEN : COLOR_GREEN; + else if (strcmp(key, "blue") == 0) + color = custom ? GNT_COLOR_BLUE : COLOR_BLUE; + else if (strcmp(key, "white") == 0) + color = custom ? GNT_COLOR_WHITE : COLOR_WHITE; + else if (strcmp(key, "gray") == 0) + color = custom ? GNT_COLOR_GRAY : COLOR_YELLOW; /* eh? */ + else if (strcmp(key, "darkgray") == 0) + color = custom ? GNT_COLOR_DARK_GRAY : COLOR_BLACK; + else if (strcmp(key, "magenta") == 0) + color = COLOR_MAGENTA; + else if (strcmp(key, "cyan") == 0) + color = COLOR_CYAN; + else + color = -1; + return color; +} + +#if GLIB_CHECK_VERSION(2,6,0) +void gnt_colors_parse(GKeyFile *kfile) +{ + GError *error = NULL; + gsize nkeys; + char **keys = g_key_file_get_keys(kfile, "colors", &nkeys, &error); + + if (error) + { + g_printerr("GntColors: %s\n", error->message); + g_error_free(error); + error = NULL; + } + else if (nkeys) + { + gnt_init_colors(); + while (nkeys--) + { + gsize len; + char *key = keys[nkeys]; + char **list = g_key_file_get_string_list(kfile, "colors", key, &len, NULL); + if (len == 3) + { + int r = atoi(list[0]); + int g = atoi(list[1]); + int b = atoi(list[2]); + int color = -1; + + g_ascii_strdown(key, -1); + color = get_color(key); + if (color == -1) + continue; + + init_color(color, r, g, b); + } + g_strfreev(list); + } + + g_strfreev(keys); + } + + gnt_color_pairs_parse(kfile); +} + +void gnt_color_pairs_parse(GKeyFile *kfile) +{ + GError *error = NULL; + gsize nkeys; + char **keys = g_key_file_get_keys(kfile, "colorpairs", &nkeys, &error); + + if (error) + { + g_printerr("GntColors: %s\n", error->message); + g_error_free(error); + return; + } + else if (nkeys) + gnt_init_colors(); + + while (nkeys--) + { + gsize len; + char *key = keys[nkeys]; + char **list = g_key_file_get_string_list(kfile, "colorpairs", key, &len, NULL); + if (len == 2) + { + GntColorType type = 0; + int fg = get_color(g_ascii_strdown(list[0], -1)); + int bg = get_color(g_ascii_strdown(list[1], -1)); + if (fg == -1 || bg == -1) + continue; + + g_ascii_strdown(key, -1); + + if (strcmp(key, "normal") == 0) + type = GNT_COLOR_NORMAL; + else if (strcmp(key, "highlight") == 0) + type = GNT_COLOR_HIGHLIGHT; + else if (strcmp(key, "highlightd") == 0) + type = GNT_COLOR_HIGHLIGHT_D; + else if (strcmp(key, "shadow") == 0) + type = GNT_COLOR_SHADOW; + else if (strcmp(key, "title") == 0) + type = GNT_COLOR_TITLE; + else if (strcmp(key, "titled") == 0) + type = GNT_COLOR_TITLE_D; + else if (strcmp(key, "text") == 0) + type = GNT_COLOR_TEXT_NORMAL; + else if (strcmp(key, "disabled") == 0) + type = GNT_COLOR_DISABLED; + else if (strcmp(key, "urgent") == 0) + type = GNT_COLOR_URGENT; + else + continue; + + init_pair(type, fg, bg); + } + g_strfreev(list); + } + + g_strfreev(keys); +} + +#endif /* GKeyFile */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntcolors.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,46 @@ +#ifndef GNT_COLORS_H +#define GNT_COLORS_H + +#include <glib.h> + +typedef enum +{ + GNT_COLOR_NORMAL = 1, + GNT_COLOR_HIGHLIGHT, /* eg. when a button is selected */ + GNT_COLOR_DISABLED, /* eg. when a button is disabled */ + GNT_COLOR_HIGHLIGHT_D, /* eg. when a button is selected, but some other window is in focus */ + GNT_COLOR_TEXT_NORMAL, + GNT_COLOR_TEXT_INACTIVE, /* when the entry is out of focus */ + GNT_COLOR_MNEMONIC, + GNT_COLOR_MNEMONIC_D, + GNT_COLOR_SHADOW, + GNT_COLOR_TITLE, + GNT_COLOR_TITLE_D, + GNT_COLOR_URGENT, /* this is for the 'urgent' windows */ + GNT_COLORS +} GntColorType; + +enum +{ + GNT_COLOR_BLACK = 0, + GNT_COLOR_RED, + GNT_COLOR_GREEN, + GNT_COLOR_BLUE, + GNT_COLOR_WHITE, + GNT_COLOR_GRAY, + GNT_COLOR_DARK_GRAY, + GNT_TOTAL_COLORS +}; + +/* populate some default colors */ +void gnt_init_colors(); + +void gnt_uninit_colors(); + +#if GLIB_CHECK_VERSION(2,6,0) +void gnt_colors_parse(GKeyFile *kfile); + +void gnt_color_pairs_parse(GKeyFile *kfile); +#endif + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntcombobox.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,305 @@ +#include "gntbox.h" +#include "gntcombobox.h" +#include "gnttree.h" +#include "gntmarshal.h" +#include "gntutils.h" + +#include <string.h> + +enum +{ + SIG_SELECTION_CHANGED, + SIGS, +}; + +static GntWidgetClass *parent_class = NULL; +static guint signals[SIGS] = { 0 }; +static void (*widget_lost_focus)(GntWidget *widget); + +static void +set_selection(GntComboBox *box, gpointer key) +{ + if (box->selected != key) + { + /* XXX: make sure the key actually does exist */ + gpointer old = box->selected; + box->selected = key; + if (GNT_WIDGET(box)->window) + gnt_widget_draw(GNT_WIDGET(box)); + if (box->dropdown) + gnt_tree_set_selected(GNT_TREE(box->dropdown), key); + g_signal_emit(box, signals[SIG_SELECTION_CHANGED], 0, old, key); + } +} + +static void +gnt_combo_box_draw(GntWidget *widget) +{ + GntComboBox *box = GNT_COMBO_BOX(widget); + char *text = NULL, *s; + GntColorType type; + int len; + + if (box->dropdown && box->selected) + text = gnt_tree_get_selection_text(GNT_TREE(box->dropdown)); + + if (text == NULL) + text = g_strdup(""); + + if (gnt_widget_has_focus(widget)) + type = GNT_COLOR_HIGHLIGHT; + else + type = GNT_COLOR_NORMAL; + + wbkgdset(widget->window, '\0' | COLOR_PAIR(type)); + + s = (char*)gnt_util_onscreen_width_to_pointer(text, widget->priv.width - 4, &len); + *s = '\0'; + + mvwprintw(widget->window, 1, 1, text); + whline(widget->window, ' ' | COLOR_PAIR(type), widget->priv.width - 4 - len); + mvwaddch(widget->window, 1, widget->priv.width - 3, ACS_VLINE | COLOR_PAIR(GNT_COLOR_NORMAL)); + mvwaddch(widget->window, 1, widget->priv.width - 2, ACS_DARROW | COLOR_PAIR(GNT_COLOR_NORMAL)); + + g_free(text); + GNTDEBUG; +} + +static void +gnt_combo_box_size_request(GntWidget *widget) +{ + if (!GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_MAPPED)) + { + GntWidget *dd = GNT_COMBO_BOX(widget)->dropdown; + gnt_widget_size_request(dd); + widget->priv.height = 3; /* For now, a combobox will have border */ + widget->priv.width = MAX(10, dd->priv.width + 4); + } +} + +static void +gnt_combo_box_map(GntWidget *widget) +{ + if (widget->priv.width == 0 || widget->priv.height == 0) + gnt_widget_size_request(widget); + GNTDEBUG; +} + +static void +popup_dropdown(GntComboBox *box) +{ + GntWidget *widget = GNT_WIDGET(box); + GntWidget *parent = box->dropdown->parent; + int height = g_list_length(GNT_TREE(box->dropdown)->list); + int y = widget->priv.y + widget->priv.height - 1; + gnt_widget_set_size(box->dropdown, widget->priv.width, height + 2); + + if (y + height + 2 >= getmaxy(stdscr)) + y = widget->priv.y - height - 1; + gnt_widget_set_position(parent, widget->priv.x, y); + if (parent->window) + { + mvwin(parent->window, y, widget->priv.x); + wresize(parent->window, height+2, widget->priv.width); + } + parent->priv.width = widget->priv.width; + parent->priv.height = height + 2; + + GNT_WIDGET_UNSET_FLAGS(parent, GNT_WIDGET_INVISIBLE); + gnt_widget_draw(parent); +} + +static gboolean +gnt_combo_box_key_pressed(GntWidget *widget, const char *text) +{ + GntComboBox *box = GNT_COMBO_BOX(widget); + if (GNT_WIDGET_IS_FLAG_SET(box->dropdown->parent, GNT_WIDGET_MAPPED)) + { + if (text[1] == 0) + { + switch (text[0]) + { + case '\r': + case '\t': + set_selection(box, gnt_tree_get_selection_data(GNT_TREE(box->dropdown))); + case 27: + gnt_tree_set_selected(GNT_TREE(box->dropdown), box->selected); + gnt_widget_hide(box->dropdown->parent); + return TRUE; + break; + } + } + if (gnt_widget_key_pressed(box->dropdown, text)) + return TRUE; + } + else + { + if (text[0] == 27) + { + if (strcmp(text, GNT_KEY_UP) == 0 || + strcmp(text, GNT_KEY_DOWN) == 0) + { + popup_dropdown(box); + return TRUE; + } + } + } + + return FALSE; +} + +static void +gnt_combo_box_destroy(GntWidget *widget) +{ + gnt_widget_destroy(GNT_COMBO_BOX(widget)->dropdown->parent); +} + +static void +gnt_combo_box_lost_focus(GntWidget *widget) +{ + GntComboBox *combo = GNT_COMBO_BOX(widget); + if (GNT_WIDGET_IS_FLAG_SET(combo->dropdown->parent, GNT_WIDGET_MAPPED)) + gnt_widget_hide(GNT_COMBO_BOX(widget)->dropdown->parent); + widget_lost_focus(widget); +} + +static gboolean +gnt_combo_box_clicked(GntWidget *widget, GntMouseEvent event, int x, int y) +{ + GntComboBox *box = GNT_COMBO_BOX(widget); + gboolean dshowing = GNT_WIDGET_IS_FLAG_SET(box->dropdown->parent, GNT_WIDGET_MAPPED); + + if (event == GNT_MOUSE_SCROLL_UP) { + if (dshowing) + gnt_widget_key_pressed(box->dropdown, GNT_KEY_UP); + } else if (event == GNT_MOUSE_SCROLL_DOWN) { + if (dshowing) + gnt_widget_key_pressed(box->dropdown, GNT_KEY_DOWN); + } else if (event == GNT_LEFT_MOUSE_DOWN) { + if (dshowing) { + set_selection(box, gnt_tree_get_selection_data(GNT_TREE(box->dropdown))); + gnt_widget_hide(box->dropdown->parent); + } else { + popup_dropdown(GNT_COMBO_BOX(widget)); + } + } else + return FALSE; + return TRUE; +} + +static void +gnt_combo_box_class_init(GntComboBoxClass *klass) +{ + parent_class = GNT_WIDGET_CLASS(klass); + + parent_class->destroy = gnt_combo_box_destroy; + parent_class->draw = gnt_combo_box_draw; + parent_class->map = gnt_combo_box_map; + parent_class->size_request = gnt_combo_box_size_request; + parent_class->key_pressed = gnt_combo_box_key_pressed; + parent_class->clicked = gnt_combo_box_clicked; + + widget_lost_focus = parent_class->lost_focus; + parent_class->lost_focus = gnt_combo_box_lost_focus; + + signals[SIG_SELECTION_CHANGED] = + g_signal_new("selection-changed", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + gnt_closure_marshal_VOID__POINTER_POINTER, + G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER); + + GNTDEBUG; +} + +static void +gnt_combo_box_init(GTypeInstance *instance, gpointer class) +{ + GntWidget *box; + GntWidget *widget = GNT_WIDGET(instance); + GntComboBox *combo = GNT_COMBO_BOX(instance); + + GNT_WIDGET_SET_FLAGS(GNT_WIDGET(instance), + GNT_WIDGET_GROW_X | GNT_WIDGET_CAN_TAKE_FOCUS | GNT_WIDGET_NO_SHADOW); + combo->dropdown = gnt_tree_new(); + + box = gnt_box_new(FALSE, FALSE); + GNT_WIDGET_SET_FLAGS(box, GNT_WIDGET_NO_SHADOW | GNT_WIDGET_NO_BORDER | GNT_WIDGET_TRANSIENT); + gnt_box_set_pad(GNT_BOX(box), 0); + gnt_box_add_widget(GNT_BOX(box), combo->dropdown); + + widget->priv.minw = 4; + widget->priv.minh = 3; + GNTDEBUG; +} + +/****************************************************************************** + * GntComboBox API + *****************************************************************************/ +GType +gnt_combo_box_get_gtype(void) +{ + static GType type = 0; + + if(type == 0) + { + static const GTypeInfo info = { + sizeof(GntComboBoxClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)gnt_combo_box_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof(GntComboBox), + 0, /* n_preallocs */ + gnt_combo_box_init, /* instance_init */ + }; + + type = g_type_register_static(GNT_TYPE_WIDGET, + "GntComboBox", + &info, 0); + } + + return type; +} + +GntWidget *gnt_combo_box_new() +{ + GntWidget *widget = g_object_new(GNT_TYPE_COMBO_BOX, NULL); + + return widget; +} + +void gnt_combo_box_add_data(GntComboBox *box, gpointer key, const char *text) +{ + gnt_tree_add_row_last(GNT_TREE(box->dropdown), key, + gnt_tree_create_row(GNT_TREE(box->dropdown), text), NULL); + if (box->selected == NULL) + set_selection(box, key); +} + +gpointer gnt_combo_box_get_selected_data(GntComboBox *box) +{ + return box->selected; +} + +void gnt_combo_box_set_selected(GntComboBox *box, gpointer key) +{ + set_selection(box, key); +} + +void gnt_combo_box_remove(GntComboBox *box, gpointer key) +{ + gnt_tree_remove(GNT_TREE(box->dropdown), key); + if (box->selected == key) + set_selection(box, NULL); +} + +void gnt_combo_box_remove_all(GntComboBox *box) +{ + gnt_tree_remove_all(GNT_TREE(box->dropdown)); + set_selection(box, NULL); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntcombobox.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,61 @@ +#ifndef GNT_COMBO_BOX_H +#define GNT_COMBO_BOX_H + +#include "gnt.h" +#include "gntcolors.h" +#include "gntkeys.h" +#include "gntwidget.h" + +#define GNT_TYPE_COMBO_BOX (gnt_combo_box_get_gtype()) +#define GNT_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_COMBO_BOX, GntComboBox)) +#define GNT_COMBO_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_COMBO_BOX, GntComboBoxClass)) +#define GNT_IS_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_COMBO_BOX)) +#define GNT_IS_COMBO_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GNT_TYPE_COMBO_BOX)) +#define GNT_COMBO_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GNT_TYPE_COMBO_BOX, GntComboBoxClass)) + +#define GNT_COMBO_BOX_FLAGS(obj) (GNT_COMBO_BOX(obj)->priv.flags) +#define GNT_COMBO_BOX_SET_FLAGS(obj, flags) (GNT_COMBO_BOX_FLAGS(obj) |= flags) +#define GNT_COMBO_BOX_UNSET_FLAGS(obj, flags) (GNT_COMBO_BOX_FLAGS(obj) &= ~(flags)) + +typedef struct _GnComboBox GntComboBox; +typedef struct _GnComboBoxPriv GntComboBoxPriv; +typedef struct _GnComboBoxClass GntComboBoxClass; + +struct _GnComboBox +{ + GntWidget parent; + + GntWidget *dropdown; /* This is a GntTree */ + + void *selected; /* Currently selected key */ +}; + +struct _GnComboBoxClass +{ + GntWidgetClass parent; + + void (*gnt_reserved1)(void); + void (*gnt_reserved2)(void); + void (*gnt_reserved3)(void); + void (*gnt_reserved4)(void); +}; + +G_BEGIN_DECLS + +GType gnt_combo_box_get_gtype(void); + +GntWidget *gnt_combo_box_new(); + +void gnt_combo_box_add_data(GntComboBox *box, gpointer key, const char *text); + +void gnt_combo_box_remove(GntComboBox *box, gpointer key); + +void gnt_combo_box_remove_all(GntComboBox *box); + +gpointer gnt_combo_box_get_selected_data(GntComboBox *box); + +void gnt_combo_box_set_selected(GntComboBox *box, gpointer key); + +G_END_DECLS + +#endif /* GNT_COMBO_BOX_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntentry.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,909 @@ +#include <ctype.h> +#include <string.h> + +#include "gntbox.h" +#include "gntentry.h" +#include "gntstyle.h" +#include "gnttree.h" +#include "gntutils.h" + +enum +{ + SIG_TEXT_CHANGED, + SIGS, +}; +static guint signals[SIGS] = { 0 }; + +static GntWidgetClass *parent_class = NULL; + +static void gnt_entry_set_text_internal(GntEntry *entry, const char *text); + +static void +destroy_suggest(GntEntry *entry) +{ + if (entry->ddown) + { + gnt_widget_destroy(entry->ddown->parent); + entry->ddown = NULL; + } +} + +static char * +get_beginning_of_word(GntEntry *entry) +{ + char *s = entry->cursor; + while (s > entry->start) + { + char *t = g_utf8_find_prev_char(entry->start, s); + if (isspace(*t)) + break; + s = t; + } + return s; +} + +static gboolean +show_suggest_dropdown(GntEntry *entry) +{ + char *suggest = NULL; + int len; + int offset = 0, x, y; + int count = 0; + GList *iter; + + if (entry->word) + { + char *s = get_beginning_of_word(entry); + suggest = g_strndup(s, entry->cursor - s); + if (entry->scroll < s) + offset = gnt_util_onscreen_width(entry->scroll, s); + } + else + suggest = g_strdup(entry->start); + len = strlen(suggest); /* Don't need to use the utf8-function here */ + + if (entry->ddown == NULL) + { + GntWidget *box = gnt_vbox_new(FALSE); + entry->ddown = gnt_tree_new(); + gnt_tree_set_compare_func(GNT_TREE(entry->ddown), (GCompareFunc)g_utf8_collate); + gnt_box_add_widget(GNT_BOX(box), entry->ddown); + /* XXX: Connect to the "activate" signal for the dropdown tree */ + + GNT_WIDGET_SET_FLAGS(box, GNT_WIDGET_TRANSIENT); + + gnt_widget_get_position(GNT_WIDGET(entry), &x, &y); + x += offset; + y++; + if (y + 10 >= getmaxy(stdscr)) + y -= 11; + gnt_widget_set_position(box, x, y); + } + else + gnt_tree_remove_all(GNT_TREE(entry->ddown)); + + for (count = 0, iter = entry->suggests; iter; iter = iter->next) + { + const char *text = iter->data; + if (g_ascii_strncasecmp(suggest, text, len) == 0 && strlen(text) >= len) + { + gnt_tree_add_row_after(GNT_TREE(entry->ddown), (gpointer)text, + gnt_tree_create_row(GNT_TREE(entry->ddown), text), + NULL, NULL); + count++; + } + } + g_free(suggest); + + if (count == 0) + { + destroy_suggest(entry); + return FALSE; + } + + gnt_widget_draw(entry->ddown->parent); + return TRUE; +} + +static void +gnt_entry_draw(GntWidget *widget) +{ + GntEntry *entry = GNT_ENTRY(widget); + int stop; + gboolean focus; + + if ((focus = gnt_widget_has_focus(widget))) + wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_TEXT_NORMAL)); + else + wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_HIGHLIGHT_D)); + + if (entry->masked) + { + mvwhline(widget->window, 0, 0, gnt_ascii_only() ? '*' : ACS_BULLET, + g_utf8_pointer_to_offset(entry->scroll, entry->end)); + } + else + mvwprintw(widget->window, 0, 0, "%s", entry->scroll); + + stop = gnt_util_onscreen_width(entry->scroll, entry->end); + if (stop < widget->priv.width) + whline(widget->window, ENTRY_CHAR, widget->priv.width - stop); + + if (focus) + mvwchgat(widget->window, 0, gnt_util_onscreen_width(entry->scroll, entry->cursor), + 1, A_REVERSE, GNT_COLOR_TEXT_NORMAL, NULL); + + GNTDEBUG; +} + +static void +gnt_entry_size_request(GntWidget *widget) +{ + if (!GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_MAPPED)) + { + widget->priv.height = 1; + widget->priv.width = 20; + } +} + +static void +gnt_entry_map(GntWidget *widget) +{ + if (widget->priv.width == 0 || widget->priv.height == 0) + gnt_widget_size_request(widget); + GNTDEBUG; +} + +static void +entry_redraw(GntWidget *widget) +{ + gnt_entry_draw(widget); + gnt_widget_queue_update(widget); +} + +static void +entry_text_changed(GntEntry *entry) +{ + g_signal_emit(entry, signals[SIG_TEXT_CHANGED], 0); +} + +static gboolean +move_back(GntBindable *bind, GList *null) +{ + GntEntry *entry = GNT_ENTRY(bind); + if (entry->cursor <= entry->start) + return FALSE; + entry->cursor = g_utf8_find_prev_char(entry->start, entry->cursor); + if (entry->cursor < entry->scroll) + entry->scroll = entry->cursor; + entry_redraw(GNT_WIDGET(entry)); + return TRUE; +} + +static gboolean +move_forward(GntBindable *bind, GList *list) +{ + GntEntry *entry = GNT_ENTRY(bind); + if (entry->cursor >= entry->end) + return FALSE; + entry->cursor = g_utf8_find_next_char(entry->cursor, NULL); + while (gnt_util_onscreen_width(entry->scroll, entry->cursor) >= GNT_WIDGET(entry)->priv.width) + entry->scroll = g_utf8_find_next_char(entry->scroll, NULL); + entry_redraw(GNT_WIDGET(entry)); + return TRUE; +} + +static gboolean +backspace(GntBindable *bind, GList *null) +{ + int len; + GntEntry *entry = GNT_ENTRY(bind); + + if (entry->cursor <= entry->start) + return TRUE; + + len = entry->cursor - g_utf8_find_prev_char(entry->start, entry->cursor); + entry->cursor -= len; + memmove(entry->cursor, entry->cursor + len, entry->end - entry->cursor); + entry->end -= len; + + if (entry->scroll > entry->start) + entry->scroll = g_utf8_find_prev_char(entry->start, entry->scroll); + + entry_redraw(GNT_WIDGET(entry)); + if (entry->ddown) + show_suggest_dropdown(entry); + entry_text_changed(entry); + return TRUE; +} + +static gboolean +delkey(GntBindable *bind, GList *null) +{ + int len; + GntEntry *entry = GNT_ENTRY(bind); + + if (entry->cursor >= entry->end) + return FALSE; + + len = g_utf8_find_next_char(entry->cursor, NULL) - entry->cursor; + memmove(entry->cursor, entry->cursor + len, entry->end - entry->cursor - len + 1); + entry->end -= len; + entry_redraw(GNT_WIDGET(entry)); + + if (entry->ddown) + show_suggest_dropdown(entry); + entry_text_changed(entry); + return TRUE; +} + +static gboolean +move_start(GntBindable *bind, GList *null) +{ + GntEntry *entry = GNT_ENTRY(bind); + entry->scroll = entry->cursor = entry->start; + entry_redraw(GNT_WIDGET(entry)); + return TRUE; +} + +static gboolean +move_end(GntBindable *bind, GList *null) +{ + GntEntry *entry = GNT_ENTRY(bind); + entry->cursor = entry->end; + /* This should be better than this */ + while (gnt_util_onscreen_width(entry->scroll, entry->cursor) >= GNT_WIDGET(entry)->priv.width) + entry->scroll = g_utf8_find_next_char(entry->scroll, NULL); + entry_redraw(GNT_WIDGET(entry)); + return TRUE; +} + +static gboolean +history_prev(GntBindable *bind, GList *null) +{ + GntEntry *entry = GNT_ENTRY(bind); + if (entry->histlength && entry->history->prev) + { + entry->history = entry->history->prev; + gnt_entry_set_text_internal(entry, entry->history->data); + destroy_suggest(entry); + entry_text_changed(entry); + + return TRUE; + } + return FALSE; +} + +static gboolean +history_next(GntBindable *bind, GList *null) +{ + GntEntry *entry = GNT_ENTRY(bind); + if (entry->histlength && entry->history->next) + { + if (entry->history->prev == NULL) + { + /* Save the current contents */ + char *text = g_strdup(gnt_entry_get_text(entry)); + g_free(entry->history->data); + entry->history->data = text; + } + + entry->history = entry->history->next; + gnt_entry_set_text_internal(entry, entry->history->data); + destroy_suggest(entry); + entry_text_changed(entry); + + return TRUE; + } + return FALSE; +} + +static gboolean +suggest_show(GntBindable *bind, GList *null) +{ + return show_suggest_dropdown(GNT_ENTRY(bind)); +} + +static gboolean +suggest_next(GntBindable *bind, GList *null) +{ + GntEntry *entry = GNT_ENTRY(bind); + if (entry->ddown) { + gnt_bindable_perform_action_named(GNT_BINDABLE(entry->ddown), "move-down", NULL); + return TRUE; + } + return FALSE; +} + +static gboolean +suggest_prev(GntBindable *bind, GList *null) +{ + GntEntry *entry = GNT_ENTRY(bind); + if (entry->ddown) { + gnt_bindable_perform_action_named(GNT_BINDABLE(entry->ddown), "move-up", NULL); + return TRUE; + } + return FALSE; +} + +static gboolean +del_to_home(GntBindable *bind, GList *null) +{ + GntEntry *entry = GNT_ENTRY(bind); + if (entry->cursor <= entry->start) + return TRUE; + memmove(entry->start, entry->cursor, entry->end - entry->cursor); + entry->end -= (entry->cursor - entry->start); + entry->cursor = entry->scroll = entry->start; + memset(entry->end, '\0', entry->buffer - (entry->end - entry->start)); + entry_redraw(GNT_WIDGET(bind)); + entry_text_changed(entry); + return TRUE; +} + +static gboolean +del_to_end(GntBindable *bind, GList *null) +{ + GntEntry *entry = GNT_ENTRY(bind); + if (entry->end <= entry->cursor) + return TRUE; + entry->end = entry->cursor; + memset(entry->end, '\0', entry->buffer - (entry->end - entry->start)); + entry_redraw(GNT_WIDGET(bind)); + entry_text_changed(entry); + return TRUE; +} + +#define SAME(a,b) ((g_unichar_isalpha(a) && g_unichar_isalpha(b)) || \ + (g_unichar_isdigit(a) && g_unichar_isdigit(b)) || \ + (g_unichar_isspace(a) && g_unichar_isspace(b)) || \ + (g_unichar_iswide(a) && g_unichar_iswide(b))) + +static const char * +begin_word(const char *text, const char *begin) +{ + gunichar ch = 0; + while (text > begin && (!*text || g_unichar_isspace(g_utf8_get_char(text)))) + text = g_utf8_find_prev_char(begin, text); + ch = g_utf8_get_char(text); + while ((text = g_utf8_find_prev_char(begin, text)) >= begin) { + gunichar cur = g_utf8_get_char(text); + if (!SAME(ch, cur)) + break; + } + + return (text ? g_utf8_find_next_char(text, NULL) : begin); +} + +static const char * +next_begin_word(const char *text, const char *end) +{ + gunichar ch = 0; + ch = g_utf8_get_char(text); + while ((text = g_utf8_find_next_char(text, end)) != NULL && text <= end) { + gunichar cur = g_utf8_get_char(text); + if (!SAME(ch, cur)) + break; + } + + while (text && text < end && g_unichar_isspace(g_utf8_get_char(text))) + text = g_utf8_find_next_char(text, end); + return (text ? text : end); +} + +#undef SAME +static gboolean +move_back_word(GntBindable *bind, GList *null) +{ + GntEntry *entry = GNT_ENTRY(bind); + const char *iter = g_utf8_find_prev_char(entry->start, entry->cursor); + + if (iter < entry->start) + return TRUE; + iter = begin_word(iter, entry->start); + entry->cursor = (char*)iter; + if (entry->cursor < entry->scroll) + entry->scroll = entry->cursor; + entry_redraw(GNT_WIDGET(bind)); + return TRUE; +} + +static gboolean +del_prev_word(GntBindable *bind, GList *null) +{ + GntWidget *widget = GNT_WIDGET(bind); + GntEntry *entry = GNT_ENTRY(bind); + char *iter = g_utf8_find_prev_char(entry->start, entry->cursor); + int count; + + if (iter < entry->start) + return TRUE; + iter = (char*)begin_word(iter, entry->start); + count = entry->cursor - iter; + memmove(iter, entry->cursor, entry->end - entry->cursor); + entry->end -= count; + entry->cursor = iter; + if (entry->cursor <= entry->scroll) { + entry->scroll = entry->cursor - widget->priv.width + 2; + if (entry->scroll < entry->start) + entry->scroll = entry->start; + } + memset(entry->end, '\0', entry->buffer - (entry->end - entry->start)); + entry_redraw(widget); + entry_text_changed(entry); + + return TRUE; +} + +static gboolean +move_forward_word(GntBindable *bind, GList *list) +{ + GntEntry *entry = GNT_ENTRY(bind); + GntWidget *widget = GNT_WIDGET(bind); + entry->cursor = (char *)next_begin_word(entry->cursor, entry->end); + while (gnt_util_onscreen_width(entry->scroll, entry->cursor) >= widget->priv.width) { + entry->scroll = g_utf8_find_next_char(entry->scroll, NULL); + } + entry_redraw(widget); + return TRUE; +} + +static gboolean +delete_forward_word(GntBindable *bind, GList *list) +{ + GntEntry *entry = GNT_ENTRY(bind); + GntWidget *widget = GNT_WIDGET(bind); + char *iter = (char *)next_begin_word(entry->cursor, entry->end); + int len = entry->end - iter + 1; + if (len <= 0) + return TRUE; + memmove(entry->cursor, iter, len); + len = iter - entry->cursor; + entry->end -= len; + memset(entry->end, '\0', len); + entry_redraw(widget); + entry_text_changed(entry); + return TRUE; +} + +static gboolean +gnt_entry_key_pressed(GntWidget *widget, const char *text) +{ + GntEntry *entry = GNT_ENTRY(widget); + + if (text[0] == 27) + { + if (text[1] == 0) + { + destroy_suggest(entry); + return TRUE; + } + + return FALSE; + } + else + { + if (text[0] == '\t') + { + if (entry->ddown) + destroy_suggest(entry); + else if (entry->suggests) + return show_suggest_dropdown(entry); + + return FALSE; + } + else if (text[0] == '\r' && entry->ddown) + { + char *text = g_strdup(gnt_tree_get_selection_data(GNT_TREE(entry->ddown))); + destroy_suggest(entry); + if (entry->word) + { + char *s = get_beginning_of_word(entry); + char *iter = text; + while (*iter && toupper(*s) == toupper(*iter)) + { + *s++ = *iter++; + } + gnt_entry_key_pressed(widget, iter); + } + else + { + gnt_entry_set_text_internal(entry, text); + } + g_free(text); + entry_text_changed(entry); + return TRUE; + } + + if (!iscntrl(text[0])) + { + const char *str, *next; + + for (str = text; *str; str = next) + { + int len; + next = g_utf8_find_next_char(str, NULL); + len = next - str; + + /* Valid input? */ + /* XXX: Is it necessary to use _unichar_ variants here? */ + if (ispunct(*str) && (entry->flag & GNT_ENTRY_FLAG_NO_PUNCT)) + continue; + if (isspace(*str) && (entry->flag & GNT_ENTRY_FLAG_NO_SPACE)) + continue; + if (isalpha(*str) && !(entry->flag & GNT_ENTRY_FLAG_ALPHA)) + continue; + if (isdigit(*str) && !(entry->flag & GNT_ENTRY_FLAG_INT)) + continue; + + /* Reached the max? */ + if (entry->max && g_utf8_pointer_to_offset(entry->start, entry->end) >= entry->max) + continue; + + if (entry->end + len - entry->start >= entry->buffer) + { + /* This will cause the buffer to grow */ + char *tmp = g_strdup_printf("%s%*s", entry->start, len, ""); + gnt_entry_set_text_internal(entry, tmp); + g_free(tmp); + } + + memmove(entry->cursor + len, entry->cursor, entry->end - entry->cursor + 1); + entry->end += len; + + while (str < next) + { + if (*str == '\r' || *str == '\n') + *entry->cursor = ' '; + else + *entry->cursor = *str; + entry->cursor++; + str++; + } + + while (gnt_util_onscreen_width(entry->scroll, entry->cursor) >= widget->priv.width) + entry->scroll = g_utf8_find_next_char(entry->scroll, NULL); + + if (entry->ddown) + show_suggest_dropdown(entry); + } + entry_redraw(widget); + entry_text_changed(entry); + return TRUE; + } + } + + return FALSE; +} + +static void +gnt_entry_destroy(GntWidget *widget) +{ + GntEntry *entry = GNT_ENTRY(widget); + g_free(entry->start); + + if (entry->history) + { + entry->history = g_list_first(entry->history); + g_list_foreach(entry->history, (GFunc)g_free, NULL); + g_list_free(entry->history); + } + + if (entry->suggests) + { + g_list_foreach(entry->suggests, (GFunc)g_free, NULL); + g_list_free(entry->suggests); + } + + if (entry->ddown) + { + gnt_widget_destroy(entry->ddown->parent); + } +} + +static void +gnt_entry_lost_focus(GntWidget *widget) +{ + GntEntry *entry = GNT_ENTRY(widget); + destroy_suggest(entry); + entry_redraw(widget); +} + +static void +gnt_entry_class_init(GntEntryClass *klass) +{ + GntBindableClass *bindable = GNT_BINDABLE_CLASS(klass); + char s[2] = {erasechar(), 0}; + + parent_class = GNT_WIDGET_CLASS(klass); + parent_class->destroy = gnt_entry_destroy; + parent_class->draw = gnt_entry_draw; + parent_class->map = gnt_entry_map; + parent_class->size_request = gnt_entry_size_request; + parent_class->key_pressed = gnt_entry_key_pressed; + parent_class->lost_focus = gnt_entry_lost_focus; + + signals[SIG_TEXT_CHANGED] = + g_signal_new("text_changed", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntEntryClass, text_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + gnt_bindable_class_register_action(bindable, "cursor-home", move_start, + GNT_KEY_CTRL_A, NULL); + gnt_bindable_register_binding(bindable, "cursor-home", GNT_KEY_HOME, NULL); + gnt_bindable_class_register_action(bindable, "cursor-end", move_end, + GNT_KEY_CTRL_E, NULL); + gnt_bindable_register_binding(bindable, "cursor-end", GNT_KEY_END, NULL); + gnt_bindable_class_register_action(bindable, "delete-prev", backspace, + GNT_KEY_BACKSPACE, NULL); + gnt_bindable_register_binding(bindable, "delete-prev", s, NULL); + gnt_bindable_register_binding(bindable, "delete-prev", GNT_KEY_CTRL_H, NULL); + gnt_bindable_class_register_action(bindable, "delete-next", delkey, + GNT_KEY_DEL, NULL); + gnt_bindable_register_binding(bindable, "delete-next", GNT_KEY_CTRL_D, NULL); + gnt_bindable_class_register_action(bindable, "delete-start", del_to_home, + GNT_KEY_CTRL_U, NULL); + gnt_bindable_class_register_action(bindable, "delete-end", del_to_end, + GNT_KEY_CTRL_K, NULL); + gnt_bindable_class_register_action(bindable, "delete-prev-word", del_prev_word, + GNT_KEY_CTRL_W, NULL); + gnt_bindable_class_register_action(bindable, "cursor-prev-word", move_back_word, + "\033" "b", NULL); + gnt_bindable_class_register_action(bindable, "cursor-prev", move_back, + GNT_KEY_LEFT, NULL); + gnt_bindable_register_binding(bindable, "cursor-prev", GNT_KEY_CTRL_B, NULL); + gnt_bindable_class_register_action(bindable, "cursor-next", move_forward, + GNT_KEY_RIGHT, NULL); + gnt_bindable_register_binding(bindable, "cursor-next", GNT_KEY_CTRL_F, NULL); + gnt_bindable_class_register_action(bindable, "cursor-next-word", move_forward_word, + "\033" "f", NULL); + gnt_bindable_class_register_action(bindable, "delete-next-word", delete_forward_word, + "\033" "d", NULL); + gnt_bindable_class_register_action(bindable, "suggest-show", suggest_show, + "\t", NULL); + gnt_bindable_class_register_action(bindable, "suggest-next", suggest_next, + GNT_KEY_DOWN, NULL); + gnt_bindable_class_register_action(bindable, "suggest-prev", suggest_prev, + GNT_KEY_UP, NULL); + gnt_bindable_class_register_action(bindable, "history-prev", history_prev, + GNT_KEY_CTRL_DOWN, NULL); + gnt_bindable_class_register_action(bindable, "history-next", history_next, + GNT_KEY_CTRL_UP, NULL); + + gnt_style_read_actions(G_OBJECT_CLASS_TYPE(klass), GNT_BINDABLE_CLASS(klass)); + GNTDEBUG; +} + +static void +gnt_entry_init(GTypeInstance *instance, gpointer class) +{ + GntWidget *widget = GNT_WIDGET(instance); + GntEntry *entry = GNT_ENTRY(instance); + + entry->flag = GNT_ENTRY_FLAG_ALL; + entry->max = 0; + + entry->histlength = 0; + entry->history = NULL; + + entry->word = TRUE; + entry->always = FALSE; + entry->suggests = NULL; + + GNT_WIDGET_SET_FLAGS(GNT_WIDGET(entry), + GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW | GNT_WIDGET_CAN_TAKE_FOCUS); + GNT_WIDGET_SET_FLAGS(GNT_WIDGET(entry), GNT_WIDGET_GROW_X); + + widget->priv.minw = 3; + widget->priv.minh = 1; + + GNTDEBUG; +} + +/****************************************************************************** + * GntEntry API + *****************************************************************************/ +GType +gnt_entry_get_gtype(void) +{ + static GType type = 0; + + if(type == 0) + { + static const GTypeInfo info = { + sizeof(GntEntryClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)gnt_entry_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof(GntEntry), + 0, /* n_preallocs */ + gnt_entry_init, /* instance_init */ + }; + + type = g_type_register_static(GNT_TYPE_WIDGET, + "GntEntry", + &info, 0); + } + + return type; +} + +GntWidget *gnt_entry_new(const char *text) +{ + GntWidget *widget = g_object_new(GNT_TYPE_ENTRY, NULL); + GntEntry *entry = GNT_ENTRY(widget); + + gnt_entry_set_text_internal(entry, text); + + return widget; +} + +static void +gnt_entry_set_text_internal(GntEntry *entry, const char *text) +{ + int len; + int scroll, cursor; + + g_free(entry->start); + + if (text && text[0]) + { + len = strlen(text); + } + else + { + len = 0; + } + + entry->buffer = len + 128; + + scroll = entry->scroll - entry->start; + cursor = entry->end - entry->cursor; + + entry->start = g_new0(char, entry->buffer); + if (text) + snprintf(entry->start, len + 1, "%s", text); + entry->end = entry->start + len; + + entry->scroll = entry->start + scroll; + entry->cursor = entry->end - cursor; + + if (GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(entry), GNT_WIDGET_MAPPED)) + entry_redraw(GNT_WIDGET(entry)); +} + +void gnt_entry_set_text(GntEntry *entry, const char *text) +{ + gboolean changed = TRUE; + if (text == NULL && entry->start == NULL) + changed = FALSE; + if (text && entry->start && g_utf8_collate(text, entry->start) == 0) + changed = FALSE; + gnt_entry_set_text_internal(entry, text); + if (changed) + entry_text_changed(entry); +} + +void gnt_entry_set_max(GntEntry *entry, int max) +{ + entry->max = max; +} + +void gnt_entry_set_flag(GntEntry *entry, GntEntryFlag flag) +{ + entry->flag = flag; + /* XXX: Check the existing string to make sure the flags are respected? */ +} + +const char *gnt_entry_get_text(GntEntry *entry) +{ + return entry->start; +} + +void gnt_entry_clear(GntEntry *entry) +{ + gnt_entry_set_text_internal(entry, NULL); + entry->scroll = entry->cursor = entry->end = entry->start; + entry_redraw(GNT_WIDGET(entry)); + destroy_suggest(entry); + entry_text_changed(entry); +} + +void gnt_entry_set_masked(GntEntry *entry, gboolean set) +{ + entry->masked = set; +} + +void gnt_entry_add_to_history(GntEntry *entry, const char *text) +{ + g_return_if_fail(entry->history != NULL); /* Need to set_history_length first */ + + if (g_list_length(entry->history) >= entry->histlength) + return; + + entry->history = g_list_first(entry->history); + g_free(entry->history->data); + entry->history->data = g_strdup(text); + entry->history = g_list_prepend(entry->history, NULL); +} + +void gnt_entry_set_history_length(GntEntry *entry, int num) +{ + if (num == 0) + { + entry->histlength = num; + if (entry->history) + { + entry->history = g_list_first(entry->history); + g_list_foreach(entry->history, (GFunc)g_free, NULL); + g_list_free(entry->history); + entry->history = NULL; + } + return; + } + + if (entry->histlength == 0) + { + entry->histlength = num; + entry->history = g_list_append(NULL, NULL); + return; + } + + if (num > 0 && num < entry->histlength) + { + GList *first, *iter; + int index = 0; + for (first = entry->history, index = 0; first->prev; first = first->prev, index++); + while ((iter = g_list_nth(first, num)) != NULL) + { + g_free(iter->data); + first = g_list_delete_link(first, iter); + } + entry->histlength = num; + if (index >= num) + entry->history = g_list_last(first); + return; + } + + entry->histlength = num; +} + +void gnt_entry_set_word_suggest(GntEntry *entry, gboolean word) +{ + entry->word = word; +} + +void gnt_entry_set_always_suggest(GntEntry *entry, gboolean always) +{ + entry->always = always; +} + +void gnt_entry_add_suggest(GntEntry *entry, const char *text) +{ + GList *find; + + if (!text || !*text) + return; + + find = g_list_find_custom(entry->suggests, text, (GCompareFunc)g_utf8_collate); + if (find) + return; + entry->suggests = g_list_append(entry->suggests, g_strdup(text)); +} + +void gnt_entry_remove_suggest(GntEntry *entry, const char *text) +{ + GList *find = g_list_find_custom(entry->suggests, text, (GCompareFunc)g_utf8_collate); + if (find) + { + g_free(find->data); + entry->suggests = g_list_delete_link(entry->suggests, find); + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntentry.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,106 @@ +#ifndef GNT_ENTRY_H +#define GNT_ENTRY_H + +#include "gntwidget.h" +#include "gnt.h" +#include "gntcolors.h" +#include "gntkeys.h" + +#define GNT_TYPE_ENTRY (gnt_entry_get_gtype()) +#define GNT_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_ENTRY, GntEntry)) +#define GNT_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_ENTRY, GntEntryClass)) +#define GNT_IS_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_ENTRY)) +#define GNT_IS_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GNT_TYPE_ENTRY)) +#define GNT_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GNT_TYPE_ENTRY, GntEntryClass)) + +#define GNT_ENTRY_FLAGS(obj) (GNT_ENTRY(obj)->priv.flags) +#define GNT_ENTRY_SET_FLAGS(obj, flags) (GNT_ENTRY_FLAGS(obj) |= flags) +#define GNT_ENTRY_UNSET_FLAGS(obj, flags) (GNT_ENTRY_FLAGS(obj) &= ~(flags)) + +#define ENTRY_CHAR '_' /* The character to use to fill in the blank places */ + +typedef struct _GnEntry GntEntry; +typedef struct _GnEntryPriv GntEntryPriv; +typedef struct _GnEntryClass GntEntryClass; + +typedef enum +{ + GNT_ENTRY_FLAG_ALPHA = 1 << 0, /* Only alpha */ + GNT_ENTRY_FLAG_INT = 1 << 1, /* Only integer */ + GNT_ENTRY_FLAG_NO_SPACE = 1 << 2, /* No blank space is allowed */ + GNT_ENTRY_FLAG_NO_PUNCT = 1 << 3, /* No punctuations */ + GNT_ENTRY_FLAG_MASK = 1 << 4, /* Mask the inputs */ +} GntEntryFlag; + +#define GNT_ENTRY_FLAG_ALL (GNT_ENTRY_FLAG_ALPHA | GNT_ENTRY_FLAG_INT) + +struct _GnEntry +{ + GntWidget parent; + + GntEntryFlag flag; + + char *start; + char *end; + char *scroll; /* Current scrolling position */ + char *cursor; /* Cursor location */ + /* 0 <= cursor - scroll < widget-width */ + + size_t buffer; /* Size of the buffer */ + + int max; /* 0 means infinite */ + gboolean masked; + + GList *history; /* History of the strings. User can use this by pressing ctrl+up/down */ + int histlength; /* How long can the history be? */ + + GList *suggests; /* List of suggestions */ + gboolean word; /* Are the suggestions for only a word, or for the whole thing? */ + gboolean always; /* Should the list of suggestions show at all times, or only on tab-press? */ + GntWidget *ddown; /* The dropdown with the suggested list */ +}; + +struct _GnEntryClass +{ + GntWidgetClass parent; + + void (*text_changed)(GntEntry *entry); + void (*gnt_reserved1)(void); + void (*gnt_reserved2)(void); + void (*gnt_reserved3)(void); + void (*gnt_reserved4)(void); +}; + +G_BEGIN_DECLS + +GType gnt_entry_get_gtype(void); + +GntWidget *gnt_entry_new(const char *text); + +void gnt_entry_set_max(GntEntry *entry, int max); + +void gnt_entry_set_text(GntEntry *entry, const char *text); + +void gnt_entry_set_flag(GntEntry *entry, GntEntryFlag flag); + +const char *gnt_entry_get_text(GntEntry *entry); + +void gnt_entry_clear(GntEntry *entry); + +void gnt_entry_set_masked(GntEntry *entry, gboolean set); + +void gnt_entry_add_to_history(GntEntry *entry, const char *text); + +void gnt_entry_set_history_length(GntEntry *entry, int num); + +void gnt_entry_set_word_suggest(GntEntry *entry, gboolean word); + +void gnt_entry_set_always_suggest(GntEntry *entry, gboolean always); + +void gnt_entry_add_suggest(GntEntry *entry, const char *text); + +void gnt_entry_remove_suggest(GntEntry *entry, const char *text); + +G_END_DECLS + +#endif /* GNT_ENTRY_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntkeys.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,50 @@ +#include "gntkeys.h" + +#include <stdlib.h> +#include <string.h> + +char *gnt_key_cup; +char *gnt_key_cdown; +char *gnt_key_cleft; +char *gnt_key_cright; + + +static const char *term; + +void gnt_init_keys() +{ + if (term == NULL) { + term = getenv("TERM"); + if (!term) + term = ""; /* Just in case */ + } + + if (strcmp(term, "xterm") == 0 || strcmp(term, "rxvt") == 0) { + gnt_key_cup = "\033" "[1;5A"; + gnt_key_cdown = "\033" "[1;5B"; + gnt_key_cright = "\033" "[1;5C"; + gnt_key_cleft = "\033" "[1;5D"; + } else if (strcmp(term, "screen") == 0 || strcmp(term, "rxvt-unicode") == 0) { + gnt_key_cup = "\033" "Oa"; + gnt_key_cdown = "\033" "Ob"; + gnt_key_cright = "\033" "Oc"; + gnt_key_cleft = "\033" "Od"; + } +} + +void gnt_keys_refine(char *text) +{ + if (*text == 27 && *(text + 1) == '[' && *(text + 3) == '\0' && + (*(text + 2) >= 'A' && *(text + 2) <= 'D')) { + /* Apparently this is necessary for urxvt and screen and xterm */ + if (strcmp(term, "screen") == 0 || strcmp(term, "rxvt-unicode") == 0 || + strcmp(term, "xterm") == 0) + *(text + 1) = 'O'; + } else if (*(unsigned char*)text == 195) { + if (*(text + 2) == 0 && strcmp(term, "xterm") == 0) { + *(text) = 27; + *(text + 1) -= 64; /* Say wha? */ + } + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntkeys.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,94 @@ +#ifndef GNT_KEYS_H +#define GNT_KEYS_H + +#include <curses.h> +#include <term.h> + +/** + * terminfo/termcap doesn't provide all the information that I want to use, eg. + * ctrl-up, ctrl-down etc. So I am going to hard-code some of the information + * for some popular $TERMs + */ +extern char *gnt_key_cup; +extern char *gnt_key_cdown; +extern char *gnt_key_cleft; +extern char *gnt_key_cright; + +#define SAFE(x) ((x) ? (x) : "") + +#define GNT_KEY_POPUP SAFE(key_f16) /* Apparently */ + +/* Arrow keys */ +#define GNT_KEY_LEFT SAFE(key_left) +#define GNT_KEY_RIGHT SAFE(key_right) +#define GNT_KEY_UP SAFE(key_up) +#define GNT_KEY_DOWN SAFE(key_down) + +#define GNT_KEY_CTRL_UP SAFE(gnt_key_cup) +#define GNT_KEY_CTRL_DOWN SAFE(gnt_key_cdown) +#define GNT_KEY_CTRL_RIGHT SAFE(gnt_key_cright) +#define GNT_KEY_CTRL_LEFT SAFE(gnt_key_cleft) + +#define GNT_KEY_PGUP SAFE(key_ppage) +#define GNT_KEY_PGDOWN SAFE(key_npage) +#define GNT_KEY_HOME SAFE(key_home) +#define GNT_KEY_END SAFE(key_end) + +#define GNT_KEY_ENTER carriage_return + +#define GNT_KEY_BACKSPACE SAFE(key_backspace) +#define GNT_KEY_DEL SAFE(key_dc) +#define GNT_KEY_INS SAFE(key_ic) + +#define GNT_KEY_CTRL_A "\001" +#define GNT_KEY_CTRL_B "\002" +#define GNT_KEY_CTRL_D "\004" +#define GNT_KEY_CTRL_E "\005" +#define GNT_KEY_CTRL_F "\006" +#define GNT_KEY_CTRL_G "\007" +#define GNT_KEY_CTRL_H "\010" +#define GNT_KEY_CTRL_I "\011" +#define GNT_KEY_CTRL_J "\012" +#define GNT_KEY_CTRL_K "\013" +#define GNT_KEY_CTRL_L "\014" +#define GNT_KEY_CTRL_M "\012" +#define GNT_KEY_CTRL_N "\016" +#define GNT_KEY_CTRL_O "\017" +#define GNT_KEY_CTRL_P "\020" +#define GNT_KEY_CTRL_R "\022" +#define GNT_KEY_CTRL_T "\024" +#define GNT_KEY_CTRL_U "\025" +#define GNT_KEY_CTRL_V "\026" +#define GNT_KEY_CTRL_W "\027" +#define GNT_KEY_CTRL_X "\030" +#define GNT_KEY_CTRL_Y "\031" + +#define GNT_KEY_F1 SAFE(key_f1) +#define GNT_KEY_F2 SAFE(key_f2) +#define GNT_KEY_F3 SAFE(key_f3) +#define GNT_KEY_F4 SAFE(key_f4) +#define GNT_KEY_F5 SAFE(key_f5) +#define GNT_KEY_F6 SAFE(key_f6) +#define GNT_KEY_F7 SAFE(key_f7) +#define GNT_KEY_F8 SAFE(key_f8) +#define GNT_KEY_F9 SAFE(key_f9) +#define GNT_KEY_F10 SAFE(key_f10) +#define GNT_KEY_F11 SAFE(key_f11) +#define GNT_KEY_F12 SAFE(key_f12) + +/** + * This will do stuff with the terminal settings and stuff. + */ +void gnt_init_keys(); +void gnt_keys_refine(char *text); + + +/* A lot of commonly used variable names are defined in <term.h>. + * #undef them to make life easier for everyone. */ + +#undef columns +#undef lines +#undef buttons +#undef newline + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntlabel.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,123 @@ +#include "gntlabel.h" +#include "gntutils.h" + +#include <string.h> + +enum +{ + SIGS = 1, +}; + +static GntWidgetClass *parent_class = NULL; + +static void +gnt_label_destroy(GntWidget *widget) +{ + GntLabel *label = GNT_LABEL(widget); + g_free(label->text); +} + +static void +gnt_label_draw(GntWidget *widget) +{ + GntLabel *label = GNT_LABEL(widget); + chtype flag = gnt_text_format_flag_to_chtype(label->flags); + + wbkgdset(widget->window, '\0' | flag); + mvwprintw(widget->window, 0, 0, label->text); + + GNTDEBUG; +} + +static void +gnt_label_size_request(GntWidget *widget) +{ + GntLabel *label = GNT_LABEL(widget); + + gnt_util_get_text_bound(label->text, + &widget->priv.width, &widget->priv.height); +} + +static void +gnt_label_class_init(GntLabelClass *klass) +{ + parent_class = GNT_WIDGET_CLASS(klass); + parent_class->destroy = gnt_label_destroy; + parent_class->draw = gnt_label_draw; + parent_class->map = NULL; + parent_class->size_request = gnt_label_size_request; + + GNTDEBUG; +} + +static void +gnt_label_init(GTypeInstance *instance, gpointer class) +{ + GntWidget *widget = GNT_WIDGET(instance); + GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_GROW_X); + widget->priv.minw = 3; + widget->priv.minh = 1; + GNTDEBUG; +} + +/****************************************************************************** + * GntLabel API + *****************************************************************************/ +GType +gnt_label_get_gtype(void) +{ + static GType type = 0; + + if(type == 0) + { + static const GTypeInfo info = { + sizeof(GntLabelClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)gnt_label_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof(GntLabel), + 0, /* n_preallocs */ + gnt_label_init, /* instance_init */ + }; + + type = g_type_register_static(GNT_TYPE_WIDGET, + "GntLabel", + &info, 0); + } + + return type; +} + +GntWidget *gnt_label_new(const char *text) +{ + return gnt_label_new_with_format(text, 0); +} + +GntWidget *gnt_label_new_with_format(const char *text, GntTextFormatFlags flags) +{ + GntWidget *widget = g_object_new(GNT_TYPE_LABEL, NULL); + GntLabel *label = GNT_LABEL(widget); + + label->text = gnt_util_onscreen_fit_string(text, -1); + label->flags = flags; + gnt_widget_set_take_focus(widget, FALSE); + GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW); + + return widget; +} + +void gnt_label_set_text(GntLabel *label, const char *text) +{ + g_free(label->text); + label->text = gnt_util_onscreen_fit_string(text, -1); + + if (GNT_WIDGET(label)->window) + { + gnt_widget_hide(GNT_WIDGET(label)); + gnt_label_size_request(GNT_WIDGET(label)); + gnt_widget_draw(GNT_WIDGET(label)); + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntlabel.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,54 @@ +#ifndef GNT_LABEL_H +#define GNT_LABEL_H + +#include "gnt.h" +#include "gntwidget.h" +#include "gnttextview.h" + +#define GNT_TYPE_LABEL (gnt_label_get_gtype()) +#define GNT_LABEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_LABEL, GntLabel)) +#define GNT_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_LABEL, GntLabelClass)) +#define GNT_IS_LABEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_LABEL)) +#define GNT_IS_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GNT_TYPE_LABEL)) +#define GNT_LABEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GNT_TYPE_LABEL, GntLabelClass)) + +typedef struct _GnLabel GntLabel; +typedef struct _GnLabelClass GntLabelClass; + +struct _GnLabel +{ + GntWidget parent; + + char *text; + GntTextFormatFlags flags; + + void (*gnt_reserved1)(void); + void (*gnt_reserved2)(void); + void (*gnt_reserved3)(void); + void (*gnt_reserved4)(void); +}; + +struct _GnLabelClass +{ + GntWidgetClass parent; + + void (*gnt_reserved1)(void); + void (*gnt_reserved2)(void); + void (*gnt_reserved3)(void); + void (*gnt_reserved4)(void); +}; + +G_BEGIN_DECLS + +GType gnt_label_get_gtype(void); + +GntWidget *gnt_label_new(const char *text); + +GntWidget *gnt_label_new_with_format(const char *text, GntTextFormatFlags flags); + +void gnt_label_set_text(GntLabel *label, const char *text); + +G_END_DECLS + +#endif /* GNT_LABEL_H */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntline.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,114 @@ +#include "gntline.h" + +enum +{ + SIGS = 1, +}; + +static GntWidgetClass *parent_class = NULL; + +static void +gnt_line_draw(GntWidget *widget) +{ + GntLine *line = GNT_LINE(widget); + if (line->vertical) + mvwvline(widget->window, 1, 0, ACS_VLINE | COLOR_PAIR(GNT_COLOR_NORMAL), + widget->priv.height - 2); + else + mvwhline(widget->window, 0, 1, ACS_HLINE | COLOR_PAIR(GNT_COLOR_NORMAL), + widget->priv.width - 2); +} + +static void +gnt_line_size_request(GntWidget *widget) +{ + if (GNT_LINE(widget)->vertical) + { + widget->priv.width = 1; + widget->priv.height = 5; + } + else + { + widget->priv.width = 5; + widget->priv.height = 1; + } +} + +static void +gnt_line_map(GntWidget *widget) +{ + if (widget->priv.width == 0 || widget->priv.height == 0) + gnt_widget_size_request(widget); + GNTDEBUG; +} + +static void +gnt_line_class_init(GntLineClass *klass) +{ + parent_class = GNT_WIDGET_CLASS(klass); + parent_class->draw = gnt_line_draw; + parent_class->map = gnt_line_map; + parent_class->size_request = gnt_line_size_request; + + GNTDEBUG; +} + +static void +gnt_line_init(GTypeInstance *instance, gpointer class) +{ + GntWidget *widget = GNT_WIDGET(instance); + GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_NO_SHADOW | GNT_WIDGET_NO_BORDER); + widget->priv.minw = 1; + widget->priv.minh = 1; + GNTDEBUG; +} + +/****************************************************************************** + * GntLine API + *****************************************************************************/ +GType +gnt_line_get_gtype(void) +{ + static GType type = 0; + + if(type == 0) + { + static const GTypeInfo info = { + sizeof(GntLineClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)gnt_line_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof(GntLine), + 0, /* n_preallocs */ + gnt_line_init, /* instance_init */ + }; + + type = g_type_register_static(GNT_TYPE_WIDGET, + "GntLine", + &info, 0); + } + + return type; +} + +GntWidget *gnt_line_new(gboolean vertical) +{ + GntWidget *widget = g_object_new(GNT_TYPE_LINE, NULL); + GntLine *line = GNT_LINE(widget); + + line->vertical = vertical; + + if (vertical) + { + GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_GROW_Y); + } + else + { + GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_GROW_X); + } + + return widget; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntline.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,52 @@ +#ifndef GNT_LINE_H +#define GNT_LINE_H + +#include "gntwidget.h" +#include "gnt.h" +#include "gntcolors.h" +#include "gntkeys.h" + +#define GNT_TYPE_LINE (gnt_line_get_gtype()) +#define GNT_LINE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_LINE, GntLine)) +#define GNT_LINE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_LINE, GntLineClass)) +#define GNT_IS_LINE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_LINE)) +#define GNT_IS_LINE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GNT_TYPE_LINE)) +#define GNT_LINE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GNT_TYPE_LINE, GntLineClass)) + +#define GNT_LINE_FLAGS(obj) (GNT_LINE(obj)->priv.flags) +#define GNT_LINE_SET_FLAGS(obj, flags) (GNT_LINE_FLAGS(obj) |= flags) +#define GNT_LINE_UNSET_FLAGS(obj, flags) (GNT_LINE_FLAGS(obj) &= ~(flags)) + +typedef struct _GnLine GntLine; +typedef struct _GnLinePriv GntLinePriv; +typedef struct _GnLineClass GntLineClass; + +struct _GnLine +{ + GntWidget parent; + + gboolean vertical; +}; + +struct _GnLineClass +{ + GntWidgetClass parent; + + void (*gnt_reserved1)(void); + void (*gnt_reserved2)(void); + void (*gnt_reserved3)(void); + void (*gnt_reserved4)(void); +}; + +G_BEGIN_DECLS + +GType gnt_line_get_gtype(void); + +#define gnt_hline_new() gnt_line_new(FALSE) +#define gnt_vline_new() gnt_line_new(TRUE) + +GntWidget *gnt_line_new(gboolean vertical); + +G_END_DECLS + +#endif /* GNT_LINE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntmain.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,477 @@ +#define _GNU_SOURCE +#if defined(__APPLE__) +#define _XOPEN_SOURCE_EXTENDED +#endif + +#include "config.h" + +#include <gmodule.h> + +#include "gnt.h" +#include "gntbox.h" +#include "gntcolors.h" +#include "gntkeys.h" +#include "gntmenu.h" +#include "gntstyle.h" +#include "gnttree.h" +#include "gntutils.h" +#include "gntwm.h" + +#include <panel.h> + +#include <stdio.h> +#include <stdlib.h> +#include <locale.h> +#include <unistd.h> +#include <signal.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> + +#include <sys/types.h> +#include <sys/wait.h> + +/** + * Notes: Interesting functions to look at: + * scr_dump, scr_init, scr_restore: for workspaces + * + * Need to wattrset for colors to use with PDCurses. + */ + +static GIOChannel *channel = NULL; + +static gboolean ascii_only; +static gboolean mouse_enabled; + +static void setup_io(); + +static gboolean refresh_screen(); + +GntWM *wm; + +/** + * Mouse support: + * - bring a window on top if you click on its taskbar + * - click on the top-bar of the active window and drag+drop to move a window + * - click on a window to bring it to focus + * - allow scrolling in tree/textview on wheel-scroll event + * - click to activate button or select a row in tree + * wishlist: + * - have a little [X] on the windows, and clicking it will close that window. + */ +static gboolean +detect_mouse_action(const char *buffer) +{ + int x, y; + static enum { + MOUSE_NONE, + MOUSE_LEFT, + MOUSE_RIGHT, + MOUSE_MIDDLE + } button = MOUSE_NONE; + static GntWidget *remember = NULL; + static int offset = 0; + GntMouseEvent event; + GntWidget *widget = NULL; + PANEL *p = NULL; + + if (!wm->ordered || buffer[0] != 27) + return FALSE; + + buffer++; + if (strlen(buffer) < 5) + return FALSE; + + x = buffer[3]; + y = buffer[4]; + if (x < 0) x += 256; + if (y < 0) y += 256; + x -= 33; + y -= 33; + + while ((p = panel_below(p)) != NULL) { + const GntNode *node = panel_userptr(p); + GntWidget *wid; + if (!node) + continue; + wid = node->me; + if (x >= wid->priv.x && x < wid->priv.x + wid->priv.width) { + if (y >= wid->priv.y && y < wid->priv.y + wid->priv.height) { + widget = wid; + break; + } + } + } + + if (strncmp(buffer, "[M ", 3) == 0) { + /* left button down */ + /* Bring the window you clicked on to front */ + /* If you click on the topbar, then you can drag to move the window */ + event = GNT_LEFT_MOUSE_DOWN; + } else if (strncmp(buffer, "[M\"", 3) == 0) { + /* right button down */ + event = GNT_RIGHT_MOUSE_DOWN; + } else if (strncmp(buffer, "[M!", 3) == 0) { + /* middle button down */ + event = GNT_MIDDLE_MOUSE_DOWN; + } else if (strncmp(buffer, "[M`", 3) == 0) { + /* wheel up*/ + event = GNT_MOUSE_SCROLL_UP; + } else if (strncmp(buffer, "[Ma", 3) == 0) { + /* wheel down */ + event = GNT_MOUSE_SCROLL_DOWN; + } else if (strncmp(buffer, "[M#", 3) == 0) { + /* button up */ + event = GNT_MOUSE_UP; + } else + return FALSE; + + if (gnt_wm_process_click(wm, event, x, y, widget)) + return TRUE; + + if (event == GNT_LEFT_MOUSE_DOWN && widget && widget != wm->_list.window && + !GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_TRANSIENT)) { + if (widget != wm->ordered->data) { + gnt_wm_raise_window(wm, widget); + } + if (y == widget->priv.y) { + offset = x - widget->priv.x; + remember = widget; + button = MOUSE_LEFT; + } + } else if (event == GNT_MOUSE_UP) { + if (button == MOUSE_NONE && y == getmaxy(stdscr) - 1) { + /* Clicked on the taskbar */ + int n = g_list_length(wm->list); + if (n) { + int width = getmaxx(stdscr) / n; + gnt_bindable_perform_action_named(GNT_BINDABLE(wm), "switch-window-n", x/width, NULL); + } + } else if (button == MOUSE_LEFT && remember) { + x -= offset; + if (x < 0) x = 0; + if (y < 0) y = 0; + gnt_screen_move_widget(remember, x, y); + } + button = MOUSE_NONE; + remember = NULL; + offset = 0; + } + + gnt_widget_clicked(widget, event, x, y); + return TRUE; +} + +static gboolean +io_invoke_error(GIOChannel *source, GIOCondition cond, gpointer data) +{ + int id = GPOINTER_TO_INT(data); + g_source_remove(id); + g_io_channel_unref(source); + + channel = NULL; + setup_io(); + return TRUE; +} + +static gboolean +io_invoke(GIOChannel *source, GIOCondition cond, gpointer null) +{ + char keys[256]; + int rd = read(STDIN_FILENO, keys, sizeof(keys) - 1); + if (rd < 0) + { + int ch = getch(); /* This should return ERR, but let's see what it really returns */ + endwin(); + printf("ERROR: %s\n", strerror(errno)); + printf("File descriptor is: %d\n\nGIOChannel is: %p\ngetch() = %d\n", STDIN_FILENO, source, ch); + raise(SIGABRT); + } + else if (rd == 0) + { + endwin(); + printf("EOF\n"); + raise(SIGABRT); + } + + keys[rd] = 0; + gnt_keys_refine(keys); + + if (mouse_enabled && detect_mouse_action(keys)) + return TRUE; + + gnt_wm_process_input(wm, keys); + + return TRUE; +} + +static void +setup_io() +{ + int result; + channel = g_io_channel_unix_new(STDIN_FILENO); + + g_io_channel_set_encoding(channel, NULL, NULL); + g_io_channel_set_buffered(channel, FALSE); +#if 0 + g_io_channel_set_flags(channel, G_IO_FLAG_NONBLOCK, NULL ); +#endif + + result = g_io_add_watch_full(channel, G_PRIORITY_HIGH, + (G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI), + io_invoke, NULL, NULL); + + g_io_add_watch_full(channel, G_PRIORITY_HIGH, + (G_IO_NVAL), + io_invoke_error, GINT_TO_POINTER(result), NULL); + + g_io_channel_unref(channel); /* Apparently this caused crashes for some people. + But irssi does this, so I am going to assume the + crashes were caused by some other stuff. */ + + g_printerr("gntmain: setting up IO\n"); +} + +static gboolean +refresh_screen() +{ + gnt_bindable_perform_action_named(GNT_BINDABLE(wm), "refresh-screen", NULL); + return FALSE; +} + +/* Xerox */ +static void +clean_pid(void) +{ + int status; + pid_t pid; + + do { + pid = waitpid(-1, &status, WNOHANG); + } while (pid != 0 && pid != (pid_t)-1); + + if ((pid == (pid_t) - 1) && (errno != ECHILD)) { + char errmsg[BUFSIZ]; + g_snprintf(errmsg, BUFSIZ, "Warning: waitpid() returned %d", pid); + perror(errmsg); + } +} + +static void +sighandler(int sig) +{ + switch (sig) { +#ifdef SIGWINCH + case SIGWINCH: + werase(stdscr); + wrefresh(stdscr); + g_idle_add(refresh_screen, NULL); + signal(SIGWINCH, sighandler); + break; +#endif + case SIGCHLD: + clean_pid(); + signal(SIGCHLD, sighandler); + break; + } +} + +static void +init_wm() +{ + const char *name = gnt_style_get(GNT_STYLE_WM); + gpointer handle; + + if (name && *name) { + handle = g_module_open(name, G_MODULE_BIND_LAZY); + if (handle) { + gboolean (*init)(GntWM **); + if (g_module_symbol(handle, "gntwm_init", (gpointer)&init)) { + init(&wm); + } + } + } + if (wm == NULL) + wm = g_object_new(GNT_TYPE_WM, NULL); +} + +void gnt_init() +{ + char *filename; + const char *locale; + + if (channel) + return; + + setup_io(); + + locale = setlocale(LC_ALL, ""); + + if (locale && (strstr(locale, "UTF") || strstr(locale, "utf"))) + ascii_only = FALSE; + else + ascii_only = TRUE; + + initscr(); + typeahead(-1); + noecho(); + curs_set(0); + + gnt_init_styles(); + + filename = g_build_filename(g_get_home_dir(), ".gntrc", NULL); + gnt_style_read_configure_file(filename); + g_free(filename); + + gnt_init_colors(); + gnt_init_keys(); + + wbkgdset(stdscr, '\0' | COLOR_PAIR(GNT_COLOR_NORMAL)); + refresh(); + +#ifdef ALL_MOUSE_EVENTS + if ((mouse_enabled = gnt_style_get_bool(GNT_STYLE_MOUSE, FALSE))) + mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, NULL); +#endif + + wbkgdset(stdscr, '\0' | COLOR_PAIR(GNT_COLOR_NORMAL)); + werase(stdscr); + wrefresh(stdscr); + +#ifdef SIGWINCH + signal(SIGWINCH, sighandler); +#endif + signal(SIGCHLD, sighandler); + signal(SIGPIPE, SIG_IGN); + + g_type_init(); + + init_wm(); +} + +void gnt_main() +{ + wm->loop = g_main_loop_new(NULL, FALSE); + g_main_loop_run(wm->loop); +} + +/********************************* + * Stuff for 'window management' * + *********************************/ + +void gnt_screen_occupy(GntWidget *widget) +{ + gnt_wm_new_window(wm, widget); +} + +void gnt_screen_release(GntWidget *widget) +{ + gnt_wm_window_close(wm, widget); +} + +void gnt_screen_update(GntWidget *widget) +{ + gnt_wm_update_window(wm, widget); +} + +gboolean gnt_widget_has_focus(GntWidget *widget) +{ + GntWidget *w; + if (!widget) + return FALSE; + + if (GNT_IS_MENU(widget)) + return TRUE; + + w = widget; + + while (widget->parent) + widget = widget->parent; + + if (widget == wm->_list.window) + return TRUE; + if (wm->ordered && wm->ordered->data == widget) { + if (GNT_IS_BOX(widget) && + (GNT_BOX(widget)->active == w || widget == w)) + return TRUE; + } + return FALSE; +} + +void gnt_widget_set_urgent(GntWidget *widget) +{ + while (widget->parent) + widget = widget->parent; + + if (wm->ordered && wm->ordered->data == widget) + return; + + GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_URGENT); + + gnt_wm_update_window(wm, widget); +} + +void gnt_quit() +{ + g_hash_table_destroy(wm->nodes); /* XXX: */ + update_panels(); + doupdate(); + gnt_uninit_colors(); + gnt_uninit_styles(); + endwin(); +} + +gboolean gnt_ascii_only() +{ + return ascii_only; +} + +void gnt_screen_resize_widget(GntWidget *widget, int width, int height) +{ + gnt_wm_resize_window(wm, widget, width, height); +} + +void gnt_screen_move_widget(GntWidget *widget, int x, int y) +{ + gnt_wm_move_window(wm, widget, x, y); +} + +void gnt_screen_rename_widget(GntWidget *widget, const char *text) +{ + gnt_box_set_title(GNT_BOX(widget), text); + gnt_widget_draw(widget); + gnt_wm_update_window(wm, widget); +} + +void gnt_register_action(const char *label, void (*callback)()) +{ + GntAction *action = g_new0(GntAction, 1); + action->label = g_strdup(label); + action->callback = callback; + + wm->acts = g_list_append(wm->acts, action); +} + +static void +reset_menu(GntWidget *widget, gpointer null) +{ + wm->menu = NULL; +} + +gboolean gnt_screen_menu_show(gpointer newmenu) +{ + if (wm->menu) { + /* For now, if a menu is being displayed, then another menu + * can NOT take over. */ + return FALSE; + } + + wm->menu = newmenu; + GNT_WIDGET_UNSET_FLAGS(GNT_WIDGET(wm->menu), GNT_WIDGET_INVISIBLE); + gnt_widget_draw(GNT_WIDGET(wm->menu)); + + g_signal_connect(G_OBJECT(wm->menu), "hide", G_CALLBACK(reset_menu), NULL); + + return TRUE; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntmenu.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,315 @@ +#include "gntmenu.h" +#include "gntmenuitemcheck.h" + +#include <string.h> + +enum +{ + SIGS = 1, +}; + +static GntTreeClass *parent_class = NULL; + +static void (*org_draw)(GntWidget *wid); +static void (*org_destroy)(GntWidget *wid); +static void (*org_map)(GntWidget *wid); +static gboolean (*org_key_pressed)(GntWidget *w, const char *t); + +static void +gnt_menu_draw(GntWidget *widget) +{ + GntMenu *menu = GNT_MENU(widget); + GList *iter; + chtype type; + int i; + + if (menu->type == GNT_MENU_TOPLEVEL) { + wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_HIGHLIGHT)); + werase(widget->window); + + for (i = 0, iter = menu->list; iter; iter = iter->next, i++) { + GntMenuItem *item = GNT_MENUITEM(iter->data); + type = ' ' | COLOR_PAIR(GNT_COLOR_HIGHLIGHT); + if (i == menu->selected) + type |= A_REVERSE; + item->priv.x = getcurx(widget->window) + widget->priv.x; + item->priv.y = getcury(widget->window) + widget->priv.y + 1; + wbkgdset(widget->window, type); + wprintw(widget->window, " %s ", item->text); + } + } else { + org_draw(widget); + } + + GNTDEBUG; +} + +static void +gnt_menu_size_request(GntWidget *widget) +{ + GntMenu *menu = GNT_MENU(widget); + + if (menu->type == GNT_MENU_TOPLEVEL) { + widget->priv.height = 1; + widget->priv.width = getmaxx(stdscr); + } else { + widget->priv.height = g_list_length(menu->list) + 2; + widget->priv.width = 25; /* XXX: */ + } +} + +static void +menu_tree_add(GntMenu *menu, GntMenuItem *item, GntMenuItem *parent) +{ + if (GNT_IS_MENUITEM_CHECK(item)) { + gnt_tree_add_choice(GNT_TREE(menu), item, + gnt_tree_create_row(GNT_TREE(menu), item->text, " "), parent, NULL); + gnt_tree_set_choice(GNT_TREE(menu), item, gnt_menuitem_check_get_checked(GNT_MENUITEM_CHECK(item))); + } else + gnt_tree_add_row_last(GNT_TREE(menu), item, + gnt_tree_create_row(GNT_TREE(menu), item->text, item->submenu ? ">" : " "), parent); + + if (0 && item->submenu) { + GntMenu *sub = GNT_MENU(item->submenu); + GList *iter; + for (iter = sub->list; iter; iter = iter->next) { + GntMenuItem *it = GNT_MENUITEM(iter->data); + menu_tree_add(menu, it, item); + } + } +} + +static void +gnt_menu_map(GntWidget *widget) +{ + GntMenu *menu = GNT_MENU(widget); + + if (menu->type == GNT_MENU_TOPLEVEL) { + gnt_widget_size_request(widget); + } else { + /* Populate the tree */ + GList *iter; + gnt_tree_remove_all(GNT_TREE(widget)); + for (iter = menu->list; iter; iter = iter->next) { + GntMenuItem *item = GNT_MENUITEM(iter->data); + menu_tree_add(menu, item, NULL); + } + org_map(widget); + gnt_tree_adjust_columns(GNT_TREE(widget)); + } + GNTDEBUG; +} + +static void +menuitem_activate(GntMenu *menu, GntMenuItem *item) +{ + if (item) { + if (item->submenu) { + GntMenu *sub = GNT_MENU(item->submenu); + menu->submenu = sub; + sub->type = GNT_MENU_POPUP; /* Submenus are *never* toplevel */ + sub->parentmenu = menu; + if (menu->type != GNT_MENU_TOPLEVEL) { + GntWidget *widget = GNT_WIDGET(menu); + item->priv.x = widget->priv.x + widget->priv.width - 1; + item->priv.y = widget->priv.y + gnt_tree_get_selection_visible_line(GNT_TREE(menu)); + } + gnt_widget_set_position(GNT_WIDGET(sub), item->priv.x, item->priv.y); + GNT_WIDGET_UNSET_FLAGS(GNT_WIDGET(sub), GNT_WIDGET_INVISIBLE); + gnt_widget_draw(GNT_WIDGET(sub)); + } else if (item->callback) { + item->callback(item, item->callbackdata); + while (menu) { + gnt_widget_hide(GNT_WIDGET(menu)); + menu = menu->parentmenu; + } + } + } +} + +static gboolean +gnt_menu_key_pressed(GntWidget *widget, const char *text) +{ + GntMenu *menu = GNT_MENU(widget); + int current = menu->selected; + + if (menu->submenu) { + return (gnt_widget_key_pressed(GNT_WIDGET(menu->submenu), text)); + } + + if (text[0] == 27 && text[1] == 0) { + /* Escape closes menu */ + GntMenu *par = menu->parentmenu; + if (par != NULL) { + par->submenu = NULL; + gnt_widget_hide(widget); + } else + gnt_widget_hide(widget); + return TRUE; + } + + if (menu->type == GNT_MENU_TOPLEVEL) { + if (strcmp(text, GNT_KEY_LEFT) == 0) { + menu->selected--; + if (menu->selected < 0) + menu->selected = g_list_length(menu->list) - 1; + } else if (strcmp(text, GNT_KEY_RIGHT) == 0) { + menu->selected++; + if (menu->selected >= g_list_length(menu->list)) + menu->selected = 0; + } else if (strcmp(text, GNT_KEY_ENTER) == 0) { + gnt_widget_activate(widget); + } + + if (current != menu->selected) { + gnt_widget_draw(widget); + return TRUE; + } + } else { + return org_key_pressed(widget, text); + } + + return FALSE; +} + +static void +gnt_menu_destroy(GntWidget *widget) +{ + GntMenu *menu = GNT_MENU(widget); + g_list_foreach(menu->list, (GFunc)g_object_unref, NULL); + g_list_free(menu->list); + org_destroy(widget); +} + +static void +gnt_menu_toggled(GntTree *tree, gpointer key) +{ + GntMenuItem *item = GNT_MENUITEM(key); + GntMenu *menu = GNT_MENU(tree); + gboolean check = gnt_menuitem_check_get_checked(GNT_MENUITEM_CHECK(item)); + gnt_menuitem_check_set_checked(GNT_MENUITEM_CHECK(item), !check); + if (item->callback) + item->callback(item, item->callbackdata); + while (menu) { + gnt_widget_hide(GNT_WIDGET(menu)); + menu = menu->parentmenu; + } +} + +static void +gnt_menu_activate(GntWidget *widget) +{ + GntMenu *menu = GNT_MENU(widget); + GntMenuItem *item; + + if (menu->type == GNT_MENU_TOPLEVEL) { + item = g_list_nth_data(menu->list, menu->selected); + } else { + item = gnt_tree_get_selection_data(GNT_TREE(menu)); + } + + if (item) { + if (GNT_IS_MENUITEM_CHECK(item)) + gnt_menu_toggled(GNT_TREE(widget), item); + else + menuitem_activate(menu, item); + } +} + +static void +gnt_menu_hide(GntWidget *widget) +{ + GntMenu *menu = GNT_MENU(widget); + if (menu->parentmenu) + menu->parentmenu->submenu = NULL; +} + +static void +gnt_menu_class_init(GntMenuClass *klass) +{ + GntWidgetClass *wid_class = GNT_WIDGET_CLASS(klass); + parent_class = GNT_TREE_CLASS(klass); + + org_destroy = wid_class->destroy; + org_map = wid_class->map; + org_draw = wid_class->draw; + org_key_pressed = wid_class->key_pressed; + + wid_class->destroy = gnt_menu_destroy; + wid_class->draw = gnt_menu_draw; + wid_class->map = gnt_menu_map; + wid_class->size_request = gnt_menu_size_request; + wid_class->key_pressed = gnt_menu_key_pressed; + wid_class->activate = gnt_menu_activate; + wid_class->hide = gnt_menu_hide; + + parent_class->toggled = gnt_menu_toggled; + + GNTDEBUG; +} + +static void +gnt_menu_init(GTypeInstance *instance, gpointer class) +{ + GntWidget *widget = GNT_WIDGET(instance); + GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_NO_SHADOW | GNT_WIDGET_NO_BORDER | + GNT_WIDGET_CAN_TAKE_FOCUS | GNT_WIDGET_TRANSIENT); + GNTDEBUG; +} + +/****************************************************************************** + * GntMenu API + *****************************************************************************/ +GType +gnt_menu_get_gtype(void) +{ + static GType type = 0; + + if(type == 0) + { + static const GTypeInfo info = { + sizeof(GntMenuClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)gnt_menu_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof(GntMenu), + 0, /* n_preallocs */ + gnt_menu_init, /* instance_init */ + }; + + type = g_type_register_static(GNT_TYPE_TREE, + "GntMenu", + &info, 0); + } + + return type; +} + +GntWidget *gnt_menu_new(GntMenuType type) +{ + GntWidget *widget = g_object_new(GNT_TYPE_MENU, NULL); + GntMenu *menu = GNT_MENU(widget); + menu->list = NULL; + menu->selected = 0; + menu->type = type; + + if (type == GNT_MENU_TOPLEVEL) { + widget->priv.x = 0; + widget->priv.y = 0; + } else { + GNT_TREE(widget)->show_separator = FALSE; + _gnt_tree_init_internals(GNT_TREE(widget), 2); + gnt_tree_set_col_width(GNT_TREE(widget), 1, 1); /* The second column is to indicate that it has a submenu */ + GNT_WIDGET_UNSET_FLAGS(widget, GNT_WIDGET_NO_BORDER); + } + + return widget; +} + +void gnt_menu_add_item(GntMenu *menu, GntMenuItem *item) +{ + menu->list = g_list_append(menu->list, item); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntmenu.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,70 @@ +#ifndef GNT_MENU_H +#define GNT_MENU_H + +#include "gnttree.h" +#include "gntcolors.h" +#include "gntkeys.h" + +#define GNT_TYPE_MENU (gnt_menu_get_gtype()) +#define GNT_MENU(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_MENU, GntMenu)) +#define GNT_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_MENU, GntMenuClass)) +#define GNT_IS_MENU(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_MENU)) +#define GNT_IS_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GNT_TYPE_MENU)) +#define GNT_MENU_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GNT_TYPE_MENU, GntMenuClass)) + +#define GNT_MENU_FLAGS(obj) (GNT_MENU(obj)->priv.flags) +#define GNT_MENU_SET_FLAGS(obj, flags) (GNT_MENU_FLAGS(obj) |= flags) +#define GNT_MENU_UNSET_FLAGS(obj, flags) (GNT_MENU_FLAGS(obj) &= ~(flags)) + +typedef struct _GnMenu GntMenu; +typedef struct _GnMenuPriv GntMenuPriv; +typedef struct _GnMenuClass GntMenuClass; + +#include "gntmenuitem.h" + +/** + * A toplevel-menu is displayed at the top of the screen, and it spans accross + * the entire width of the screen. + * A popup-menu could be displayed, for example, as a context menu for widgets. + */ +typedef enum +{ + GNT_MENU_TOPLEVEL = 1, /* Menu for a toplevel window */ + GNT_MENU_POPUP, /* A popup menu */ +} GntMenuType; + +struct _GnMenu +{ + GntTree parent; + GntMenuType type; + + GList *list; + int selected; + + /* This will keep track of its immediate submenu which is visible so that + * keystrokes can be passed to it. */ + GntMenu *submenu; + GntMenu *parentmenu; +}; + +struct _GnMenuClass +{ + GntTreeClass parent; + + void (*gnt_reserved1)(void); + void (*gnt_reserved2)(void); + void (*gnt_reserved3)(void); + void (*gnt_reserved4)(void); +}; + +G_BEGIN_DECLS + +GType gnt_menu_get_gtype(void); + +GntWidget *gnt_menu_new(GntMenuType type); + +void gnt_menu_add_item(GntMenu *menu, GntMenuItem *item); + +G_END_DECLS + +#endif /* GNT_MENU_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntmenuitem.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,83 @@ +#include "gntmenu.h" +#include "gntmenuitem.h" + +static GObjectClass *parent_class = NULL; + +static void +gnt_menuitem_destroy(GObject *obj) +{ + GntMenuItem *item = GNT_MENUITEM(obj); + g_free(item->text); + item->text = NULL; + if (item->submenu) + gnt_widget_destroy(GNT_WIDGET(item->submenu)); + parent_class->dispose(obj); +} + +static void +gnt_menuitem_class_init(GntMenuItemClass *klass) +{ + GObjectClass *obj_class = G_OBJECT_CLASS(klass); + parent_class = g_type_class_peek_parent(klass); + + obj_class->dispose = gnt_menuitem_destroy; +} + +static void +gnt_menuitem_init(GTypeInstance *instance, gpointer class) +{ +} + +/****************************************************************************** + * GntMenuItem API + *****************************************************************************/ +GType +gnt_menuitem_get_gtype(void) +{ + static GType type = 0; + + if(type == 0) + { + static const GTypeInfo info = { + sizeof(GntMenuItemClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)gnt_menuitem_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof(GntMenuItem), + 0, /* n_preallocs */ + gnt_menuitem_init, /* instance_init */ + }; + + type = g_type_register_static(G_TYPE_OBJECT, + "GntMenuItem", + &info, 0); + } + + return type; +} + +GntMenuItem *gnt_menuitem_new(const char *text) +{ + GObject *item = g_object_new(GNT_TYPE_MENUITEM, NULL); + GntMenuItem *menuitem = GNT_MENUITEM(item); + + menuitem->text = g_strdup(text); + + return menuitem; +} + +void gnt_menuitem_set_callback(GntMenuItem *item, GntMenuItemCallback callback, gpointer data) +{ + item->callback = callback; + item->callbackdata = data; +} + +void gnt_menuitem_set_submenu(GntMenuItem *item, GntMenu *menu) +{ + if (item->submenu) + gnt_widget_destroy(GNT_WIDGET(item->submenu)); + item->submenu = menu; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntmenuitem.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,71 @@ +#ifndef GNT_MENUITEM_H +#define GNT_MENUITEM_H + +#include <glib.h> +#include <glib-object.h> + +#define GNT_TYPE_MENUITEM (gnt_menuitem_get_gtype()) +#define GNT_MENUITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_MENUITEM, GntMenuItem)) +#define GNT_MENUITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_MENUITEM, GntMenuItemClass)) +#define GNT_IS_MENUITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_MENUITEM)) +#define GNT_IS_MENUITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GNT_TYPE_MENUITEM)) +#define GNT_MENUITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GNT_TYPE_MENUITEM, GntMenuItemClass)) + +#define GNT_MENUITEM_FLAGS(obj) (GNT_MENUITEM(obj)->priv.flags) +#define GNT_MENUITEM_SET_FLAGS(obj, flags) (GNT_MENUITEM_FLAGS(obj) |= flags) +#define GNT_MENUITEM_UNSET_FLAGS(obj, flags) (GNT_MENUITEM_FLAGS(obj) &= ~(flags)) + +typedef struct _GnMenuItem GntMenuItem; +typedef struct _GnMenuItemPriv GntMenuItemPriv; +typedef struct _GnMenuItemClass GntMenuItemClass; + +#include "gntmenu.h" + +struct _GnMenuItemPriv +{ + /* These will be used to determine the position of the submenu */ + int x; + int y; +}; + +typedef void (*GntMenuItemCallback)(GntMenuItem *item, gpointer data); + +struct _GnMenuItem +{ + GObject parent; + GntMenuItemPriv priv; + + char *text; + + /* A GntMenuItem can have a callback associated with it. + * The callback will be activated whenever the suer selects it and presses enter (or clicks). + * However, if the GntMenuItem has some child, then the callback and callbackdata will be ignored. */ + gpointer callbackdata; + GntMenuItemCallback callback; + + GntMenu *submenu; +}; + +struct _GnMenuItemClass +{ + GObjectClass parent; + + void (*gnt_reserved1)(void); + void (*gnt_reserved2)(void); + void (*gnt_reserved3)(void); + void (*gnt_reserved4)(void); +}; + +G_BEGIN_DECLS + +GType gnt_menuitem_get_gtype(void); + +GntMenuItem *gnt_menuitem_new(const char *text); + +void gnt_menuitem_set_callback(GntMenuItem *item, GntMenuItemCallback callback, gpointer data); + +void gnt_menuitem_set_submenu(GntMenuItem *item, GntMenu *menu); + +G_END_DECLS + +#endif /* GNT_MENUITEM_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntmenuitemcheck.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,67 @@ +#include "gntmenuitemcheck.h" + +static GntMenuItemClass *parent_class = NULL; + +static void +gnt_menuitem_check_class_init(GntMenuItemCheckClass *klass) +{ + parent_class = GNT_MENUITEM_CLASS(klass); + + GNTDEBUG; +} + +static void +gnt_menuitem_check_init(GTypeInstance *instance, gpointer class) +{ + GNTDEBUG; +} + +/****************************************************************************** + * GntMenuItemCheck API + *****************************************************************************/ +GType +gnt_menuitem_check_get_gtype(void) +{ + static GType type = 0; + + if(type == 0) + { + static const GTypeInfo info = { + sizeof(GntMenuItemCheckClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)gnt_menuitem_check_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof(GntMenuItemCheck), + 0, /* n_preallocs */ + gnt_menuitem_check_init, /* instance_init */ + }; + + type = g_type_register_static(GNT_TYPE_MENUITEM, + "GntMenuItemCheck", + &info, 0); + } + + return type; +} + +GntMenuItem *gnt_menuitem_check_new(const char *text) +{ + GntMenuItem *item = g_object_new(GNT_TYPE_MENUITEM_CHECK, NULL); + GntMenuItem *menuitem = GNT_MENUITEM(item); + + menuitem->text = g_strdup(text); + return item; +} + +gboolean gnt_menuitem_check_get_checked(GntMenuItemCheck *item) +{ + return item->checked; +} + +void gnt_menuitem_check_set_checked(GntMenuItemCheck *item, gboolean set) +{ + item->checked = set; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntmenuitemcheck.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,52 @@ +#ifndef GNT_MENUITEM_CHECK_H +#define GNT_MENUITEM_CHECK_H + +#include "gnt.h" +#include "gntcolors.h" +#include "gntkeys.h" +#include "gntmenuitem.h" + +#define GNT_TYPE_MENUITEM_CHECK (gnt_menuitem_check_get_gtype()) +#define GNT_MENUITEM_CHECK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_MENUITEM_CHECK, GntMenuItemCheck)) +#define GNT_MENUITEM_CHECK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_MENUITEM_CHECK, GntMenuItemCheckClass)) +#define GNT_IS_MENUITEM_CHECK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_MENUITEM_CHECK)) +#define GNT_IS_MENUITEM_CHECK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GNT_TYPE_MENUITEM_CHECK)) +#define GNT_MENUITEM_CHECK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GNT_TYPE_MENUITEM_CHECK, GntMenuItemCheckClass)) + +#define GNT_MENUITEM_CHECK_FLAGS(obj) (GNT_MENUITEM_CHECK(obj)->priv.flags) +#define GNT_MENUITEM_CHECK_SET_FLAGS(obj, flags) (GNT_MENUITEM_CHECK_FLAGS(obj) |= flags) +#define GNT_MENUITEM_CHECK_UNSET_FLAGS(obj, flags) (GNT_MENUITEM_CHECK_FLAGS(obj) &= ~(flags)) + +typedef struct _GnMenuItemCheck GntMenuItemCheck; +typedef struct _GnMenuItemCheckPriv GntMenuItemCheckPriv; +typedef struct _GnMenuItemCheckClass GntMenuItemCheckClass; + +struct _GnMenuItemCheck +{ + GntMenuItem parent; + gboolean checked; +}; + +struct _GnMenuItemCheckClass +{ + GntMenuItemClass parent; + + void (*gnt_reserved1)(void); + void (*gnt_reserved2)(void); + void (*gnt_reserved3)(void); + void (*gnt_reserved4)(void); +}; + +G_BEGIN_DECLS + +GType gnt_menuitem_check_get_gtype(void); + +GntMenuItem *gnt_menuitem_check_new(const char *text); + +gboolean gnt_menuitem_check_get_checked(GntMenuItemCheck *item); + +void gnt_menuitem_check_set_checked(GntMenuItemCheck *item, gboolean set); + +G_END_DECLS + +#endif /* GNT_MENUITEM_CHECK_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntrc.sample Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,21 @@ +[general] +shadow = 0 + +[colors] +black = 0; 0; 0 +red = 1000; 0; 0 +green = 0; 1000; 0 +blue = 250; 250; 700 +white = 1000; 1000; 1000 +gray = 700; 700; 700 +darkgray = 256; 256; 256 + +[colorpairs] +normal = black; white +highlight = white; blue +highlightd = black; gray +shadow = black; darkgray +title = white; blue +titled = white; gray +text = white; blue +disabled = gray; white
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntstyle.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,344 @@ +#include "gntstyle.h" +#include "gntcolors.h" + +#include <ctype.h> +#include <string.h> + +#if GLIB_CHECK_VERSION(2,6,0) +static GKeyFile *gkfile; +#endif + +static char * str_styles[GNT_STYLES]; +static int int_styles[GNT_STYLES]; +static int bool_styles[GNT_STYLES]; + +const char *gnt_style_get(GntStyle style) +{ + return str_styles[style]; +} + +gboolean gnt_style_get_bool(GntStyle style, gboolean def) +{ + int i; + const char * str; + + if (bool_styles[style] != -1) + return bool_styles[style]; + + str = gnt_style_get(style); + + if (str) + { + if (strcmp(str, "false") == 0) + def = FALSE; + else if (strcmp(str, "true") == 0) + def = TRUE; + else if (sscanf(str, "%d", &i) == 1) + { + if (i) + def = TRUE; + else + def = FALSE; + } + } + + bool_styles[style] = def; + return bool_styles[style]; +} + +static void +refine(char *text) +{ + char *s = text, *t = text; + + while (*s) + { + if (*s == '^' && *(s + 1) == '[') + { + *t = '\033'; /* escape */ + s++; + } + else if (*s == '\\') + { + if (*(s + 1) == '\0') + *t = ' '; + else + { + s++; + if (*s == 'r' || *s == 'n') + *t = '\r'; + else if (*s == 't') + *t = '\t'; + else + *t = *s; + } + } + else + *t = *s; + t++; + s++; + } + *t = '\0'; +} + +static char * +parse_key(const char *key) +{ + char *ret = NULL; + int ctrl = 0, alt = 0; + char k; + + /* XXX: Need to do something about ctrl/alt+home, end etc. */ + +#define SPECIAL_KEY(k, code) do { \ + if (strcasecmp(key, k) == 0) \ + return g_strdup(code); \ + } while (0) + + SPECIAL_KEY("home", GNT_KEY_HOME); + SPECIAL_KEY("end", GNT_KEY_END); + SPECIAL_KEY("pageup", GNT_KEY_PGUP); + SPECIAL_KEY("pagedown", GNT_KEY_PGDOWN); + SPECIAL_KEY("insert", GNT_KEY_INS); + SPECIAL_KEY("delete", GNT_KEY_DEL); + + SPECIAL_KEY("left", GNT_KEY_LEFT); + SPECIAL_KEY("right", GNT_KEY_RIGHT); + SPECIAL_KEY("up", GNT_KEY_UP); + SPECIAL_KEY("down", GNT_KEY_DOWN); + + SPECIAL_KEY("tab", "\t"); + SPECIAL_KEY("menu", GNT_KEY_POPUP); + + SPECIAL_KEY("f1", GNT_KEY_F1); + SPECIAL_KEY("f2", GNT_KEY_F2); + SPECIAL_KEY("f3", GNT_KEY_F3); + SPECIAL_KEY("f4", GNT_KEY_F4); + SPECIAL_KEY("f5", GNT_KEY_F5); + SPECIAL_KEY("f6", GNT_KEY_F6); + SPECIAL_KEY("f7", GNT_KEY_F7); + SPECIAL_KEY("f8", GNT_KEY_F8); + SPECIAL_KEY("f9", GNT_KEY_F9); + SPECIAL_KEY("f10", GNT_KEY_F10); + SPECIAL_KEY("f11", GNT_KEY_F11); + SPECIAL_KEY("f12", GNT_KEY_F12); + +#undef SPECIAL_KEY + +#define MATCH(string, var) do { \ + if (strncasecmp(key, string, sizeof(string) - 1) == 0) { \ + key += sizeof(string) - 1; \ + var = 1; \ + } \ + }while (0) + + MATCH("c-", ctrl); + MATCH("ctl-", ctrl); + MATCH("ctr-", ctrl); + MATCH("ctrl-", ctrl); + + MATCH("alt-", alt); + MATCH("a-", alt); + MATCH("m-", alt); + MATCH("meta-", alt); + + if (strlen(key) != 1) /* We can only have stuff like "ctrl-alt-a" */ + return NULL; + + if (ctrl && !isalpha(*key)) { + /* These keys cannot be used with ctrl */ + return NULL; + } + + if (ctrl) + k = *key | 0x20; + else + k = *key; + + ret = g_strdup_printf("%s%c", alt ? "\033" : "", ctrl ? k - 0x60 : k); + +#undef MATCH + + return ret; +} + +void gnt_style_read_actions(GType type, GntBindableClass *klass) +{ +#if GLIB_CHECK_VERSION(2,6,0) + char *name; + GError *error = NULL; + + name = g_strdup_printf("%s::binding", g_type_name(type)); + + if (g_key_file_has_group(gkfile, name)) + { + gsize len = 0; + char **keys; + + keys = g_key_file_get_keys(gkfile, name, &len, &error); + if (error) + { + g_printerr("GntStyle: %s\n", error->message); + g_error_free(error); + g_free(name); + return; + } + + while (len--) + { + char *key, *action; + + key = g_strdup(keys[len]); + action = g_key_file_get_string(gkfile, name, keys[len], &error); + + if (error) + { + g_printerr("GntStyle: %s\n", error->message); + g_error_free(error); + error = NULL; + } + else + { + char *keycode = parse_key(key); + if (keycode == NULL) { + g_printerr("GntStyle: Invalid key-binding %s\n", key); + } else { + gnt_bindable_register_binding(klass, action, keycode, NULL); + g_free(keycode); + } + } + g_free(key); + g_free(action); + } + g_strfreev(keys); + } + g_free(name); +#endif +} + +void gnt_styles_get_keyremaps(GType type, GHashTable *hash) +{ +#if GLIB_CHECK_VERSION(2,6,0) + char *name; + GError *error = NULL; + + name = g_strdup_printf("%s::remap", g_type_name(type)); + + if (g_key_file_has_group(gkfile, name)) + { + gsize len = 0; + char **keys; + + keys = g_key_file_get_keys(gkfile, name, &len, &error); + if (error) + { + g_printerr("GntStyle: %s\n", error->message); + g_error_free(error); + g_free(name); + return; + } + + while (len--) + { + char *key, *replace; + + key = g_strdup(keys[len]); + replace = g_key_file_get_string(gkfile, name, keys[len], &error); + + if (error) + { + g_printerr("GntStyle: %s\n", error->message); + g_error_free(error); + error = NULL; + g_free(key); + } + else + { + refine(key); + refine(replace); + g_hash_table_insert(hash, key, replace); + } + } + g_strfreev(keys); + } + + g_free(name); +#endif +} + +#if GLIB_CHECK_VERSION(2,6,0) +static void +read_general_style(GKeyFile *kfile) +{ + GError *error = NULL; + gsize nkeys; + char **keys = g_key_file_get_keys(kfile, "general", &nkeys, &error); + int i; + struct + { + const char *style; + GntStyle en; + } styles[] = {{"shadow", GNT_STYLE_SHADOW}, + {"customcolor", GNT_STYLE_COLOR}, + {"mouse", GNT_STYLE_MOUSE}, + {"wm", GNT_STYLE_WM}, + {"remember_position", GNT_STYLE_REMPOS}, + {NULL, 0}}; + + if (error) + { + g_printerr("GntStyle: %s\n", error->message); + g_error_free(error); + } + else + { + for (i = 0; styles[i].style; i++) + { + error = NULL; + str_styles[styles[i].en] = + g_key_file_get_string(kfile, "general", styles[i].style, &error); + } + } + g_strfreev(keys); +} +#endif + +void gnt_style_read_configure_file(const char *filename) +{ +#if GLIB_CHECK_VERSION(2,6,0) + GError *error = NULL; + gkfile = g_key_file_new(); + + if (!g_key_file_load_from_file(gkfile, filename, G_KEY_FILE_NONE, &error)) + { + g_printerr("GntStyle: %s\n", error->message); + g_error_free(error); + return; + } + gnt_colors_parse(gkfile); + read_general_style(gkfile); +#endif +} + +void gnt_init_styles() +{ + int i; + for (i = 0; i < GNT_STYLES; i++) + { + str_styles[i] = NULL; + int_styles[i] = -1; + bool_styles[i] = -1; + } +} + +void gnt_uninit_styles() +{ + int i; + for (i = 0; i < GNT_STYLES; i++) + g_free(str_styles[i]); + +#if GLIB_CHECK_VERSION(2,6,0) + g_key_file_free(gkfile); +#endif +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntstyle.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,27 @@ +#include "gnt.h" + +typedef enum +{ + GNT_STYLE_SHADOW = 0, + GNT_STYLE_COLOR = 1, + GNT_STYLE_MOUSE = 2, + GNT_STYLE_WM = 3, + GNT_STYLE_REMPOS = 4, + GNT_STYLES +} GntStyle; + +void gnt_style_read_configure_file(const char *filename); + +const char *gnt_style_get(GntStyle style); + +gboolean gnt_style_get_bool(GntStyle style, gboolean def); + +/* This should be called only once for the each type */ +void gnt_styles_get_keyremaps(GType type, GHashTable *hash); + +void gnt_style_read_actions(GType type, GntBindableClass *klass); + +void gnt_init_styles(); + +void gnt_uninit_styles(); +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gnttextview.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,593 @@ +#include "gnttextview.h" +#include "gntutils.h" + +#include <string.h> + +enum +{ + SIGS = 1, +}; + +typedef struct +{ + GntTextFormatFlags tvflag; + chtype flags; + int start; + int end; /* This is the next byte of the last character of this segment */ +} GntTextSegment; + +typedef struct +{ + GList *segments; /* A list of GntTextSegments */ + int length; /* The current length of the line so far (ie. onscreen width) */ + gboolean soft; /* TRUE if it's an overflow from prev. line */ +} GntTextLine; + +typedef struct +{ + char *name; + int start; + int end; +} GntTextTag; + +static GntWidgetClass *parent_class = NULL; + +static void +gnt_text_view_draw(GntWidget *widget) +{ + GntTextView *view = GNT_TEXT_VIEW(widget); + int i = 0; + GList *lines; + int rows, scrcol; + + werase(widget->window); + + for (i = 0, lines = view->list; i < widget->priv.height && lines; i++, lines = lines->next) + { + GList *iter; + GntTextLine *line = lines->data; + + wmove(widget->window, widget->priv.height - 1 - i, 0); + + for (iter = line->segments; iter; iter = iter->next) + { + GntTextSegment *seg = iter->data; + char *end = view->string->str + seg->end; + char back = *end; + *end = '\0'; + wattrset(widget->window, seg->flags); + wprintw(widget->window, "%s", (view->string->str + seg->start)); + *end = back; + } + wattroff(widget->window, A_UNDERLINE | A_BLINK | A_REVERSE); + whline(widget->window, ' ', widget->priv.width - line->length - 1); + } + + scrcol = widget->priv.width - 1; + rows = widget->priv.height - 2; + if (rows > 0) + { + int total = g_list_length(g_list_first(view->list)); + int showing, position, up, down; + + showing = rows * rows / total + 1; + showing = MIN(rows, showing); + + total -= rows; + up = g_list_length(lines); + down = total - up; + + position = (rows - showing) * up / MAX(1, up + down); + position = MAX((lines != NULL), position); + + if (showing + position > rows) + position = rows - showing; + + if (showing + position == rows && view->list && view->list->prev) + position = MAX(1, rows - 1 - showing); + else if (showing + position < rows && view->list && !view->list->prev) + position = rows - showing; + + mvwvline(widget->window, position + 1, scrcol, + ACS_CKBOARD | COLOR_PAIR(GNT_COLOR_HIGHLIGHT_D), showing); + } + + mvwaddch(widget->window, 0, scrcol, + (lines ? ACS_UARROW : ' ') | COLOR_PAIR(GNT_COLOR_HIGHLIGHT_D)); + mvwaddch(widget->window, widget->priv.height - 1, scrcol, + ((view->list && view->list->prev) ? ACS_DARROW : ' ') | + COLOR_PAIR(GNT_COLOR_HIGHLIGHT_D)); + + GNTDEBUG; +} + +static void +gnt_text_view_size_request(GntWidget *widget) +{ + if (!GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_MAPPED)) + { + gnt_widget_set_size(widget, 64, 20); + } +} + +static void +gnt_text_view_map(GntWidget *widget) +{ + if (widget->priv.width == 0 || widget->priv.height == 0) + gnt_widget_size_request(widget); + GNTDEBUG; +} + +static gboolean +gnt_text_view_key_pressed(GntWidget *widget, const char *text) +{ + return FALSE; +} + +static void +free_text_segment(gpointer data, gpointer null) +{ + GntTextSegment *seg = data; + g_free(seg); +} + +static void +free_text_line(gpointer data, gpointer null) +{ + GntTextLine *line = data; + g_list_foreach(line->segments, free_text_segment, NULL); + g_list_free(line->segments); + g_free(line); +} + +static void +free_tag(gpointer data, gpointer null) +{ + GntTextTag *tag = data; + g_free(tag->name); + g_free(tag); +} + +static void +gnt_text_view_destroy(GntWidget *widget) +{ + GntTextView *view = GNT_TEXT_VIEW(widget); + view->list = g_list_first(view->list); + g_list_foreach(view->list, free_text_line, NULL); + g_list_free(view->list); + g_list_foreach(view->tags, free_tag, NULL); + g_list_free(view->tags); + g_string_free(view->string, TRUE); +} + +static gboolean +gnt_text_view_clicked(GntWidget *widget, GntMouseEvent event, int x, int y) +{ + if (event == GNT_MOUSE_SCROLL_UP) { + gnt_text_view_scroll(GNT_TEXT_VIEW(widget), -1); + } else if (event == GNT_MOUSE_SCROLL_DOWN) { + gnt_text_view_scroll(GNT_TEXT_VIEW(widget), 1); + } else + return FALSE; + return TRUE; +} + +static void +gnt_text_view_reflow(GntTextView *view) +{ + /* This is pretty ugly, and inefficient. Someone do something about it. */ + GntTextLine *line; + GList *back, *iter, *list; + GString *string; + int pos = 0; /* no. of 'real' lines */ + + list = view->list; + while (list->prev) { + line = list->data; + if (!line->soft) + pos++; + list = list->prev; + } + + back = g_list_last(view->list); + view->list = NULL; + + string = view->string; + view->string = NULL; + gnt_text_view_clear(view); + + view->string = g_string_set_size(view->string, string->len); + view->string->len = 0; + GNT_WIDGET_SET_FLAGS(GNT_WIDGET(view), GNT_WIDGET_DRAWING); + + for (; back; back = back->prev) { + line = back->data; + if (back->next && !line->soft) { + gnt_text_view_append_text_with_flags(view, "\n", GNT_TEXT_FLAG_NORMAL); + } + + for (iter = line->segments; iter; iter = iter->next) { + GntTextSegment *seg = iter->data; + char *start = string->str + seg->start; + char *end = string->str + seg->end; + char back = *end; + *end = '\0'; + gnt_text_view_append_text_with_flags(view, start, seg->tvflag); + *end = back; + } + free_text_line(line, NULL); + } + g_list_free(list); + + list = view->list = g_list_first(view->list); + /* Go back to the line that was in view before resizing started */ + while (pos--) { + while (((GntTextLine*)list->data)->soft) + list = list->next; + list = list->next; + } + view->list = list; + GNT_WIDGET_UNSET_FLAGS(GNT_WIDGET(view), GNT_WIDGET_DRAWING); + if (GNT_WIDGET(view)->window) + gnt_widget_draw(GNT_WIDGET(view)); + g_string_free(string, TRUE); +} + +static void +gnt_text_view_size_changed(GntWidget *widget, int w, int h) +{ + if (w != widget->priv.width) { + gnt_text_view_reflow(GNT_TEXT_VIEW(widget)); + } +} + +static void +gnt_text_view_class_init(GntTextViewClass *klass) +{ + parent_class = GNT_WIDGET_CLASS(klass); + parent_class->destroy = gnt_text_view_destroy; + parent_class->draw = gnt_text_view_draw; + parent_class->map = gnt_text_view_map; + parent_class->size_request = gnt_text_view_size_request; + parent_class->key_pressed = gnt_text_view_key_pressed; + parent_class->clicked = gnt_text_view_clicked; + parent_class->size_changed = gnt_text_view_size_changed; + + GNTDEBUG; +} + +static void +gnt_text_view_init(GTypeInstance *instance, gpointer class) +{ + GntWidget *widget = GNT_WIDGET(instance); + + GNT_WIDGET_SET_FLAGS(GNT_WIDGET(instance), GNT_WIDGET_GROW_Y | GNT_WIDGET_GROW_X); + + widget->priv.minw = 5; + widget->priv.minh = 2; + GNTDEBUG; +} + +/****************************************************************************** + * GntTextView API + *****************************************************************************/ +GType +gnt_text_view_get_gtype(void) +{ + static GType type = 0; + + if(type == 0) + { + static const GTypeInfo info = { + sizeof(GntTextViewClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)gnt_text_view_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof(GntTextView), + 0, /* n_preallocs */ + gnt_text_view_init, /* instance_init */ + }; + + type = g_type_register_static(GNT_TYPE_WIDGET, + "GntTextView", + &info, 0); + } + + return type; +} + +GntWidget *gnt_text_view_new() +{ + GntWidget *widget = g_object_new(GNT_TYPE_TEXTVIEW, NULL); + GntTextView *view = GNT_TEXT_VIEW(widget); + GntTextLine *line = g_new0(GntTextLine, 1); + + GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW); + + view->string = g_string_new(NULL); + view->list = g_list_append(view->list, line); + + return widget; +} + +void gnt_text_view_append_text_with_flags(GntTextView *view, const char *text, GntTextFormatFlags flags) +{ + gnt_text_view_append_text_with_tag(view, text, flags, NULL); +} + +void gnt_text_view_append_text_with_tag(GntTextView *view, const char *text, + GntTextFormatFlags flags, const char *tagname) +{ + GntWidget *widget = GNT_WIDGET(view); + int fl = 0; + const char *start, *end; + GList *list = view->list; + GntTextLine *line; + int len; + + if (text == NULL || *text == '\0') + return; + + fl = gnt_text_format_flag_to_chtype(flags); + + len = view->string->len; + view->string = g_string_append(view->string, text); + + if (tagname) { + GntTextTag *tag = g_new0(GntTextTag, 1); + tag->name = g_strdup(tagname); + tag->start = len; + tag->end = view->string->len; + view->tags = g_list_append(view->tags, tag); + } + + view->list = g_list_first(view->list); + + start = end = view->string->str + len; + + while (*start) { + GntTextSegment *seg = NULL; + + if (*end == '\n' || *end == '\r') { + end++; + start = end; + gnt_text_view_next_line(view); + view->list = g_list_first(view->list); + continue; + } + + line = view->list->data; + if (line->length == widget->priv.width - 1) { + /* The last added line was exactly the same width as the widget */ + line = g_new0(GntTextLine, 1); + line->soft = TRUE; + view->list = g_list_prepend(view->list, line); + } + + if ((end = strchr(start, '\n')) != NULL || + (end = strchr(start, '\r')) != NULL) { + len = gnt_util_onscreen_width(start, end - 1); + if (len >= widget->priv.width - line->length - 1) { + end = NULL; + } + } + + if (end == NULL) + end = gnt_util_onscreen_width_to_pointer(start, + widget->priv.width - line->length - 1, &len); + + /* Try to append to the previous segment if possible */ + if (line->segments) { + seg = g_list_last(line->segments)->data; + if (seg->flags != fl) + seg = NULL; + } + + if (seg == NULL) { + seg = g_new0(GntTextSegment, 1); + seg->start = start - view->string->str; + seg->tvflag = flags; + seg->flags = fl; + line->segments = g_list_append(line->segments, seg); + } + seg->end = end - view->string->str; + line->length += len; + + start = end; + if (*end && *end != '\n' && *end != '\r') { + line = g_new0(GntTextLine, 1); + line->soft = TRUE; + view->list = g_list_prepend(view->list, line); + } + } + + view->list = list; + + gnt_widget_draw(widget); +} + +void gnt_text_view_scroll(GntTextView *view, int scroll) +{ + if (scroll == 0) + { + view->list = g_list_first(view->list); + } + else if (scroll > 0) + { + GList *list = g_list_nth_prev(view->list, scroll); + if (list == NULL) + list = g_list_first(view->list); + view->list = list; + } + else if (scroll < 0) + { + GList *list = g_list_nth(view->list, -scroll); + if (list == NULL) + list = g_list_last(view->list); + view->list = list; + } + + gnt_widget_draw(GNT_WIDGET(view)); +} + +void gnt_text_view_next_line(GntTextView *view) +{ + GntTextLine *line = g_new0(GntTextLine, 1); + GList *list = view->list; + + view->list = g_list_prepend(g_list_first(view->list), line); + view->list = list; + gnt_widget_draw(GNT_WIDGET(view)); +} + +chtype gnt_text_format_flag_to_chtype(GntTextFormatFlags flags) +{ + chtype fl = 0; + + if (flags & GNT_TEXT_FLAG_BOLD) + fl |= A_BOLD; + if (flags & GNT_TEXT_FLAG_UNDERLINE) + fl |= A_UNDERLINE; + if (flags & GNT_TEXT_FLAG_BLINK) + fl |= A_BLINK; + + if (flags & GNT_TEXT_FLAG_DIM) + fl |= (A_DIM | COLOR_PAIR(GNT_COLOR_DISABLED)); + else if (flags & GNT_TEXT_FLAG_HIGHLIGHT) + fl |= (A_DIM | COLOR_PAIR(GNT_COLOR_HIGHLIGHT)); + else + fl |= COLOR_PAIR(GNT_COLOR_NORMAL); + + return fl; +} + +void gnt_text_view_clear(GntTextView *view) +{ + GntTextLine *line; + + g_list_foreach(view->list, free_text_line, NULL); + g_list_free(view->list); + view->list = NULL; + + line = g_new0(GntTextLine, 1); + view->list = g_list_append(view->list, line); + if (view->string) + g_string_free(view->string, TRUE); + view->string = g_string_new(NULL); + + if (GNT_WIDGET(view)->window) + gnt_widget_draw(GNT_WIDGET(view)); +} + +int gnt_text_view_get_lines_below(GntTextView *view) +{ + int below = 0; + GList *list = view->list; + while ((list = list->prev)) + ++below; + return below; +} + +int gnt_text_view_get_lines_above(GntTextView *view) +{ + int above = 0; + GList *list = view->list; + list = g_list_nth(view->list, GNT_WIDGET(view)->priv.height); + if (!list) + return 0; + while ((list = list->next)) + ++above; + return above; +} + +/** + * XXX: There are quite possibly more than a few bugs here. + */ +int gnt_text_view_tag_change(GntTextView *view, const char *name, const char *text, gboolean all) +{ + GList *alllines = g_list_first(view->list); + GList *list, *next, *iter, *inext; + const int text_length = text ? strlen(text) : 0; + int count = 0; + for (list = view->tags; list; list = next) { + GntTextTag *tag = list->data; + next = list->next; + if (strcmp(tag->name, name) == 0) { + int change; + char *before, *after; + + count++; + + before = g_strndup(view->string->str, tag->start); + after = g_strdup(view->string->str + tag->end); + change = (tag->end - tag->start) - text_length; + + g_string_printf(view->string, "%s%s%s", before, text ? text : "", after); + g_free(before); + g_free(after); + + /* Update the offsets of the next tags */ + for (iter = next; iter; iter = iter->next) { + GntTextTag *t = iter->data; + t->start -= change; + t->end -= change; + } + + /* Update the offsets of the segments */ + for (iter = alllines; iter; iter = inext) { + GList *segs, *snext; + GntTextLine *line = iter->data; + inext = iter->next; + for (segs = line->segments; segs; segs = snext) { + GntTextSegment *seg = segs->data; + snext = segs->next; + if (seg->start >= tag->end) { + /* The segment is somewhere after the tag */ + seg->start -= change; + seg->end -= change; + } else if (seg->end <= tag->start) { + /* This segment is somewhere in front of the tag */ + } else if (seg->start >= tag->start) { + /* This segment starts in the middle of the tag */ + if (text == NULL) { + free_text_segment(seg, NULL); + line->segments = g_list_delete_link(line->segments, segs); + if (line->segments == NULL) { + free_text_line(line, NULL); + if (view->list == iter) { + if (inext) + view->list = inext; + else + view->list = iter->prev; + } + alllines = g_list_delete_link(alllines, iter); + } + } else { + /* XXX: (null) */ + seg->start = tag->start; + seg->end = tag->end - change; + } + line->length -= change; + /* XXX: Make things work if the tagged text spans over several lines. */ + } else { + /* XXX: handle the rest of the conditions */ + g_printerr("WTF! This needs to be handled properly!!\n"); + } + } + } + if (text == NULL) { + /* Remove the tag */ + view->tags = g_list_delete_link(view->tags, list); + free_tag(tag, NULL); + } else { + tag->end -= change; + } + if (!all) + break; + } + } + return count; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gnttextview.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,86 @@ +#ifndef GNT_TEXT_VIEW_H +#define GNT_TEXT_VIEW_H + +#include "gntwidget.h" +#include "gnt.h" +#include "gntcolors.h" +#include "gntkeys.h" + +#define GNT_TYPE_TEXTVIEW (gnt_text_view_get_gtype()) +#define GNT_TEXT_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_TEXTVIEW, GntTextView)) +#define GNT_TEXT_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_TEXTVIEW, GntTextViewClass)) +#define GNT_IS_TEXTVIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_TEXTVIEW)) +#define GNT_IS_TEXTVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GNT_TYPE_TEXTVIEW)) +#define GNT_TEXT_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GNT_TYPE_TEXTVIEW, GntTextViewClass)) + +#define GNT_TEXT_VIEW_FLAGS(obj) (GNT_TEXT_VIEW(obj)->priv.flags) +#define GNT_TEXT_VIEW_SET_FLAGS(obj, flags) (GNT_TEXT_VIEW_FLAGS(obj) |= flags) +#define GNT_TEXT_VIEW_UNSET_FLAGS(obj, flags) (GNT_TEXT_VIEW_FLAGS(obj) &= ~(flags)) + +typedef struct _GnTextView GntTextView; +typedef struct _GnTextViewPriv GntTextViewPriv; +typedef struct _GnTextViewClass GntTextViewClass; + +struct _GnTextView +{ + GntWidget parent; + + GString *string; + GList *list; /* List of GntTextLine */ + + GList *tags; /* A list of tags */ +}; + +typedef enum +{ + GNT_TEXT_FLAG_NORMAL = 0, + GNT_TEXT_FLAG_BOLD = 1 << 0, + GNT_TEXT_FLAG_UNDERLINE = 1 << 1, + GNT_TEXT_FLAG_BLINK = 1 << 2, + GNT_TEXT_FLAG_DIM = 1 << 3, + GNT_TEXT_FLAG_HIGHLIGHT = 1 << 4, +} GntTextFormatFlags; + +struct _GnTextViewClass +{ + GntWidgetClass parent; + + void (*gnt_reserved1)(void); + void (*gnt_reserved2)(void); + void (*gnt_reserved3)(void); + void (*gnt_reserved4)(void); +}; + +G_BEGIN_DECLS + +GType gnt_text_view_get_gtype(void); + +/* XXX: For now, don't set a textview to have any border. + * If you want borders real bad, put it in a box. */ +GntWidget *gnt_text_view_new(); + +/* scroll > 0 means scroll up, < 0 means scroll down, == 0 means scroll to the end */ +void gnt_text_view_scroll(GntTextView *view, int scroll); + +void gnt_text_view_append_text_with_flags(GntTextView *view, const char *text, GntTextFormatFlags flags); + +void gnt_text_view_append_text_with_tag(GntTextView *view, const char *text, GntTextFormatFlags flags, const char *tag); + +/* Move the cursor to the beginning of the next line and resets text-attributes. + * It first completes the current line with the current text-attributes. */ +void gnt_text_view_next_line(GntTextView *view); + +chtype gnt_text_format_flag_to_chtype(GntTextFormatFlags flags); + +void gnt_text_view_clear(GntTextView *view); + +int gnt_text_view_get_lines_below(GntTextView *view); + +int gnt_text_view_get_lines_above(GntTextView *view); + +/* If text is NULL, then the tag is removed. */ +int gnt_text_view_tag_change(GntTextView *view, const char *name, const char *text, gboolean all); + +G_END_DECLS + +#endif /* GNT_TEXT_VIEW_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gnttree.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,1494 @@ +#include "gntmarshal.h" +#include "gntstyle.h" +#include "gnttree.h" +#include "gntutils.h" + +#include <string.h> +#include <ctype.h> + +#define SEARCH_TIMEOUT 4000 /* 4 secs */ + +enum +{ + SIG_SELECTION_CHANGED, + SIG_SCROLLED, + SIG_TOGGLED, + SIGS, +}; + +#define TAB_SIZE 3 + +/* XXX: Make this one into a GObject? + * ... Probably not */ +struct _GnTreeRow +{ + void *key; + void *data; /* XXX: unused */ + + gboolean collapsed; + gboolean choice; /* Is this a choice-box? + If choice is true, then child will be NULL */ + gboolean isselected; + GntTextFormatFlags flags; + + GntTreeRow *parent; + GntTreeRow *child; + GntTreeRow *next; + GntTreeRow *prev; + + GList *columns; + GntTree *tree; +}; + +struct _GnTreeCol +{ + char *text; + int span; /* How many columns does it span? */ +}; + +static GntWidgetClass *parent_class = NULL; +static guint signals[SIGS] = { 0 }; + +/* Move the item at position old to position new */ +static GList * +g_list_reposition_child(GList *list, int old, int new) +{ + gpointer item = g_list_nth_data(list, old); + list = g_list_remove(list, item); + if (old < new) + new--; /* because the positions would have shifted after removing the item */ + list = g_list_insert(list, item, new); + return list; +} + +static GntTreeRow * +_get_next(GntTreeRow *row, gboolean godeep) +{ + if (row == NULL) + return NULL; + if (godeep && row->child) + return row->child; + if (row->next) + return row->next; + return _get_next(row->parent, FALSE); +} + +static gboolean +row_matches_search(GntTreeRow *row) +{ + GntTree *t = row->tree; + if (t->search && t->search->len > 0) { + char *one = g_utf8_casefold(((GntTreeCol*)row->columns->data)->text, -1); + char *two = g_utf8_casefold(t->search->str, -1); + char *z = strstr(one, two); + g_free(one); + g_free(two); + if (z == NULL) + return FALSE; + } + return TRUE; +} + +static GntTreeRow * +get_next(GntTreeRow *row) +{ + if (row == NULL) + return NULL; + while ((row = _get_next(row, !row->collapsed)) != NULL) { + if (row_matches_search(row)) + break; + } + return row; +} + +/* Returns the n-th next row. If it doesn't exist, returns NULL */ +static GntTreeRow * +get_next_n(GntTreeRow *row, int n) +{ + while (row && n--) + row = get_next(row); + return row; +} + +/* Returns the n-th next row. If it doesn't exist, then the last non-NULL node */ +static GntTreeRow * +get_next_n_opt(GntTreeRow *row, int n, int *pos) +{ + GntTreeRow *next = row; + int r = 0; + + if (row == NULL) + return NULL; + + while (row && n--) + { + row = get_next(row); + if (row) + { + next = row; + r++; + } + } + + if (pos) + *pos = r; + + return next; +} + +static GntTreeRow * +get_last_child(GntTreeRow *row) +{ + if (row == NULL) + return NULL; + if (!row->collapsed && row->child) + row = row->child; + else + return row; + + while(row->next) + row = row->next; + if (!row->collapsed && row->child) + row = get_last_child(row->child); + return row; +} + +static GntTreeRow * +get_prev(GntTreeRow *row) +{ + if (row == NULL) + return NULL; + while (row) { + if (row->prev) + row = get_last_child(row->prev); + else + row = row->parent; + if (!row || row_matches_search(row)) + break; + } + return row; +} + +static GntTreeRow * +get_prev_n(GntTreeRow *row, int n) +{ + while (row && n--) + row = get_prev(row); + return row; +} + +/* Distance of row from the root */ +/* XXX: This is uber-inefficient */ +static int +get_root_distance(GntTreeRow *row) +{ + if (row == NULL) + return -1; + return get_root_distance(get_prev(row)) + 1; +} + +/* Returns the distance between a and b. + * If a is 'above' b, then the distance is positive */ +static int +get_distance(GntTreeRow *a, GntTreeRow *b) +{ + /* First get the distance from a to the root. + * Then the distance from b to the root. + * Subtract. + * It's not that good, but it works. */ + int ha = get_root_distance(a); + int hb = get_root_distance(b); + + return (hb - ha); +} + +static int +find_depth(GntTreeRow *row) +{ + int dep = -1; + + while (row) + { + dep++; + row = row->parent; + } + + return dep; +} + +static char * +update_row_text(GntTree *tree, GntTreeRow *row) +{ + GString *string = g_string_new(NULL); + GList *iter; + int i; + + for (i = 0, iter = row->columns; i < tree->ncol && iter; i++, iter = iter->next) + { + GntTreeCol *col = iter->data; + const char *text; + int len = gnt_util_onscreen_width(col->text, NULL); + int fl = 0; + gboolean cut = FALSE; + + if (i == 0) + { + if (row->choice) + { + g_string_append_printf(string, "[%c] ", + row->isselected ? 'X' : ' '); + fl = 4; + } + else if (row->parent == NULL && row->child) + { + if (row->collapsed) + { + string = g_string_append(string, "+ "); + } + else + { + string = g_string_append(string, "- "); + } + fl = 2; + } + else + { + fl = TAB_SIZE * find_depth(row); + g_string_append_printf(string, "%*s", fl, ""); + } + len += fl; + } + else + g_string_append_c(string, '|'); + + if (len > tree->columns[i].width) { + len = tree->columns[i].width - 1; + cut = TRUE; + } + text = gnt_util_onscreen_width_to_pointer(col->text, len - fl, NULL); + string = g_string_append_len(string, col->text, text - col->text); + if (cut) { /* ellipsis */ + if (gnt_ascii_only()) + g_string_append_c(string, '~'); + else + string = g_string_append(string, "\342\200\246"); + len++; + } + + if (len < tree->columns[i].width && iter->next) + g_string_append_printf(string, "%*s", tree->columns[i].width - len, ""); + } + return g_string_free(string, FALSE); +} + +static void +tree_mark_columns(GntTree *tree, int pos, int y, chtype type) +{ + GntWidget *widget = GNT_WIDGET(tree); + int i; + int x = pos; + + for (i = 0; i < tree->ncol - 1; i++) + { + x += tree->columns[i].width; + mvwaddch(widget->window, y, x + i, type); + } +} + +static void +redraw_tree(GntTree *tree) +{ + int start, i; + GntWidget *widget = GNT_WIDGET(tree); + GntTreeRow *row; + int pos, up, down; + int rows, scrcol; + + if (!GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(tree), GNT_WIDGET_MAPPED)) + return; + + if (GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_NO_BORDER)) + pos = 0; + else + pos = 1; + + if (tree->top == NULL) + tree->top = tree->root; + if (tree->current == NULL) + tree->current = tree->root; + + wbkgd(widget->window, COLOR_PAIR(GNT_COLOR_NORMAL)); + + start = 0; + if (tree->show_title) + { + int i; + int x = pos; + + mvwhline(widget->window, pos + 1, pos, ACS_HLINE | COLOR_PAIR(GNT_COLOR_NORMAL), + widget->priv.width - pos - 1); + + for (i = 0; i < tree->ncol; i++) + { + mvwprintw(widget->window, pos, x + i, tree->columns[i].title); + x += tree->columns[i].width; + } + if (pos) + { + tree_mark_columns(tree, pos, 0, ACS_TTEE | COLOR_PAIR(GNT_COLOR_NORMAL)); + tree_mark_columns(tree, pos, widget->priv.height - pos, + ACS_BTEE | COLOR_PAIR(GNT_COLOR_NORMAL)); + } + tree_mark_columns(tree, pos, pos + 1, + (tree->show_separator ? ACS_PLUS : ACS_HLINE) | COLOR_PAIR(GNT_COLOR_NORMAL)); + tree_mark_columns(tree, pos, pos, + (tree->show_separator ? ACS_VLINE : ' ') | COLOR_PAIR(GNT_COLOR_NORMAL)); + start = 2; + } + + rows = widget->priv.height - pos * 2 - start - 1; + tree->bottom = get_next_n_opt(tree->top, rows, &down); + if (down < rows) + { + tree->top = get_prev_n(tree->bottom, rows); + if (tree->top == NULL) + tree->top = tree->root; + } + + up = get_distance(tree->top, tree->current); + if (up < 0) + tree->top = tree->current; + else if (up >= widget->priv.height - pos) + tree->top = get_prev_n(tree->current, rows); + + if (tree->top && !row_matches_search(tree->top)) + tree->top = get_next(tree->top); + row = tree->top; + scrcol = widget->priv.width - 1 - 2 * pos; /* exclude the borders and the scrollbar */ + for (i = start + pos; row && i < widget->priv.height - pos; + i++, row = get_next(row)) + { + char *str; + int wr; + + GntTextFormatFlags flags = row->flags; + int attr = 0; + + if (!row_matches_search(row)) + continue; + str = update_row_text(tree, row); + + if ((wr = gnt_util_onscreen_width(str, NULL)) > scrcol) + { + char *s = (char*)gnt_util_onscreen_width_to_pointer(str, scrcol, &wr); + *s = '\0'; + } + + if (flags & GNT_TEXT_FLAG_BOLD) + attr |= A_BOLD; + if (flags & GNT_TEXT_FLAG_UNDERLINE) + attr |= A_UNDERLINE; + if (flags & GNT_TEXT_FLAG_BLINK) + attr |= A_BLINK; + + if (row == tree->current) + { + if (gnt_widget_has_focus(widget)) + attr |= COLOR_PAIR(GNT_COLOR_HIGHLIGHT); + else + attr |= COLOR_PAIR(GNT_COLOR_HIGHLIGHT_D); + } + else + { + if (flags & GNT_TEXT_FLAG_DIM) + attr |= (A_DIM | COLOR_PAIR(GNT_COLOR_DISABLED)); + else if (flags & GNT_TEXT_FLAG_HIGHLIGHT) + attr |= (A_DIM | COLOR_PAIR(GNT_COLOR_HIGHLIGHT)); + else + attr |= COLOR_PAIR(GNT_COLOR_NORMAL); + } + + wbkgdset(widget->window, '\0' | attr); + mvwprintw(widget->window, i, pos, str); + whline(widget->window, ' ', scrcol - wr); + tree->bottom = row; + g_free(str); + tree_mark_columns(tree, pos, i, + (tree->show_separator ? ACS_VLINE : ' ') | attr); + } + + wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_NORMAL)); + while (i < widget->priv.height - pos) + { + mvwhline(widget->window, i, pos, ' ', + widget->priv.width - pos * 2 - 1); + tree_mark_columns(tree, pos, i, + (tree->show_separator ? ACS_VLINE : ' ')); + i++; + } + + scrcol = widget->priv.width - pos - 1; /* position of the scrollbar */ + rows--; + if (rows > 0) + { + int total; + int showing, position; + + get_next_n_opt(tree->root, g_list_length(tree->list), &total); + showing = rows * rows / MAX(total, 1) + 1; + showing = MIN(rows, showing); + + total -= rows; + up = get_distance(tree->root, tree->top); + down = total - up; + + position = (rows - showing) * up / MAX(1, up + down); + position = MAX((tree->top != tree->root), position); + + if (showing + position > rows) + position = rows - showing; + + if (showing + position == rows && row) + position = MAX(0, rows - 1 - showing); + else if (showing + position < rows && !row) + position = rows - showing; + + position += pos + start + 1; + + mvwvline(widget->window, pos + start + 1, scrcol, + ' ' | COLOR_PAIR(GNT_COLOR_NORMAL), rows); + mvwvline(widget->window, position, scrcol, + ACS_CKBOARD | COLOR_PAIR(GNT_COLOR_HIGHLIGHT_D), showing); + } + + mvwaddch(widget->window, start + pos, scrcol, + ((tree->top != tree->root) ? ACS_UARROW : ' ') | + COLOR_PAIR(GNT_COLOR_HIGHLIGHT_D)); + + mvwaddch(widget->window, widget->priv.height - pos - 1, scrcol, + (row ? ACS_DARROW : ' ') | COLOR_PAIR(GNT_COLOR_HIGHLIGHT_D)); + + gnt_widget_queue_update(widget); +} + +static void +gnt_tree_draw(GntWidget *widget) +{ + GntTree *tree = GNT_TREE(widget); + + redraw_tree(tree); + + GNTDEBUG; +} + +static void +gnt_tree_size_request(GntWidget *widget) +{ + if (widget->priv.height == 0) + widget->priv.height = 10; /* XXX: Why?! */ + if (widget->priv.width == 0) + { + GntTree *tree = GNT_TREE(widget); + int i, width = 0; + for (i = 0; i < tree->ncol; i++) + width += tree->columns[i].width; + widget->priv.width = width + i; + } +} + +static void +gnt_tree_map(GntWidget *widget) +{ + GntTree *tree = GNT_TREE(widget); + if (widget->priv.width == 0 || widget->priv.height == 0) + { + gnt_widget_size_request(widget); + } + tree->top = tree->root; + tree->current = tree->root; + GNTDEBUG; +} + +static void +tree_selection_changed(GntTree *tree, GntTreeRow *old, GntTreeRow *current) +{ + g_signal_emit(tree, signals[SIG_SELECTION_CHANGED], 0, old->key, current->key); +} + +static gboolean +action_down(GntBindable *bind, GList *null) +{ + int dist; + GntTree *tree = GNT_TREE(bind); + GntTreeRow *old = tree->current; + GntTreeRow *row = get_next(tree->current); + if (row == NULL) + return FALSE; + tree->current = row; + if ((dist = get_distance(tree->current, tree->bottom)) < 0) + gnt_tree_scroll(tree, -dist); + else + redraw_tree(tree); + if (old != tree->current) + tree_selection_changed(tree, old, tree->current); + return TRUE; +} + +static gboolean +action_up(GntBindable *bind, GList *list) +{ + int dist; + GntTree *tree = GNT_TREE(bind); + GntTreeRow *old = tree->current; + GntTreeRow *row = get_prev(tree->current); + if (!row) + return FALSE; + tree->current = row; + if ((dist = get_distance(tree->current, tree->top)) > 0) + gnt_tree_scroll(tree, -dist); + else + redraw_tree(tree); + if (old != tree->current) + tree_selection_changed(tree, old, tree->current); + + return TRUE; +} + +static gboolean +action_page_down(GntBindable *bind, GList *null) +{ + GntTree *tree = GNT_TREE(bind); + GntTreeRow *old = tree->current; + GntTreeRow *row = get_next(tree->bottom); + if (row) + { + int dist = get_distance(tree->top, tree->current); + tree->top = tree->bottom; + tree->current = get_next_n_opt(tree->top, dist, NULL); + redraw_tree(tree); + } + else if (tree->current != tree->bottom) + { + tree->current = tree->bottom; + redraw_tree(tree); + } + + if (old != tree->current) + tree_selection_changed(tree, old, tree->current); + return TRUE; +} + +static gboolean +action_page_up(GntBindable *bind, GList *null) +{ + GntWidget *widget = GNT_WIDGET(bind); + GntTree *tree = GNT_TREE(bind); + GntTreeRow *row; + GntTreeRow *old = tree->current; + + if (tree->top != tree->root) + { + int dist = get_distance(tree->top, tree->current); + row = get_prev_n(tree->top, widget->priv.height - 1 - + tree->show_title * 2 - 2 * (GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_NO_BORDER) == 0)); + if (row == NULL) + row = tree->root; + tree->top = row; + tree->current = get_next_n_opt(tree->top, dist, NULL); + redraw_tree(tree); + } + else if (tree->current != tree->top) + { + tree->current = tree->top; + redraw_tree(tree); + } + if (old != tree->current) + tree_selection_changed(tree, old, tree->current); + return TRUE; +} + +static void +end_search(GntTree *tree) +{ + if (tree->search) { + g_source_remove(tree->search_timeout); + g_string_free(tree->search, TRUE); + tree->search = NULL; + tree->search_timeout = 0; + } +} + +static gboolean +search_timeout(gpointer data) +{ + GntTree *tree = data; + + end_search(tree); + redraw_tree(tree); + + return FALSE; +} + +static gboolean +gnt_tree_key_pressed(GntWidget *widget, const char *text) +{ + GntTree *tree = GNT_TREE(widget); + GntTreeRow *old = tree->current; + + if (text[0] == '\r') { + end_search(tree); + gnt_widget_activate(widget); + } else if (tree->search) { + if (isalnum(*text)) { + tree->search = g_string_append_c(tree->search, *text); + redraw_tree(tree); + g_source_remove(tree->search_timeout); + tree->search_timeout = g_timeout_add(SEARCH_TIMEOUT, search_timeout, tree); + } + return TRUE; + } else if (text[0] == ' ' && text[1] == 0) { + /* Space pressed */ + GntTreeRow *row = tree->current; + if (row && row->child) + { + row->collapsed = !row->collapsed; + redraw_tree(tree); + } + else if (row && row->choice) + { + row->isselected = !row->isselected; + g_signal_emit(tree, signals[SIG_TOGGLED], 0, row->key); + redraw_tree(tree); + } + } + + if (old != tree->current) + { + tree_selection_changed(tree, old, tree->current); + return TRUE; + } + + return FALSE; +} + +static void +gnt_tree_destroy(GntWidget *widget) +{ + GntTree *tree = GNT_TREE(widget); + int i; + + end_search(tree); + g_hash_table_destroy(tree->hash); + g_list_free(tree->list); + + for (i = 0; i < tree->ncol; i++) + { + g_free(tree->columns[i].title); + } + g_free(tree->columns); +} + +static gboolean +gnt_tree_clicked(GntWidget *widget, GntMouseEvent event, int x, int y) +{ + GntTree *tree = GNT_TREE(widget); + GntTreeRow *old = tree->current; + if (event == GNT_MOUSE_SCROLL_UP) { + action_up(GNT_BINDABLE(widget), NULL); + } else if (event == GNT_MOUSE_SCROLL_DOWN) { + action_down(GNT_BINDABLE(widget), NULL); + } else if (event == GNT_LEFT_MOUSE_DOWN) { + GntTreeRow *row; + GntTree *tree = GNT_TREE(widget); + int pos = 1; + if (GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_NO_BORDER)) + pos = 0; + if (tree->show_title) + pos += 2; + pos = y - widget->priv.y - pos; + row = get_next_n(tree->top, pos); + if (row && tree->current != row) { + GntTreeRow *old = tree->current; + tree->current = row; + redraw_tree(tree); + tree_selection_changed(tree, old, tree->current); + } else if (row && row == tree->current) { + if (row->choice) { + row->isselected = !row->isselected; + g_signal_emit(tree, signals[SIG_TOGGLED], 0, row->key); + redraw_tree(tree); + } else { + gnt_widget_activate(widget); + } + } + } else { + return FALSE; + } + if (old != tree->current) { + tree_selection_changed(tree, old, tree->current); + } + return TRUE; +} + +static void +gnt_tree_size_changed(GntWidget *widget, int w, int h) +{ + GntTree *tree = GNT_TREE(widget); + int i; + int n = 0; + if (widget->priv.width <= 0) + return; + for (i = 0; i < tree->ncol; ++i) + n += tree->columns[i].width; + if (GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_NO_BORDER)) + tree->columns[tree->ncol - 1].width += widget->priv.width - n - 1 * tree->ncol; + else + tree->columns[tree->ncol - 1].width += widget->priv.width - n - 2 - 1 * tree->ncol; +} + +static gboolean +start_search(GntBindable *bindable, GList *list) +{ + GntTree *tree = GNT_TREE(bindable); + if (tree->search) + return FALSE; + tree->search = g_string_new(NULL); + tree->search_timeout = g_timeout_add(SEARCH_TIMEOUT, search_timeout, tree); + return TRUE; +} + +static gboolean +end_search_action(GntBindable *bindable, GList *list) +{ + GntTree *tree = GNT_TREE(bindable); + if (tree->search == NULL) + return FALSE; + end_search(tree); + redraw_tree(tree); + return TRUE; +} + +static void +gnt_tree_class_init(GntTreeClass *klass) +{ + GntBindableClass *bindable = GNT_BINDABLE_CLASS(klass); + parent_class = GNT_WIDGET_CLASS(klass); + parent_class->destroy = gnt_tree_destroy; + parent_class->draw = gnt_tree_draw; + parent_class->map = gnt_tree_map; + parent_class->size_request = gnt_tree_size_request; + parent_class->key_pressed = gnt_tree_key_pressed; + parent_class->clicked = gnt_tree_clicked; + parent_class->size_changed = gnt_tree_size_changed; + + signals[SIG_SELECTION_CHANGED] = + g_signal_new("selection-changed", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntTreeClass, selection_changed), + NULL, NULL, + gnt_closure_marshal_VOID__POINTER_POINTER, + G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER); + signals[SIG_SCROLLED] = + g_signal_new("scrolled", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, 1, G_TYPE_INT); + signals[SIG_TOGGLED] = + g_signal_new("toggled", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntTreeClass, toggled), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + + gnt_bindable_class_register_action(bindable, "move-up", action_up, + GNT_KEY_UP, NULL); + gnt_bindable_register_binding(bindable, "move-up", GNT_KEY_CTRL_P, NULL); + gnt_bindable_class_register_action(bindable, "move-down", action_down, + GNT_KEY_DOWN, NULL); + gnt_bindable_register_binding(bindable, "move-down", GNT_KEY_CTRL_N, NULL); + gnt_bindable_class_register_action(bindable, "page-up", action_page_up, + GNT_KEY_PGUP, NULL); + gnt_bindable_class_register_action(bindable, "page-down", action_page_down, + GNT_KEY_PGDOWN, NULL); + gnt_bindable_class_register_action(bindable, "start-search", start_search, + "/", NULL); + gnt_bindable_class_register_action(bindable, "end-search", end_search_action, + "\033", NULL); + + gnt_style_read_actions(G_OBJECT_CLASS_TYPE(klass), bindable); + GNTDEBUG; +} + +static void +gnt_tree_init(GTypeInstance *instance, gpointer class) +{ + GntWidget *widget = GNT_WIDGET(instance); + GntTree *tree = GNT_TREE(widget); + tree->show_separator = TRUE; + GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_GROW_X | GNT_WIDGET_GROW_Y); + widget->priv.minw = 4; + widget->priv.minh = 4; + GNTDEBUG; +} + +/****************************************************************************** + * GntTree API + *****************************************************************************/ +GType +gnt_tree_get_gtype(void) +{ + static GType type = 0; + + if(type == 0) + { + static const GTypeInfo info = { + sizeof(GntTreeClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)gnt_tree_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof(GntTree), + 0, /* n_preallocs */ + gnt_tree_init, /* instance_init */ + }; + + type = g_type_register_static(GNT_TYPE_WIDGET, + "GntTree", + &info, 0); + } + + return type; +} + +static void +free_tree_col(gpointer data) +{ + GntTreeCol *col = data; + + g_free(col->text); + g_free(col); +} + +static void +free_tree_row(gpointer data) +{ + GntTreeRow *row = data; + + if (!row) + return; + + g_list_foreach(row->columns, (GFunc)free_tree_col, NULL); + g_list_free(row->columns); + g_free(row); +} + +GntWidget *gnt_tree_new() +{ + return gnt_tree_new_with_columns(1); +} + +void gnt_tree_set_visible_rows(GntTree *tree, int rows) +{ + GntWidget *widget = GNT_WIDGET(tree); + widget->priv.height = rows; + if (!GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_NO_BORDER)) + widget->priv.height += 2; +} + +int gnt_tree_get_visible_rows(GntTree *tree) +{ + GntWidget *widget = GNT_WIDGET(tree); + int ret = widget->priv.height; + if (!GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_NO_BORDER)) + ret -= 2; + return ret; +} + +const GList *gnt_tree_get_rows(GntTree *tree) +{ + return tree->list; +} + +void gnt_tree_scroll(GntTree *tree, int count) +{ + GntTreeRow *row; + + if (count < 0) + { + if (get_root_distance(tree->top) == 0) + return; + row = get_prev_n(tree->top, -count); + if (row == NULL) + row = tree->root; + tree->top = row; + } + else + { + get_next_n_opt(tree->bottom, count, &count); + tree->top = get_next_n(tree->top, count); + } + + redraw_tree(tree); + g_signal_emit(tree, signals[SIG_SCROLLED], 0, count); +} + +static gpointer +find_position(GntTree *tree, gpointer key, gpointer parent) +{ + GntTreeRow *row; + + if (tree->compare == NULL) + return NULL; + + if (parent == NULL) + row = tree->root; + else + row = g_hash_table_lookup(tree->hash, parent); + + if (!row) + return NULL; + + if (parent) + row = row->child; + + while (row) + { + if (tree->compare(key, row->key) < 0) + return (row->prev ? row->prev->key : NULL); + if (row->next) + row = row->next; + else + return row->key; + } + return NULL; +} + +void gnt_tree_sort_row(GntTree *tree, gpointer key) +{ + GntTreeRow *row, *q, *s; + int current, newp; + + if (!tree->compare) + return; + + row = g_hash_table_lookup(tree->hash, key); + g_return_if_fail(row != NULL); + + current = g_list_index(tree->list, key); + + if (row->parent) + s = row->parent->child; + else + s = tree->root; + + q = NULL; + while (s) { + if (tree->compare(row->key, s->key) < 0) + break; + q = s; + s = s->next; + } + + /* Move row between q and s */ + if (row == q || row == s) + return; + + if (q == NULL) { + /* row becomes the first child of its parent */ + row->prev->next = row->next; /* row->prev cannot be NULL at this point */ + if (row->next) + row->next->prev = row->prev; + if (row->parent) + row->parent->child = row; + else + tree->root = row; + row->next = s; + s->prev = row; /* s cannot be NULL */ + row->prev = NULL; + newp = g_list_index(tree->list, s) - 1; + } else { + if (row->prev) { + row->prev->next = row->next; + } else { + /* row was the first child of its parent */ + if (row->parent) + row->parent->child = row->next; + else + tree->top = row->next; + } + + if (row->next) + row->next->prev = row->prev; + + q->next = row; + row->prev = q; + if (s) + s->prev = row; + row->next = s; + newp = g_list_index(tree->list, q) + 1; + } + tree->list = g_list_reposition_child(tree->list, current, newp); + + redraw_tree(tree); +} + +GntTreeRow *gnt_tree_add_row_after(GntTree *tree, void *key, GntTreeRow *row, void *parent, void *bigbro) +{ + GntTreeRow *pr = NULL; + + g_hash_table_replace(tree->hash, key, row); + row->tree = tree; + + if (bigbro == NULL && tree->compare) + { + bigbro = find_position(tree, key, parent); + } + + if (tree->root == NULL) + { + tree->root = row; + tree->list = g_list_prepend(tree->list, key); + } + else + { + int position = 0; + + if (bigbro) + { + pr = g_hash_table_lookup(tree->hash, bigbro); + if (pr) + { + if (pr->next) pr->next->prev = row; + row->next = pr->next; + row->prev = pr; + pr->next = row; + row->parent = pr->parent; + + position = g_list_index(tree->list, bigbro); + } + } + + if (pr == NULL && parent) + { + pr = g_hash_table_lookup(tree->hash, parent); + if (pr) + { + if (pr->child) pr->child->prev = row; + row->next = pr->child; + pr->child = row; + row->parent = pr; + + position = g_list_index(tree->list, parent); + } + } + + if (pr == NULL) + { + GntTreeRow *r = tree->root; + row->next = r; + if (r) r->prev = row; + if (tree->current == tree->root) + tree->current = row; + tree->root = row; + tree->list = g_list_prepend(tree->list, key); + } + else + { + tree->list = g_list_insert(tree->list, key, position + 1); + } + } + + row->key = key; + row->data = NULL; + + redraw_tree(tree); + + return row; +} + +GntTreeRow *gnt_tree_add_row_last(GntTree *tree, void *key, GntTreeRow *row, void *parent) +{ + GntTreeRow *pr = NULL, *br = NULL; + + if (parent) + pr = g_hash_table_lookup(tree->hash, parent); + + if (pr) + br = pr->child; + else + br = tree->root; + + if (br) + { + while (br->next) + br = br->next; + } + + return gnt_tree_add_row_after(tree, key, row, parent, br ? br->key : NULL); +} + +gpointer gnt_tree_get_selection_data(GntTree *tree) +{ + if (tree->current) + return tree->current->key; /* XXX: perhaps we should just get rid of 'data' */ + return NULL; +} + +char *gnt_tree_get_selection_text(GntTree *tree) +{ + if (tree->current) + return update_row_text(tree, tree->current); + return NULL; +} + +GList *gnt_tree_get_selection_text_list(GntTree *tree) +{ + GList *list = NULL, *iter; + int i; + + if (!tree->current) + return NULL; + + for (i = 0, iter = tree->current->columns; i < tree->ncol && iter; + i++, iter = iter->next) + { + GntTreeCol *col = iter->data; + list = g_list_append(list, g_strdup(col->text)); + } + + return list; +} + +void gnt_tree_remove(GntTree *tree, gpointer key) +{ + GntTreeRow *row = g_hash_table_lookup(tree->hash, key); + if (row) + { + gboolean redraw = FALSE; + + if (row->child) { + GntTreeRow *ch = row->child; + while (ch) { + GntTreeRow *n = ch->next; + tree->list = g_list_remove(tree->list, ch->key); + g_hash_table_remove(tree->hash, ch->key); + ch = n; + } + } + + if (get_distance(tree->top, row) >= 0 && get_distance(row, tree->bottom) >= 0) + redraw = TRUE; + + /* Update root/top/current/bottom if necessary */ + if (tree->root == row) + tree->root = get_next(row); + if (tree->top == row) + { + if (tree->top != tree->root) + tree->top = get_prev(row); + else + tree->top = get_next(row); + if (tree->current == row) + tree->current = tree->top; + } + else if (tree->current == row) + { + if (tree->current != tree->root) + tree->current = get_prev(row); + else + tree->current = get_next(row); + } + else if (tree->bottom == row) + { + tree->bottom = get_prev(row); + } + + /* Fix the links */ + if (row->next) + row->next->prev = row->prev; + if (row->parent && row->parent->child == row) + row->parent->child = row->next; + if (row->prev) + row->prev->next = row->next; + + g_hash_table_remove(tree->hash, key); + tree->list = g_list_remove(tree->list, key); + + if (redraw) + { + redraw_tree(tree); + } + } +} + +static gboolean +return_true(gpointer key, gpointer data, gpointer null) +{ + return TRUE; +} + +void gnt_tree_remove_all(GntTree *tree) +{ + tree->root = NULL; + g_hash_table_foreach_remove(tree->hash, (GHRFunc)return_true, tree); + g_list_free(tree->list); + tree->list = NULL; + tree->current = tree->top = tree->bottom = NULL; +} + +int gnt_tree_get_selection_visible_line(GntTree *tree) +{ + return get_distance(tree->top, tree->current) + + !!(GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(tree), GNT_WIDGET_NO_BORDER)); +} + +void gnt_tree_change_text(GntTree *tree, gpointer key, int colno, const char *text) +{ + GntTreeRow *row; + GntTreeCol *col; + + g_return_if_fail(colno < tree->ncol); + + row = g_hash_table_lookup(tree->hash, key); + if (row) + { + col = g_list_nth_data(row->columns, colno); + g_free(col->text); + col->text = g_strdup(text); + + if (get_distance(tree->top, row) >= 0 && get_distance(row, tree->bottom) >= 0) + redraw_tree(tree); + } +} + +GntTreeRow *gnt_tree_add_choice(GntTree *tree, void *key, GntTreeRow *row, void *parent, void *bigbro) +{ + GntTreeRow *r; + r = g_hash_table_lookup(tree->hash, key); + g_return_val_if_fail(!r || !r->choice, NULL); + + if (bigbro == NULL) { + r = g_hash_table_lookup(tree->hash, parent); + if (!r) + r = tree->root; + else + r = r->child; + if (r) { + while (r->next) + r = r->next; + bigbro = r->key; + } + } + row = gnt_tree_add_row_after(tree, key, row, parent, bigbro); + row->choice = TRUE; + + return row; +} + +void gnt_tree_set_choice(GntTree *tree, void *key, gboolean set) +{ + GntTreeRow *row = g_hash_table_lookup(tree->hash, key); + + if (!row) + return; + g_return_if_fail(row->choice); + + row->isselected = set; + redraw_tree(tree); +} + +gboolean gnt_tree_get_choice(GntTree *tree, void *key) +{ + GntTreeRow *row = g_hash_table_lookup(tree->hash, key); + + if (!row) + return FALSE; + g_return_val_if_fail(row->choice, FALSE); + + return row->isselected; +} + +void gnt_tree_set_row_flags(GntTree *tree, void *key, GntTextFormatFlags flags) +{ + GntTreeRow *row = g_hash_table_lookup(tree->hash, key); + if (!row || row->flags == flags) + return; + + row->flags = flags; + redraw_tree(tree); /* XXX: It shouldn't be necessary to redraw the whole darned tree */ +} + +void gnt_tree_set_selected(GntTree *tree , void *key) +{ + int dist; + GntTreeRow *row = g_hash_table_lookup(tree->hash, key); + if (!row) + return; + + if (tree->top == NULL) + tree->top = row; + if (tree->bottom == NULL) + tree->bottom = row; + + tree->current = row; + if ((dist = get_distance(tree->current, tree->bottom)) < 0) + gnt_tree_scroll(tree, -dist); + else if ((dist = get_distance(tree->current, tree->top)) > 0) + gnt_tree_scroll(tree, -dist); + else + redraw_tree(tree); +} + +void _gnt_tree_init_internals(GntTree *tree, int col) +{ + tree->ncol = col; + tree->hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_tree_row); + tree->columns = g_new0(struct _GntTreeColInfo, col); + while (col--) + { + tree->columns[col].width = 15; + } + tree->list = NULL; + tree->show_title = FALSE; +} + +GntWidget *gnt_tree_new_with_columns(int col) +{ + GntWidget *widget = g_object_new(GNT_TYPE_TREE, NULL); + GntTree *tree = GNT_TREE(widget); + + _gnt_tree_init_internals(tree, col); + + GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_NO_SHADOW); + gnt_widget_set_take_focus(widget, TRUE); + + return widget; +} + +GntTreeRow *gnt_tree_create_row_from_list(GntTree *tree, GList *list) +{ + GList *iter; + int i; + GntTreeRow *row = g_new0(GntTreeRow, 1); + + for (i = 0, iter = list; i < tree->ncol && iter; iter = iter->next, i++) + { + GntTreeCol *col = g_new0(GntTreeCol, 1); + col->span = 1; + col->text = g_strdup(iter->data); + + row->columns = g_list_append(row->columns, col); + } + + return row; +} + +GntTreeRow *gnt_tree_create_row(GntTree *tree, ...) +{ + int i; + va_list args; + GList *list = NULL; + GntTreeRow *row; + + va_start(args, tree); + for (i = 0; i < tree->ncol; i++) + { + list = g_list_append(list, va_arg(args, char *)); + } + va_end(args); + + row = gnt_tree_create_row_from_list(tree, list); + g_list_free(list); + + return row; +} + +void gnt_tree_set_col_width(GntTree *tree, int col, int width) +{ + g_return_if_fail(col < tree->ncol); + + tree->columns[col].width = width; +} + +void gnt_tree_set_column_titles(GntTree *tree, ...) +{ + int i; + va_list args; + + va_start(args, tree); + for (i = 0; i < tree->ncol; i++) + { + const char *title = va_arg(args, const char *); + tree->columns[i].title = g_strdup(title); + } + va_end(args); +} + +void gnt_tree_set_show_title(GntTree *tree, gboolean set) +{ + tree->show_title = set; + GNT_WIDGET(tree)->priv.minh = (set ? 6 : 4); +} + +void gnt_tree_set_compare_func(GntTree *tree, GCompareFunc func) +{ + tree->compare = func; +} + +void gnt_tree_set_expanded(GntTree *tree, void *key, gboolean expanded) +{ + GntTreeRow *row = g_hash_table_lookup(tree->hash, key); + if (row) { + row->collapsed = !expanded; + if (GNT_WIDGET(tree)->window) + gnt_widget_draw(GNT_WIDGET(tree)); + } +} + +void gnt_tree_set_show_separator(GntTree *tree, gboolean set) +{ + tree->show_separator = set; +} + +void gnt_tree_adjust_columns(GntTree *tree) +{ + GntTreeRow *row = tree->root; + int *widths, i, twidth, height; + + widths = g_new0(int, tree->ncol); + while (row) { + GList *iter; + for (i = 0, iter = row->columns; iter; iter = iter->next, i++) { + GntTreeCol *col = iter->data; + int w = gnt_util_onscreen_width(col->text, NULL); + if (widths[i] < w) + widths[i] = w; + } + row = row->next; + } + + twidth = 1 + 2 * (!GNT_WIDGET_IS_FLAG_SET(GNT_WIDGET(tree), GNT_WIDGET_NO_BORDER)); + for (i = 0; i < tree->ncol; i++) { + gnt_tree_set_col_width(tree, i, widths[i]); + twidth += widths[i] + (tree->show_separator ? 1 : 0) + 1; + } + g_free(widths); + + gnt_widget_get_size(GNT_WIDGET(tree), NULL, &height); + gnt_widget_set_size(GNT_WIDGET(tree), twidth, height); +} + +void gnt_tree_set_hash_fns(GntTree *tree, gpointer hash, gpointer eq, gpointer kd) +{ + g_hash_table_foreach_remove(tree->hash, return_true, NULL); + g_hash_table_destroy(tree->hash); + tree->hash = g_hash_table_new_full(hash, eq, kd, free_tree_row); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gnttree.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,150 @@ +#ifndef GNT_TREE_H +#define GNT_TREE_H + +#include "gntwidget.h" +#include "gnt.h" +#include "gntcolors.h" +#include "gntkeys.h" +#include "gnttextview.h" + +#define GNT_TYPE_TREE (gnt_tree_get_gtype()) +#define GNT_TREE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_TREE, GntTree)) +#define GNT_TREE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_TREE, GntTreeClass)) +#define GNT_IS_TREE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_TREE)) +#define GNT_IS_TREE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GNT_TYPE_TREE)) +#define GNT_TREE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GNT_TYPE_TREE, GntTreeClass)) + +#define GNT_TREE_FLAGS(obj) (GNT_TREE(obj)->priv.flags) +#define GNT_TREE_SET_FLAGS(obj, flags) (GNT_TREE_FLAGS(obj) |= flags) +#define GNT_TREE_UNSET_FLAGS(obj, flags) (GNT_TREE_FLAGS(obj) &= ~(flags)) + +typedef struct _GnTree GntTree; +typedef struct _GnTreePriv GntTreePriv; +typedef struct _GnTreeClass GntTreeClass; + +typedef struct _GnTreeRow GntTreeRow; +typedef struct _GnTreeCol GntTreeCol; + +struct _GnTree +{ + GntWidget parent; + + GntTreeRow *current; /* current selection */ + + GntTreeRow *top; /* The topmost visible item */ + GntTreeRow *bottom; /* The bottommost visible item */ + + GntTreeRow *root; /* The root of all evil */ + + GList *list; /* List of GntTreeRow s */ + GHashTable *hash; /* We need this for quickly referencing the rows */ + guint (*hash_func)(gconstpointer); + gboolean (*hash_eq_func)(gconstpointer, gconstpointer); + GDestroyNotify key_destroy; + GDestroyNotify value_destroy; + + int ncol; /* No. of columns */ + struct _GntTreeColInfo + { + int width; + char *title; + } *columns; /* Would a GList be better? */ + gboolean show_title; + gboolean show_separator; /* Whether to show column separators */ + + GString *search; + int search_timeout; + + GCompareFunc compare; +}; + +struct _GnTreeClass +{ + GntWidgetClass parent; + + void (*selection_changed)(GntTreeRow *old, GntTreeRow * current); + void (*toggled)(GntTree *tree, gpointer key); + + void (*gnt_reserved1)(void); + void (*gnt_reserved2)(void); + void (*gnt_reserved3)(void); + void (*gnt_reserved4)(void); +}; + +G_BEGIN_DECLS + +GType gnt_tree_get_gtype(void); + +GntWidget *gnt_tree_new(); /* A tree with just one column */ + +GntWidget *gnt_tree_new_with_columns(int columns); + +void gnt_tree_set_visible_rows(GntTree *tree, int rows); + +int gnt_tree_get_visible_rows(GntTree *tree); + +void gnt_tree_scroll(GntTree *tree, int count); + +GntTreeRow *gnt_tree_add_row_after(GntTree *tree, void *key, GntTreeRow *row, void *parent, void *bigbro); + +GntTreeRow *gnt_tree_add_row_last(GntTree *tree, void *key, GntTreeRow *row, void *parent); + +gpointer gnt_tree_get_selection_data(GntTree *tree); + +/* Returned string needs to be freed */ +char *gnt_tree_get_selection_text(GntTree *tree); + +GList *gnt_tree_get_selection_text_list(GntTree *tree); + +const GList *gnt_tree_get_rows(GntTree *tree); + +void gnt_tree_remove(GntTree *tree, gpointer key); + +void gnt_tree_remove_all(GntTree *tree); + +/* Returns the visible line number of the selected row */ +int gnt_tree_get_selection_visible_line(GntTree *tree); + +void gnt_tree_change_text(GntTree *tree, gpointer key, int colno, const char *text); + +GntTreeRow *gnt_tree_add_choice(GntTree *tree, void *key, GntTreeRow *row, void *parent, void *bigbro); + +void gnt_tree_set_choice(GntTree *tree, void *key, gboolean set); + +gboolean gnt_tree_get_choice(GntTree *tree, void *key); + +void gnt_tree_set_row_flags(GntTree *tree, void *key, GntTextFormatFlags flags); + +void gnt_tree_set_selected(GntTree *tree , void *key); + +GntTreeRow *gnt_tree_create_row(GntTree *tree, ...); + +GntTreeRow *gnt_tree_create_row_from_list(GntTree *tree, GList *list); + +void gnt_tree_set_col_width(GntTree *tree, int col, int width); + +void gnt_tree_set_column_titles(GntTree *tree, ...); + +void gnt_tree_set_show_title(GntTree *tree, gboolean set); + +void gnt_tree_set_compare_func(GntTree *tree, GCompareFunc func); + +void gnt_tree_set_expanded(GntTree *tree, void *key, gboolean expanded); + +void gnt_tree_set_show_separator(GntTree *tree, gboolean set); + +void gnt_tree_sort_row(GntTree *tree, void *row); + +/* This will try to automatically adjust the width of the columns in the tree */ +void gnt_tree_adjust_columns(GntTree *tree); + +void gnt_tree_set_hash_fns(GntTree *tree, gpointer hash, gpointer eq, gpointer kd); + +G_END_DECLS + +/* The following functions should NOT be used by applications. */ + +/* This should be called by the subclasses of GntTree's in their _new function */ +void _gnt_tree_init_internals(GntTree *tree, int col); + +#endif /* GNT_TREE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntutils.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,147 @@ +#include "gntutils.h" + +#include <stdlib.h> +#include <string.h> + +#include "config.h" + +void gnt_util_get_text_bound(const char *text, int *width, int *height) +{ + const char *s = text, *last; + int count = 1, max = 0; + int len; + + /* XXX: ew ... everyone look away */ + last = s; + if (s) + { + while (*s) + { + if (*s == '\n' || *s == '\r') + { + count++; + len = gnt_util_onscreen_width(last, s); + if (max < len) + max = len; + last = s + 1; + } + s = g_utf8_next_char(s); + } + + len = gnt_util_onscreen_width(last, s); + if (max < len) + max = len; + } + + if (height) + *height = count; + if (width) + *width = max + (count > 1); +} + +int gnt_util_onscreen_width(const char *start, const char *end) +{ + int width = 0; + + if (end == NULL) + end = start + strlen(start); + + while (start < end) { + width += g_unichar_iswide(g_utf8_get_char(start)) ? 2 : 1; + start = g_utf8_next_char(start); + } + return width; +} + +const char *gnt_util_onscreen_width_to_pointer(const char *string, int len, int *w) +{ + int size; + int width = 0; + const char *str = string; + + if (len <= 0) { + len = gnt_util_onscreen_width(string, NULL); + } + + while (width < len && *str) { + size = g_unichar_iswide(g_utf8_get_char(str)) ? 2 : 1; + if (width + size > len) + break; + str = g_utf8_next_char(str); + width += size; + } + if (w) + *w = width; + return str; +} + +char *gnt_util_onscreen_fit_string(const char *string, int maxw) +{ + const char *start, *end; + GString *str; + + if (maxw <= 0) + maxw = getmaxx(stdscr) - 4; + + start = string; + str = g_string_new(NULL); + + while (*start) { + if ((end = strchr(start, '\n')) != NULL || + (end = strchr(start, '\r')) != NULL) { + if (gnt_util_onscreen_width(start, end) > maxw) + end = NULL; + } + if (end == NULL) + end = gnt_util_onscreen_width_to_pointer(start, maxw, NULL); + str = g_string_append_len(str, start, end - start); + if (*end) { + str = g_string_append_c(str, '\n'); + if (*end == '\n' || *end == '\r') + end++; + } + start = end; + } + return g_string_free(str, FALSE); +} + +struct duplicate_fns +{ + GDupFunc key_dup; + GDupFunc value_dup; + GHashTable *table; +}; + +static void +duplicate_values(gpointer key, gpointer value, gpointer data) +{ + struct duplicate_fns *fns = data; + g_hash_table_insert(fns->table, fns->key_dup ? fns->key_dup(key) : key, + fns->value_dup ? fns->value_dup(value) : value); +} + +GHashTable *g_hash_table_duplicate(GHashTable *src, GHashFunc hash, + GEqualFunc equal, GDestroyNotify key_d, GDestroyNotify value_d, + GDupFunc key_dup, GDupFunc value_dup) +{ + GHashTable *dest = g_hash_table_new_full(hash, equal, key_d, value_d); + struct duplicate_fns fns = {key_dup, value_dup, dest}; + g_hash_table_foreach(src, duplicate_values, &fns); + return dest; +} + +gboolean gnt_boolean_handled_accumulator(GSignalInvocationHint *ihint, + GValue *return_accu, + const GValue *handler_return, + gpointer dummy) +{ + gboolean continue_emission; + gboolean signal_handled; + + signal_handled = g_value_get_boolean (handler_return); + g_value_set_boolean (return_accu, signal_handled); + continue_emission = !signal_handled; + + return continue_emission; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntutils.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,36 @@ +#include <glib.h> + +#include "gnt.h" +#include "gntwidget.h" + +typedef gpointer (*GDupFunc)(gconstpointer data); + +void gnt_util_get_text_bound(const char *text, int *width, int *height); + +/* excluding *end */ +int gnt_util_onscreen_width(const char *start, const char *end); + +const char *gnt_util_onscreen_width_to_pointer(const char *str, int len, int *w); + +/* Inserts newlines in 'string' where necessary so that its onscreen width is + * no more than 'maxw'. + * 'maxw' can be <= 0, in which case the maximum screen width is considered. + * + * Returns a newly allocated string. + */ +char *gnt_util_onscreen_fit_string(const char *string, int maxw); + +GHashTable *g_hash_table_duplicate(GHashTable *src, GHashFunc hash, + GEqualFunc equal, GDestroyNotify key_d, GDestroyNotify value_d, + GDupFunc key_dup, GDupFunc value_dup); + + +/** + * To be used with g_signal_new. Look in the key_pressed signal-definition in + * gntwidget.c for usage. + */ +gboolean gnt_boolean_handled_accumulator(GSignalInvocationHint *ihint, + GValue *return_accu, + const GValue *handler_return, + gpointer dummy); +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntwidget.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,624 @@ +/* Stuff brutally ripped from Gflib */ + +#include "gntwidget.h" +#include "gntstyle.h" +#include "gntmarshal.h" +#include "gntutils.h" +#include "gnt.h" + +enum +{ + SIG_DESTROY, + SIG_DRAW, + SIG_HIDE, + SIG_GIVE_FOCUS, + SIG_LOST_FOCUS, + SIG_KEY_PRESSED, + SIG_MAP, + SIG_ACTIVATE, + SIG_EXPOSE, + SIG_SIZE_REQUEST, + SIG_CONFIRM_SIZE, + SIG_SIZE_CHANGED, + SIG_POSITION, + SIG_CLICKED, + SIG_CONTEXT_MENU, + SIGS +}; + +static GObjectClass *parent_class = NULL; +static guint signals[SIGS] = { 0 }; + +static void init_widget(GntWidget *widget); + +static void +gnt_widget_init(GTypeInstance *instance, gpointer class) +{ + GntWidget *widget = GNT_WIDGET(instance); + widget->priv.name = NULL; + GNTDEBUG; +} + +static void +gnt_widget_map(GntWidget *widget) +{ + /* Get some default size for the widget */ + GNTDEBUG; + g_signal_emit(widget, signals[SIG_MAP], 0); + GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_MAPPED); +} + +static void +gnt_widget_dispose(GObject *obj) +{ + GntWidget *self = GNT_WIDGET(obj); + + if(!(GNT_WIDGET_FLAGS(self) & GNT_WIDGET_DESTROYING)) { + GNT_WIDGET_SET_FLAGS(self, GNT_WIDGET_DESTROYING); + + g_signal_emit(self, signals[SIG_DESTROY], 0); + + GNT_WIDGET_UNSET_FLAGS(self, GNT_WIDGET_DESTROYING); + } + + parent_class->dispose(obj); + GNTDEBUG; +} + +static void +gnt_widget_focus_change(GntWidget *widget) +{ + if (GNT_WIDGET_FLAGS(widget) & GNT_WIDGET_MAPPED) + gnt_widget_draw(widget); +} + +static gboolean +gnt_widget_dummy_confirm_size(GntWidget *widget, int width, int height) +{ + if (width < widget->priv.minw || height < widget->priv.minh) + return FALSE; + if (widget->priv.width != width && !GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_GROW_X)) + return FALSE; + if (widget->priv.height != height && !GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_GROW_Y)) + return FALSE; + return TRUE; +} + +static gboolean +context_menu(GntBindable *bind, GList *null) +{ + gboolean ret = FALSE; + g_signal_emit(bind, signals[SIG_CONTEXT_MENU], 0, &ret); + return ret; +} + +static void +gnt_widget_class_init(GntWidgetClass *klass) +{ + GObjectClass *obj_class = G_OBJECT_CLASS(klass); + + parent_class = g_type_class_peek_parent(klass); + + obj_class->dispose = gnt_widget_dispose; + + klass->destroy = gnt_widget_destroy; + klass->show = gnt_widget_show; + klass->draw = gnt_widget_draw; + klass->expose = gnt_widget_expose; + klass->map = gnt_widget_map; + klass->lost_focus = gnt_widget_focus_change; + klass->gained_focus = gnt_widget_focus_change; + klass->confirm_size = gnt_widget_dummy_confirm_size; + + klass->key_pressed = NULL; + klass->activate = NULL; + klass->clicked = NULL; + + signals[SIG_DESTROY] = + g_signal_new("destroy", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWidgetClass, destroy), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + signals[SIG_GIVE_FOCUS] = + g_signal_new("gained-focus", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWidgetClass, gained_focus), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + signals[SIG_LOST_FOCUS] = + g_signal_new("lost-focus", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWidgetClass, lost_focus), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + signals[SIG_ACTIVATE] = + g_signal_new("activate", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWidgetClass, activate), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + signals[SIG_MAP] = + g_signal_new("map", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWidgetClass, map), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + signals[SIG_DRAW] = + g_signal_new("draw", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWidgetClass, draw), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + signals[SIG_HIDE] = + g_signal_new("hide", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWidgetClass, hide), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + signals[SIG_EXPOSE] = + g_signal_new("expose", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWidgetClass, expose), + NULL, NULL, + gnt_closure_marshal_VOID__INT_INT_INT_INT, + G_TYPE_NONE, 4, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT); + signals[SIG_POSITION] = + g_signal_new("position-set", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWidgetClass, set_position), + NULL, NULL, + gnt_closure_marshal_VOID__INT_INT, + G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT); + signals[SIG_SIZE_REQUEST] = + g_signal_new("size_request", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWidgetClass, size_request), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + signals[SIG_SIZE_CHANGED] = + g_signal_new("size_changed", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWidgetClass, size_changed), + NULL, NULL, + gnt_closure_marshal_VOID__INT_INT, + G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT); + signals[SIG_CONFIRM_SIZE] = + g_signal_new("confirm_size", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWidgetClass, confirm_size), + NULL, NULL, + gnt_closure_marshal_BOOLEAN__INT_INT, + G_TYPE_BOOLEAN, 2, G_TYPE_INT, G_TYPE_INT); + signals[SIG_KEY_PRESSED] = + g_signal_new("key_pressed", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWidgetClass, key_pressed), + gnt_boolean_handled_accumulator, NULL, + gnt_closure_marshal_BOOLEAN__STRING, + G_TYPE_BOOLEAN, 1, G_TYPE_STRING); + + signals[SIG_CLICKED] = + g_signal_new("clicked", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWidgetClass, clicked), + gnt_boolean_handled_accumulator, NULL, + gnt_closure_marshal_BOOLEAN__INT_INT_INT, + G_TYPE_BOOLEAN, 3, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT); + + signals[SIG_CONTEXT_MENU] = + g_signal_new("context-menu", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + 0, + gnt_boolean_handled_accumulator, NULL, + gnt_closure_marshal_BOOLEAN__VOID, + G_TYPE_BOOLEAN, 0); + + /* This is relevant for all widgets */ + gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "context-menu", context_menu, + GNT_KEY_POPUP, NULL); + gnt_bindable_register_binding(GNT_BINDABLE_CLASS(klass), "context-menu", GNT_KEY_F11, NULL); + + gnt_style_read_actions(G_OBJECT_CLASS_TYPE(klass), GNT_BINDABLE_CLASS(klass)); + GNTDEBUG; +} + +/****************************************************************************** + * GntWidget API + *****************************************************************************/ +GType +gnt_widget_get_gtype(void) +{ + static GType type = 0; + + if(type == 0) { + static const GTypeInfo info = { + sizeof(GntWidgetClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)gnt_widget_class_init, + NULL, + NULL, /* class_data */ + sizeof(GntWidget), + 0, /* n_preallocs */ + gnt_widget_init, /* instance_init */ + }; + + type = g_type_register_static(GNT_TYPE_BINDABLE, + "GntWidget", + &info, G_TYPE_FLAG_ABSTRACT); + } + + return type; +} + +void gnt_widget_set_take_focus(GntWidget *widget, gboolean can) +{ + if (can) + GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_CAN_TAKE_FOCUS); + else + GNT_WIDGET_UNSET_FLAGS(widget, GNT_WIDGET_CAN_TAKE_FOCUS); +} + +/** + * gnt_widget_destroy: + * @obj: The #GntWidget instance. + * + * Emits the "destroy" signal notifying all reference holders that they + * should release @obj. + */ +void +gnt_widget_destroy(GntWidget *obj) +{ + g_return_if_fail(GNT_IS_WIDGET(obj)); + + gnt_widget_hide(obj); + delwin(obj->window); + if(!(GNT_WIDGET_FLAGS(obj) & GNT_WIDGET_DESTROYING)) + g_object_run_dispose(G_OBJECT(obj)); + GNTDEBUG; +} + +void +gnt_widget_show(GntWidget *widget) +{ + gnt_widget_draw(widget); + gnt_screen_occupy(widget); +} + +void +gnt_widget_draw(GntWidget *widget) +{ + /* Draw the widget */ + if (GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_DRAWING)) + return; + + GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_DRAWING); + if (!(GNT_WIDGET_FLAGS(widget) & GNT_WIDGET_MAPPED)) { + gnt_widget_map(widget); + } + + if (widget->window == NULL) + { + int x, y, maxx, maxy, w, h; + int oldw, oldh; + gboolean shadow = TRUE; + + if (!gnt_widget_has_shadow(widget)) + shadow = FALSE; + + x = widget->priv.x; + y = widget->priv.y; + w = oldw = widget->priv.width + shadow; + h = oldh = widget->priv.height + shadow; + + getmaxyx(stdscr, maxy, maxx); + maxy -= 1; /* room for the taskbar */ + + x = MAX(0, x); + y = MAX(0, y); + if (x + w >= maxx) + x = MAX(0, maxx - w); + if (y + h >= maxy) + y = MAX(0, maxy - h); + + w = MIN(w, maxx); + h = MIN(h, maxy); + + widget->priv.x = x; + widget->priv.y = y; + if (w != oldw || h != oldh) { + widget->priv.width = w - shadow; + widget->priv.height = h - shadow; + g_signal_emit(widget, signals[SIG_SIZE_CHANGED], 0, oldw, oldh); + } + + widget->window = newwin(widget->priv.height + shadow, widget->priv.width + shadow, + widget->priv.y, widget->priv.x); + init_widget(widget); + } + + g_signal_emit(widget, signals[SIG_DRAW], 0); + gnt_widget_queue_update(widget); + GNT_WIDGET_UNSET_FLAGS(widget, GNT_WIDGET_DRAWING); +} + +gboolean +gnt_widget_key_pressed(GntWidget *widget, const char *keys) +{ + gboolean ret; + if (!GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_CAN_TAKE_FOCUS)) + return FALSE; + + if (gnt_bindable_perform_action_key(GNT_BINDABLE(widget), keys)) + return TRUE; + + keys = gnt_bindable_remap_keys(GNT_BINDABLE(widget), keys); + g_signal_emit(widget, signals[SIG_KEY_PRESSED], 0, keys, &ret); + return ret; +} + +gboolean +gnt_widget_clicked(GntWidget *widget, GntMouseEvent event, int x, int y) +{ + gboolean ret; + g_signal_emit(widget, signals[SIG_CLICKED], 0, event, x, y, &ret); + return ret; +} + +void +gnt_widget_expose(GntWidget *widget, int x, int y, int width, int height) +{ + g_signal_emit(widget, signals[SIG_EXPOSE], 0, x, y, width, height); +} + +void +gnt_widget_hide(GntWidget *widget) +{ + g_signal_emit(widget, signals[SIG_HIDE], 0); + wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_NORMAL)); +#if 0 + /* XXX: I have no clue why, but this seemed to be necessary. */ + if (gnt_widget_has_shadow(widget)) + mvwvline(widget->window, 1, widget->priv.width, ' ', widget->priv.height); +#endif + gnt_screen_release(widget); + GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_INVISIBLE); + GNT_WIDGET_UNSET_FLAGS(widget, GNT_WIDGET_MAPPED); +} + +void +gnt_widget_set_position(GntWidget *wid, int x, int y) +{ + g_signal_emit(wid, signals[SIG_POSITION], 0, x, y); + /* XXX: Need to install properties for these and g_object_notify */ + wid->priv.x = x; + wid->priv.y = y; +} + +void +gnt_widget_get_position(GntWidget *wid, int *x, int *y) +{ + if (x) + *x = wid->priv.x; + if (y) + *y = wid->priv.y; +} + +void +gnt_widget_size_request(GntWidget *widget) +{ + g_signal_emit(widget, signals[SIG_SIZE_REQUEST], 0); +} + +void +gnt_widget_get_size(GntWidget *wid, int *width, int *height) +{ + gboolean shadow = TRUE; + if (!gnt_widget_has_shadow(wid)) + shadow = FALSE; + + if (width) + *width = wid->priv.width + shadow; + if (height) + *height = wid->priv.height + shadow; + +} + +static void +init_widget(GntWidget *widget) +{ + gboolean shadow = TRUE; + + if (!gnt_widget_has_shadow(widget)) + shadow = FALSE; + + wbkgd(widget->window, COLOR_PAIR(GNT_COLOR_NORMAL)); + werase(widget->window); + + if (!(GNT_WIDGET_FLAGS(widget) & GNT_WIDGET_NO_BORDER)) + { + /* - This is ugly. */ + /* - What's your point? */ + mvwvline(widget->window, 0, 0, ACS_VLINE | COLOR_PAIR(GNT_COLOR_NORMAL), widget->priv.height); + mvwvline(widget->window, 0, widget->priv.width - 1, + ACS_VLINE | COLOR_PAIR(GNT_COLOR_NORMAL), widget->priv.height); + mvwhline(widget->window, widget->priv.height - 1, 0, + ACS_HLINE | COLOR_PAIR(GNT_COLOR_NORMAL), widget->priv.width); + mvwhline(widget->window, 0, 0, ACS_HLINE | COLOR_PAIR(GNT_COLOR_NORMAL), widget->priv.width); + mvwaddch(widget->window, 0, 0, ACS_ULCORNER | COLOR_PAIR(GNT_COLOR_NORMAL)); + mvwaddch(widget->window, 0, widget->priv.width - 1, + ACS_URCORNER | COLOR_PAIR(GNT_COLOR_NORMAL)); + mvwaddch(widget->window, widget->priv.height - 1, 0, + ACS_LLCORNER | COLOR_PAIR(GNT_COLOR_NORMAL)); + mvwaddch(widget->window, widget->priv.height - 1, widget->priv.width - 1, + ACS_LRCORNER | COLOR_PAIR(GNT_COLOR_NORMAL)); + } + + if (shadow) + { + wbkgdset(widget->window, '\0' | COLOR_PAIR(GNT_COLOR_SHADOW)); + mvwvline(widget->window, 1, widget->priv.width, ' ', widget->priv.height); + mvwhline(widget->window, widget->priv.height, 1, ' ', widget->priv.width); + } +} + +gboolean +gnt_widget_set_size(GntWidget *widget, int width, int height) +{ + gboolean ret = TRUE; + + if (gnt_widget_has_shadow(widget)) + { + width--; + height--; + } + if (width <= 0) + width = widget->priv.width; + if (height <= 0) + height = widget->priv.height; + + if (GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_MAPPED)) + { + ret = gnt_widget_confirm_size(widget, width, height); + } + + if (ret) + { + gboolean shadow = TRUE; + int oldw, oldh; + + if (!gnt_widget_has_shadow(widget)) + shadow = FALSE; + + oldw = widget->priv.width; + oldh = widget->priv.height; + + widget->priv.width = width; + widget->priv.height = height; + + g_signal_emit(widget, signals[SIG_SIZE_CHANGED], 0, oldw, oldh); + + if (widget->window) + { + wresize(widget->window, height + shadow, width + shadow); + init_widget(widget); + } + if (GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_MAPPED)) + init_widget(widget); + else + GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_MAPPED); + } + + return ret; +} + +gboolean +gnt_widget_set_focus(GntWidget *widget, gboolean set) +{ + if (!(GNT_WIDGET_FLAGS(widget) & GNT_WIDGET_CAN_TAKE_FOCUS)) + return FALSE; + + if (set && !GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_HAS_FOCUS)) + { + GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_HAS_FOCUS); + g_signal_emit(widget, signals[SIG_GIVE_FOCUS], 0); + } + else if (!set) + { + GNT_WIDGET_UNSET_FLAGS(widget, GNT_WIDGET_HAS_FOCUS); + g_signal_emit(widget, signals[SIG_LOST_FOCUS], 0); + } + else + return FALSE; + + return TRUE; +} + +void gnt_widget_set_name(GntWidget *widget, const char *name) +{ + g_free(widget->priv.name); + widget->priv.name = g_strdup(name); +} + +const char *gnt_widget_get_name(GntWidget *widget) +{ + return widget->priv.name; +} + +void gnt_widget_activate(GntWidget *widget) +{ + g_signal_emit(widget, signals[SIG_ACTIVATE], 0); +} + +static gboolean +update_queue_callback(gpointer data) +{ + GntWidget *widget = GNT_WIDGET(data); + + if (!g_object_get_data(G_OBJECT(widget), "gnt:queue_update")) + return FALSE; + gnt_screen_update(widget); + g_object_set_data(G_OBJECT(widget), "gnt:queue_update", NULL); + return FALSE; +} + +void gnt_widget_queue_update(GntWidget *widget) +{ + if (widget->window == NULL) + return; + while (widget->parent) + widget = widget->parent; + + if (!g_object_get_data(G_OBJECT(widget), "gnt:queue_update")) + { + int id = g_timeout_add(0, update_queue_callback, widget); + g_object_set_data_full(G_OBJECT(widget), "gnt:queue_update", GINT_TO_POINTER(id), + (GDestroyNotify)g_source_remove); + } +} + +gboolean gnt_widget_confirm_size(GntWidget *widget, int width, int height) +{ + gboolean ret = FALSE; + g_signal_emit(widget, signals[SIG_CONFIRM_SIZE], 0, width, height, &ret); + return ret; +} + +void gnt_widget_set_visible(GntWidget *widget, gboolean set) +{ + if (set) + GNT_WIDGET_UNSET_FLAGS(widget, GNT_WIDGET_INVISIBLE); + else + GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_INVISIBLE); +} + +gboolean gnt_widget_has_shadow(GntWidget *widget) +{ + return (!GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_NO_SHADOW) && + gnt_style_get_bool(GNT_STYLE_SHADOW, FALSE)); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntwidget.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,152 @@ +#ifndef GNT_WIDGET_H +#define GNT_WIDGET_H + +#include <stdio.h> +#include <glib.h> +#include <ncurses.h> + +#include "gntbindable.h" + +#define GNT_TYPE_WIDGET (gnt_widget_get_gtype()) +#define GNT_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_WIDGET, GntWidget)) +#define GNT_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_WIDGET, GntWidgetClass)) +#define GNT_IS_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_WIDGET)) +#define GNT_IS_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GNT_TYPE_WIDGET)) +#define GNT_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GNT_TYPE_WIDGET, GntWidgetClass)) + +#define GNT_WIDGET_FLAGS(obj) (GNT_WIDGET(obj)->priv.flags) +#define GNT_WIDGET_SET_FLAGS(obj, flags) (GNT_WIDGET_FLAGS(obj) |= flags) +#define GNT_WIDGET_UNSET_FLAGS(obj, flags) (GNT_WIDGET_FLAGS(obj) &= ~(flags)) +#define GNT_WIDGET_IS_FLAG_SET(obj, flags) (GNT_WIDGET_FLAGS(obj) & (flags)) + +typedef struct _GnWidget GntWidget; +typedef struct _GnWidgetPriv GntWidgetPriv; +typedef struct _GnWidgetClass GntWidgetClass; + +typedef enum _GnWidgetFlags +{ + GNT_WIDGET_DESTROYING = 1 << 0, + GNT_WIDGET_CAN_TAKE_FOCUS = 1 << 1, + GNT_WIDGET_MAPPED = 1 << 2, + /* XXX: Need to set the following two as properties, and setup a callback whenever these + * get chnaged. */ + GNT_WIDGET_NO_BORDER = 1 << 3, + GNT_WIDGET_NO_SHADOW = 1 << 4, + GNT_WIDGET_HAS_FOCUS = 1 << 5, + GNT_WIDGET_DRAWING = 1 << 6, + GNT_WIDGET_URGENT = 1 << 7, + GNT_WIDGET_GROW_X = 1 << 8, + GNT_WIDGET_GROW_Y = 1 << 9, + GNT_WIDGET_INVISIBLE = 1 << 10, + GNT_WIDGET_TRANSIENT = 1 << 11, +} GntWidgetFlags; + +/* XXX: This will probably move elsewhere */ +typedef enum _GnMouseEvent +{ + GNT_LEFT_MOUSE_DOWN = 1, + GNT_RIGHT_MOUSE_DOWN, + GNT_MIDDLE_MOUSE_DOWN, + GNT_MOUSE_UP, + GNT_MOUSE_SCROLL_UP, + GNT_MOUSE_SCROLL_DOWN +} GntMouseEvent; + +/* XXX: I'll have to ask grim what he's using this for in guifications. */ +typedef enum _GnParamFlags +{ + GNT_PARAM_SERIALIZABLE = 1 << G_PARAM_USER_SHIFT +} GntParamFlags; + +struct _GnWidgetPriv +{ + int x, y; + int width, height; + GntWidgetFlags flags; + char *name; + + int minw, minh; /* Minimum size for the widget */ +}; + +struct _GnWidget +{ + GntBindable inherit; + + GntWidget *parent; + + GntWidgetPriv priv; + WINDOW *window; + + void (*gnt_reserved1)(void); + void (*gnt_reserved2)(void); + void (*gnt_reserved3)(void); + void (*gnt_reserved4)(void); +}; + +struct _GnWidgetClass +{ + GntBindableClass parent; + + void (*map)(GntWidget *obj); + void (*show)(GntWidget *obj); /* This will call draw() and take focus (if it can take focus) */ + void (*destroy)(GntWidget *obj); + void (*draw)(GntWidget *obj); /* This will draw the widget */ + void (*hide)(GntWidget *obj); + void (*expose)(GntWidget *widget, int x, int y, int width, int height); + void (*gained_focus)(GntWidget *widget); + void (*lost_focus)(GntWidget *widget); + + void (*size_request)(GntWidget *widget); + gboolean (*confirm_size)(GntWidget *widget, int x, int y); + void (*size_changed)(GntWidget *widget, int w, int h); + void (*set_position)(GntWidget *widget, int x, int y); + gboolean (*key_pressed)(GntWidget *widget, const char *key); + void (*activate)(GntWidget *widget); + gboolean (*clicked)(GntWidget *widget, GntMouseEvent event, int x, int y); + + void (*gnt_reserved1)(void); + void (*gnt_reserved2)(void); + void (*gnt_reserved3)(void); + void (*gnt_reserved4)(void); +}; + +G_BEGIN_DECLS + +GType gnt_widget_get_gtype(void); +void gnt_widget_destroy(GntWidget *widget); +void gnt_widget_show(GntWidget *widget); +void gnt_widget_draw(GntWidget *widget); +void gnt_widget_expose(GntWidget *widget, int x, int y, int width, int height); +void gnt_widget_hide(GntWidget *widget); + +void gnt_widget_get_position(GntWidget *widget, int *x, int *y); +void gnt_widget_set_position(GntWidget *widget, int x, int y); +void gnt_widget_size_request(GntWidget *widget); +void gnt_widget_get_size(GntWidget *widget, int *width, int *height); +gboolean gnt_widget_set_size(GntWidget *widget, int width, int height); +gboolean gnt_widget_confirm_size(GntWidget *widget, int width, int height); + +gboolean gnt_widget_key_pressed(GntWidget *widget, const char *keys); + +gboolean gnt_widget_clicked(GntWidget *widget, GntMouseEvent event, int x, int y); + +gboolean gnt_widget_set_focus(GntWidget *widget, gboolean set); +void gnt_widget_activate(GntWidget *widget); + +void gnt_widget_set_name(GntWidget *widget, const char *name); + +const char *gnt_widget_get_name(GntWidget *widget); + +/* Widget-subclasses should call this from the draw-callback. + * Applications should just call gnt_widget_draw instead of this. */ +void gnt_widget_queue_update(GntWidget *widget); + +void gnt_widget_set_take_focus(GntWidget *widget, gboolean set); + +void gnt_widget_set_visible(GntWidget *widget, gboolean set); + +gboolean gnt_widget_has_shadow(GntWidget *widget); + +G_END_DECLS + +#endif /* GNT_WIDGET_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntwindow.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,118 @@ +#include "gntstyle.h" +#include "gntwindow.h" + +#include <string.h> + +enum +{ + SIGS = 1, +}; + +static GntBoxClass *parent_class = NULL; + +static void (*org_destroy)(GntWidget *widget); + +static gboolean +show_menu(GntBindable *bind, GList *null) +{ + GntWindow *win = GNT_WINDOW(bind); + if (win->menu) { + gnt_screen_menu_show(win->menu); + return TRUE; + } + return FALSE; +} + +static void +gnt_window_destroy(GntWidget *widget) +{ + GntWindow *window = GNT_WINDOW(widget); + if (window->menu) + gnt_widget_destroy(GNT_WIDGET(window->menu)); + org_destroy(widget); +} + +static void +gnt_window_class_init(GntWindowClass *klass) +{ + GntBindableClass *bindable = GNT_BINDABLE_CLASS(klass); + GntWidgetClass *wid_class = GNT_WIDGET_CLASS(klass); + parent_class = GNT_BOX_CLASS(klass); + + org_destroy = wid_class->destroy; + wid_class->destroy = gnt_window_destroy; + + gnt_bindable_class_register_action(bindable, "show-menu", show_menu, + GNT_KEY_CTRL_O, NULL); + gnt_bindable_register_binding(bindable, "show-menu", GNT_KEY_F10, NULL); + gnt_style_read_actions(G_OBJECT_CLASS_TYPE(klass), bindable); + + GNTDEBUG; +} + +static void +gnt_window_init(GTypeInstance *instance, gpointer class) +{ + GntWidget *widget = GNT_WIDGET(instance); + GNT_WIDGET_UNSET_FLAGS(widget, GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW); + GNT_WIDGET_SET_FLAGS(widget, GNT_WIDGET_CAN_TAKE_FOCUS); + GNTDEBUG; +} + +/****************************************************************************** + * GntWindow API + *****************************************************************************/ +GType +gnt_window_get_gtype(void) +{ + static GType type = 0; + + if(type == 0) + { + static const GTypeInfo info = { + sizeof(GntWindowClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)gnt_window_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof(GntWindow), + 0, /* n_preallocs */ + gnt_window_init, /* instance_init */ + }; + + type = g_type_register_static(GNT_TYPE_BOX, + "GntWindow", + &info, 0); + } + + return type; +} + +GntWidget *gnt_window_new() +{ + GntWidget *widget = g_object_new(GNT_TYPE_WINDOW, NULL); + + return widget; +} + +GntWidget *gnt_window_box_new(gboolean homo, gboolean vert) +{ + GntWidget *wid = gnt_window_new(); + GntBox *box = GNT_BOX(wid); + + box->homogeneous = homo; + box->vertical = vert; + box->alignment = vert ? GNT_ALIGN_LEFT : GNT_ALIGN_MID; + + return wid; +} + +void gnt_window_set_menu(GntWindow *window, GntMenu *menu) +{ + /* If a menu already existed, then destroy that first. */ + if (window->menu) + gnt_widget_destroy(GNT_WIDGET(window->menu)); + window->menu = menu; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntwindow.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,56 @@ +#ifndef GNT_WINDOW_H +#define GNT_WINDOW_H + +#include "gnt.h" +#include "gntbox.h" +#include "gntcolors.h" +#include "gntkeys.h" +#include "gntmenu.h" + +#define GNT_TYPE_WINDOW (gnt_window_get_gtype()) +#define GNT_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_WINDOW, GntWindow)) +#define GNT_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_WINDOW, GntWindowClass)) +#define GNT_IS_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_WINDOW)) +#define GNT_IS_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GNT_TYPE_WINDOW)) +#define GNT_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GNT_TYPE_WINDOW, GntWindowClass)) + +#define GNT_WINDOW_FLAGS(obj) (GNT_WINDOW(obj)->priv.flags) +#define GNT_WINDOW_SET_FLAGS(obj, flags) (GNT_WINDOW_FLAGS(obj) |= flags) +#define GNT_WINDOW_UNSET_FLAGS(obj, flags) (GNT_WINDOW_FLAGS(obj) &= ~(flags)) + +typedef struct _GnWindow GntWindow; +typedef struct _GnWindowPriv GntWindowPriv; +typedef struct _GnWindowClass GntWindowClass; + +struct _GnWindow +{ + GntBox parent; + GntMenu *menu; +}; + +struct _GnWindowClass +{ + GntBoxClass parent; + + void (*gnt_reserved1)(void); + void (*gnt_reserved2)(void); + void (*gnt_reserved3)(void); + void (*gnt_reserved4)(void); +}; + +G_BEGIN_DECLS + +GType gnt_window_get_gtype(void); + +#define gnt_vwindow_new(homo) gnt_window_box_new(homo, TRUE) +#define gnt_hwindow_new(homo) gnt_window_box_new(homo, FALSE) + +GntWidget *gnt_window_new(); + +GntWidget *gnt_window_box_new(gboolean homo, gboolean vert); + +void gnt_window_set_menu(GntWindow *window, GntMenu *menu); + +G_END_DECLS + +#endif /* GNT_WINDOW_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntwm.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,1322 @@ +#define _GNU_SOURCE +#if defined(__APPLE__) +#define _XOPEN_SOURCE_EXTENDED +#endif + +#include "config.h" + +#include "gntwm.h" +#include "gntstyle.h" +#include "gntmarshal.h" +#include "gnt.h" +#include "gntbox.h" +#include "gntmenu.h" +#include "gnttextview.h" +#include "gnttree.h" +#include "gntutils.h" + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +enum +{ + SIG_NEW_WIN, + SIG_DECORATE_WIN, + SIG_CLOSE_WIN, + SIG_CONFIRM_RESIZE, + SIG_RESIZED, + SIG_CONFIRM_MOVE, + SIG_MOVED, + SIG_UPDATE_WIN, + SIG_GIVE_FOCUS, + SIG_KEY_PRESS, + SIG_MOUSE_CLICK, + SIGS +}; + +static guint signals[SIGS] = { 0 }; +static void gnt_wm_new_window_real(GntWM *wm, GntWidget *widget); +static void gnt_wm_win_resized(GntWM *wm, GntNode *node); +static void gnt_wm_win_moved(GntWM *wm, GntNode *node); +static void gnt_wm_give_focus(GntWM *wm, GntWidget *widget); +static void update_window_in_list(GntWM *wm, GntWidget *wid); + +static gboolean write_already(gpointer data); +static int write_timeout; + +static GList * +g_list_bring_to_front(GList *list, gpointer data) +{ + list = g_list_remove(list, data); + list = g_list_prepend(list, data); + return list; +} + +static void +free_node(gpointer data) +{ + GntNode *node = data; + hide_panel(node->panel); + del_panel(node->panel); + g_free(node); +} + +static void +draw_taskbar(GntWM *wm, gboolean reposition) +{ + static WINDOW *taskbar = NULL; + GList *iter; + int n, width = 0; + int i; + + if (taskbar == NULL) { + taskbar = newwin(1, getmaxx(stdscr), getmaxy(stdscr) - 1, 0); + } else if (reposition) { + int Y_MAX = getmaxy(stdscr) - 1; + mvwin(taskbar, Y_MAX, 0); + } + + wbkgdset(taskbar, '\0' | COLOR_PAIR(GNT_COLOR_NORMAL)); + werase(taskbar); + + n = g_list_length(wm->list); + if (n) + width = getmaxx(stdscr) / n; + + for (i = 0, iter = wm->list; iter; iter = iter->next, i++) + { + GntWidget *w = iter->data; + int color; + const char *title; + + if (w == wm->ordered->data) { + /* This is the current window in focus */ + color = GNT_COLOR_TITLE; + } else if (GNT_WIDGET_IS_FLAG_SET(w, GNT_WIDGET_URGENT)) { + /* This is a window with the URGENT hint set */ + color = GNT_COLOR_URGENT; + } else { + color = GNT_COLOR_NORMAL; + } + wbkgdset(taskbar, '\0' | COLOR_PAIR(color)); + if (iter->next) + mvwhline(taskbar, 0, width * i, ' ' | COLOR_PAIR(color), width); + else + mvwhline(taskbar, 0, width * i, ' ' | COLOR_PAIR(color), getmaxx(stdscr) - width * i); + title = GNT_BOX(w)->title; + mvwprintw(taskbar, 0, width * i, "%s", title ? title : "<gnt>"); + if (i) + mvwaddch(taskbar, 0, width *i - 1, ACS_VLINE | A_STANDOUT | COLOR_PAIR(GNT_COLOR_NORMAL)); + + update_window_in_list(wm, w); + } + + wrefresh(taskbar); +} +static gboolean +update_screen(GntWM *wm) +{ + if (wm->menu) { + GntMenu *top = wm->menu; + while (top) { + GntNode *node = g_hash_table_lookup(wm->nodes, top); + if (node) + top_panel(node->panel); + top = top->submenu; + } + } + update_panels(); + doupdate(); + return TRUE; +} + +static gboolean +sanitize_position(GntWidget *widget, int *x, int *y) +{ + int X_MAX = getmaxx(stdscr); + int Y_MAX = getmaxy(stdscr) - 1; + int w, h; + int nx, ny; + gboolean changed = FALSE; + + gnt_widget_get_size(widget, &w, &h); + if (x) { + if (*x + w > X_MAX) { + nx = MAX(0, X_MAX - w); + if (nx != *x) { + *x = nx; + changed = TRUE; + } + } + } + if (y) { + if (*y + h > Y_MAX) { + ny = MAX(0, Y_MAX - h); + if (ny != *y) { + *y = ny; + changed = TRUE; + } + } + } + return changed; +} + +static void +refresh_node(GntWidget *widget, GntNode *node, gpointer null) +{ + int x, y, w, h; + int nw, nh; + + int X_MAX = getmaxx(stdscr); + int Y_MAX = getmaxy(stdscr) - 1; + + gnt_widget_get_position(widget, &x, &y); + gnt_widget_get_size(widget, &w, &h); + + if (sanitize_position(widget, &x, &y)) + gnt_screen_move_widget(widget, x, y); + + nw = MIN(w, X_MAX); + nh = MIN(h, Y_MAX); + if (nw != w || nh != h) + gnt_screen_resize_widget(widget, nw, nh); +} + +static void +read_window_positions(GntWM *wm) +{ +#if GLIB_CHECK_VERSION(2,6,0) + GKeyFile *gfile = g_key_file_new(); + char *filename = g_build_filename(g_get_home_dir(), ".gntpositions", NULL); + GError *error = NULL; + char **keys; + gsize nk; + + if (!g_key_file_load_from_file(gfile, filename, G_KEY_FILE_NONE, &error)) { + g_printerr("GntWM: %s\n", error->message); + g_error_free(error); + g_free(filename); + return; + } + + keys = g_key_file_get_keys(gfile, "positions", &nk, &error); + if (error) { + g_printerr("GntWM: %s\n", error->message); + g_error_free(error); + error = NULL; + } else { + while (nk--) { + char *title = keys[nk]; + gsize l; + char **coords = g_key_file_get_string_list(gfile, "positions", title, &l, NULL); + if (l == 2) { + int x = atoi(coords[0]); + int y = atoi(coords[1]); + GntPosition *p = g_new0(GntPosition, 1); + p->x = x; + p->y = y; + g_hash_table_replace(wm->positions, g_strdup(title + 1), p); + } else { + g_printerr("GntWM: Invalid number of arguments for positioing a window.\n"); + } + g_strfreev(coords); + } + g_strfreev(keys); + } + + g_free(filename); +#endif +} + +static void +gnt_wm_init(GTypeInstance *instance, gpointer class) +{ + GntWM *wm = GNT_WM(instance); + wm->list = NULL; + wm->ordered = NULL; + wm->event_stack = FALSE; + wm->windows = NULL; + wm->actions = NULL; + wm->nodes = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_node); + wm->positions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + if (gnt_style_get_bool(GNT_STYLE_REMPOS, TRUE)) + read_window_positions(wm); +} + +static void +switch_window(GntWM *wm, int direction) +{ + GntWidget *w = NULL, *wid = NULL; + int pos; + + if (wm->_list.window || wm->menu) + return; + + if (!wm->ordered || !wm->ordered->next) + return; + + w = wm->ordered->data; + pos = g_list_index(wm->list, w); + pos += direction; + + if (pos < 0) + wid = g_list_last(wm->list)->data; + else if (pos >= g_list_length(wm->list)) + wid = wm->list->data; + else if (pos >= 0) + wid = g_list_nth_data(wm->list, pos); + + wm->ordered = g_list_bring_to_front(wm->ordered, wid); + + gnt_wm_raise_window(wm, wm->ordered->data); + + if (w != wid) { + gnt_widget_set_focus(w, FALSE); + } +} + +static gboolean +window_next(GntBindable *bindable, GList *null) +{ + GntWM *wm = GNT_WM(bindable); + switch_window(wm, 1); + return TRUE; +} + +static gboolean +window_prev(GntBindable *bindable, GList *null) +{ + GntWM *wm = GNT_WM(bindable); + switch_window(wm, -1); + return TRUE; +} + +static gboolean +switch_window_n(GntBindable *bind, GList *list) +{ + GntWM *wm = GNT_WM(bind); + GntWidget *w = NULL; + GList *l; + int n; + + if (!wm->ordered) + return TRUE; + + if (list) + n = GPOINTER_TO_INT(list->data); + else + n = 0; + + w = wm->ordered->data; + + if ((l = g_list_nth(wm->list, n)) != NULL) + { + gnt_wm_raise_window(wm, l->data); + } + + if (l && w != l->data) + { + gnt_widget_set_focus(w, FALSE); + } + return TRUE; +} +static gboolean +window_close(GntBindable *bindable, GList *null) +{ + GntWM *wm = GNT_WM(bindable); + + if (wm->_list.window) + return TRUE; + + if (wm->ordered) { + gnt_widget_destroy(wm->ordered->data); + } + + return TRUE; +} + +static void +destroy__list(GntWidget *widget, GntWM *wm) +{ + wm->_list.window = NULL; + wm->_list.tree = NULL; + wm->windows = NULL; + wm->actions = NULL; + update_screen(wm); +} + +static void +setup__list(GntWM *wm) +{ + GntWidget *tree, *win; + win = wm->_list.window = gnt_box_new(FALSE, FALSE); + gnt_box_set_toplevel(GNT_BOX(win), TRUE); + gnt_box_set_pad(GNT_BOX(win), 0); + GNT_WIDGET_SET_FLAGS(win, GNT_WIDGET_TRANSIENT); + + tree = wm->_list.tree = gnt_tree_new(); + gnt_box_add_widget(GNT_BOX(win), tree); + + g_signal_connect(G_OBJECT(win), "destroy", G_CALLBACK(destroy__list), wm); +} + +static void +window_list_activate(GntTree *tree, GntWM *wm) +{ + GntWidget *widget = gnt_tree_get_selection_data(GNT_TREE(tree)); + + if (!wm->ordered || !widget) + return; + + gnt_widget_destroy(wm->_list.window); + gnt_wm_raise_window(wm, widget); +} + +static gboolean +window_list(GntBindable *bindable, GList *null) +{ + GntWM *wm = GNT_WM(bindable); + GntWidget *tree, *win; + GList *iter; + + if (wm->_list.window || wm->menu) + return TRUE; + + if (!wm->ordered) + return TRUE; + + setup__list(wm); + wm->windows = &wm->_list; + + win = wm->windows->window; + tree = wm->windows->tree; + + gnt_box_set_title(GNT_BOX(win), "Window List"); + + for (iter = wm->list; iter; iter = iter->next) { + GntBox *box = GNT_BOX(iter->data); + + gnt_tree_add_row_last(GNT_TREE(tree), box, + gnt_tree_create_row(GNT_TREE(tree), box->title), NULL); + update_window_in_list(wm, GNT_WIDGET(box)); + } + + gnt_tree_set_selected(GNT_TREE(tree), wm->ordered->data); + g_signal_connect(G_OBJECT(tree), "activate", G_CALLBACK(window_list_activate), wm); + + gnt_tree_set_col_width(GNT_TREE(tree), 0, getmaxx(stdscr) / 3); + gnt_widget_set_size(tree, 0, getmaxy(stdscr) / 2); + gnt_widget_set_position(win, getmaxx(stdscr) / 3, getmaxy(stdscr) / 4); + + gnt_widget_show(win); + return TRUE; +} + +static gboolean +dump_screen(GntBindable *bindable, GList *null) +{ + int x, y; + chtype old = 0, now = 0; + FILE *file = fopen("dump.html", "w"); + + fprintf(file, "<pre>"); + for (y = 0; y < getmaxy(stdscr); y++) { + for (x = 0; x < getmaxx(stdscr); x++) { + char ch; + now = mvwinch(curscr, y, x); + ch = now & A_CHARTEXT; + now ^= ch; + +#define CHECK(attr, start, end) \ + do \ + { \ + if (now & attr) \ + { \ + if (!(old & attr)) \ + fprintf(file, "%s", start); \ + } \ + else if (old & attr) \ + { \ + fprintf(file, "%s", end); \ + } \ + } while (0) + + CHECK(A_BOLD, "<b>", "</b>"); + CHECK(A_UNDERLINE, "<u>", "</u>"); + CHECK(A_BLINK, "<blink>", "</blink>"); + + if ((now & A_COLOR) != (old & A_COLOR) || + (now & A_REVERSE) != (old & A_REVERSE)) + { + int ret; + short fgp, bgp, r, g, b; + struct + { + int r, g, b; + } fg, bg; + + ret = pair_content(PAIR_NUMBER(now & A_COLOR), &fgp, &bgp); + if (fgp == -1) + fgp = COLOR_BLACK; + if (bgp == -1) + bgp = COLOR_WHITE; + if (now & A_REVERSE) + fgp ^= bgp ^= fgp ^= bgp; /* *wink* */ + ret = color_content(fgp, &r, &g, &b); + fg.r = r; fg.b = b; fg.g = g; + ret = color_content(bgp, &r, &g, &b); + bg.r = r; bg.b = b; bg.g = g; +#define ADJUST(x) (x = x * 255 / 1000) + ADJUST(fg.r); + ADJUST(fg.g); + ADJUST(fg.b); + ADJUST(bg.r); + ADJUST(bg.b); + ADJUST(bg.g); + + if (x) fprintf(file, "</span>"); + fprintf(file, "<span style=\"background:#%02x%02x%02x;color:#%02x%02x%02x\">", + bg.r, bg.g, bg.b, fg.r, fg.g, fg.b); + } + if (now & A_ALTCHARSET) + { + switch (ch) + { + case 'q': + ch = '-'; break; + case 't': + case 'u': + case 'x': + ch = '|'; break; + case 'v': + case 'w': + case 'l': + case 'm': + case 'k': + case 'j': + case 'n': + ch = '+'; break; + case '-': + ch = '^'; break; + case '.': + ch = 'v'; break; + case 'a': + ch = '#'; break; + default: + ch = ' '; break; + } + } + if (ch == '&') + fprintf(file, "&"); + else if (ch == '<') + fprintf(file, "<"); + else if (ch == '>') + fprintf(file, ">"); + else + fprintf(file, "%c", ch); + old = now; + } + fprintf(file, "</span>\n"); + old = 0; + } + fprintf(file, "</pre>"); + fclose(file); + return TRUE; +} + +static void +shift_window(GntWM *wm, GntWidget *widget, int dir) +{ + GList *all = wm->list; + GList *list = g_list_find(all, widget); + int length, pos; + if (!list) + return; + + length = g_list_length(all); + pos = g_list_position(all, list); + + pos += dir; + if (dir > 0) + pos++; + + if (pos < 0) + pos = length; + else if (pos > length) + pos = 0; + + all = g_list_insert(all, widget, pos); + all = g_list_delete_link(all, list); + wm->list = all; + draw_taskbar(wm, FALSE); +} + +static gboolean +shift_left(GntBindable *bindable, GList *null) +{ + GntWM *wm = GNT_WM(bindable); + if (wm->_list.window) + return TRUE; + + shift_window(wm, wm->ordered->data, -1); + return TRUE; +} + +static gboolean +shift_right(GntBindable *bindable, GList *null) +{ + GntWM *wm = GNT_WM(bindable); + if (wm->_list.window) + return TRUE; + + shift_window(wm, wm->ordered->data, 1); + return TRUE; +} + +static void +action_list_activate(GntTree *tree, GntWM *wm) +{ + GntAction *action = gnt_tree_get_selection_data(tree); + action->callback(); + gnt_widget_destroy(wm->_list.window); +} + +static int +compare_action(gconstpointer p1, gconstpointer p2) +{ + const GntAction *a1 = p1; + const GntAction *a2 = p2; + + return g_utf8_collate(a1->label, a2->label); +} + +static gboolean +list_actions(GntBindable *bindable, GList *null) +{ + GntWidget *tree, *win; + GList *iter; + GntWM *wm = GNT_WM(bindable); + if (wm->_list.window || wm->menu) + return TRUE; + + if (wm->acts == NULL) + return TRUE; + + setup__list(wm); + wm->actions = &wm->_list; + + win = wm->actions->window; + tree = wm->actions->tree; + + gnt_box_set_title(GNT_BOX(win), "Actions"); + GNT_WIDGET_SET_FLAGS(tree, GNT_WIDGET_NO_BORDER); + /* XXX: Do we really want this? */ + gnt_tree_set_compare_func(GNT_TREE(tree), compare_action); + + for (iter = wm->acts; iter; iter = iter->next) { + GntAction *action = iter->data; + gnt_tree_add_row_last(GNT_TREE(tree), action, + gnt_tree_create_row(GNT_TREE(tree), action->label), NULL); + } + g_signal_connect(G_OBJECT(tree), "activate", G_CALLBACK(action_list_activate), wm); + gnt_widget_set_size(tree, 0, g_list_length(wm->acts)); + gnt_widget_set_position(win, 0, getmaxy(stdscr) - 3 - g_list_length(wm->acts)); + + gnt_widget_show(win); + return TRUE; +} + +#ifndef NO_WIDECHAR +static int +widestringwidth(wchar_t *wide) +{ + int len, ret; + char *string; + + len = wcstombs(NULL, wide, 0) + 1; + string = g_new0(char, len); + wcstombs(string, wide, len); + ret = gnt_util_onscreen_width(string, NULL); + g_free(string); + return ret; +} +#endif + +/* Returns the onscreen width of the character at the position */ +static int +reverse_char(WINDOW *d, int y, int x, gboolean set) +{ +#define DECIDE(ch) (set ? ((ch) | A_REVERSE) : ((ch) & ~A_REVERSE)) + +#ifdef NO_WIDECHAR + chtype ch; + ch = mvwinch(d, y, x); + mvwaddch(d, y, x, DECIDE(ch)); + return 1; +#else + cchar_t ch; + int wc = 1; + if (mvwin_wch(d, y, x, &ch) == OK) { + wc = widestringwidth(ch.chars); + ch.attr = DECIDE(ch.attr); + ch.attr &= WA_ATTRIBUTES; /* XXX: This is a workaround for a bug */ + mvwadd_wch(d, y, x, &ch); + } + + return wc; +#endif +} + +static void +window_reverse(GntWidget *win, gboolean set) +{ + int i; + int w, h; + WINDOW *d; + + if (GNT_WIDGET_IS_FLAG_SET(win, GNT_WIDGET_NO_BORDER)) + return; + + d = win->window; + gnt_widget_get_size(win, &w, &h); + + if (gnt_widget_has_shadow(win)) { + --w; + --h; + } + + /* the top and bottom */ + for (i = 0; i < w; i += reverse_char(d, 0, i, set)); + for (i = 0; i < w; i += reverse_char(d, h-1, i, set)); + + /* the left and right */ + for (i = 0; i < h; i += reverse_char(d, i, 0, set)); + for (i = 0; i < h; i += reverse_char(d, i, w-1, set)); + + wrefresh(win->window); +} + +static gboolean +start_move(GntBindable *bindable, GList *null) +{ + GntWM *wm = GNT_WM(bindable); + if (wm->_list.window || wm->menu) + return TRUE; + if (!wm->ordered) + return TRUE; + + wm->mode = GNT_KP_MODE_MOVE; + window_reverse(GNT_WIDGET(wm->ordered->data), TRUE); + + return TRUE; +} + +static gboolean +start_resize(GntBindable *bindable, GList *null) +{ + GntWM *wm = GNT_WM(bindable); + if (wm->_list.window || wm->menu) + return TRUE; + if (!wm->ordered) + return TRUE; + + wm->mode = GNT_KP_MODE_RESIZE; + window_reverse(GNT_WIDGET(wm->ordered->data), TRUE); + + return TRUE; +} + +static gboolean +wm_quit(GntBindable *bindable, GList *list) +{ + GntWM *wm = GNT_WM(bindable); + if (write_timeout) + write_already(wm); + g_main_loop_quit(wm->loop); + return TRUE; +} + +static gboolean +return_true(GntWM *wm, GntWidget *w, int *a, int *b) +{ + return TRUE; +} + +static gboolean +refresh_screen(GntBindable *bindable, GList *null) +{ + GntWM *wm = GNT_WM(bindable); + + endwin(); + refresh(); + curs_set(0); /* endwin resets the cursor to normal */ + + g_hash_table_foreach(wm->nodes, (GHFunc)refresh_node, NULL); + update_screen(wm); + draw_taskbar(wm, TRUE); + + return FALSE; +} + +static void +gnt_wm_class_init(GntWMClass *klass) +{ + klass->new_window = gnt_wm_new_window_real; + klass->decorate_window = NULL; + klass->close_window = NULL; + klass->window_resize_confirm = return_true; + klass->window_resized = gnt_wm_win_resized; + klass->window_move_confirm = return_true; + klass->window_moved = gnt_wm_win_moved; + klass->window_update = NULL; + klass->key_pressed = NULL; + klass->mouse_clicked = NULL; + klass->give_focus = gnt_wm_give_focus; + + signals[SIG_NEW_WIN] = + g_signal_new("new_win", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWMClass, new_window), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + signals[SIG_DECORATE_WIN] = + g_signal_new("decorate_win", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWMClass, decorate_window), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + signals[SIG_CLOSE_WIN] = + g_signal_new("close_win", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWMClass, close_window), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + signals[SIG_CONFIRM_RESIZE] = + g_signal_new("confirm_resize", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWMClass, window_resize_confirm), + gnt_boolean_handled_accumulator, NULL, + gnt_closure_marshal_BOOLEAN__POINTER_POINTER_POINTER, + G_TYPE_BOOLEAN, 3, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER); + + signals[SIG_CONFIRM_MOVE] = + g_signal_new("confirm_move", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWMClass, window_move_confirm), + gnt_boolean_handled_accumulator, NULL, + gnt_closure_marshal_BOOLEAN__POINTER_POINTER_POINTER, + G_TYPE_BOOLEAN, 3, G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_POINTER); + + signals[SIG_RESIZED] = + g_signal_new("window_resized", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWMClass, window_resized), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + signals[SIG_MOVED] = + g_signal_new("window_moved", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWMClass, window_moved), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + signals[SIG_UPDATE_WIN] = + g_signal_new("window_update", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWMClass, window_update), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + + signals[SIG_GIVE_FOCUS] = + g_signal_new("give_focus", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWMClass, give_focus), + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + + signals[SIG_MOUSE_CLICK] = + g_signal_new("mouse_clicked", + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GntWMClass, mouse_clicked), + gnt_boolean_handled_accumulator, NULL, + gnt_closure_marshal_BOOLEAN__INT_INT_INT_POINTER, + G_TYPE_BOOLEAN, 4, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_POINTER); + + gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-next", window_next, + "\033" "n", NULL); + gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-prev", window_prev, + "\033" "p", NULL); + gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-close", window_close, + "\033" "c", NULL); + gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "window-list", window_list, + "\033" "w", NULL); + gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "dump-screen", dump_screen, + "\033" "d", NULL); + gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "shift-left", shift_left, + "\033" ",", NULL); + gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "shift-right", shift_right, + "\033" ".", NULL); + gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "action-list", list_actions, + "\033" "a", NULL); + gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "start-move", start_move, + "\033" "m", NULL); + gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "start-resize", start_resize, + "\033" "r", NULL); + gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "wm-quit", wm_quit, + "\033" "q", NULL); + gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "refresh-screen", refresh_screen, + "\033" "l", NULL); + gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "switch-window-n", switch_window_n, + NULL, NULL); + + gnt_style_read_actions(G_OBJECT_CLASS_TYPE(klass), GNT_BINDABLE_CLASS(klass)); + GNTDEBUG; +} + +/****************************************************************************** + * GntWM API + *****************************************************************************/ +GType +gnt_wm_get_gtype(void) +{ + static GType type = 0; + + if(type == 0) { + static const GTypeInfo info = { + sizeof(GntWMClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)gnt_wm_class_init, + NULL, + NULL, /* class_data */ + sizeof(GntWM), + 0, /* n_preallocs */ + gnt_wm_init, /* instance_init */ + }; + + type = g_type_register_static(GNT_TYPE_BINDABLE, + "GntWM", + &info, 0); + } + + return type; +} +static void +update_window_in_list(GntWM *wm, GntWidget *wid) +{ + GntTextFormatFlags flag = 0; + + if (wm->windows == NULL) + return; + + if (wid == wm->ordered->data) + flag |= GNT_TEXT_FLAG_DIM; + else if (GNT_WIDGET_IS_FLAG_SET(wid, GNT_WIDGET_URGENT)) + flag |= GNT_TEXT_FLAG_BOLD; + + gnt_tree_set_row_flags(GNT_TREE(wm->windows->tree), wid, flag); +} + +static void +gnt_wm_new_window_real(GntWM *wm, GntWidget *widget) +{ + GntNode *node; + gboolean transient = FALSE; + + if (widget->window == NULL) + return; + + node = g_new0(GntNode, 1); + node->me = widget; + + g_hash_table_replace(wm->nodes, widget, node); + + refresh_node(widget, node, NULL); + + transient = !!GNT_WIDGET_IS_FLAG_SET(node->me, GNT_WIDGET_TRANSIENT); + + node->panel = new_panel(node->me->window); + set_panel_userptr(node->panel, node); + + if (!transient) { + if (node->me != wm->_list.window) { + GntWidget *w = NULL; + + if (wm->ordered) + w = wm->ordered->data; + + wm->list = g_list_append(wm->list, widget); + + if (wm->event_stack) + wm->ordered = g_list_prepend(wm->ordered, widget); + else + wm->ordered = g_list_append(wm->ordered, widget); + + gnt_widget_set_focus(widget, TRUE); + if (w) + gnt_widget_set_focus(w, FALSE); + } + + if (wm->event_stack || node->me == wm->_list.window) { + gnt_wm_raise_window(wm, node->me); + } else { + bottom_panel(node->panel); /* New windows should not grab focus */ + gnt_widget_set_urgent(node->me); + } + } +} + +void gnt_wm_new_window(GntWM *wm, GntWidget *widget) +{ + while (widget->parent) + widget = widget->parent; + + if (GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_INVISIBLE) || + g_hash_table_lookup(wm->nodes, widget)) { + update_screen(wm); + return; + } + + if (GNT_IS_BOX(widget)) { + const char *title = GNT_BOX(widget)->title; + GntPosition *p = NULL; + if (title && (p = g_hash_table_lookup(wm->positions, title)) != NULL) { + sanitize_position(widget, &p->x, &p->y); + gnt_widget_set_position(widget, p->x, p->y); + mvwin(widget->window, p->y, p->x); + } + } + + g_signal_emit(wm, signals[SIG_NEW_WIN], 0, widget); + g_signal_emit(wm, signals[SIG_DECORATE_WIN], 0, widget); + + if (wm->windows && !GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_TRANSIENT)) { + if ((GNT_IS_BOX(widget) && GNT_BOX(widget)->title) && wm->_list.window != widget + && GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_CAN_TAKE_FOCUS)) { + gnt_tree_add_row_last(GNT_TREE(wm->windows->tree), widget, + gnt_tree_create_row(GNT_TREE(wm->windows->tree), GNT_BOX(widget)->title), + NULL); + update_window_in_list(wm, widget); + } + } + + update_screen(wm); + draw_taskbar(wm, FALSE); +} + +void gnt_wm_window_decorate(GntWM *wm, GntWidget *widget) +{ + g_signal_emit(wm, signals[SIG_DECORATE_WIN], 0, widget); +} + +void gnt_wm_window_close(GntWM *wm, GntWidget *widget) +{ + GntNode *node; + int pos; + + if ((node = g_hash_table_lookup(wm->nodes, widget)) == NULL) + return; + + g_signal_emit(wm, signals[SIG_CLOSE_WIN], 0, widget); + g_hash_table_remove(wm->nodes, widget); + + if (wm->windows) { + gnt_tree_remove(GNT_TREE(wm->windows->tree), widget); + } + + pos = g_list_index(wm->list, widget); + + if (pos != -1) { + wm->list = g_list_remove(wm->list, widget); + wm->ordered = g_list_remove(wm->ordered, widget); + + if (wm->ordered) + gnt_wm_raise_window(wm, wm->ordered->data); + } + + update_screen(wm); + draw_taskbar(wm, FALSE); +} + +void gnt_wm_process_input(GntWM *wm, const char *keys) +{ + keys = gnt_bindable_remap_keys(GNT_BINDABLE(wm), keys); + + if (gnt_bindable_perform_action_key(GNT_BINDABLE(wm), keys)) + return; + + /* Do some manual checking */ + if (wm->ordered && wm->mode != GNT_KP_MODE_NORMAL) { + int xmin = 0, ymin = 0, xmax = getmaxx(stdscr), ymax = getmaxy(stdscr) - 1; + int x, y, w, h; + GntWidget *widget = GNT_WIDGET(wm->ordered->data); + int ox, oy, ow, oh; + + gnt_widget_get_position(widget, &x, &y); + gnt_widget_get_size(widget, &w, &h); + ox = x; oy = y; + ow = w; oh = h; + + if (wm->mode == GNT_KP_MODE_MOVE) { + if (strcmp(keys, GNT_KEY_LEFT) == 0) { + if (x > xmin) + x--; + } else if (strcmp(keys, GNT_KEY_RIGHT) == 0) { + if (x + w < xmax) + x++; + } else if (strcmp(keys, GNT_KEY_UP) == 0) { + if (y > ymin) + y--; + } else if (strcmp(keys, GNT_KEY_DOWN) == 0) { + if (y + h < ymax) + y++; + } + if (ox != x || oy != y) { + gnt_screen_move_widget(widget, x, y); + window_reverse(widget, TRUE); + return; + } + } else if (wm->mode == GNT_KP_MODE_RESIZE) { + if (strcmp(keys, GNT_KEY_LEFT) == 0) { + w--; + } else if (strcmp(keys, GNT_KEY_RIGHT) == 0) { + if (x + w < xmax) + w++; + } else if (strcmp(keys, GNT_KEY_UP) == 0) { + h--; + } else if (strcmp(keys, GNT_KEY_DOWN) == 0) { + if (y + h < ymax) + h++; + } + if (oh != h || ow != w) { + gnt_screen_resize_widget(widget, w, h); + window_reverse(widget, TRUE); + return; + } + } + if (strcmp(keys, "\r") == 0 || strcmp(keys, "\033") == 0) { + window_reverse(widget, FALSE); + wm->mode = GNT_KP_MODE_NORMAL; + } + return; + } + + wm->event_stack = TRUE; + + /* Escape to close the window-list or action-list window */ + if (strcmp(keys, "\033") == 0) { + if (wm->_list.window) { + gnt_widget_destroy(wm->_list.window); + wm->event_stack = FALSE; + return; + } + } else if (keys[0] == '\033' && isdigit(keys[1]) && keys[2] == '\0') { + /* Alt+x for quick switch */ + int n = *(keys + 1) - '0'; + GList *list = NULL; + + if (n == 0) + n = 10; + + list = g_list_append(list, GINT_TO_POINTER(n - 1)); + switch_window_n(GNT_BINDABLE(wm), list); + g_list_free(list); + return; + } + + if (wm->menu) + gnt_widget_key_pressed(GNT_WIDGET(wm->menu), keys); + else if (wm->_list.window) + gnt_widget_key_pressed(wm->_list.window, keys); + else if (wm->ordered) + gnt_widget_key_pressed(GNT_WIDGET(wm->ordered->data), keys); + wm->event_stack = FALSE; +} + +static void +gnt_wm_win_resized(GntWM *wm, GntNode *node) +{ + refresh_node(node->me, node, NULL); + replace_panel(node->panel, node->me->window); +} + +static void +gnt_wm_win_moved(GntWM *wm, GntNode *node) +{ + refresh_node(node->me, node, NULL); +} + +void gnt_wm_resize_window(GntWM *wm, GntWidget *widget, int width, int height) +{ + gboolean ret = TRUE; + GntNode *node; + + while (widget->parent) + widget = widget->parent; + node = g_hash_table_lookup(wm->nodes, widget); + if (!node) + return; + + g_signal_emit(wm, signals[SIG_CONFIRM_RESIZE], 0, widget, &width, &height, &ret); + if (!ret) + return; /* resize is not permitted */ + hide_panel(node->panel); + gnt_widget_set_size(widget, width, height); + gnt_widget_draw(widget); + + g_signal_emit(wm, signals[SIG_RESIZED], 0, node); + + show_panel(node->panel); + update_screen(wm); +} + +static void +write_gdi(gpointer key, gpointer value, gpointer data) +{ + GntPosition *p = value; + fprintf(data, ".%s = %d;%d\n", key, p->x, p->y); +} + +static gboolean +write_already(gpointer data) +{ + GntWM *wm = data; + FILE *file; + char *filename; + + filename = g_build_filename(g_get_home_dir(), ".gntpositions", NULL); + + file = fopen(filename, "wb"); + if (file == NULL) { + g_printerr("GntWM: error opening file to save positions\n"); + } else { + fprintf(file, "[positions]\n"); + g_hash_table_foreach(wm->positions, write_gdi, file); + fclose(file); + } + + g_free(filename); + g_source_remove(write_timeout); + write_timeout = 0; + return FALSE; +} + +static void +write_positions_to_file(GntWM *wm) +{ + if (write_timeout) { + g_source_remove(write_timeout); + } + write_timeout = g_timeout_add(10000, write_already, wm); +} + +void gnt_wm_move_window(GntWM *wm, GntWidget *widget, int x, int y) +{ + gboolean ret = TRUE; + GntNode *node; + + while (widget->parent) + widget = widget->parent; + node = g_hash_table_lookup(wm->nodes, widget); + if (!node) + return; + + g_signal_emit(wm, signals[SIG_CONFIRM_MOVE], 0, widget, &x, &y, &ret); + if (!ret) + return; /* resize is not permitted */ + + gnt_widget_set_position(widget, x, y); + move_panel(node->panel, y, x); + + g_signal_emit(wm, signals[SIG_MOVED], 0, node); + if (gnt_style_get_bool(GNT_STYLE_REMPOS, TRUE) && GNT_IS_BOX(widget)) { + const char *title = GNT_BOX(widget)->title; + if (title) { + GntPosition *p = g_new0(GntPosition, 1); + GntWidget *wid = node->me; + p->x = wid->priv.x; + p->y = wid->priv.y; + g_hash_table_replace(wm->positions, g_strdup(title), p); + write_positions_to_file(wm); + } + } + + update_screen(wm); +} + +static void +gnt_wm_give_focus(GntWM *wm, GntWidget *widget) +{ + GntNode *node = g_hash_table_lookup(wm->nodes, widget); + + if (!node) + return; + + if (widget != wm->_list.window && !GNT_IS_MENU(widget) && + wm->ordered->data != widget) { + GntWidget *w = wm->ordered->data; + wm->ordered = g_list_bring_to_front(wm->ordered, widget); + gnt_widget_set_focus(w, FALSE); + } + + gnt_widget_set_focus(widget, TRUE); + GNT_WIDGET_UNSET_FLAGS(widget, GNT_WIDGET_URGENT); + gnt_widget_draw(widget); + top_panel(node->panel); + + if (wm->_list.window) { + GntNode *nd = g_hash_table_lookup(wm->nodes, wm->_list.window); + top_panel(nd->panel); + } + update_screen(wm); + draw_taskbar(wm, FALSE); +} + +void gnt_wm_update_window(GntWM *wm, GntWidget *widget) +{ + GntNode *node; + + while (widget->parent) + widget = widget->parent; + if (!GNT_IS_MENU(widget)) + gnt_box_sync_children(GNT_BOX(widget)); + + node = g_hash_table_lookup(wm->nodes, widget); + if (node == NULL) { + gnt_wm_new_window(wm, widget); + } else + g_signal_emit(wm, signals[SIG_UPDATE_WIN], 0, node); + + update_screen(wm); + draw_taskbar(wm, FALSE); +} + +gboolean gnt_wm_process_click(GntWM *wm, GntMouseEvent event, int x, int y, GntWidget *widget) +{ + gboolean ret = TRUE; + g_signal_emit(wm, signals[SIG_MOUSE_CLICK], 0, event, x, y, widget, &ret); + return ret; +} + +void gnt_wm_raise_window(GntWM *wm, GntWidget *widget) +{ + g_signal_emit(wm, signals[SIG_GIVE_FOCUS], 0, widget); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/gntwm.h Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,164 @@ + +#include "gntwidget.h" +#include "gntmenu.h" + +#include <panel.h> + +#define GNT_TYPE_WM (gnt_wm_get_gtype()) +#define GNT_WM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GNT_TYPE_WM, GntWM)) +#define GNT_WM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GNT_TYPE_WM, GntWMClass)) +#define GNT_IS_WM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GNT_TYPE_WM)) +#define GNT_IS_WM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GNT_TYPE_WM)) +#define GNT_WM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GNT_TYPE_WM, GntWMClass)) + +typedef enum +{ + GNT_KP_MODE_NORMAL, + GNT_KP_MODE_RESIZE, + GNT_KP_MODE_MOVE, +} GntKeyPressMode; + +typedef struct +{ + GntWidget *me; + + PANEL *panel; +} GntNode; + +typedef struct _GntWM GntWM; + +typedef struct _GnPosition +{ + int x; + int y; +} GntPosition; + +/** + * An application can register actions which will show up in a 'start-menu' like popup + */ +typedef struct _GnAction +{ + const char *label; + void (*callback)(); +} GntAction; + +struct _GntWM +{ + GntBindable inherit; + + GMainLoop *loop; + + GList *list; /* List of windows ordered on their creation time */ + GList *ordered; /* List of windows ordered on their focus */ + + struct { + GntWidget *window; + GntWidget *tree; + } _list, + *windows, /* Window-list window */ + *actions; /* Action-list window */ + + GHashTable *nodes; /* GntWidget -> GntNode */ + + GList *acts; /* List of actions */ + + /** + * There can be at most one menu at a time on the screen. + * If there is a menu being displayed, then all the keystrokes will be sent to + * the menu until it is closed, either when the user activates a menuitem, or + * presses Escape to cancel the menu. + */ + GntMenu *menu; /* Currently active menu */ + + /** + * 'event_stack' will be set to TRUE when a user-event, ie. a mouse-click + * or a key-press is being processed. This variable will be used to determine + * whether to give focus to a new window. + */ + gboolean event_stack; + + GntKeyPressMode mode; + + GHashTable *positions; + + void *res1; + void *res2; + void *res3; + void *res4; +}; + +typedef struct _GnWMClass GntWMClass; + +struct _GnWMClass +{ + GntBindableClass parent; + + /* This is called when a new window is shown */ + void (*new_window)(GntWM *wm, GntWidget *win); + + void (*decorate_window)(GntWM *wm, GntWidget *win); + /* This is called when a window is being closed */ + gboolean (*close_window)(GntWM *wm, GntWidget *win); + + /* The WM may want to confirm a size for a window first */ + gboolean (*window_resize_confirm)(GntWM *wm, GntWidget *win, int *w, int *h); + + void (*window_resized)(GntWM *wm, GntNode *node); + + /* The WM may want to confirm the position of a window */ + gboolean (*window_move_confirm)(GntWM *wm, GntWidget *win, int *x, int *y); + + void (*window_moved)(GntWM *wm, GntNode *node); + + /* This gets called when: + * - the title of the window changes + * - the 'urgency' of the window changes + */ + void (*window_update)(GntWM *wm, GntNode *node); + + /* This should usually return NULL if the keys were processed by the WM. + * If not, the WM can simply return the original string, which will be + * processed by the default WM. The custom WM can also return a different + * static string for the default WM to process. + */ + gboolean (*key_pressed)(GntWM *wm, const char *key); + + gboolean (*mouse_clicked)(GntWM *wm, GntMouseEvent event, int x, int y, GntWidget *widget); + + /* Whatever the WM wants to do when a window is given focus */ + void (*give_focus)(GntWM *wm, GntWidget *widget); + + /* List of windows. Although the WM can keep a list of its own for the windows, + * it'd be better if there was a way to share between the 'core' and the WM. + */ + /*const GList *(*window_list)();*/ + + void (*res1)(void); + void (*res2)(void); + void (*res3)(void); + void (*res4)(void); +}; + +G_BEGIN_DECLS + +GType gnt_wm_get_gtype(void); + +void gnt_wm_new_window(GntWM *wm, GntWidget *widget); + +void gnt_wm_window_decorate(GntWM *wm, GntWidget *widget); + +void gnt_wm_window_close(GntWM *wm, GntWidget *widget); + +void gnt_wm_process_input(GntWM *wm, const char *string); + +gboolean gnt_wm_process_click(GntWM *wm, GntMouseEvent event, int x, int y, GntWidget *widget); + +void gnt_wm_resize_window(GntWM *wm, GntWidget *widget, int width, int height); + +void gnt_wm_move_window(GntWM *wm, GntWidget *widget, int x, int y); + +void gnt_wm_update_window(GntWM *wm, GntWidget *widget); + +void gnt_wm_raise_window(GntWM *wm, GntWidget *widget); + +G_END_DECLS
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/test.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,125 @@ +#include "gntbutton.h" +#include "gnt.h" +#include "gntkeys.h" +#include "gnttree.h" +#include "gntbox.h" + +static gboolean +key_pressed(GntWidget *widget, const char *text, gpointer null) +{ + GntWidget *w = null; + GntWidget *box = gnt_box_new(FALSE, FALSE); + GntWidget *label = gnt_label_new("so wassup!!"); + + gnt_box_add_widget(GNT_BOX(box), label); + GNT_WIDGET_UNSET_FLAGS(box, GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW); + gnt_box_set_title(GNT_BOX(box), "This is a test"); + + gnt_widget_show(box); +#if 0 + + gnt_widget_set_focus(w, TRUE); + + /* XXX: This is to just test stuff */ + if (text[0] == 27) + { + if (strcmp(text+1, GNT_KEY_LEFT) == 0 && w->priv.x) + (w->priv.x)--; + else if (strcmp(text+1, GNT_KEY_RIGHT) == 0) + (w->priv.x)++; + else if (strcmp(text+1, GNT_KEY_UP) == 0 && w->priv.y) + (w->priv.y)--; + else if (strcmp(text+1, GNT_KEY_DOWN) == 0) + (w->priv.y)++; + } + + gnt_widget_draw(w); +#endif + + return FALSE; +} + +static void +button1(GntWidget *widget, gpointer null) +{ + printf("OLAAA"); + gnt_widget_destroy(null); +} + +static void +button2(GntWidget *widget, gpointer null) +{ + printf("BOOYAA"); +} + +static gboolean +w_scroll(GntWidget *tree) +{ + g_return_val_if_fail(GNT_IS_TREE(tree), FALSE); + gnt_tree_scroll(GNT_TREE(tree), 1); + /*wscrl(tree->window, 1);*/ + /*box(tree->window, ACS_VLINE, ACS_HLINE);*/ + /*wrefresh(tree->window);*/ + /*char *s = 0;*/ + /**s = 'a';*/ + return TRUE; +} + +int main() +{ + gnt_init(); + + GntWidget *widget = gnt_button_new("Button 1"); + GntWidget *widget2 = gnt_button_new("Button 2 has a longish text with a UTF-8 thing …"); + GntWidget *label = gnt_label_new("So wassup dudes and dudettes!!\nSo this is, like,\nthe third line!! \\o/"); + GntWidget *vbox, *hbox, *tree; + WINDOW *test; + + box(stdscr, 0, 0); + wrefresh(stdscr); + + vbox = gnt_box_new(FALSE, FALSE); + hbox = gnt_box_new(FALSE, TRUE); + + gnt_widget_set_name(vbox, "vbox"); + gnt_widget_set_name(hbox, "hbox"); + gnt_widget_set_name(widget, "widget"); + gnt_widget_set_name(widget2, "widget2"); + + gnt_box_add_widget(GNT_BOX(vbox), widget); + gnt_box_add_widget(GNT_BOX(vbox), widget2); + + gnt_box_add_widget(GNT_BOX(hbox), label); + /*gnt_box_add_widget(GNT_BOX(hbox), vbox);*/ + + gnt_box_add_widget(GNT_BOX(hbox), gnt_entry_new("a")); + + tree = gnt_tree_new(); + gnt_box_add_widget(GNT_BOX(hbox), tree); + + gnt_tree_add_row_after(GNT_TREE(tree), "a", "a", NULL, NULL); + gnt_tree_add_row_after(GNT_TREE(tree), "c", "c", NULL, NULL); + gnt_tree_add_row_after(GNT_TREE(tree), "d", "d", NULL, NULL); + gnt_tree_add_row_after(GNT_TREE(tree), "e", "e", "a", NULL); + gnt_tree_add_row_after(GNT_TREE(tree), "b", "b", "d", NULL); + + GNT_WIDGET_UNSET_FLAGS(hbox, GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW); + gnt_box_set_title(GNT_BOX(hbox), "111111111111111111111111111111111111111111111111111111111111111This is the title …"); + + /*gnt_widget_set_take_focus(vbox, TRUE);*/ + /*gnt_widget_set_take_focus(hbox, TRUE);*/ + /*gnt_widget_set_position(hbox, 10, 10);*/ + + gnt_widget_show(hbox); + + g_signal_connect(hbox, "key_pressed", G_CALLBACK(key_pressed), tree); + g_signal_connect(widget, "activate", G_CALLBACK(button1), hbox); + g_signal_connect(widget2, "activate", G_CALLBACK(button2), hbox); + + /*g_timeout_add(1000, (GSourceFunc)w_scroll, tree);*/ + + gnt_main(); + + return 0; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/test/Makefile Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,16 @@ +CC=gcc +CFLAGS=`pkg-config --cflags gobject-2.0 gmodule-2.0` -g -I../ -DSTANDALONE +LDFLAGS=`pkg-config --libs gobject-2.0 gmodule-2.0 gnt` -pg + +EXAMPLES=combo focus tv multiwin keys menu + +all: + make examples + +clean: + rm -f $(EXAMPLES) *.so wm + +WM: wm + for i in $(EXAMPLES); do gcc -shared $(CFLAGS) -USTANDALONE $(LDFLAGS) $${i}.c -o $${i}.so ; done + +examples: $(EXAMPLES)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/test/combo.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,82 @@ +#include <gnt.h> +#include <gntbox.h> +#include <gntbutton.h> +#include <gntcheckbox.h> +#include <gntcombobox.h> +#include <gntlabel.h> + +static void +button_activated(GntWidget *b, GntComboBox *combo) +{ + GntWidget *w = b->parent; + + gnt_box_add_widget(GNT_BOX(w), + gnt_label_new(gnt_combo_box_get_selected_data(GNT_COMBO_BOX(combo)))); + fprintf(stderr, "%s\n", gnt_combo_box_get_selected_data(GNT_COMBO_BOX(combo))); + gnt_box_readjust(GNT_BOX(w->parent)); +} + +int main() +{ + GntWidget *box, *combo, *button; + GntWidget *hbox; + +#ifdef STANDALONE + freopen(".error", "w", stderr); + gnt_init(); +#endif + + box = gnt_box_new(FALSE, TRUE); + gnt_widget_set_name(box, "box"); + gnt_box_set_alignment(GNT_BOX(box), GNT_ALIGN_MID); + gnt_box_set_pad(GNT_BOX(box), 0); + + gnt_box_set_toplevel(GNT_BOX(box), TRUE); + gnt_box_set_title(GNT_BOX(box), "Checkbox"); + + hbox = gnt_box_new(FALSE, FALSE); + gnt_box_set_pad(GNT_BOX(hbox), 0); + gnt_box_set_alignment(GNT_BOX(hbox), GNT_ALIGN_MID); + gnt_widget_set_name(hbox, "upper"); + + combo = gnt_combo_box_new(); + gnt_combo_box_add_data(GNT_COMBO_BOX(combo), "1", "1"); + gnt_combo_box_add_data(GNT_COMBO_BOX(combo), "2", "2"); + gnt_combo_box_add_data(GNT_COMBO_BOX(combo), "3", "3abcdefghijklmnopqrstuvwxyz"); + gnt_combo_box_add_data(GNT_COMBO_BOX(combo), "4", "4"); + gnt_combo_box_add_data(GNT_COMBO_BOX(combo), "5", "5"); + gnt_combo_box_add_data(GNT_COMBO_BOX(combo), "6", "6"); + gnt_combo_box_add_data(GNT_COMBO_BOX(combo), "7", "7"); + gnt_combo_box_add_data(GNT_COMBO_BOX(combo), "8", "8"); + gnt_combo_box_add_data(GNT_COMBO_BOX(combo), "9", "9"); + + GntWidget *l = gnt_label_new("Select"); + gnt_box_add_widget(GNT_BOX(hbox), l); + gnt_widget_set_size(l, 0, 1); + gnt_box_add_widget(GNT_BOX(hbox), combo); + + gnt_box_add_widget(GNT_BOX(box), hbox); + + hbox = gnt_box_new(TRUE, FALSE); + gnt_box_set_alignment(GNT_BOX(hbox), GNT_ALIGN_MID); + gnt_widget_set_name(hbox, "lower"); + + button = gnt_button_new("OK"); + gnt_box_add_widget(GNT_BOX(hbox), button); + g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(button_activated), combo); + + gnt_box_add_widget(GNT_BOX(box), hbox); + + gnt_box_add_widget(GNT_BOX(box), gnt_check_box_new("check box")); + + gnt_widget_show(box); + +#ifdef STANDALONE + gnt_main(); + + gnt_quit(); +#endif + + return 0; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/test/focus.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,92 @@ +#include "gntbutton.h" +#include "gnt.h" +#include "gntkeys.h" +#include "gnttree.h" +#include "gntbox.h" +#include "gntentry.h" +#include "gntlabel.h" + +static void +toggled(GntWidget *tree, gpointer key, gpointer null) +{ + GntWidget *w = gnt_box_new(FALSE, FALSE); + + gnt_box_set_toplevel(GNT_BOX(w), TRUE); + + gnt_box_add_widget(GNT_BOX(w), + gnt_label_new(gnt_tree_get_choice(GNT_TREE(tree), key) ? "Selected" : "NOT")); + gnt_widget_show(w); +} + +int main() +{ +#ifdef STANDALONE + freopen(".error", "w", stderr); + gnt_init(); +#endif + + GntWidget *label = gnt_label_new("So wassup dudes and dudettes!!\u4e0a1\u6d772\u67003\u4f4e4\u67085\nSo this is, like,\nthe third line!! \\o/"); + GntWidget *vbox, *hbox, *tree, *box, *button; + WINDOW *test; + + vbox = gnt_box_new(FALSE, FALSE); + hbox = gnt_box_new(FALSE, TRUE); + gnt_box_set_alignment(GNT_BOX(hbox), GNT_ALIGN_MID); + + gnt_widget_set_name(vbox, "vbox"); + gnt_widget_set_name(hbox, "hbox"); + + gnt_box_add_widget(GNT_BOX(hbox), label); + + GntWidget *entry = gnt_entry_new("a"); + gnt_widget_set_name(entry, "entry"); + gnt_box_add_widget(GNT_BOX(hbox), entry); + + box = gnt_box_new(FALSE, FALSE); + tree = gnt_tree_new(); + gnt_tree_set_compare_func(GNT_TREE(tree), g_utf8_collate); + gnt_widget_set_name(tree, "tree"); + gnt_box_add_widget(GNT_BOX(box), tree); + gnt_box_add_widget(GNT_BOX(hbox), box); + + gnt_tree_add_row_after(GNT_TREE(tree), "c", gnt_tree_create_row(GNT_TREE(tree), "c"), NULL, NULL); + gnt_tree_add_row_after(GNT_TREE(tree), "a", gnt_tree_create_row(GNT_TREE(tree), "a"), NULL, NULL); + gnt_tree_add_row_after(GNT_TREE(tree), "z", gnt_tree_create_row(GNT_TREE(tree), "z"), "a", NULL); + gnt_tree_add_row_after(GNT_TREE(tree), "y", gnt_tree_create_row(GNT_TREE(tree), "y"), "a", NULL); + gnt_tree_add_row_after(GNT_TREE(tree), "g", gnt_tree_create_row(GNT_TREE(tree), "g"), "a", NULL); + gnt_tree_add_row_after(GNT_TREE(tree), "d", gnt_tree_create_row(GNT_TREE(tree), "d"), NULL, NULL); + gnt_tree_add_row_after(GNT_TREE(tree), "x", gnt_tree_create_row(GNT_TREE(tree), "x"), "a", NULL); + gnt_tree_add_row_after(GNT_TREE(tree), "k", gnt_tree_create_row(GNT_TREE(tree), "k"), "a", NULL); + gnt_tree_add_row_after(GNT_TREE(tree), "e", gnt_tree_create_row(GNT_TREE(tree), "e"), "a", NULL); + gnt_tree_add_choice(GNT_TREE(tree), "b", gnt_tree_create_row(GNT_TREE(tree), "b"), "d", NULL); + + GNT_WIDGET_UNSET_FLAGS(hbox, GNT_WIDGET_NO_BORDER | GNT_WIDGET_NO_SHADOW); + gnt_box_set_title(GNT_BOX(hbox), "\u4e0a\u6d77\u6700\u4f4e\u6708\u5de5 \u4e0a\u6d77\u6700\u4f4e\u6708\u5de5 ……\u4e0a\u6d77\u6700\u4f4e\u6708\u5de5 …"); + + g_signal_connect(G_OBJECT(tree), "toggled", G_CALLBACK(toggled), NULL); + + button = gnt_button_new("one"); + gnt_widget_set_name(button, "one"); + gnt_box_add_widget(GNT_BOX(vbox), button); + + button = gnt_button_new("two"); + gnt_widget_set_name(button, "two"); + gnt_box_add_widget(GNT_BOX(vbox), button); + + button = gnt_button_new("three"); + gnt_widget_set_name(button, "three"); + gnt_box_add_widget(GNT_BOX(vbox), button); + + gnt_box_add_widget(GNT_BOX(hbox), vbox); + + gnt_widget_show(hbox); + +#ifdef STANDALONE + gnt_main(); + + gnt_quit(); +#endif + + return 0; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/test/key.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,18 @@ +#include <ncurses.h> + +int main() +{ + int ch; + initscr(); + + noecho(); + + while ((ch = getch()) != 27) { + printw("%d ", ch); + refresh(); + } + + endwin(); + return 0; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/test/keys.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,60 @@ +#include <gnt.h> +#include <gntbox.h> +#include <gntentry.h> +#include <gntlabel.h> + +static gboolean +print_keycode(GntEntry *entry, const char *text, gpointer null) +{ + char *s = g_strdup_printf("%s ", text); + gnt_entry_set_text(entry, s); + g_free(s); + if (text[0] == 27) + { + if (strncmp(text + 1, "[M ", 3) == 0) + { + int x = (unsigned)text[4]; + int y = (unsigned)text[5]; + if (x < 0) x += 256; + if (y < 0) y += 256; + x -= 33; + y -= 33; + s = g_strdup_printf("ldown %d %d", x, y); + gnt_entry_set_text(entry, s); + g_free(s); + } + else if (strncmp(text + 1, "[M#", 3) == 0) + gnt_entry_set_text(entry, "up"); + else + return FALSE; + return TRUE; + } + else + return TRUE; +} + +int main() +{ + GntWidget *window, *entry; + + gnt_init(); + + freopen(".error", "w", stderr); + + window = gnt_hbox_new(FALSE); + gnt_box_set_toplevel(GNT_BOX(window), TRUE); + + gnt_box_add_widget(GNT_BOX(window), gnt_label_new("Press any key: ")); + + entry = gnt_entry_new(NULL); + gnt_box_add_widget(GNT_BOX(window), entry); + g_signal_connect(G_OBJECT(entry), "key_pressed", G_CALLBACK(print_keycode), NULL); + + gnt_widget_set_position(window, getmaxx(stdscr) / 2 - 12, getmaxy(stdscr) / 2 - 3); + gnt_widget_show(window); + + gnt_main(); + gnt_quit(); + return 0; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/test/menu.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,64 @@ +#include "gnt.h" +#include "gntbox.h" +#include "gntlabel.h" +#include "gntmenu.h" +#include "gntmenuitem.h" +#include "gntwindow.h" + +void dothis(GntMenuItem *item, gpointer null) +{ + GntWidget *w = gnt_vbox_new(FALSE); + gnt_box_set_toplevel(GNT_BOX(w), TRUE); + gnt_box_add_widget(GNT_BOX(w), + gnt_label_new("Callback to a menuitem")); + gnt_widget_show(w); +} + +int main() +{ + freopen(".error", "w", stderr); + gnt_init(); + + GntWidget *menu = gnt_menu_new(GNT_MENU_TOPLEVEL); + GObject *item = gnt_menuitem_new("File"); + + gnt_menu_add_item(GNT_MENU(menu), GNT_MENUITEM(item)); + + item = gnt_menuitem_new("Edit"); + gnt_menu_add_item(GNT_MENU(menu), GNT_MENUITEM(item)); + + item = gnt_menuitem_new("Help"); + gnt_menu_add_item(GNT_MENU(menu), GNT_MENUITEM(item)); + + GntWidget *sub = gnt_menu_new(GNT_MENU_POPUP); + gnt_menuitem_set_submenu(GNT_MENUITEM(item), GNT_MENU(sub)); + + item = gnt_menuitem_new("Online Help"); + gnt_menu_add_item(GNT_MENU(sub), GNT_MENUITEM(item)); + + item = gnt_menuitem_new("About"); + gnt_menu_add_item(GNT_MENU(sub), GNT_MENUITEM(item)); + + sub = gnt_menu_new(GNT_MENU_POPUP); + gnt_menuitem_set_submenu(GNT_MENUITEM(item), GNT_MENU(sub)); + + item = gnt_menuitem_new("Online Help"); + gnt_menu_add_item(GNT_MENU(sub), GNT_MENUITEM(item)); + gnt_menuitem_set_callback(GNT_MENUITEM(item), dothis, NULL); + + gnt_screen_menu_show(menu); + + GntWidget *win = gnt_window_new(); + gnt_box_add_widget(GNT_BOX(win), + gnt_label_new("...")); + gnt_box_set_title(GNT_BOX(win), "Title"); + gnt_window_set_menu(GNT_WINDOW(win), GNT_MENU(menu)); + gnt_widget_show(win); + + gnt_main(); + + gnt_quit(); + + return 0; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/test/multiwin.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,87 @@ +#include "gnt.h" +#include "gntbutton.h" +#include "gntentry.h" +#include "gntkeys.h" +#include "gntlabel.h" +#include "gnttree.h" +#include "gntbox.h" + +gboolean show(GntWidget *w) +{ + return FALSE; +} + +int main() +{ +#ifdef STANDALONE + freopen(".error", "w", stderr); + gnt_init(); +#endif + + GntWidget *hbox, *tree, *box2; + + hbox = gnt_box_new(FALSE, TRUE); + box2 = gnt_box_new(FALSE, TRUE); + + gnt_widget_set_name(hbox, "hbox"); + gnt_widget_set_name(box2, "box2"); + + tree = gnt_tree_new_with_columns(3); + GNT_WIDGET_SET_FLAGS(tree, GNT_WIDGET_NO_BORDER); + gnt_tree_set_column_titles(GNT_TREE(tree), "12345678901234567890", "column 2", "column3"); + gnt_tree_set_show_title(GNT_TREE(tree), TRUE); + gnt_widget_set_name(tree, "tree"); + gnt_box_add_widget(GNT_BOX(hbox), tree); + + gnt_box_set_toplevel(GNT_BOX(hbox), TRUE); + gnt_box_set_title(GNT_BOX(hbox), "Testing the tree widget"); + + gnt_box_set_toplevel(GNT_BOX(box2), TRUE); + gnt_box_set_title(GNT_BOX(box2), "On top"); + + gnt_box_add_widget(GNT_BOX(box2), gnt_label_new("asdasd")); + gnt_box_add_widget(GNT_BOX(box2), gnt_entry_new(NULL)); + + gnt_widget_show(hbox); + gnt_widget_set_position(box2, 80, 40); + gnt_widget_show(box2); + + gnt_tree_add_row_after(GNT_TREE(tree), "a", + gnt_tree_create_row(GNT_TREE(tree), "alaskdjfkashfashfah kfalkdhflsiafhlasf", " long text", "a2"), NULL, NULL); + gnt_tree_add_row_after(GNT_TREE(tree), "c", + gnt_tree_create_row(GNT_TREE(tree), "casdgertqhyeqgasfeytwfga fg arf agfwa ", " long text", "a2"), NULL, NULL); + gnt_tree_add_row_after(GNT_TREE(tree), "d", gnt_tree_create_row(GNT_TREE(tree), "d", " long text", "a2"), NULL, NULL); + gnt_tree_add_row_after(GNT_TREE(tree), "e", gnt_tree_create_row(GNT_TREE(tree), "e", " long text", "a2"), "a", NULL); + gnt_tree_add_row_after(GNT_TREE(tree), "b", gnt_tree_create_row(GNT_TREE(tree), "b", "this is", "a2"), "d", NULL); + + gnt_tree_add_choice(GNT_TREE(tree), "1", gnt_tree_create_row(GNT_TREE(tree), "1", " long text", "a2"), NULL, NULL); + gnt_tree_add_row_after(GNT_TREE(tree), "2", gnt_tree_create_row(GNT_TREE(tree), "2", " long text", "a2"), NULL, NULL); + gnt_tree_add_row_after(GNT_TREE(tree), "3", gnt_tree_create_row(GNT_TREE(tree), "3", " long text", "a2"), NULL, NULL); + gnt_tree_add_row_after(GNT_TREE(tree), "4", gnt_tree_create_row(GNT_TREE(tree), "4", " long text", "a2"), "a", NULL); + gnt_tree_add_row_after(GNT_TREE(tree), "5", gnt_tree_create_row(GNT_TREE(tree), "5", " long text", "a2"), "d", NULL); + + gnt_tree_add_row_after(GNT_TREE(tree), "6", gnt_tree_create_row(GNT_TREE(tree), "6", " long text", "a2"), "4", NULL); + + int i; + for (i = 110; i < 430; i++) + { + char *s; + s = g_strdup_printf("%d", i); /* XXX: yes, leaking */ + gnt_tree_add_row_after(GNT_TREE(tree), s, gnt_tree_create_row(GNT_TREE(tree), s, " long text", "a2"), "4", NULL); + } + + gnt_tree_set_row_flags(GNT_TREE(tree), "e", GNT_TEXT_FLAG_DIM); + + gnt_tree_set_selected(GNT_TREE(tree), "2"); + + g_timeout_add(5000, (GSourceFunc)show, box2); + +#ifdef STANDALONE + gnt_main(); + + gnt_quit(); +#endif + + return 0; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/test/tv.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,106 @@ +#include "gntbutton.h" +#include "gnt.h" +#include "gntkeys.h" +#include "gnttree.h" +#include "gntbox.h" +#include "gntentry.h" +#include "gnttextview.h" + +static gboolean +key_pressed(GntWidget *w, const char *key, GntWidget *view) +{ + if (key[0] == '\r' && key[1] == 0) + { + gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), + gnt_entry_get_text(GNT_ENTRY(w)), + GNT_TEXT_FLAG_UNDERLINE | GNT_TEXT_FLAG_HIGHLIGHT); + gnt_entry_add_to_history(GNT_ENTRY(w), gnt_entry_get_text(GNT_ENTRY(w))); + gnt_text_view_next_line(GNT_TEXT_VIEW(view)); + gnt_entry_clear(GNT_ENTRY(w)); + if (gnt_text_view_get_lines_below(GNT_TEXT_VIEW(view)) <= 1) + gnt_text_view_scroll(GNT_TEXT_VIEW(view), 0); + gnt_entry_remove_suggest(GNT_ENTRY(w), "acb"); + + return TRUE; + } + else if (key[0] == 27) + { + if (strcmp(key, GNT_KEY_UP) == 0) + gnt_text_view_scroll(GNT_TEXT_VIEW(view), -1); + else if (strcmp(key, GNT_KEY_DOWN) == 0) + gnt_text_view_scroll(GNT_TEXT_VIEW(view), 1); + else + return FALSE; + return TRUE; + } + + return FALSE; +} + +int main() +{ + GntWidget *hbox, *entry, *view; + +#ifdef STANDALONE + freopen(".error", "w", stderr); + + gnt_init(); +#endif + + hbox = gnt_box_new(FALSE, TRUE); + gnt_widget_set_name(hbox, "hbox"); + gnt_box_set_toplevel(GNT_BOX(hbox), TRUE); + gnt_box_set_fill(GNT_BOX(hbox), FALSE); + gnt_box_set_title(GNT_BOX(hbox), "Textview test"); + gnt_box_set_alignment(GNT_BOX(hbox), GNT_ALIGN_MID); + + entry = gnt_entry_new(NULL); + gnt_widget_set_name(entry, "entry"); + GNT_WIDGET_SET_FLAGS(entry, GNT_WIDGET_CAN_TAKE_FOCUS); + + gnt_entry_set_word_suggest(GNT_ENTRY(entry), TRUE); + gnt_entry_set_always_suggest(GNT_ENTRY(entry), FALSE); + gnt_entry_add_suggest(GNT_ENTRY(entry), "a"); + gnt_entry_add_suggest(GNT_ENTRY(entry), "ab"); + gnt_entry_add_suggest(GNT_ENTRY(entry), "abe"); + gnt_entry_add_suggest(GNT_ENTRY(entry), "abc"); + gnt_entry_add_suggest(GNT_ENTRY(entry), "abcde"); + gnt_entry_add_suggest(GNT_ENTRY(entry), "abcd"); + gnt_entry_add_suggest(GNT_ENTRY(entry), "acb"); + + view = gnt_text_view_new(); + gnt_widget_set_name(view, "view"); + + gnt_widget_set_size(view, 20, 15); + gnt_widget_set_size(entry, 20, 1); + + gnt_box_add_widget(GNT_BOX(hbox), view); + gnt_box_add_widget(GNT_BOX(hbox), entry); + gnt_box_add_widget(GNT_BOX(hbox), gnt_button_new("OK")); + + gnt_widget_show(hbox); + + gnt_entry_set_history_length(GNT_ENTRY(entry), -1); + g_signal_connect_after(G_OBJECT(entry), "key_pressed", G_CALLBACK(key_pressed), view); + + gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "plugins: ", GNT_TEXT_FLAG_BOLD); + gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "this is the 1st line\n", GNT_TEXT_FLAG_NORMAL); + + gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "plugins: ", GNT_TEXT_FLAG_BOLD); + gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "this is the 2nd line\n", GNT_TEXT_FLAG_NORMAL); + + gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "plugins: ", GNT_TEXT_FLAG_BOLD); + gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "this is the 3rd line\n", GNT_TEXT_FLAG_NORMAL); + + gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "plugins: ", GNT_TEXT_FLAG_BOLD); + gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(view), "this is the 4th line\n", GNT_TEXT_FLAG_NORMAL); + +#ifdef STANDALONE + gnt_main(); + + gnt_quit(); +#endif + + return 0; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/test/wm.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,67 @@ +#include <gmodule.h> + +#include <gnt.h> +#include <gntbox.h> +#include <gntentry.h> +#include <gntlabel.h> + +static gboolean +key_pressed(GntEntry *entry, const char *text, gpointer null) +{ + if (*text != '\r') + return FALSE; + + { + const char *cmd; + void *handle; + void (*func)(); + + cmd = gnt_entry_get_text(entry); + handle = g_module_open(cmd, G_MODULE_BIND_LOCAL); + if (handle && g_module_symbol(handle, "main", (gpointer)&func)) + { + char *argv[] = {cmd, NULL}; + gnt_entry_clear(entry); + func(1, argv); + } + else + { + GntWidget *widget = gnt_vbox_new(FALSE); + gnt_box_set_toplevel(GNT_BOX(widget), TRUE); + gnt_box_set_title(GNT_BOX(widget), "Error"); + gnt_box_add_widget(GNT_BOX(widget), gnt_label_new("Could not execute.")); + gnt_box_add_widget(GNT_BOX(widget), gnt_label_new(g_module_error())); + + gnt_widget_show(widget); + } + } + + return TRUE; +} + +int main() +{ + GntWidget *window, *entry; + + freopen(".error", "w", stderr); + + gnt_init(); + + window = gnt_hbox_new(FALSE); + + gnt_box_add_widget(GNT_BOX(window), gnt_label_new("Command")); + + entry = gnt_entry_new(NULL); + g_signal_connect(G_OBJECT(entry), "key_pressed", G_CALLBACK(key_pressed), NULL); + gnt_box_add_widget(GNT_BOX(window), entry); + + gnt_widget_set_position(window, 0, getmaxy(stdscr) - 2); + gnt_widget_show(window); + + gnt_main(); + + gnt_quit(); + + return 0; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/wms/Makefile.am Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,26 @@ +s_la_LDFLAGS = -module -avoid-version + +plugin_LTLIBRARIES = \ + s.la + +plugindir = $(libdir)/gaim + +s_la_SOURCES = s.c +s_la_LIBADD = \ + $(GLIB_LIBS) \ + $(top_builddir)/console/libgnt/libgnt.la \ + $(top_builddir)/libgaim/libgaim.la + +EXTRA_DIST = + +AM_CPPFLAGS = \ + -DDATADIR=\"$(datadir)\" \ + -DVERSION=\"$(VERSION)\" \ + -I$(top_srcdir)/libgaim \ + -I$(top_srcdir)/console \ + -I$(top_srcdir)/console/libgnt \ + $(DEBUG_CFLAGS) \ + $(GLIB_CFLAGS) \ + $(GNT_CFLAGS) \ + $(PLUGIN_CFLAGS) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/libgnt/wms/s.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,216 @@ +#include "gnt.h" +#include "gntbox.h" +#include "gntmenu.h" +#include "gntstyle.h" +#include "gntwm.h" + +#include "blist.h" + +#include <string.h> + +#define TYPE_S (s_get_gtype()) + +typedef struct _S +{ + GntWM inherit; +} S; + +typedef struct _SClass +{ + GntWMClass inherit; +} SClass; + +GType s_get_gtype(void); +void gntwm_init(GntWM **wm); + +static void (*org_new_window)(GntWM *wm, GntWidget *win); + +static void +envelope_buddylist(GntWidget *win) +{ + int w, h; + gnt_widget_get_size(win, &w, &h); + wresize(win->window, h, w + 1); + mvwvline(win->window, 0, w, ACS_VLINE | COLOR_PAIR(GNT_COLOR_NORMAL), h); + touchwin(win->window); +} + +static void +envelope_normal_window(GntWidget *win) +{ + int w, h; + + if (GNT_WIDGET_IS_FLAG_SET(win, GNT_WIDGET_NO_BORDER | GNT_WIDGET_TRANSIENT)) + return; + + gnt_widget_get_size(win, &w, &h); + wbkgdset(win->window, ' ' | COLOR_PAIR(GNT_COLOR_NORMAL)); + mvwprintw(win->window, 0, w - 4, "[X]"); +} + +static void +s_decorate_window(GntWM *wm, GntWidget *win) +{ + const char *name; + + name = gnt_widget_get_name(win); + if (name && strcmp(name, "buddylist") == 0) { + envelope_buddylist(win); + } else { + envelope_normal_window(win); + } +} + +static void +s_window_update(GntWM *wm, GntNode *node) +{ + s_decorate_window(wm, node->me); +} + +static void +s_new_window(GntWM *wm, GntWidget *win) +{ + int x, y, w, h; + int maxx, maxy; + const char *name; + gboolean blist = FALSE; + + if (!GNT_IS_MENU(win)) { + getmaxyx(stdscr, maxy, maxx); + + gnt_widget_get_position(win, &x, &y); + gnt_widget_get_size(win, &w, &h); + + name = gnt_widget_get_name(win); + + if (name && strcmp(name, "buddylist") == 0) { + /* The buddylist doesn't have no border nor nothing! */ + x = 0; + y = 0; + h = maxy - 1; + blist = TRUE; + + gnt_box_set_toplevel(GNT_BOX(win), FALSE); + GNT_WIDGET_SET_FLAGS(win, GNT_WIDGET_CAN_TAKE_FOCUS); + + gnt_widget_set_position(win, x, y); + mvwin(win->window, y, x); + + gnt_widget_set_size(win, -1, h + 2); /* XXX: Why is the +2 needed here? -- sadrul */ + } else if (!GNT_WIDGET_IS_FLAG_SET(win, GNT_WIDGET_TRANSIENT)) { + const char *title = GNT_BOX(win)->title; + if (title == NULL || !g_hash_table_lookup(wm->positions, title)) { + /* In the middle of the screen */ + x = (maxx - w) / 2; + y = (maxy - h) / 2; + + gnt_widget_set_position(win, x, y); + mvwin(win->window, y, x); + } + } + } + org_new_window(wm, win); + + if (blist) + gnt_wm_raise_window(wm, win); +} + +static GntWidget * +find_widget(GntWM *wm, const char *wname) +{ + const GList *iter = wm->list; + for (; iter; iter = iter->next) { + GntWidget *widget = iter->data; + const char *name = gnt_widget_get_name(widget); + if (name && strcmp(name, wname) == 0) { + return widget; + } + } + return NULL; +} + +static gboolean +s_mouse_clicked(GntWM *wm, GntMouseEvent event, int cx, int cy, GntWidget *widget) +{ + int x, y, w, h; + + if (!widget) + return FALSE; + /* This might be a place to bring up a context menu */ + + if (event != GNT_LEFT_MOUSE_DOWN || + GNT_WIDGET_IS_FLAG_SET(widget, GNT_WIDGET_NO_BORDER)) + return FALSE; + + gnt_widget_get_position(widget, &x, &y); + gnt_widget_get_size(widget, &w, &h); + + if (cy == y && cx == x + w - 3) { + gnt_widget_destroy(widget); + return TRUE; + } + + return FALSE; +} + +static gboolean +toggle_buddylist(GntBindable *bindable, GList *null) +{ + GntWM *wm = GNT_WM(bindable); + GntWidget *blist = find_widget(wm, "buddylist"); + if (blist) + gnt_widget_destroy(blist); + else + gaim_blist_show(); + return TRUE; +} + +static void +s_class_init(SClass *klass) +{ + GntWMClass *pclass = GNT_WM_CLASS(klass); + + org_new_window = pclass->new_window; + + pclass->new_window = s_new_window; + pclass->decorate_window = s_decorate_window; + pclass->window_update = s_window_update; + pclass->mouse_clicked = s_mouse_clicked; + + gnt_bindable_class_register_action(GNT_BINDABLE_CLASS(klass), "toggle-buddylist", + toggle_buddylist, "\033" "b", NULL); + gnt_style_read_actions(G_OBJECT_CLASS_TYPE(klass), GNT_BINDABLE_CLASS(klass)); + GNTDEBUG; +} + +void gntwm_init(GntWM **wm) +{ + *wm = g_object_new(TYPE_S, NULL); +} + +GType s_get_gtype(void) +{ + static GType type = 0; + + if(type == 0) { + static const GTypeInfo info = { + sizeof(SClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc)s_class_init, + NULL, + NULL, /* class_data */ + sizeof(S), + 0, /* n_preallocs */ + NULL, /* instance_init */ + NULL + }; + + type = g_type_register_static(GNT_TYPE_WM, + "GntS", + &info, 0); + } + + return type; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/plugins/Makefile.am Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,51 @@ +gntgf_la_LDFLAGS = -module -avoid-version +gnthistory_la_LDFLAGS = -module -avoid-version +gntlastlog_la_LDFLAGS = -module -avoid-version + +if PLUGINS + +plugin_LTLIBRARIES = \ + gntgf.la \ + gnthistory.la \ + gntlastlog.la + +plugindir = $(libdir)/gaim + +gntgf_la_SOURCES = gntgf.c +gnthistory_la_SOURCES = gnthistory.c +gntlastlog_la_SOURCES = lastlog.c + +gntgf_la_CFLAGS = $(X11_CFLAGS) + +gntgf_la_LIBADD = $(GLIB_LIBS) $(X11_LIBS) $(top_builddir)/console/libgnt/libgnt.la +gnthistory_la_LIBADD = $(GLIB_LIBS) +gntlastlog_la_LIBADD = $(GLIB_LIBS) + +endif # PLUGINS + +EXTRA_DIST = + +AM_CPPFLAGS = \ + -DDATADIR=\"$(datadir)\" \ + -DVERSION=\"$(VERSION)\" \ + -I$(top_builddir)/libgaim \ + -I$(top_srcdir)/libgaim \ + -I$(top_srcdir) \ + -I$(top_srcdir)/console \ + -I$(top_srcdir)/console/libgnt \ + $(DEBUG_CFLAGS) \ + $(GLIB_CFLAGS) \ + $(GNT_CFLAGS) \ + $(PLUGIN_CFLAGS) + +# +# This part allows people to build their own plugins in here. +# Yes, it's a mess. +# +SUFFIXES = .c .so +.c.so: + $(LIBTOOL) --mode=compile $(CC) -DHAVE_CONFIG_H -I$(top_srcdir) $(AM_CPPFLAGS) $(CFLAGS) -c $< -o tmp$@.lo $(PLUGIN_CFLAGS) + $(LIBTOOL) --mode=link $(CC) $(CFLAGS) -o libtmp$@.la -rpath $(plugindir) tmp$@.lo $(LIBS) $(LDFLAGS) -module -avoid-version $(PLUGIN_LIBS) + @rm -f tmp$@.lo tmp$@.o libtmp$@.la + @cp .libs/libtmp$@.so* $@ + @rm -f .libs/libtmp$@.*
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/plugins/gntgf.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,390 @@ +/** + * @file gntgf.c Minimal toaster plugin in Gnt. + * + * Copyright (C) 2006 Sadrul Habib Chowdhury <sadrul@users.sourceforge.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "internal.h" + +#define PLUGIN_STATIC_NAME "GntGf" + +#define PREFS_PREFIX "/plugins/gnt/gntgf" +#define PREFS_EVENT PREFS_PREFIX "/events" +#define PREFS_EVENT_SIGNONF PREFS_EVENT "/signonf" +#define PREFS_EVENT_IM_MSG PREFS_EVENT "/immsg" +#define PREFS_EVENT_CHAT_MSG PREFS_EVENT "/chatmsg" +#define PREFS_EVENT_CHAT_NICK PREFS_EVENT "/chatnick" +#define PREFS_BEEP PREFS_PREFIX "/beep" + +#define MAX_COLS 3 + +#ifdef HAVE_X11 +#define PREFS_URGENT PREFS_PREFIX "/urgent" + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#endif + +#include <glib.h> + +#include <plugin.h> +#include <version.h> +#include <blist.h> +#include <conversation.h> +#include <debug.h> +#include <util.h> + +#include <gnt.h> +#include <gntbox.h> +#include <gntbutton.h> +#include <gntcheckbox.h> +#include <gntlabel.h> +#include <gnttree.h> + +#include <gntplugin.h> + +typedef struct +{ + GntWidget *window; + int timer; + int column; +} GntToast; + +static GList *toasters; +static int gpsy[MAX_COLS]; +static int gpsw[MAX_COLS]; + +static void +destroy_toaster(GntToast *toast) +{ + toasters = g_list_remove(toasters, toast); + gnt_widget_destroy(toast->window); + g_source_remove(toast->timer); + g_free(toast); +} + +static gboolean +remove_toaster(GntToast *toast) +{ + GList *iter; + int h; + int col; + int nwin[MAX_COLS]; + + gnt_widget_get_size(toast->window, NULL, &h); + gpsy[toast->column] -= h; + col = toast->column; + + memset(&nwin, 0, sizeof(nwin)); + destroy_toaster(toast); + + for (iter = toasters; iter; iter = iter->next) + { + int x, y; + toast = iter->data; + nwin[toast->column]++; + if (toast->column != col) continue; + gnt_widget_get_position(toast->window, &x, &y); + y += h; + gnt_screen_move_widget(toast->window, x, y); + } + + if (nwin[col] == 0) + gpsw[col] = 0; + + return FALSE; +} + +#ifdef HAVE_X11 +static void +urgent() +{ + /* This is from deryni/tuomov's urgent_test.c */ + Display *dpy; + Window id; + const char *ids; + XWMHints *hints; + + ids = getenv("WINDOWID"); + if (ids == NULL) + return; + + id = atoi(ids); + + dpy = XOpenDisplay(NULL); + if (dpy == NULL) + return; + + hints = XGetWMHints(dpy, id); + hints->flags|=XUrgencyHint; + XSetWMHints(dpy, id, hints); + + XFlush(dpy); + XCloseDisplay(dpy); +} +#endif + +static void +notify(const char *fmt, ...) +{ + GntWidget *window; + GntToast *toast; + char *str; + int h, w, i; + va_list args; + + if (gaim_prefs_get_bool(PREFS_BEEP)) + beep(); +#ifdef HAVE_X11 + if (gaim_prefs_get_bool(PREFS_URGENT)) + urgent(); +#endif + + window = gnt_vbox_new(FALSE); + GNT_WIDGET_SET_FLAGS(window, GNT_WIDGET_TRANSIENT); + GNT_WIDGET_UNSET_FLAGS(window, GNT_WIDGET_NO_BORDER); + + va_start(args, fmt); + str = g_strdup_vprintf(fmt, args); + va_end(args); + + gnt_box_add_widget(GNT_BOX(window), + gnt_label_new_with_format(str, GNT_TEXT_FLAG_HIGHLIGHT)); + + g_free(str); + gnt_widget_size_request(window); + gnt_widget_get_size(window, &w, &h); + for (i = 0; i < MAX_COLS && gpsy[i] + h >= getmaxy(stdscr) ; ++i) + ; + if (i >= MAX_COLS) { + gaim_debug_warning("GntGf", "Dude, that's way too many popups\n"); + gnt_widget_destroy(window); + return; + } + + toast = g_new0(GntToast, 1); + toast->window = window; + toast->column = i; + gpsy[i] += h; + if (w > gpsw[i]) { + if (i == 0) + gpsw[i] = w; + else + gpsw[i] = gpsw[i - 1] + w + 1; + } + + if (i == 0 || (w + gpsw[i - 1] >= getmaxx(stdscr))) { + /* if it's going to be too far left, overlap. */ + gnt_widget_set_position(window, getmaxx(stdscr) - w - 1, + getmaxy(stdscr) - gpsy[i] - 1); + } else { + gnt_widget_set_position(window, getmaxx(stdscr) - gpsw[i - 1] - w - 1, + getmaxy(stdscr) - gpsy[i] - 1); + } + gnt_widget_draw(window); + + toast->timer = g_timeout_add(4000, (GSourceFunc)remove_toaster, toast); + toasters = g_list_prepend(toasters, toast); +} + +static void +buddy_signed_on(GaimBuddy *buddy, gpointer null) +{ + if (gaim_prefs_get_bool(PREFS_EVENT_SIGNONF)) + notify(_("%s just signed on"), gaim_buddy_get_alias(buddy)); +} + +static void +buddy_signed_off(GaimBuddy *buddy, gpointer null) +{ + if (gaim_prefs_get_bool(PREFS_EVENT_SIGNONF)) + notify(_("%s just signed off"), gaim_buddy_get_alias(buddy)); +} + +static void +received_im_msg(GaimAccount *account, const char *sender, const char *msg, + GaimConversation *conv, GaimMessageFlags flags, gpointer null) +{ + if (gaim_prefs_get_bool(PREFS_EVENT_IM_MSG)) + notify(_("%s sent you a message"), sender); +} + +static void +received_chat_msg(GaimAccount *account, const char *sender, const char *msg, + GaimConversation *conv, GaimMessageFlags flags, gpointer null) +{ + const char *nick; + + if (flags & GAIM_MESSAGE_WHISPER) + return; + + nick = GAIM_CONV_CHAT(conv)->nick; + + if (g_utf8_collate(sender, nick) == 0) + return; + + if (gaim_prefs_get_bool(PREFS_EVENT_CHAT_NICK) && + (gaim_utf8_has_word(msg, nick))) + notify(_("%s said your nick in %s"), sender, gaim_conversation_get_name(conv)); + else if (gaim_prefs_get_bool(PREFS_EVENT_CHAT_MSG)) + notify(_("%s sent a message in %s"), sender, gaim_conversation_get_name(conv)); +} + +static gboolean +plugin_load(GaimPlugin *plugin) +{ + gaim_signal_connect(gaim_blist_get_handle(), "buddy-signed-on", plugin, + GAIM_CALLBACK(buddy_signed_on), NULL); + gaim_signal_connect(gaim_blist_get_handle(), "buddy-signed-off", plugin, + GAIM_CALLBACK(buddy_signed_off), NULL); + gaim_signal_connect(gaim_conversations_get_handle(), "received-im-msg", plugin, + GAIM_CALLBACK(received_im_msg), NULL); + gaim_signal_connect(gaim_conversations_get_handle(), "received-chat-msg", plugin, + GAIM_CALLBACK(received_chat_msg), NULL); + + memset(&gpsy, 0, sizeof(gpsy)); + memset(&gpsw, 0, sizeof(gpsw)); + + return TRUE; +} + +static gboolean +plugin_unload(GaimPlugin *plugin) +{ + while (toasters) + { + GntToast *toast = toasters->data; + destroy_toaster(toast); + } + return TRUE; +} + +static struct +{ + char *pref; + char *display; +} prefs[] = +{ + {PREFS_EVENT_SIGNONF, N_("Buddy signs on/off")}, + {PREFS_EVENT_IM_MSG, N_("You receive an IM")}, + {PREFS_EVENT_CHAT_MSG, N_("Someone speaks in a chat")}, + {PREFS_EVENT_CHAT_NICK, N_("Someone says your name in a chat")}, + {NULL, NULL} +}; + +static void +pref_toggled(GntTree *tree, char *key, gpointer null) +{ + gaim_prefs_set_bool(key, gnt_tree_get_choice(tree, key)); +} + +static void +toggle_option(GntCheckBox *check, gpointer str) +{ + gaim_prefs_set_bool(str, gnt_check_box_get_checked(check)); +} + +static GntWidget * +config_frame() +{ + GntWidget *window, *tree, *check; + int i; + + window = gnt_vbox_new(FALSE); + gnt_box_set_pad(GNT_BOX(window), 0); + gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID); + gnt_box_set_fill(GNT_BOX(window), TRUE); + + gnt_box_add_widget(GNT_BOX(window), + gnt_label_new(_("Notify with a toaster when"))); + + tree = gnt_tree_new(); + gnt_box_add_widget(GNT_BOX(window), tree); + + for (i = 0; prefs[i].pref; i++) + { + gnt_tree_add_choice(GNT_TREE(tree), prefs[i].pref, + gnt_tree_create_row(GNT_TREE(tree), prefs[i].display), NULL, NULL); + gnt_tree_set_choice(GNT_TREE(tree), prefs[i].pref, + gaim_prefs_get_bool(prefs[i].pref)); + } + gnt_tree_set_col_width(GNT_TREE(tree), 0, 40); + g_signal_connect(G_OBJECT(tree), "toggled", G_CALLBACK(pref_toggled), NULL); + + check = gnt_check_box_new(_("Beep too!")); + gnt_check_box_set_checked(GNT_CHECK_BOX(check), gaim_prefs_get_bool(PREFS_BEEP)); + g_signal_connect(G_OBJECT(check), "toggled", G_CALLBACK(toggle_option), PREFS_BEEP); + gnt_box_add_widget(GNT_BOX(window), check); + +#ifdef HAVE_X11 + check = gnt_check_box_new(_("Set URGENT for the terminal window.")); + gnt_check_box_set_checked(GNT_CHECK_BOX(check), gaim_prefs_get_bool(PREFS_URGENT)); + g_signal_connect(G_OBJECT(check), "toggled", G_CALLBACK(toggle_option), PREFS_URGENT); + gnt_box_add_widget(GNT_BOX(window), check); +#endif + + return window; +} + +static GaimPluginInfo info = +{ + GAIM_PLUGIN_MAGIC, + GAIM_MAJOR_VERSION, + GAIM_MINOR_VERSION, + GAIM_PLUGIN_STANDARD, + GAIM_GNT_PLUGIN_TYPE, + 0, + NULL, + GAIM_PRIORITY_DEFAULT, + "gntgf", + N_("GntGf"), + VERSION, + N_("Toaster plugin for Gaim-Text."), + N_("Toaster plugin for Gaim-Text."), + "Sadrul H Chowdhury <sadrul@users.sourceforge.net>", + "http://gaim.sourceforge.net", + plugin_load, + plugin_unload, + NULL, + config_frame, + NULL, + NULL, + NULL +}; + +static void +init_plugin(GaimPlugin *plugin) +{ + gaim_prefs_add_none("/plugins"); + gaim_prefs_add_none("/plugins/gnt"); + + gaim_prefs_add_none("/plugins/gnt/gntgf"); + gaim_prefs_add_none(PREFS_EVENT); + + gaim_prefs_add_bool(PREFS_EVENT_SIGNONF, TRUE); + gaim_prefs_add_bool(PREFS_EVENT_IM_MSG, TRUE); + gaim_prefs_add_bool(PREFS_EVENT_CHAT_MSG, TRUE); + gaim_prefs_add_bool(PREFS_EVENT_CHAT_NICK, TRUE); + + gaim_prefs_add_bool(PREFS_BEEP, TRUE); +#ifdef HAVE_X11 + gaim_prefs_add_bool(PREFS_URGENT, FALSE); +#endif +} + +GAIM_INIT_PLUGIN(PLUGIN_STATIC_NAME, init_plugin, info)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/console/plugins/gnthistory.c Sun May 20 06:19:49 2007 +0000 @@ -0,0 +1,202 @@ +/** + * @file gnthistory.c Show log from previous conversation + * + * Copyright (C) 2006 Sadrul Habib Chowdhury <sadrul@users.sourceforge.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Ripped from gtk/plugins/history.c */ + +#include "internal.h" + +#include "conversation.h" +#include "debug.h" +#include "log.h" +#include "notify.h" +#include "prefs.h" +#include "signals.h" +#include "util.h" +#include "version.h" + +#include "gntplugin.h" + +#define HISTORY_PLUGIN_ID "gnt-history" + +#define HISTORY_SIZE (4 * 1024) + +static void historize(GaimConversation *c) +{ + GaimAccount *account = gaim_conversation_get_account(c); + const char *name = gaim_conversation_get_name(c); + GaimConversationType convtype; + GList *logs = NULL; + const char *alias = name; + GaimLogReadFlags flags; + char *history; + char *header; + GaimMessageFlags mflag; + + convtype = gaim_conversation_get_type(c); + if (convtype == GAIM_CONV_TYPE_IM) + { + GSList *buddies; + GSList *cur; + + /* If we're not logging, don't show anything. + * Otherwise, we might show a very old log. */ + if (!gaim_prefs_get_bool("/core/logging/log_ims")) + return; + + /* Find buddies for this conversation. */ + buddies = gaim_find_buddies(account, name); + + /* If we found at least one buddy, save the first buddy's alias. */ + if (buddies != NULL) + alias = gaim_buddy_get_contact_alias((GaimBuddy *)buddies->data); + + for (cur = buddies; cur != NULL; cur = cur->next) + { + GaimBlistNode *node = cur->data; + if ((node != NULL) && ((node->prev != NULL) || (node->next != NULL))) + { + GaimBlistNode *node2; + + alias = gaim_buddy_get_contact_alias((GaimBuddy *)node); + + /* We've found a buddy that matches this conversation. It's part of a + * GaimContact with more than one GaimBuddy. Loop through the GaimBuddies + * in the contact and get all the logs. */ + for (node2 = node->parent->child ; node2 != NULL ; node2 = node2->next) + { + logs = g_list_concat( + gaim_log_get_logs(GAIM_LOG_IM, + gaim_buddy_get_name((GaimBuddy *)node2), + gaim_buddy_get_account((GaimBuddy *)node2)), + logs); + } + break; + } + } + g_slist_free(buddies); + + if (logs == NULL) + logs = gaim_log_get_logs(GAIM_LOG_IM, name, account); + else + logs = g_list_sort(logs, gaim_log_compare); + } + else if (convtype == GAIM_CONV_TYPE_CHAT) + { + /* If we're not logging, don't show anything. + * Otherwise, we might show a very old log. */ + if (!gaim_prefs_get_bool("/core/logging/log_chats")) + return; + + logs = gaim_log_get_logs(GAIM_LOG_CHAT, name, account); + } + + if (logs == NULL) + return; + + mflag = GAIM_MESSAGE_NO_LOG | GAIM_MESSAGE_SYSTEM | GAIM_MESSAGE_DELAYED; + history = gaim_log_read((GaimLog*)logs->data, &flags); + + header = g_strdup_printf(_("<b>Conversation with %s on %s:</b><br>"), alias, + gaim_date_format_full(localtime(&((GaimLog *)logs->data)->time))); + gaim_conversation_write(c, "", header, mflag, time(NULL)); + g_free(header); + + if (flags & GAIM_LOG_READ_NO_NEWLINE) + gaim_str_strip_char(history, '\n'); + gaim_conversation_write(c, "", history, mflag, time(NULL)); + g_free(history); + + gaim_conversation_write(c, "", "<hr>", mflag, time(NULL)); + + g_list_foreach(logs, (GFunc)gaim_log_free, NULL); + g_list_free(logs); +} + +static void +history_prefs_check(GaimPlugin *plugin) +{ + if (!gaim_prefs_get_bool("/core/logging/log_ims") && + !gaim_prefs_get_bool("/core/logging/log_chats")) + { + gaim_notify_warning(plugin, NULL, _("History Plugin Requires Logging"), + _("Logging can be enabled from Tools -> Preferences -> Logging.\n\n" + "Enabling logs for instant messages and/or chats will activate " + "history for the same conversation type(s).")); + } +} + +static void history_prefs_cb(const char *name, GaimPrefType type, + gconstpointer val, gpointer data) +{ + history_prefs_check((GaimPlugin *)data); +} + +static gboolean +plugin_load(GaimPlugin *plugin) +{ + gaim_signal_connect(gaim_conversations_get_handle(), + "conversation-created", + plugin, GAIM_CALLBACK(historize), NULL); + + gaim_prefs_connect_callback(plugin, "/core/logging/log_ims", + history_prefs_cb, plugin); + gaim_prefs_connect_callback(plugin, "/core/logging/log_chats", + history_prefs_cb, plugin); + + history_prefs_check(plugin); + + return TRUE; +} + +static GaimPluginInfo info = +{ + GAIM_PLUGIN_MAGIC, + GAIM_MAJOR_VERSION, + GAIM_MINOR_VERSION, + GAIM_PLUGIN_STANDARD, + NULL, + 0, + NULL, + GAIM_PRIORITY_DEFAULT, + HISTORY_PLUGIN_ID, + N_("GntHistory"), + VERSION, + N_("Shows recently logged conversations in new conversations."), + N_("When a new conversation is opened this plugin will insert " + "the last conversation into the current conversation."), + "Sean Egan <seanegan@gmail.com>\n" + "Sadrul H Chowdhury <sadrul@users.sourceforge.net>", + GAIM_WEBSITE, + plugin_load, + NULL, + NULL, + NULL, + NULL, + NULL,